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,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class CancelWorkitem
5
+ prepend SimpleCommand
6
+ attr_reader :workitem
7
+ def initialize(workitem)
8
+ @workitem = workitem
9
+ end
10
+
11
+ def call
12
+ raise("The workitem is not in state #{workitem.state}") unless workitem.started?
13
+ Wf::ApplicationRecord.transaction do
14
+ workitem.update!(state: :canceled, :canceled_at: Time.zone.now)
15
+ ReleaseToken.call(workitem)
16
+ SweepAutomaticTransitions.call(workitem.case)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class ClearManualAssignments
5
+ prepend SimpleCommand
6
+ attr_reader :wf_case, :transition
7
+ def initialize(wf_case, transition)
8
+ @wf_case = wf_case
9
+ @transition = transition
10
+ end
11
+
12
+ def call
13
+ wf_case.case_assignments.where(transition: transition).find_each(&:destroy)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class ClearWorkitemAssignments
5
+ prepend SimpleCommand
6
+ attr_reader :workitem, :permanent
7
+ def initialize(workitem, permanent = true)
8
+ @workitem = workitem
9
+ @permanent = permanent
10
+ end
11
+
12
+ def call
13
+ Wf::ApplicationRecord.transaction do
14
+ ClearManualAssignments.call(workitem.case, workitem.transition) if permanent
15
+ workitem.workitem_assignments.delete_all
16
+ workitem.transition.unassignment_callback.constantize.new(workitem.id).perform
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class ConsumeToken
5
+ prepend SimpleCommand
6
+ attr_reader :wf_case, :place, :locked_item
7
+ def initialize(wf_case, place, locked_item = nil)
8
+ @wf_case = wf_case
9
+ @place = place
10
+ @locked_item = locked_item
11
+ end
12
+
13
+ def call
14
+ Wf::ApplicationRecord.transaction do
15
+ if locked_item
16
+ wf_case.tokens.where(place: place, state: :locked, locked_workitem_id: locked_item.id).update(consumed_at: Time.zone.now, state: :consumed)
17
+ else
18
+ wf_case.tokens.where(id: wf_case.tokens.where(place: place, state: :free).first&.id).update(consumed_at: Time.zone.now, state: :consumed)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class EnableTransitions
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
+ Wf::ApplicationRecord.transaction do
13
+ wf_case.workitems.enabled.each do |workitem|
14
+ workitem.update!(state: :overridden, overridden_at: Time.zone.now) unless wf_case.can_fire?(workitem.transition)
15
+ end
16
+ wf_case.workflow.transitions.each do |transition|
17
+ next unless wf_case.can_fire?(transition) && !transition.workitems.where(case: wf_case, state: %i[enabled started]).exists?
18
+
19
+ trigger_time = Time.zone.now + transition.trigger_limit.minutes if transition.trigger_limit && transition.time?
20
+ workitem = wf_case.workitems.create!(
21
+ workflow: wf_case.workflow,
22
+ transition: transition,
23
+ state: :enabled,
24
+ trigger_time: trigger_time
25
+ )
26
+ Wf::FireTimedWorkitemJob.set(wait: transition.trigger_limit.minutes).perform_later(workitem.id) if trigger_time
27
+ SetWorkitemAssignments.call(workitem)
28
+ workitem.transition.unassignment_callback.constantize.new(workitem.id).perform_now if workitem.workitem_assignments.count == 0
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class EndWorkitemAction
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
+ StartWorkitem.call(workitem, user)
16
+ elsif action == :finish
17
+ FinishWorkitem.call(workitem, user)
18
+ elsif action == :cancel
19
+ CancelWorkitem.call(workitem, user)
20
+ elsif action == :comment
21
+ raise("Unknown action #{action}")
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class FinishWorkitem
5
+ prepend SimpleCommand
6
+ attr_reader :workitem
7
+ def initialize(workitem)
8
+ @workitem = workitem
9
+ end
10
+
11
+ def call
12
+ Wf::ApplicationRecord.transaction do
13
+ FireTransitionInternal.call(workitem)
14
+ SweepAutomaticTransitions.call(workitem.case)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class FinishedP
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
+ return true if wf_case.finished?
13
+
14
+ end_place = wf_case.workflow.places.end.first
15
+ end_place_token_num = Wf::ApplicationRecord.uncached { wf_case.tokens.where(place: end_place).count }
16
+ if end_place_token_num == 0
17
+ false
18
+ else
19
+ free_and_locked_token_num = wf_case.tokens.where(place: end_place).where(state: %i[free locked]).count
20
+ raise("The workflow net is misconstructed: Some parallel executions have not finished.") if free_and_locked_token_num > 1
21
+
22
+ ConsumeToken.call(wf_case, end_place)
23
+ wf_case.finished!
24
+ true
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class FireMessageTransition
5
+ prepend SimpleCommand
6
+ attr_reader :workitem
7
+ def initialize(workitem)
8
+ @workitem = workitem
9
+ end
10
+
11
+ def call
12
+ raise("Transition #{workitem.transition.name} is not message triggered") unless workitem.transition.message?
13
+
14
+ Wf::ApplicationRecord.transaction do
15
+ FireTransitionInternal.call(workitem)
16
+ SweepAutomaticTransitions.call(workitem.case)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class FireTransitionInternal
5
+ prepend SimpleCommand
6
+ attr_reader :workitem
7
+ def initialize(workitem)
8
+ @workitem = workitem
9
+ end
10
+
11
+ def call
12
+ if workitem.enabled?
13
+ locked_item = nil
14
+ elsif workitem.started?
15
+ locked_item = workitem
16
+ else
17
+ raise("can not fire the transition if it is not in state enabled or started.")
18
+ end
19
+ Wf::ApplicationRecord.transaction do
20
+ workitem.update!(finished_at: Time.zone.now, state: :finished)
21
+ # TODO: only in?
22
+ workitem.transition.arcs.each do |arc|
23
+ ConsumeToken.call(workitem.case, arc.place, locked_item)
24
+ end
25
+ # last arc without guard -> pass
26
+ has_passed = false
27
+ workitem.transition.arcs.out.order("guards_count DESC").each do |arc|
28
+ if workitem.transition.explicit_or_split?
29
+ if workitem.pass_guard?(arc, has_passed)
30
+ has_passed = true
31
+ AddToken.call(workitem.case, arc.place)
32
+ end
33
+ else
34
+ AddToken.call(workitem.case, arc.place)
35
+ end
36
+ end
37
+ workitem.transition.fire_callback.constantize.new(workitem.id).perform_now
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class LockToken
5
+ prepend SimpleCommand
6
+ attr_reader :wf_case, :place, :workitem
7
+ def initialize(wf_case, place, workitem)
8
+ @wf_case = wf_case
9
+ @place = place
10
+ @workitem = workitem
11
+ end
12
+
13
+ def call
14
+ wf_case.tokens.free.where(place: place).limit(1).update_all(
15
+ state: :locked,
16
+ locked_at: Time.zone.now,
17
+ locked_workitem_id: workitem.id
18
+ )
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class New
5
+ prepend SimpleCommand
6
+ attr_reader :workflow, :target
7
+ def initialize(workflow, target = nil)
8
+ @workflow = workflow
9
+ @target = target
10
+ end
11
+
12
+ def call
13
+ wf_case = workflow.cases.create!(targetable: target, state: :created)
14
+ wf_case
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class ReleaseToken
5
+ prepend SimpleCommand
6
+ attr_reader :workitem
7
+ def initialize(workitem)
8
+ @workitem = workitem
9
+ end
10
+
11
+ def call
12
+ Wf::ApplicationRecord.transaction do
13
+ Wf::Token.where(locked_workitem_id: workitem.id).locked.each do |token|
14
+ AddToken.call(token.case, token.place)
15
+ token.update!(state: :canceled, canceled_at: Time.zone.now)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class RemoveManualAssignment
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.where(transition: transition, party: party).find_each(&:destroy)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class RemoveWorkitemAssignment
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
+ RemoveManualAssignment.call(workitem.case, workitem.transition, party) if permanent
18
+ workitem.workitem_assignments.where(party: party).first&.destroy
19
+
20
+ workitem.transition.unassignment_callback.constantize.new(workitem.id).perform_now if workitem.workitem_assignments.count == 0
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class Resume
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 suspended or canceled cases can be resumed") unless wf_case.suspended? || wf_case.canceled?
13
+
14
+ wf_case.active!
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class SetWorkitemAssignments
5
+ prepend SimpleCommand
6
+ attr_reader :workitem
7
+ def initialize(workitem)
8
+ @workitem = workitem
9
+ end
10
+
11
+ def call
12
+ Wf::ApplicationRecord.transaction do
13
+ has_case_ass = false
14
+ workitem.case.case_assignments.where(transition: workitem.transition).find_each do |case_ass|
15
+ AddWorkitemAssignment.call(workitem, case_ass.party, false)
16
+ has_case_ass = true
17
+ end
18
+
19
+ unless has_case_ass
20
+ callback_values = workitem.transition.assignment_callback.constantize.new(workitem.id).perform
21
+ if callback_values.present?
22
+ # TODO: do assignment for callback.
23
+ else
24
+ workitem.transition.transition_static_assignments.each do |static_assignment|
25
+ AddWorkitemAssignment.call(workitem, static_assignment.party, false)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class StartCase
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
+ Wf::ApplicationRecord.transaction do
13
+ wf_case.active!
14
+ AddToken.call(wf_case, wf_case.workflow.places.start.first)
15
+ SweepAutomaticTransitions.call(wf_case)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class StartWorkitem
5
+ prepend SimpleCommand
6
+ attr_reader :workitem, :user
7
+ def initialize(workitem, user)
8
+ @workitem = workitem
9
+ @user = user
10
+ end
11
+
12
+ def call
13
+ raise("The workitem is not in state #{workitem.state}") unless workitem.enabled?
14
+
15
+ # TODO: holding timeout
16
+ Wf::ApplicationRecord.transaction do
17
+ workitem.update!(state: :started, holding_user: user)
18
+
19
+ workitem.transition.arcs.in.each do |arc|
20
+ LockToken.call(workitem.case, arc.place, workitem)
21
+ end
22
+ end
23
+ workitem
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class Suspend
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.active?
13
+
14
+ wf_case.suspended!
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class SweepAutomaticTransitions
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
+ Wf::ApplicationRecord.transaction do
13
+ EnableTransitions.call(wf_case)
14
+ done = false
15
+ until done
16
+ done = true
17
+ finished = FinishedP.call(wf_case).result
18
+ next if finished
19
+
20
+ Wf::ApplicationRecord.uncached do
21
+ wf_case.workitems.joins(:transition).where(state: :enabled).where(Wf::Transition.table_name => { trigger_type: Wf::Transition.trigger_types[:automatic] }).find_each do |item|
22
+ FireTransitionInternal.call(item)
23
+ done = false
24
+ end
25
+ end
26
+ EnableTransitions.call(wf_case)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class SweepTimedTransitions
5
+ prepend SimpleCommand
6
+
7
+ def call
8
+ Wf::ApplicationRecord.transaction do
9
+ Wf::Workitem.enabled.where("trigger_time <= ?", Time.zone.now).find_each do |item|
10
+ FireTransitionInternal.call(item)
11
+ SweepAutomaticTransitions.call(item.case)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class WorkitemAction
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
+ Wf::ApplicationRecord.transaction do
15
+ BeginWorkitemAction.call(workitem, user, action)
16
+ EndWorkitemAction.call(workitem, user, action)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ # == Schema Information
3
+ #
4
+ # Table name: wf_comments
5
+ #
6
+ # id :integer not null, primary key
7
+ # workitem_id :integer
8
+ # user_id :string
9
+ # body :text
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 Comment < ApplicationRecord
18
+ belongs_to :workitem
19
+ belongs_to :user, class_name: Wf::Workflow.user_class.to_s
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # == Schema Information
4
+ #
5
+ # Table name: wf_demo_targets
6
+ #
7
+ # id :integer not null, primary key
8
+ # name :string
9
+ # description :string
10
+ # created_at :datetime not null
11
+ # updated_at :datetime not null
12
+ #
13
+
14
+ module Wf
15
+ class DemoTarget < ApplicationRecord
16
+ has_many :cases, as: :targetable
17
+ end
18
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # == Schema Information
4
+ #
5
+ # Table name: wf_entries
6
+ #
7
+ # id :integer not null, primary key
8
+ # user_id :string
9
+ # workitem_id :integer
10
+ # payload :json default("{}")
11
+ # created_at :datetime not null
12
+ # updated_at :datetime not null
13
+ #
14
+
15
+ module Wf
16
+ class Entry < ApplicationRecord
17
+ belongs_to :user, class_name: Wf::Workflow.user_class.to_s
18
+ belongs_to :workitem
19
+ has_many :field_values
20
+
21
+ after_initialize do
22
+ self.payload = {} if payload.blank?
23
+ end
24
+
25
+ def json
26
+ field_values.includes(:field).map { |x| [x.field_id.to_i, { field_id: x.id.to_i, field_name: x.field.name, value: x.value_after_cast }] }.to_h
27
+ end
28
+
29
+ def update_payload!
30
+ update(payload: json)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ # == Schema Information
4
+ #
5
+ # Table name: wf_fields
6
+ #
7
+ # id :integer not null, primary key
8
+ # name :string
9
+ # form_id :integer
10
+ # position :integer default("0")
11
+ # field_type :integer default("0")
12
+ # field_type_name :string
13
+ # default_value :string
14
+ # created_at :datetime not null
15
+ # updated_at :datetime not null
16
+ #
17
+
18
+ module Wf
19
+ class Field < ApplicationRecord
20
+ belongs_to :form, touch: true
21
+
22
+ enum field_type: {
23
+ string: 0,
24
+ integer: 1,
25
+ boolean: 2,
26
+ date: 3,
27
+ datetime: 4,
28
+ decimal: 5,
29
+ float: 6,
30
+ json: 7,
31
+ text: 8,
32
+
33
+ "string[]": 20,
34
+ "integer[]": 21,
35
+ "date[]": 23,
36
+ "datetime[]": 24,
37
+ "decimal[]": 25,
38
+ "float[]": 26,
39
+ "json[]": 27,
40
+ "text[]": 28
41
+ }
42
+
43
+ def type_for_cast
44
+ type = field_type.to_s.match(/^(\w+)(\[\])?$/)[1]
45
+ is_array = field_type.to_s.match(/^(\w+)(\[\])?$/)[2] == "[]"
46
+ if is_array
47
+ ActiveRecord::Type.lookup(type.to_sym, array: true)
48
+ else
49
+ ActiveRecord::Type.lookup(type.to_sym)
50
+ end
51
+ end
52
+ end
53
+ end