openproject-service_packs 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +19 -0
  3. data/README.md +4 -0
  4. data/app/assets/javascripts/assigns.js +20 -0
  5. data/app/assets/javascripts/service_packs.js +17 -0
  6. data/app/assets/stylesheets/assigns.css +9 -0
  7. data/app/assets/stylesheets/service_packs.css +132 -0
  8. data/app/controllers/assigns_controller.rb +157 -0
  9. data/app/controllers/service_packs_controller.rb +219 -0
  10. data/app/helpers/service_pack_presenter.rb +39 -0
  11. data/app/helpers/service_pack_report.rb +65 -0
  12. data/app/helpers/service_packs_notification.rb +13 -0
  13. data/app/helpers/sp_assignment_manager.rb +25 -0
  14. data/app/mailers/application_mailer.rb +5 -0
  15. data/app/mailers/service_packs_mailer.rb +43 -0
  16. data/app/models/application_record.rb +3 -0
  17. data/app/models/assign.rb +13 -0
  18. data/app/models/mapping_rate.rb +14 -0
  19. data/app/models/service_pack.rb +180 -0
  20. data/app/models/service_pack_entry.rb +6 -0
  21. data/app/views/assigns/already_assigned.html.erb +14 -0
  22. data/app/views/assigns/not_assigned_yet.html.erb +19 -0
  23. data/app/views/assigns/unassignable.html.erb +4 -0
  24. data/app/views/layouts/mailer.html.erb +13 -0
  25. data/app/views/layouts/mailer.text.erb +1 -0
  26. data/app/views/service_packs/_active_assignments.html.erb +14 -0
  27. data/app/views/service_packs/_form.html.erb +80 -0
  28. data/app/views/service_packs/_rates_input.html.erb +15 -0
  29. data/app/views/service_packs/edit.html.erb +75 -0
  30. data/app/views/service_packs/index.html.erb +33 -0
  31. data/app/views/service_packs/new.html.erb +6 -0
  32. data/app/views/service_packs/show.html.erb +48 -0
  33. data/app/views/service_packs_mailer/expired_email.html.erb +1 -0
  34. data/app/views/service_packs_mailer/expired_email.text.erb +1 -0
  35. data/app/views/service_packs_mailer/notify_under_threshold1.html.erb +2 -0
  36. data/app/views/service_packs_mailer/notify_under_threshold1.text.erb +1 -0
  37. data/app/views/service_packs_mailer/notify_under_threshold2.html.erb +2 -0
  38. data/app/views/service_packs_mailer/notify_under_threshold2.text.erb +1 -0
  39. data/app/views/service_packs_mailer/used_up_email.html.erb +14 -0
  40. data/app/views/service_packs_mailer/used_up_email.text.erb +1 -0
  41. data/app/workers/expired_sp_worker.rb +11 -0
  42. data/app/workers/used_up_service_pack_job.rb +13 -0
  43. data/config/application.rb +4 -0
  44. data/config/routes.rb +13 -0
  45. data/config/schedule.example.rb +27 -0
  46. data/config/schedule.rb +28 -0
  47. data/config/sidekiq.yml +4 -0
  48. data/db/migrate/20190108031704_create_assigns.rb +11 -0
  49. data/db/migrate/20190108031712_create_service_packs.rb +14 -0
  50. data/db/migrate/20190108111243_create_mapping_rates.rb +11 -0
  51. data/db/migrate/20190113111300_rename_assign.rb +6 -0
  52. data/db/migrate/20190116085528_create_service_pack_entries.rb +9 -0
  53. data/db/migrate/20190121072000_final_assigns_table.rb +5 -0
  54. data/db/migrate/20190123150023_add_service_pack_ref_to_service_pack_entries.rb +5 -0
  55. data/db/migrate/20190123205130_feature_a_done.rb +16 -0
  56. data/db/migrate/20190301070654_change_units_of_service_pack_entries.rb +5 -0
  57. data/db/migrate/20190301071911_change_total_and_remained_units_of_service_pack.rb +6 -0
  58. data/lib/open_project/service_packs.rb +5 -0
  59. data/lib/open_project/service_packs/engine.rb +50 -0
  60. data/lib/open_project/service_packs/patches.rb +4 -0
  61. data/lib/open_project/service_packs/patches/enumeration_patch.rb +37 -0
  62. data/lib/open_project/service_packs/patches/project_patch.rb +14 -0
  63. data/lib/open_project/service_packs/patches/time_entry_activity_patch.rb +16 -0
  64. data/lib/open_project/service_packs/patches/time_entry_patch.rb +89 -0
  65. data/lib/open_project/service_packs/version.rb +5 -0
  66. data/lib/openproject-service_packs.rb +1 -0
  67. metadata +121 -0
