Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
open-source
aoz-003
Commits
18cc9697
Verified
Commit
18cc9697
authored
Mar 27, 2018
by
Markus Koller
🦊
Browse files
Implement new workflow for billing expenses
parent
d1d6d484
Pipeline
#17277
passed with stage
in 52 minutes and 46 seconds
Changes
40
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
.rubocop.yml
View file @
18cc9697
...
...
@@ -22,7 +22,11 @@ Style/LineEndConcatenation:
Enabled
:
false
Style/FrozenStringLiteralComment
:
Enabled
:
false
Style/NumericPredicate
:
Enabled
:
false
Layout/IndentArray
:
EnforcedStyle
:
consistent
Layout/MultilineOperationIndentation
:
EnforcedStyle
:
indented
Layout/MultilineMethodCallIndentation
:
...
...
@@ -40,6 +44,7 @@ Metrics/ClassLength:
Exclude
:
-
'
test/**/*'
Metrics/MethodLength
:
Max
:
15
Exclude
:
-
'
test/**/*'
-
'
db/seeds.rb'
...
...
@@ -63,7 +68,7 @@ Style/ClassAndModuleChildren:
Enabled
:
false
Style/FormatStringToken
:
En
forcedStyle
:
templat
e
En
abled
:
fals
e
Style/SymbolArray
:
EnforcedStyle
:
brackets
...
...
app/assets/javascripts/all_turbolinks_on_load.es6
View file @
18cc9697
...
...
@@ -2,7 +2,7 @@ $(() => $(document).on('turbolinks:render, turbolinks:load', () => {
truncateModal();
dateTimePicker();
conditionalField();
mailings
Select
All
();
tableRow
Select
able
();
volunteerForm();
groupOfferForm();
}));
app/assets/javascripts/application.js
View file @
18cc9697
...
...
@@ -19,4 +19,5 @@
//= require bootstrap-datepicker/locales/bootstrap-datepicker.de.js
//= require cocoon
//= require selectize
//
//= require_tree .
app/assets/javascripts/mailings_select_all.es6
deleted
100644 → 0
View file @
d1d6d484
function mailingsSelectAll() {
$('.reminder-select .click-selecting').on('click', event => {
event.stopPropagation();
const row = $(event.target).closest('tr');
const selectBox = $(`#reminder_mailing_reminder_mailing_volunteers_attributes_${row.data().index}_picked`);
row.toggleClass('mailing-selected', !selectBox.is(':checked'));
selectBox.prop('checked', !selectBox.is(':checked'));
});
const selectedBoxes = $('input.boolean[id$="_picked"]');
selectedBoxes.on('change', ({target, preventDefault}) => {
preventDefault();
target = $(target);
target.prop('checked', !target.is(':checked'));
const row = $(target).closest('tr');
row.toggleClass('mailing-selected', target.is(':checked'));
});
$('.select-all-mailings input[type="checkbox"]').on('change', ({target}) => {
const mailingRows = $('.reminder-select tbody tr');
mailingRows.toggleClass('mailing-selected', target.checked);
selectedBoxes.prop('checked', target.checked);
});
}
app/assets/javascripts/table_row_selectable.es6
0 → 100644
View file @
18cc9697
function tableRowSelectable() {
var rows = $('tr.table-row-selectable');
rows.on('click', event => {
var row = $(event.currentTarget);
var checkbox = row.find(':checkbox:first');
var selected = checkbox.is(':checked');
if (checkbox.is(':disabled')) {
return;
}
if ($(event.target).is(checkbox)) {
selected = !selected;
} else {
checkbox.prop({ checked: !selected });
}
row.toggleClass('success', !selected);
});
// open links in new tabs, don't select when clicking on them
var links = rows.find('a');
links.attr({ target: '_blank' });
links.on('click', event => {
event.stopPropagation();
});
// toggle all rows
$('.table-row-select-all').on('click', function() {
if ($(this).is(':checked')) {
rows.find(':checkbox:not(:checked)').click();
} else {
rows.find(':checkbox:checked').click();
}
});
// restore state on page load
rows.find(':checkbox:checked:not(:disabled)').each(function() {
$(this).closest('tr').addClass('success');
});
}
app/assets/stylesheets/layout/_tables.scss
0 → 100644
View file @
18cc9697
.table
{
.limit-width
{
width
:
5%
;
}
}
.table-scrollable
{
overflow-y
:
auto
;
}
.assignments.find-client
.table-scrollable
{
max-height
:
300px
;
}
.billing-expenses.new
.table-scrollable
{
max-height
:
500px
;
}
app/assets/stylesheets/pages/_list_responses.scss
deleted
100644 → 0
View file @
d1d6d484
.list-responses-table
{
.limit-width
{
width
:
5%
;
}
}
app/assets/stylesheets/pages/_reminder_mailings.scss
View file @
18cc9697
...
...
@@ -3,27 +3,6 @@
min-height
:
30vh
;
}
.select-all-mailings
{
padding-left
:
7px
;
input
{
margin
:
0
5px
0
0
;
}
label
{
font-weight
:
500
;
margin
:
0
;
}
}
.mailing-selected
{
td
{
background-color
:
$brand-success-lighter
;
border-bottom
:
1px
solid
$gray-lighter
;
color
:
$gray-base
;
}
}
.mailing-body-preview
{
background-color
:
$gray-lighter
;
border
:
solid
1px
$gray-lighter
;
...
...
app/controllers/billing_expenses_controller.rb
View file @
18cc9697
class
BillingExpensesController
<
ApplicationController
before_action
:set_billing_expense
,
only:
[
:show
,
:destroy
]
before_action
:set_volunteer
before_action
:set_volunteer
,
only:
[
:index
]
def
index
authorize
BillingExpense
@billing_expenses
=
BillingExpense
.
where
(
volunteer:
@volunteer
)
@q
=
BillingExpense
.
ransack
(
params
[
:q
])
@q
.
sorts
=
[
'created_at desc'
]
if
@q
.
sorts
.
empty?
@billing_expenses
=
@q
.
result
.
page
(
params
[
:page
])
@billing_expenses
=
@billing_expenses
.
where
(
volunteer_id:
@volunteer
.
id
)
if
@volunteer
end
def
show
...
...
@@ -18,21 +23,26 @@ class BillingExpensesController < ApplicationController
end
def
new
@billing_expense
=
BillingExpense
.
new
(
volunteer:
@volunteer
)
@billing_expense
=
BillingExpense
.
new
authorize
@billing_expense
@q
=
Volunteer
.
with_billable_hours
.
ransack
(
params
[
:q
])
@volunteers
=
@q
.
result
@selected_volunteers
=
params
[
:selected_volunteers
].
presence
||
[]
end
def
create
@billing_expense
=
BillingExpense
.
new
(
@volunteer
.
slice
(
:bank
,
:iban
))
@billing_expense
.
hours
=
@volunteer
.
hours
.
billable
@billing_expense
.
volunteer
=
@volunteer
@billing_expense
.
user
=
current_user
authorize
@billing_expense
if
@billing_expense
.
save
redirect_to
volunteer_billing_expenses_url
,
make_notice
else
redirect_to
volunteer_billing_expenses_url
,
notice:
t
(
'already_computed'
)
end
authorize
BillingExpense
,
:create?
selected_volunteers
=
params
[
:selected_volunteers
]
volunteers
=
Volunteer
.
need_refunds
.
where
(
id:
selected_volunteers
)
BillingExpense
.
create_for!
(
volunteers
,
current_user
)
redirect_to
billing_expenses_url
,
notice:
'Spesenformulare wurden erfolgreich erstellt.'
rescue
ActiveRecord
::
RecordInvalid
=>
error
redirect_to
new_billing_expense_url
(
selected_volunteers:
selected_volunteers
),
notice:
error
.
message
end
def
destroy
...
...
@@ -53,7 +63,10 @@ class BillingExpensesController < ApplicationController
def
pdf_file_name
'Spesenauszahlung-'
+
[
@volunteer
.
contact
.
full_name
,
@volunteer
.
hours
.
maximum
(
:meeting_date
)].
join
(
'-'
).
parameterize
[
@billing_expense
.
volunteer
.
contact
.
full_name
,
@billing_expense
.
volunteer
.
hours
.
maximum
(
:meeting_date
)
].
join
(
'-'
).
parameterize
end
def
billing_expense_params
...
...
app/controllers/hours_controller.rb
View file @
18cc9697
class
HoursController
<
ApplicationController
before_action
:set_hour
,
only:
[
:show
,
:edit
,
:update
,
:destroy
,
:create_redirect
,
:mark_as_done
]
before_action
:set_hour
,
only:
[
:show
,
:edit
,
:update
,
:destroy
,
:create_redirect
]
before_action
:set_volunteer
def
index
...
...
@@ -43,16 +43,8 @@ class HoursController < ApplicationController
redirect_to
@volunteer
,
make_notice
end
def
mark_as_done
redirect_path
=
list_responses_hours_path
(
params
.
to_unsafe_hash
.
slice
(
:q
))
if
@hour
.
update
(
reviewer:
current_user
)
redirect_to
(
redirect_path
,
notice:
'Stunden als angeschaut markiert.'
)
else
redirect_to
(
redirect_path
,
notice:
'Fehler: Angeschaut markieren fehlgeschlagen.'
)
end
end
private
def
simple_form_params
@simple_form_for_params
=
[
[
@volunteer
,
@hour
.
hourable
,
@hour
],
...
...
app/controllers/list_responses_controller.rb
View file @
18cc9697
class
ListResponsesController
<
ApplicationController
before_action
{
set_default_filter
(
author_volunteer:
'true'
,
reviewer_id_null:
'true'
)
}
def
feedbacks
authorize
:list_response
@q
=
Feedback
.
created_asc
.
author_volunteer
(
params
[
:q
]).
ransack
(
params
[
:q
])
...
...
@@ -12,11 +14,4 @@ class ListResponsesController < ApplicationController
@q
.
sorts
=
[
'updated_at asc'
]
if
@q
.
sorts
.
empty?
@trial_feedbacks
=
@q
.
result
.
paginate
(
page:
params
[
:page
])
end
def
hours
authorize
:list_response
@q
=
Hour
.
created_asc
.
ransack
(
params
[
:q
])
@q
.
sorts
=
[
'updated_at asc'
]
if
@q
.
sorts
.
empty?
@hours
=
@q
.
result
.
paginate
(
page:
params
[
:page
])
end
end
app/helpers/application_helper.rb
View file @
18cc9697
...
...
@@ -112,13 +112,13 @@ module ApplicationHelper
end
end
def
default_list_response_query
{
author_volunteer:
'true'
,
reviewer_id_null:
'true'
,
s:
'updated_at asc'
}
def
section_nav_button
(
text
,
url
)
style
=
current_page?
(
url
)
?
'btn-section-active'
:
'btn-default'
link_to
text
,
url
,
class:
"btn btn-sm
#{
style
}
"
end
def
section_nav_button
(
actions_name
,
text
,
url
)
link_to_unless
(
action_name
==
actions_name
,
text
,
url
,
class:
'btn btn-default btn-sm'
)
do
link_to
(
text
,
url
,
class:
'btn btn-sm btn-section-active'
)
end
def
select_all_rows
check_box_tag
'table-row-select-all'
,
''
,
false
,
class:
'table-row-select-all'
,
title:
'Alle auswählen'
end
end
app/helpers/format_helper.rb
0 → 100644
View file @
18cc9697
module
FormatHelper
def
format_currency
(
amount
)
number_to_currency
amount
,
unit:
'Fr.'
,
format:
'%u %n'
,
separator:
'.'
,
delimiter:
"'"
end
def
format_hours
(
hours
)
hours
=
hours
.
to_i
if
(
hours
%
1
).
zero?
pluralize
hours
,
'Stunde'
,
'Stunden'
end
def
format_hours_period
(
hours
)
dates
=
hours
.
map
(
&
:meeting_date
)
"
#{
I18n
.
l
dates
.
min
}
-
#{
I18n
.
l
dates
.
max
}
"
end
end
app/models/billing_expense.rb
View file @
18cc9697
...
...
@@ -6,28 +6,55 @@ class BillingExpense < ApplicationRecord
before_validation
:compute_amount
,
unless: :import_mode
belongs_to
:volunteer
,
->
{
with_deleted
},
inverse_of:
'
billing_expenses
'
belongs_to
:user
,
->
{
with_deleted
},
inverse_of:
'
billing_expenses
'
belongs_to
:volunteer
,
->
{
with_deleted
},
inverse_of:
:
billing_expenses
belongs_to
:user
,
->
{
with_deleted
},
inverse_of:
:
billing_expenses
has_many
:hours
,
dependent: :nullify
default_scope
{
order
(
created_at: :desc
)
}
AMOUNT
=
[
50
,
100
,
150
].
freeze
validates
:iban
,
presence:
true
validates
:amount
,
inclusion:
{
in:
AMOUNT
},
unless: :import_mode
def
self
.
amount_for
(
hours
)
if
hours
>
50
150
elsif
hours
>
25
100
elsif
hours
>
0
50
else
0
end
end
def
self
.
create_for!
(
volunteers
,
creator
)
transaction
do
volunteers
.
find_each
do
|
volunteer
|
hours
=
volunteer
.
hours
.
billable
hours
.
find_each
do
|
hour
|
hour
.
update!
(
reviewer:
creator
)
end
create!
(
volunteer:
volunteer
,
user:
creator
,
hours:
hours
,
bank:
volunteer
.
bank
,
iban:
volunteer
.
iban
)
end
end
end
private
def
compute_amount
hour_count
=
id
?
hours
.
total_hours
:
volunteer
.
hours
.
billable
.
total_hours
if
hour_count
>
50
self
.
amount
=
150
elsif
hour_count
>
25
self
.
amount
=
100
elsif
hour_count
>=
1
self
.
amount
=
50
else
self
.
amount
=
0
end
return
if
hours
.
blank?
# FIXME: we can't use total_hours here because of some weirdness
# with ActiveRecord::AssociationRelation
self
.
amount
=
self
.
class
.
amount_for
(
hours
.
sum
(
&
:hours
))
end
end
app/models/concerns/full_bank_details.rb
View file @
18cc9697
...
...
@@ -3,7 +3,7 @@ module FullBankDetails
included
do
def
full_bank_details
[
bank
,
iban
].
reject
(
&
:blank?
).
join
(
' '
)
[
bank
,
iban
].
reject
(
&
:blank?
).
join
(
'
,
'
)
end
end
end
app/models/hour.rb
View file @
18cc9697
...
...
@@ -12,7 +12,7 @@ class Hour < ApplicationRecord
belongs_to
:billing_expense
,
->
{
with_deleted
},
optional:
true
,
inverse_of:
'hours'
belongs_to
:certificate
,
optional:
true
validates
:hours
,
presence:
true
,
numericality:
{
greater_than
_or_equal_to
:
0
}
validates
:hours
,
presence:
true
,
numericality:
{
greater_than:
0
}
validates
:meeting_date
,
presence:
true
validates
:hourable
,
presence:
true
...
...
@@ -22,10 +22,6 @@ class Hour < ApplicationRecord
where
(
'created_at > ?'
,
submitted_at
)
if
submitted_at
}
scope
:need_refund
,
lambda
{
joins
(
:volunteer
).
where
(
'volunteers.waive = FALSE'
)
}
scope
:assignment
,
(
->
{
where
(
hourable_type:
'Assignment'
)
})
scope
:group_offer
,
(
->
{
where
(
hourable_type:
'GroupOffer'
)
})
scope
:from_assignments
,
lambda
{
|
assignment_ids
|
...
...
app/models/volunteer.rb
View file @
18cc9697
...
...
@@ -183,6 +183,17 @@ class Volunteer < ApplicationRecord
volunteers
.
where
(
"NOT EXISTS (
#{
assignments
.
to_sql
}
)"
)
}
scope
:need_refunds
,
(
->
{
where
(
waive:
false
)
})
scope
:with_billable_hours
,
lambda
{
need_refunds
.
joins
(
:contact
)
.
joins
(
:hours
).
merge
(
Hour
.
billable
)
.
select
(
'volunteers.*, SUM(hours.hours) AS total_hours'
)
.
group
(
:id
,
'contacts.full_name'
)
.
order
(
"(CASE WHEN COALESCE(iban, '') = '' THEN 2 ELSE 1 END), contacts.full_name"
)
}
def
verify_and_update_state
update
(
active:
active?
,
activeness_might_end:
relevant_period_end_max
)
end
...
...
app/policies/list_response_policy.rb
View file @
18cc9697
class
ListResponsePolicy
<
ApplicationPolicy
alias_method
:feedbacks?
,
:superadmin?
alias_method
:hours?
,
:superadmin?
alias_method
:trial_feedbacks?
,
:superadmin?
alias_method
:feedbacks?
,
:superadmin?
alias_method
:trial_feedbacks?
,
:superadmin?
end
app/views/assignments/find_client.html.slim
View file @
18cc9697
...
...
@@ -20,7 +20,7 @@ nav.navbar.section-navigation
li
=
render
'age_request_select'
li
=
render
'language_skills_language_select'
#find-clients-table
.table-responsiv
e
.table-responsive.table-scrollabl
e
table
.table.table-striped
thead
tr
...
...
app/views/billing_expenses/index.html.slim
View file @
18cc9697
h1
=
@volunteer
.
contact
.
full_name
h1
=
t_title
(
:index
,
BillingExpense
)
-
unless
@volunteer
=
render
'reminder_mailings/section_navigation'
h1
'
Spesenformulare
-
if
@volunteer
=
"für
#{
@volunteer
}
"
.row
.col-xs-12
-
if
@volunteer
=
>
button_link
navigation_glyph
(
:back
),
@volunteer
-
else
=
>
button_link
'Spesenformulare erstellen'
,
new_billing_expense_path
=
bootstrap_paginate
(
@billing_expenses
)
.table-responsive
table
.table
thead
tr
th
.hidden-print
Aktionen
th
=
t_model
(
Volunteer
)
th
=
t_attr
(
:address
)
th
=
sort_link
@q
,
:volunteer_contact_last_name
,
t_model
(
Volunteer
)
th
=
t_attr
(
:bank_details
,
Volunteer
)
th
=
t_attr
(
:
waive
,
Volunteer
)
th
=
t_attr
(
:hours
,
Hour
)
th
=
t_attr
(
:amount
,
BillingExpense
)
th
=
t_attr
(
:created_
at
)
th
=
t_attr
(
:created_
by
)
th
=
t_attr
(
:
hours
)
th
=
sort_link
@q
,
:amount
th
Periode
th
=
sort_link
@q
,
:user_profile_contact_last_name
,
t_attr
(
:created_
by
)
th
=
sort_link
@q
,
:created_
at
tbody
-
@
volunteer
.
billing_expenses
.
each
do
|
record
|
-
@billing_expenses
.
each
do
|
record
|
tr
td
.index-action-cell.hidden-print
=
button_link
navigation_glyph
(
:show
),
volunteer_
billing_expense_path
(
@volunteer
,
record
),
=
button_link
navigation_glyph
(
:show
),
billing_expense_path
(
record
),
title:
'Anzeigen'
=
button_link
navigation_glyph
(
:download
),
volunteer_
billing_expense_path
(
@volunteer
,
record
,
format: :pdf
),
=
button_link
navigation_glyph
(
:download
),
billing_expense_path
(
record
,
format: :pdf
),
title:
'Herunterladen'
=
button_link
navigation_glyph
(
:delete
),
volunteer_
billing_expense_path
(
@volunteer
,
record
),
=
button_link
navigation_glyph
(
:delete
),
billing_expense_path
(
record
),
confirm_deleting
(
record
,
'btn btn-default'
).
merge
(
title:
'Löschen'
)
td
=
link_to
@volunteer
.
contact
.
full_name
,
volunteer_path
(
@volunteer
)
td
=
@volunteer
.
contact
.
full_address
td
=
link_to
record
.
volunteer
.
contact
.
full_name
,
volunteer_path
(
record
.
volunteer
)
td
=
record
.
full_bank_details
td
=
t
(
@volunteer
.
waive
)
td
=
record
.
hours
.
total_hours
td
=
record
.
amount
td
=
link_to
format_hours
(
record
.
hours
.
total_hours
),
volunteer_hours_path
(
record
.
volunteer
)
td
=
format_currency
record
.
amount
td
=
format_hours_period
record
.
hours
td
=
link_to
record
.
user
,
profile_link
(
record
.
user
)
td
=
l
(
record
.
created_at
.
to_date
)
td
=
record
.
user
.
profile
.
contact
.
full_name
.row
.col-xs-12
=
simple_form_for
[
@volunteer
,
BillingExpense
.
new
]
do
|
f
|
=
f
.
hidden_field
:volunteer_id
,
value:
@volunteer
.
id
=
f
.
button
:submit
.row
.col-xs-12
=
button_link
navigation_glyph
(
:back
),
@volunteer
=
bootstrap_paginate
(
@billing_expenses
)
Prev
1
2
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment