petri_flow 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (153) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +34 -0
  5. data/app/assets/config/wf_manifest.js +2 -0
  6. data/app/assets/javascripts/wf/application.js +4 -0
  7. data/app/assets/stylesheets/wf/application.scss +2 -0
  8. data/app/assets/stylesheets/wf/arcs.css +4 -0
  9. data/app/assets/stylesheets/wf/cases.css +4 -0
  10. data/app/assets/stylesheets/wf/comments.css +4 -0
  11. data/app/assets/stylesheets/wf/fields.css +4 -0
  12. data/app/assets/stylesheets/wf/forms.css +4 -0
  13. data/app/assets/stylesheets/wf/guards.css +4 -0
  14. data/app/assets/stylesheets/wf/places.css +4 -0
  15. data/app/assets/stylesheets/wf/transitions.css +4 -0
  16. data/app/assets/stylesheets/wf/workflows.css +4 -0
  17. data/app/assets/stylesheets/wf/workitem_assignments.css +4 -0
  18. data/app/assets/stylesheets/wf/workitems.css +4 -0
  19. data/app/controllers/wf/application_controller.rb +14 -0
  20. data/app/controllers/wf/arcs_controller.rb +61 -0
  21. data/app/controllers/wf/cases_controller.rb +48 -0
  22. data/app/controllers/wf/comments_controller.rb +30 -0
  23. data/app/controllers/wf/fields_controller.rb +54 -0
  24. data/app/controllers/wf/forms_controller.rb +59 -0
  25. data/app/controllers/wf/guards_controller.rb +58 -0
  26. data/app/controllers/wf/places_controller.rb +53 -0
  27. data/app/controllers/wf/transitions_controller.rb +69 -0
  28. data/app/controllers/wf/workflows_controller.rb +59 -0
  29. data/app/controllers/wf/workitem_assignments_controller.rb +30 -0
  30. data/app/controllers/wf/workitems_controller.rb +79 -0
  31. data/app/helpers/wf/application_helper.rb +6 -0
  32. data/app/helpers/wf/arcs_helper.rb +6 -0
  33. data/app/helpers/wf/cases_helper.rb +6 -0
  34. data/app/helpers/wf/comments_helper.rb +6 -0
  35. data/app/helpers/wf/fields_helper.rb +6 -0
  36. data/app/helpers/wf/forms_helper.rb +6 -0
  37. data/app/helpers/wf/guards_helper.rb +6 -0
  38. data/app/helpers/wf/places_helper.rb +6 -0
  39. data/app/helpers/wf/transitions_helper.rb +6 -0
  40. data/app/helpers/wf/workflows_helper.rb +6 -0
  41. data/app/helpers/wf/workitem_assignments_helper.rb +6 -0
  42. data/app/helpers/wf/workitems_helper.rb +6 -0
  43. data/app/jobs/wf/application_job.rb +6 -0
  44. data/app/jobs/wf/fire_timed_workitem_job.rb +15 -0
  45. data/app/mailers/wf/application_mailer.rb +8 -0
  46. data/app/models/wf/application_record.rb +7 -0
  47. data/app/models/wf/arc.rb +42 -0
  48. data/app/models/wf/callbacks/assignment_default.rb +12 -0
  49. data/app/models/wf/callbacks/deadline_default.rb +11 -0
  50. data/app/models/wf/callbacks/enable_default.rb +11 -0
  51. data/app/models/wf/callbacks/fire_default.rb +11 -0
  52. data/app/models/wf/callbacks/hold_timeout_default.rb +11 -0
  53. data/app/models/wf/callbacks/notification_default.rb +11 -0
  54. data/app/models/wf/callbacks/time_default.rb +11 -0
  55. data/app/models/wf/callbacks/unassignment_default.rb +11 -0
  56. data/app/models/wf/case.rb +44 -0
  57. data/app/models/wf/case_assignment.rb +22 -0
  58. data/app/models/wf/case_command/add_comment.rb +17 -0
  59. data/app/models/wf/case_command/add_manual_assignment.rb +17 -0
  60. data/app/models/wf/case_command/add_token.rb +20 -0
  61. data/app/models/wf/case_command/add_workitem_assignment.rb +35 -0
  62. data/app/models/wf/case_command/begin_workitem_action.rb +33 -0
  63. data/app/models/wf/case_command/cancel.rb +17 -0
  64. data/app/models/wf/case_command/cancel_workitem.rb +20 -0
  65. data/app/models/wf/case_command/clear_manual_assignments.rb +16 -0
  66. data/app/models/wf/case_command/clear_workitem_assignments.rb +20 -0
  67. data/app/models/wf/case_command/consume_token.rb +23 -0
  68. data/app/models/wf/case_command/enable_transitions.rb +33 -0
  69. data/app/models/wf/case_command/end_workitem_action.rb +25 -0
  70. data/app/models/wf/case_command/finish_workitem.rb +18 -0
  71. data/app/models/wf/case_command/finished_p.rb +28 -0
  72. data/app/models/wf/case_command/fire_message_transition.rb +20 -0
  73. data/app/models/wf/case_command/fire_transition_internal.rb +41 -0
  74. data/app/models/wf/case_command/lock_token.rb +21 -0
  75. data/app/models/wf/case_command/new.rb +17 -0
  76. data/app/models/wf/case_command/release_token.rb +20 -0
  77. data/app/models/wf/case_command/remove_manual_assignment.rb +17 -0
  78. data/app/models/wf/case_command/remove_workitem_assignment.rb +24 -0
  79. data/app/models/wf/case_command/resume.rb +17 -0
  80. data/app/models/wf/case_command/set_workitem_assignments.rb +32 -0
  81. data/app/models/wf/case_command/start_case.rb +19 -0
  82. data/app/models/wf/case_command/start_workitem.rb +26 -0
  83. data/app/models/wf/case_command/suspend.rb +17 -0
  84. data/app/models/wf/case_command/sweep_automatic_transitions.rb +31 -0
  85. data/app/models/wf/case_command/sweep_timed_transitions.rb +16 -0
  86. data/app/models/wf/case_command/workitem_action.rb +20 -0
  87. data/app/models/wf/comment.rb +21 -0
  88. data/app/models/wf/demo_target.rb +18 -0
  89. data/app/models/wf/entry.rb +33 -0
  90. data/app/models/wf/field.rb +53 -0
  91. data/app/models/wf/field_value.rb +30 -0
  92. data/app/models/wf/form.rb +18 -0
  93. data/app/models/wf/group.rb +21 -0
  94. data/app/models/wf/guard.rb +73 -0
  95. data/app/models/wf/party.rb +22 -0
  96. data/app/models/wf/place.rb +28 -0
  97. data/app/models/wf/token.rb +37 -0
  98. data/app/models/wf/transition.rb +49 -0
  99. data/app/models/wf/transition_static_assignment.rb +21 -0
  100. data/app/models/wf/user.rb +26 -0
  101. data/app/models/wf/workflow.rb +182 -0
  102. data/app/models/wf/workitem.rb +78 -0
  103. data/app/models/wf/workitem_assignment.rb +19 -0
  104. data/app/views/layouts/wf/_alert.html.erb +4 -0
  105. data/app/views/layouts/wf/_footer.html.erb +13 -0
  106. data/app/views/layouts/wf/_nav.html.erb +13 -0
  107. data/app/views/layouts/wf/_notice.html.erb +4 -0
  108. data/app/views/layouts/wf/application.html.erb +34 -0
  109. data/app/views/wf/arcs/_form.html.erb +40 -0
  110. data/app/views/wf/arcs/edit.html.erb +1 -0
  111. data/app/views/wf/arcs/new.html.erb +1 -0
  112. data/app/views/wf/arcs/show.html.erb +67 -0
  113. data/app/views/wf/cases/_form.html.erb +27 -0
  114. data/app/views/wf/cases/index.html.erb +35 -0
  115. data/app/views/wf/cases/new.html.erb +1 -0
  116. data/app/views/wf/cases/show.html.erb +113 -0
  117. data/app/views/wf/comments/new.html.erb +27 -0
  118. data/app/views/wf/fields/_form.html.erb +42 -0
  119. data/app/views/wf/fields/edit.html.erb +1 -0
  120. data/app/views/wf/fields/new.html.erb +1 -0
  121. data/app/views/wf/forms/_form.html.erb +32 -0
  122. data/app/views/wf/forms/edit.html.erb +1 -0
  123. data/app/views/wf/forms/index.html.erb +32 -0
  124. data/app/views/wf/forms/new.html.erb +1 -0
  125. data/app/views/wf/forms/show.html.erb +53 -0
  126. data/app/views/wf/guards/_form.html.erb +43 -0
  127. data/app/views/wf/guards/edit.html.erb +1 -0
  128. data/app/views/wf/guards/new.html.erb +1 -0
  129. data/app/views/wf/places/_form.html.erb +43 -0
  130. data/app/views/wf/places/edit.html.erb +1 -0
  131. data/app/views/wf/places/new.html.erb +1 -0
  132. data/app/views/wf/transitions/_form.html.erb +92 -0
  133. data/app/views/wf/transitions/edit.html.erb +1 -0
  134. data/app/views/wf/transitions/new.html.erb +1 -0
  135. data/app/views/wf/workflows/_form.html.erb +32 -0
  136. data/app/views/wf/workflows/edit.html.erb +1 -0
  137. data/app/views/wf/workflows/index.html.erb +38 -0
  138. data/app/views/wf/workflows/new.html.erb +1 -0
  139. data/app/views/wf/workflows/show.html.erb +177 -0
  140. data/app/views/wf/workitem_assignments/new.html.erb +27 -0
  141. data/app/views/wf/workitems/index.html.erb +68 -0
  142. data/app/views/wf/workitems/pre_finish.html.erb +33 -0
  143. data/app/views/wf/workitems/show.html.erb +211 -0
  144. data/config/routes.rb +30 -0
  145. data/db/migrate/20200130201043_init.rb +226 -0
  146. data/db/migrate/20200130201641_init_some_data.rb +15 -0
  147. data/db/migrate/20200131200455_create_wf_entries.rb +19 -0
  148. data/db/migrate/20200201001543_add_target_field_name_for_guard.rb +7 -0
  149. data/lib/tasks/wf_tasks.rake +5 -0
  150. data/lib/wf/engine.rb +10 -0
  151. data/lib/wf/version.rb +5 -0
  152. data/lib/wf.rb +7 -0
  153. metadata +312 -0
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "wf/application_controller"
4
+
5
+ module Wf
6
+ class WorkflowsController < ApplicationController
7
+ breadcrumb "Workflows", :workflows_path
8
+ def index
9
+ @workflows = Wf::Workflow.order("id DESC").page(params[:page])
10
+ end
11
+
12
+ def new
13
+ @workflow = Wf::Workflow.new
14
+ end
15
+
16
+ def edit
17
+ @workflow = Wf::Workflow.find(params[:id])
18
+ breadcrumb @workflow.name, workflow_path(@workflow)
19
+ end
20
+
21
+ def show
22
+ @workflow = Wf::Workflow.find(params[:id])
23
+ end
24
+
25
+ def destroy
26
+ @workflow = Wf::Workflow.find(params[:id])
27
+ @workflow.destroy
28
+ respond_to do |format|
29
+ format.html { redirect_to workflows_path, notice: "workflow was successfully deleted." }
30
+ format.js { render js: "window.location.reload();" }
31
+ end
32
+ end
33
+
34
+ def update
35
+ @workflow = Wf::Workflow.find(params[:id])
36
+ if @workflow.update(workflow_params)
37
+ redirect_to workflow_path(@workflow), notice: "workflow was successfully updated."
38
+ else
39
+ render :edit
40
+ end
41
+ end
42
+
43
+ def create
44
+ @workflow = Wf::Workflow.new(workflow_params)
45
+
46
+ if @workflow.save
47
+ redirect_to workflows_path, notice: "workflow was successfully created."
48
+ else
49
+ render :new
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def workflow_params
56
+ params.fetch(:workflow, {}).permit(:name, :description)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "wf/application_controller"
4
+
5
+ module Wf
6
+ class WorkitemAssignmentsController < ApplicationController
7
+ breadcrumb "Workflows", :workflows_path
8
+ def new
9
+ @workitem = Wf::Workitem.find(params[:workitem_id])
10
+ @workitem_assignment = @workitem.workitem_assignments.new(party_id: params[:party_id])
11
+ breadcrumb @workitem.workflow.name, workflow_path(@workitem.workflow)
12
+ breadcrumb @workitem.case.name, workflow_case_path(@workitem.workflow, @workitem.case)
13
+ breadcrumb @workitem.name, workitem_path(@workitem)
14
+ end
15
+
16
+ def create
17
+ @workitem = Wf::Workitem.find(params[:workitem_id])
18
+ party = Wf::Party.find(params[:workitem_assignment][:party_id])
19
+ Wf::CaseCommand::AddWorkitemAssignment.call(@workitem, party)
20
+ redirect_to workitem_path(@workitem), notice: "assigned party to workitem."
21
+ end
22
+
23
+ def destroy
24
+ @workitem = Wf::Workitem.find(params[:workitem_id])
25
+ party = Wf::Party.find(params[:party_id])
26
+ Wf::CaseCommand::RemoveWorkitemAssignment.call(@workitem, party)
27
+ render js: "window.location.reload()"
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "wf/application_controller"
4
+
5
+ module Wf
6
+ class WorkitemsController < ApplicationController
7
+ before_action :find_workitem, except: [:index]
8
+ before_action :check_start, only: [:start]
9
+ before_action :check_finish, only: %i[pre_finish finish]
10
+
11
+ breadcrumb "Workflows", :workflows_path
12
+
13
+ def index
14
+ current_party_ids = [
15
+ current_user,
16
+ Wf::Workflow.org_classes.map { |org, _org_class| current_user.public_send(org) }
17
+ ].flatten.map { |x| x.party&.id }
18
+ @workitems = Wf::Workitem.joins(:workitem_assignments).where(Wf::WorkitemAssignment.table_name => { party_id: current_party_ids })
19
+ @workitems = @workitems.where(state: params[:state].intern) if params[:state]
20
+ @workitems = @workitems.where(state: params[:state].intern) if params[:state].present?
21
+ @workitems = @workitems.distinct.order("id desc").page(params[:page])
22
+ end
23
+
24
+ def show
25
+ breadcrumb @workitem.workflow.name, workflow_path(@workitem.workflow)
26
+ breadcrumb @workitem.case.name, workflow_case_path(@workitem.workflow, @workitem.case)
27
+ end
28
+
29
+ def start
30
+ Wf::CaseCommand::StartWorkitem.call(@workitem, current_user)
31
+ breadcrumb @workitem.workflow.name, workflow_path(@workitem.workflow)
32
+ breadcrumb @workitem.case.name, workflow_case_path(@workitem.workflow, @workitem.case)
33
+ breadcrumb @workitem.name, workitem_path(@workitem)
34
+ render :pre_finish
35
+ end
36
+
37
+ def pre_finish
38
+ breadcrumb @workitem.workflow.name, workflow_path(@workitem.workflow)
39
+ breadcrumb @workitem.case.name, workflow_case_path(@workitem.workflow, @workitem.case)
40
+ breadcrumb @workitem.name, workitem_path(@workitem)
41
+ end
42
+
43
+ def finish
44
+ if @workitem.transition.form && params[:workitem][:entry]
45
+ entry = @workitem.entries.find_or_create_by!(user: current_user)
46
+ params[:workitem][:entry].permit!.each do |field_id, field_value|
47
+ if field = entry.field_values.where(form: @workitem.transition.form, workflow: @workitem.workflow, field_id: field_id).first
48
+ field.update!(value: field_value)
49
+ else
50
+ entry.field_values.create!(form: @workitem.transition.form, workflow: @workitem.workflow, field_id: field_id, value: field_value)
51
+ end
52
+ end
53
+ entry.update_payload!
54
+ end
55
+ Wf::CaseCommand::FinishWorkitem.call(@workitem)
56
+ if @workitem.case.finished?
57
+ redirect_to workflow_case_path(@workitem.workflow, @workitem.case), notice: "workitem is done, and the case is finished."
58
+ else
59
+ redirect_to workitem_path(Wf::Workitem.last.case.workitems.enabled.first), notice: "workitem is done, and goto next fireable workitem."
60
+ end
61
+ end
62
+
63
+ def find_workitem
64
+ @workitem = Wf::Workitem.find(params[:id])
65
+ end
66
+
67
+ def check_start
68
+ unless @workitem.started_by?(current_user)
69
+ redirect_to workitem_path(@workitem), notice: "You can not start this workitem, Please assign to youself first."
70
+ end
71
+ end
72
+
73
+ def check_finish
74
+ unless @workitem.finished_by?(current_user)
75
+ redirect_to workitem_path(@workitem), notice: "You can not the holding use of this workitem, Please assign to youself && start it first."
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf
4
+ module ApplicationHelper
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf
4
+ module ArcsHelper
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf
4
+ module CasesHelper
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf
4
+ module CommentsHelper
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf
4
+ module FieldsHelper
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf
4
+ module FormsHelper
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf
4
+ module GuardsHelper
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf
4
+ module PlacesHelper
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf
4
+ module TransitionsHelper
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf
4
+ module WorkflowsHelper
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf
4
+ module WorkitemAssignmentsHelper
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf
4
+ module WorkitemsHelper
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf
4
+ class ApplicationJob < ActiveJob::Base
5
+ end
6
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf
4
+ class FireTimedWorkitemJob < ApplicationJob
5
+ queue_as :default
6
+
7
+ def perform(workitem_id)
8
+ item = Wf::Workitem.find(workitem_id)
9
+ if item.trigger_time && item.enabled? && item.case.active?
10
+ CaseCommand::FireTransitionInternal.call(item)
11
+ CaseCommand::SweepAutomaticTransitions.call(item.case)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf
4
+ class ApplicationMailer < ActionMailer::Base
5
+ default from: "from@example.com"
6
+ layout "mailer"
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf
4
+ class ApplicationRecord < ActiveRecord::Base
5
+ self.abstract_class = true
6
+ end
7
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # == Schema Information
4
+ #
5
+ # Table name: wf_arcs
6
+ #
7
+ # id :integer not null, primary key
8
+ # workflow_id :integer
9
+ # transition_id :integer
10
+ # place_id :integer
11
+ # direction :integer default("0")
12
+ # created_at :datetime not null
13
+ # updated_at :datetime not null
14
+ # guards_count :integer default("0")
15
+ #
16
+
17
+ module Wf
18
+ class Arc < ApplicationRecord
19
+ belongs_to :workflow, touch: true
20
+ belongs_to :transition
21
+ belongs_to :place
22
+
23
+ has_many :guards, dependent: :destroy
24
+
25
+ scope :with_guards, -> { where("guards_count > 0") }
26
+ scope :without_guards, -> { where(guards_count: 0) }
27
+
28
+ # direction is relative to the transition
29
+ enum direction: {
30
+ in: 0,
31
+ out: 1
32
+ }
33
+
34
+ def name
35
+ if in?
36
+ [place&.name, transition&.name].join(" -> ")
37
+ else
38
+ [transition&.name, place&.name].join(" -> ")
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::Callbacks
4
+ class AssignmentDefault < ApplicationJob
5
+ queue_as :default
6
+
7
+ def perform(*guests)
8
+ $stdout.puts(guests.inspect)
9
+ [] # return blank default
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::Callbacks
4
+ class DeadlineDefault < ApplicationJob
5
+ queue_as :default
6
+
7
+ def perform(*guests)
8
+ $stdout.puts(guests.inspect)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::Callbacks
4
+ class EnableDefault < ApplicationJob
5
+ queue_as :default
6
+
7
+ def perform(*guests)
8
+ $stdout.puts(guests.inspect)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::Callbacks
4
+ class FireDefault < ApplicationJob
5
+ queue_as :default
6
+
7
+ def perform(*guests)
8
+ $stdout.puts(guests.inspect)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::Callbacks
4
+ class HoldTimeoutDefault < ApplicationJob
5
+ queue_as :default
6
+
7
+ def perform(*guests)
8
+ $stdout.puts(guests.inspect)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::Callbacks
4
+ class NotificationDefault < ApplicationJob
5
+ queue_as :default
6
+
7
+ def perform(*guests)
8
+ $stdout.puts(guests.inspect)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::Callbacks
4
+ class TimeDefault < ApplicationJob
5
+ queue_as :default
6
+
7
+ def perform(*guests)
8
+ $stdout.puts(guests.inspect)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::Callbacks
4
+ class UnassignmentDefault < ApplicationJob
5
+ queue_as :default
6
+
7
+ def perform(*guests)
8
+ $stdout.puts(guests.inspect)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ # == Schema Information
4
+ #
5
+ # Table name: wf_cases
6
+ #
7
+ # id :integer not null, primary key
8
+ # workflow_id :integer
9
+ # targetable_type :string
10
+ # targetable_id :string
11
+ # state :integer default("0")
12
+ # created_at :datetime not null
13
+ # updated_at :datetime not null
14
+ #
15
+
16
+ module Wf
17
+ class Case < ApplicationRecord
18
+ belongs_to :workflow
19
+ belongs_to :targetable, optional: true, polymorphic: true
20
+ has_many :workitems
21
+ has_many :tokens
22
+ has_many :case_assignments
23
+ has_many :parties, through: :case_assignments, source: "party"
24
+
25
+ enum state: {
26
+ created: 0,
27
+ active: 1,
28
+ suspended: 2,
29
+ canceled: 3,
30
+ finished: 4
31
+ }
32
+
33
+ def can_fire?(transition)
34
+ ins = transition.arcs.in.to_a
35
+ return false if ins.blank?
36
+
37
+ ins.all? { |arc| arc.place.tokens.where(case: self).where(state: :free).exists? }
38
+ end
39
+
40
+ def name
41
+ "Case->#{id}"
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ # == Schema Information
3
+ #
4
+ # Table name: wf_case_assignments
5
+ #
6
+ # id :integer not null, primary key
7
+ # case_id :integer
8
+ # transition_id :integer
9
+ # party_id :integer
10
+ # created_at :datetime not null
11
+ # updated_at :datetime not null
12
+ #
13
+
14
+ # frozen_string_literal: true
15
+
16
+ module Wf
17
+ class CaseAssignment < ApplicationRecord
18
+ belongs_to :case
19
+ belongs_to :transition
20
+ belongs_to :party
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class AddComment
5
+ prepend SimpleCommand
6
+ attr_reader :workitem, :comment, :user
7
+ def initialize(workitem, comment, user)
8
+ @workitem = workitem
9
+ @comment = comment
10
+ @user = user
11
+ end
12
+
13
+ def call
14
+ workitem.comments.create!(user: user, body: comment)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class AddManualAssignment
5
+ prepend SimpleCommand
6
+ attr_reader :wf_case, :transition, :party
7
+ def initialize(wf_case, transition, party)
8
+ @wf_case = wf_case
9
+ @transition = transition
10
+ @party = party
11
+ end
12
+
13
+ def call
14
+ wf_case.case_assignments.find_or_create_by!(transition: transition, party: party)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class AddToken
5
+ prepend SimpleCommand
6
+ attr_reader :wf_case, :place
7
+ def initialize(wf_case, place)
8
+ @wf_case = wf_case
9
+ @place = place
10
+ end
11
+
12
+ def call
13
+ wf_case.tokens.create!(
14
+ workflow: wf_case.workflow,
15
+ place: place,
16
+ state: :free
17
+ )
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class AddWorkitemAssignment
5
+ prepend SimpleCommand
6
+ attr_reader :workitem, :party, :permanent
7
+ def initialize(workitem, party, permanent = true)
8
+ @workitem = workitem
9
+ @party = party
10
+ @permanent = permanent
11
+ end
12
+
13
+ def call
14
+ return if party.nil?
15
+
16
+ Wf::ApplicationRecord.transaction do
17
+ AddManualAssignment.call(workitem.case, workitem.transition, party) if permanent
18
+
19
+ notified_users = workitem.parties.map do |p|
20
+ p.partable.users.to_a
21
+ end.flatten
22
+
23
+ assign = workitem.workitem_assignments.where(party: party).first
24
+ break if assign
25
+
26
+ workitem.workitem_assignments.create!(party: party)
27
+ new_users = party.partable.users.to_a
28
+ to_notify = new_users - notified_users
29
+ to_notify.each do |user|
30
+ workitem.transition.notification_callback.constantize.new(workitem, user.id).perform_now
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class BeginWorkitemAction
5
+ prepend SimpleCommand
6
+ attr_reader :workitem, :action, :user
7
+ def initialize(workitem, user, action = :start)
8
+ @workitem = workitem
9
+ @action = action
10
+ @user = user
11
+ end
12
+
13
+ def call
14
+ if action == :start
15
+ raise("Workitem is in state #{workitem.state}, but it must be in state enabled to be started.") unless workitem.enabled?
16
+ raise("You are not assigned to this workitem.") unless workitem.owned_by?(user)
17
+ elsif action == :finish || action == :cancel
18
+ if workitem.started?
19
+ raise("You are not the user currently working on this workitem.") if workitem.holding_user != user
20
+ elsif workitem.enabled?
21
+ raise("You can only cancel a workitem in state started, but this workitem is in state #{workitem.state}.") if action == :cancel
22
+ raise("You are not assigned to this workitem.") unless workitem.owned_by?(user)
23
+
24
+ workitem.update!(holding_user: user)
25
+ else
26
+ raise("Workitem is in state #{workitem.state}, but it must be in state enabled or started to be finished.")
27
+ end
28
+ elsif action == :comment
29
+ # TODO
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class Cancel
5
+ prepend SimpleCommand
6
+ attr_reader :wf_case
7
+ def initialize(wf_case)
8
+ @wf_case = wf_case
9
+ end
10
+
11
+ def call
12
+ raise("Only active or suspended cases can be canceled") unless wf_case.suspended? || wf_case.active?
13
+
14
+ wf_case.canceled!
15
+ end
16
+ end
17
+ end