petri_flow 0.1.0

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 (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