@@ -0,0 +1,4 @@
1
+ :schedule:
2
+ expired_sp:
3
+ every: 12h
4
+ class: ExpiredSpWorker
@@ -0,0 +1,11 @@
1
+ class CreateAssigns < ActiveRecord::Migration[5.1]
2
+ def change
3
+ create_table :assigns do |t|
4
+ t.belongs_to :service_pack
5
+ t.belongs_to :project
6
+ t.date :assign_date, null: false
7
+ t.timestamps
8
+ end
9
+ add_column(:assigns, :unassigned, :boolean, default: true, null: false)
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ class CreateServicePacks < ActiveRecord::Migration[5.1]
2
+ def change
3
+ create_table :service_packs do |t|
4
+ t.string :name, null: false
5
+ t.integer :total_units, null: false
6
+ t.integer :remained_units, null: false
7
+ t.date :started_date, null: false
8
+ t.date :expired_date, null: false
9
+ t.integer :threshold1, null: false
10
+ t.integer :threshold2, null: false
11
+ t.timestamps
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ class CreateMappingRates < ActiveRecord::Migration[5.1]
2
+ def change
3
+ create_table :mapping_rates do |t|
4
+ t.integer :activity_id
5
+ t.belongs_to :service_pack
6
+ t.integer :units_per_hour
7
+ t.timestamps
8
+ end
9
+ change_column_null :mapping_rates, :units_per_hour, false
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ class RenameAssign < ActiveRecord::Migration[5.1]
2
+ def change
3
+ # keep this name because of consensus
4
+ rename_column :assigns, :unassigned, :assigned
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ class CreateServicePackEntries < ActiveRecord::Migration[5.1]
2
+ def change
3
+ create_table :service_pack_entries do |t|
4
+ t.belongs_to :time_entry
5
+ t.integer :units
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ class FinalAssignsTable < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_column :assigns, :unassign_date, :date
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddServicePackRefToServicePackEntries < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_reference :service_pack_entries, :service_pack, foreign_key: true
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ class FeatureADone < ActiveRecord::Migration[5.1]
2
+ # enforce NOT NULL constraint
3
+ # false means NULL is not accepted.
4
+ def change
5
+ change_column_null :assigns, :unassign_date, false
6
+ change_column_null :assigns, :assign_date, false
7
+ change_column_null :service_pack_entries, :time_entry_id, false
8
+ change_column_null :service_pack_entries, :units, false
9
+ change_column_null :mapping_rates, :activity_id, false
10
+ change_column_null :mapping_rates, :service_pack_id, false
11
+ change_column_null :mapping_rates, :units_per_hour, false
12
+ if !index_exists?(:mapping_rates, [:service_pack_id, :activity_id])
13
+ add_index :mapping_rates, [:service_pack_id, :activity_id], unique: true
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ class ChangeUnitsOfServicePackEntries < ActiveRecord::Migration[5.1]
2
+ def change
3
+ change_column :service_pack_entries, :units, :float
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class ChangeTotalAndRemainedUnitsOfServicePack < ActiveRecord::Migration[5.1]
2
+ def change
3
+ change_column :service_packs, :total_units, :float
4
+ change_column :service_packs, :remained_units, :float
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module OpenProject
2
+ module ServicePacks
3
+ require 'open_project/service_packs/engine'
4
+ end
5
+ end
@@ -0,0 +1,50 @@
1
+ # Prevent load-order problems in case openproject-plugins is listed after a plugin in the Gemfile
2
+ # or not at all
3
+ require 'open_project/plugins'
4
+
5
+ module OpenProject::ServicePacks
6
+ class Engine < ::Rails::Engine
7
+ engine_name 'openproject-service_packs'
8
+
9
+ include OpenProject::Plugins::ActsAsOpEngine
10
+
11
+ register('openproject-service_packs',
12
+ :author_url => 'https://openproject.org',
13
+ :requires_openproject => '>= 6.0.0') do
14
+
15
+ project_module :service_packs do
16
+ # permission :view_ServicePacks, {ServicePacks: [:index]}
17
+ # permission :create_ServicePacks, {ServicePacks: [:new, :create]}
18
+ # permission :update_ServicePacks, {ServicePacks: [:edit]}
19
+ # permission :delete_ServicePacks, {ServicePacks: [:destroy]}
20
+ permission :assign_service_packs, {assigns: [:assign, :show]}, require: :member
21
+ permission :unassign_service_packs, {assigns: [:unassign, :show]}, require: :member
22
+ permission :see_assigned_service_packs, {assigns: [:show]}, require: :member
23
+ end
24
+
25
+ menu :admin_menu,
26
+ :service_packs,
27
+ { controller: '/service_packs', action: 'index' },
28
+ after: :overview,
29
+ param: :project_id,
30
+ caption: 'Service Packs',
31
+ icon: 'icon2 icon-bug',
32
+ html: {id: 'service_packs-menu-item'}
33
+ # if: ->(project) {true}
34
+
35
+ menu :project_menu,
36
+ :assigns,
37
+ {controller: '/assigns', action: 'show'},
38
+ after: :overview,
39
+ param: :project_id,
40
+ caption: 'Service packs',
41
+ icon: 'icon2 icon-bug',
42
+ html: {id: 'assign-menu-item'}
43
+
44
+ end
45
+ patches %i[Project TimeEntryActivity TimeEntry Enumeration]
46
+ assets %w(assigns.js service_packs.js assigns.css service_packs.css)
47
+ end
48
+ end
49
+ # preserve lost path: no, you can't add a new tab into project settings from the plugin extension.
50
+ # add_tab_entry :project_settings, name: "service_packs", partial: "assigns/show", label: :caption_service_pack
@@ -0,0 +1,4 @@
1
+ module OpenProject::ServicePacks
2
+ module Patches
3
+ end
4
+ end
@@ -0,0 +1,37 @@
1
+ module OpenProject::ServicePacks
2
+ module Patches
3
+ module EnumerationPatch
4
+
5
+ module ClassMethods
6
+
7
+ end
8
+
9
+ module InstanceMethods
10
+ # Activities are always created and updated as a new Enumeration
11
+ # (see EnumerationsController#create, #update and line 117 - 123)
12
+ # so #TEA patching doesn't work.
13
+ def update_sp_rates
14
+ if type == "TimeEntryActivity" && shared?
15
+ ServicePack.availables.each do |service_pack|
16
+ # service_pack.mapping_rates << self # WRONG: this is NOT a plain Ruby collection!
17
+ # trying to give a sensible default value
18
+ service_pack.mapping_rates.create!(units_per_hour: 0, activity_id: self.id)
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def self.included(receiver)
25
+ receiver.extend ClassMethods
26
+ receiver.send :include, InstanceMethods
27
+
28
+ receiver.class_eval do
29
+ after_create :update_sp_rates
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+
37
+ # Enumeration.send(:include, OpenProject::ServicePacks::Patches::EnumerationPatch)
@@ -0,0 +1,14 @@
1
+ module OpenProject::ServicePacks
2
+ module Patches
3
+ module ProjectPatch
4
+ def self.included(receiver)
5
+ receiver.class_eval do
6
+ has_many :assigns, dependent: :destroy
7
+ has_many :service_packs, through: :assigns
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ # Project.send(:include, OpenProject::ServicePacks::Patches::ProjectPatch)
@@ -0,0 +1,16 @@
1
+ module OpenProject::ServicePacks
2
+ module Patches
3
+ module TimeEntryActivityPatch
4
+
5
+ def self.included(receiver)
6
+ receiver.class_eval do
7
+ has_many :mapping_rates, foreign_key: :activity_id, inverse_of: 'activity', dependent: :delete_all
8
+ has_many :service_packs, through: :mapping_rates
9
+ end
10
+ end
11
+
12
+ end
13
+ end
14
+ end
15
+
16
+ # TimeEntryActivity.send(:include, OpenProject::ServicePacks::Patches::TimeEntryActivityPatch)
@@ -0,0 +1,89 @@
1
+ module OpenProject::ServicePacks
2
+ module Patches
3
+ module TimeEntryPatch
4
+ module ClassMethods
5
+
6
+ end
7
+
8
+
9
+ # pseudocode:
10
+ # Find an assignment in effect, if not leave in peace.
11
+ # Then find a rate associated with activity_id and sp in effect.
12
+ # Create an SP_entry with the log entry cost.
13
+ # Subtract the remaining counter of SP to the cost.
14
+
15
+ module InstanceMethods
16
+
17
+ def log_consumed_units
18
+ assignment = project.assigns.where(assigned: true).first
19
+ if assignment.nil?
20
+ self.errors[:base] << "Cannot log time because none SP was assigned"
21
+ raise ActiveRecord::Rollback
22
+ end
23
+ activity_of_time_entry_id = self.activity.parent_id || self.activity.id
24
+ sp_of_project = assignment.service_pack
25
+ rate = sp_of_project.mapping_rates.find_by(activity_id: activity_of_time_entry_id).units_per_hour
26
+ units_cost = rate * self.hours
27
+ # binding.pry
28
+ sp_entry = ServicePackEntry.new(time_entry_id: id, units: units_cost)
29
+ sp_of_project.service_pack_entries << sp_entry
30
+ sp_of_project.remained_units -= units_cost
31
+ sp_of_project.save(context: :consumption)
32
+ =begin
33
+ if sp_of_project.remained_units <= 0
34
+ assignment.update(assigned: false)
35
+ admin_users = User.where(admin: true)
36
+ admin_users.each do |admin_user|
37
+ ServicePacksMailer.used_up_email(admin_user, sp_of_project).deliver_now
38
+ end
39
+ end
40
+ =end
41
+
42
+ end
43
+
44
+
45
+ def update_consumed_units
46
+ # First examine the entry
47
+ # then read the service pack related
48
+ # then recalculate the cost in the entry
49
+ # Update the entry
50
+ # Take the delta and subtract to the remained count of SP.
51
+
52
+ sp_entry = self.service_pack_entry
53
+ return if sp_entry.nil?
54
+ sp_of_project = sp_entry.service_pack # the SP entry is binded at the point of creation
55
+ activity_of_time_entry_id = self.activity.parent_id || self.activity.id
56
+ rate = sp_of_project.mapping_rates.find_by(activity_id: activity_of_time_entry_id).units_per_hour
57
+ units_cost = rate * self.hours
58
+
59
+ extra_consumption = units_cost - sp_entry.units
60
+ # Keep callbacks for SP. Entries have no callback.
61
+ sp_entry.update(units: units_cost) if extra_consumption != 0
62
+ sp_of_project.remained_units -= extra_consumption
63
+ sp_of_project.save(context: :consumption)
64
+ end
65
+
66
+ def get_consumed_units_back
67
+ sp_entry = self.service_pack_entry
68
+ return if sp_entry.nil?
69
+ service_pack = sp_entry.service_pack
70
+ service_pack.remained_units += sp_entry.units
71
+ service_pack.save
72
+ end
73
+
74
+ end
75
+
76
+ def self.included(receiver)
77
+ receiver.extend ClassMethods
78
+ receiver.send :include, InstanceMethods
79
+ receiver.class_eval do
80
+ has_one :service_pack_entry, dependent: :delete
81
+ after_create :log_consumed_units
82
+ after_update :update_consumed_units
83
+ before_destroy :get_consumed_units_back
84
+ end
85
+ end
86
+
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,5 @@
1
+ module OpenProject
2
+ module ServicePacks
3
+ VERSION = '1.0.1'
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ require 'open_project/service_packs'
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: openproject-service_packs
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - An AUT University R&D project team
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-04-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ description: Service Pack plugin
28
+ email:
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - CHANGELOG.md
34
+ - README.md
35
+ - app/assets/javascripts/assigns.js
36
+ - app/assets/javascripts/service_packs.js
37
+ - app/assets/stylesheets/assigns.css
38
+ - app/assets/stylesheets/service_packs.css
39
+ - app/controllers/assigns_controller.rb
40
+ - app/controllers/service_packs_controller.rb
41
+ - app/helpers/service_pack_presenter.rb
42
+ - app/helpers/service_pack_report.rb
43
+ - app/helpers/service_packs_notification.rb
44
+ - app/helpers/sp_assignment_manager.rb
45
+ - app/mailers/application_mailer.rb
46
+ - app/mailers/service_packs_mailer.rb
47
+ - app/models/application_record.rb
48
+ - app/models/assign.rb
49
+ - app/models/mapping_rate.rb
50
+ - app/models/service_pack.rb
51
+ - app/models/service_pack_entry.rb
52
+ - app/views/assigns/already_assigned.html.erb
53
+ - app/views/assigns/not_assigned_yet.html.erb
54
+ - app/views/assigns/unassignable.html.erb
55
+ - app/views/layouts/mailer.html.erb
56
+ - app/views/layouts/mailer.text.erb
57
+ - app/views/service_packs/_active_assignments.html.erb
58
+ - app/views/service_packs/_form.html.erb
59
+ - app/views/service_packs/_rates_input.html.erb
60
+ - app/views/service_packs/edit.html.erb
61
+ - app/views/service_packs/index.html.erb
62
+ - app/views/service_packs/new.html.erb
63
+ - app/views/service_packs/show.html.erb
64
+ - app/views/service_packs_mailer/expired_email.html.erb
65
+ - app/views/service_packs_mailer/expired_email.text.erb
66
+ - app/views/service_packs_mailer/notify_under_threshold1.html.erb
67
+ - app/views/service_packs_mailer/notify_under_threshold1.text.erb
68
+ - app/views/service_packs_mailer/notify_under_threshold2.html.erb
69
+ - app/views/service_packs_mailer/notify_under_threshold2.text.erb
70
+ - app/views/service_packs_mailer/used_up_email.html.erb
71
+ - app/views/service_packs_mailer/used_up_email.text.erb
72
+ - app/workers/expired_sp_worker.rb
73
+ - app/workers/used_up_service_pack_job.rb
74
+ - config/application.rb
75
+ - config/routes.rb
76
+ - config/schedule.example.rb
77
+ - config/schedule.rb
78
+ - config/sidekiq.yml
79
+ - db/migrate/20190108031704_create_assigns.rb
80
+ - db/migrate/20190108031712_create_service_packs.rb
81
+ - db/migrate/20190108111243_create_mapping_rates.rb
82
+ - db/migrate/20190113111300_rename_assign.rb
83
+ - db/migrate/20190116085528_create_service_pack_entries.rb
84
+ - db/migrate/20190121072000_final_assigns_table.rb
85
+ - db/migrate/20190123150023_add_service_pack_ref_to_service_pack_entries.rb
86
+ - db/migrate/20190123205130_feature_a_done.rb
87
+ - db/migrate/20190301070654_change_units_of_service_pack_entries.rb
88
+ - db/migrate/20190301071911_change_total_and_remained_units_of_service_pack.rb
89
+ - lib/open_project/service_packs.rb
90
+ - lib/open_project/service_packs/engine.rb
91
+ - lib/open_project/service_packs/patches.rb
92
+ - lib/open_project/service_packs/patches/enumeration_patch.rb
93
+ - lib/open_project/service_packs/patches/project_patch.rb
94
+ - lib/open_project/service_packs/patches/time_entry_activity_patch.rb
95
+ - lib/open_project/service_packs/patches/time_entry_patch.rb
96
+ - lib/open_project/service_packs/version.rb
97
+ - lib/openproject-service_packs.rb
98
+ homepage: https://gits.fromlabs.com/openproject/service-pack
99
+ licenses:
100
+ - GPLv3
101
+ metadata: {}
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubygems_version: 3.0.3
118
+ signing_key:
119
+ specification_version: 4
120
+ summary: Allow to track units of service packs when logging time on tasks
121
+ test_files: []