escalated 0.4.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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +302 -0
- data/app/controllers/escalated/admin/bulk_actions_controller.rb +42 -0
- data/app/controllers/escalated/admin/canned_responses_controller.rb +73 -0
- data/app/controllers/escalated/admin/departments_controller.rb +135 -0
- data/app/controllers/escalated/admin/escalation_rules_controller.rb +121 -0
- data/app/controllers/escalated/admin/macros_controller.rb +73 -0
- data/app/controllers/escalated/admin/reports_controller.rb +152 -0
- data/app/controllers/escalated/admin/settings_controller.rb +111 -0
- data/app/controllers/escalated/admin/sla_policies_controller.rb +109 -0
- data/app/controllers/escalated/admin/tags_controller.rb +67 -0
- data/app/controllers/escalated/admin/tickets_controller.rb +299 -0
- data/app/controllers/escalated/agent/bulk_actions_controller.rb +42 -0
- data/app/controllers/escalated/agent/dashboard_controller.rb +94 -0
- data/app/controllers/escalated/agent/tickets_controller.rb +330 -0
- data/app/controllers/escalated/application_controller.rb +110 -0
- data/app/controllers/escalated/customer/satisfaction_ratings_controller.rb +44 -0
- data/app/controllers/escalated/customer/tickets_controller.rb +169 -0
- data/app/controllers/escalated/guest/tickets_controller.rb +231 -0
- data/app/controllers/escalated/inbound_controller.rb +79 -0
- data/app/jobs/escalated/check_sla_job.rb +36 -0
- data/app/jobs/escalated/close_resolved_job.rb +51 -0
- data/app/jobs/escalated/evaluate_escalations_job.rb +24 -0
- data/app/jobs/escalated/poll_imap_job.rb +74 -0
- data/app/jobs/escalated/purge_activities_job.rb +24 -0
- data/app/mailers/escalated/application_mailer.rb +6 -0
- data/app/mailers/escalated/ticket_mailer.rb +93 -0
- data/app/models/escalated/application_record.rb +5 -0
- data/app/models/escalated/attachment.rb +46 -0
- data/app/models/escalated/canned_response.rb +45 -0
- data/app/models/escalated/department.rb +43 -0
- data/app/models/escalated/escalated_setting.rb +43 -0
- data/app/models/escalated/escalation_rule.rb +96 -0
- data/app/models/escalated/inbound_email.rb +60 -0
- data/app/models/escalated/macro.rb +18 -0
- data/app/models/escalated/reply.rb +42 -0
- data/app/models/escalated/satisfaction_rating.rb +21 -0
- data/app/models/escalated/sla_policy.rb +54 -0
- data/app/models/escalated/tag.rb +28 -0
- data/app/models/escalated/ticket.rb +166 -0
- data/app/models/escalated/ticket_activity.rb +60 -0
- data/app/policies/escalated/canned_response_policy.rb +40 -0
- data/app/policies/escalated/department_policy.rb +36 -0
- data/app/policies/escalated/escalation_rule_policy.rb +36 -0
- data/app/policies/escalated/sla_policy_policy.rb +36 -0
- data/app/policies/escalated/tag_policy.rb +36 -0
- data/app/policies/escalated/ticket_policy.rb +111 -0
- data/config/routes.rb +81 -0
- data/db/migrate/001_create_escalated_departments.rb +18 -0
- data/db/migrate/002_create_escalated_sla_policies.rb +23 -0
- data/db/migrate/003_create_escalated_tags.rb +15 -0
- data/db/migrate/004_create_escalated_tickets.rb +48 -0
- data/db/migrate/005_create_escalated_replies.rb +21 -0
- data/db/migrate/006_create_escalated_attachments.rb +17 -0
- data/db/migrate/007_create_escalated_ticket_tags.rb +13 -0
- data/db/migrate/008_create_escalated_support_tables.rb +49 -0
- data/db/migrate/009_create_escalated_ticket_activities.rb +20 -0
- data/db/migrate/010_create_escalated_settings.rb +29 -0
- data/db/migrate/011_add_guest_fields_to_escalated_tickets.rb +28 -0
- data/db/migrate/012_create_escalated_inbound_emails.rb +30 -0
- data/db/migrate/013_create_escalated_macros.rb +18 -0
- data/db/migrate/014_create_escalated_ticket_followers.rb +18 -0
- data/db/migrate/015_create_escalated_satisfaction_ratings.rb +21 -0
- data/db/migrate/016_add_is_pinned_to_escalated_replies.rb +6 -0
- data/lib/escalated/configuration.rb +111 -0
- data/lib/escalated/drivers/cloud_driver.rb +134 -0
- data/lib/escalated/drivers/hosted_api_client.rb +166 -0
- data/lib/escalated/drivers/local_driver.rb +341 -0
- data/lib/escalated/drivers/synced_driver.rb +124 -0
- data/lib/escalated/engine.rb +45 -0
- data/lib/escalated/mail/adapters/base_adapter.rb +60 -0
- data/lib/escalated/mail/adapters/imap_adapter.rb +209 -0
- data/lib/escalated/mail/adapters/mailgun_adapter.rb +93 -0
- data/lib/escalated/mail/adapters/postmark_adapter.rb +94 -0
- data/lib/escalated/mail/adapters/ses_adapter.rb +179 -0
- data/lib/escalated/mail/inbound_message.rb +78 -0
- data/lib/escalated/manager.rb +33 -0
- data/lib/escalated/services/assignment_service.rb +85 -0
- data/lib/escalated/services/attachment_service.rb +110 -0
- data/lib/escalated/services/escalation_service.rb +159 -0
- data/lib/escalated/services/inbound_email_service.rb +255 -0
- data/lib/escalated/services/macro_service.rb +49 -0
- data/lib/escalated/services/notification_service.rb +157 -0
- data/lib/escalated/services/sla_service.rb +203 -0
- data/lib/escalated/services/ticket_service.rb +113 -0
- data/lib/escalated.rb +25 -0
- data/lib/generators/escalated/install_generator.rb +75 -0
- data/lib/generators/escalated/templates/initializer.rb +89 -0
- metadata +227 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Escalated
|
|
2
|
+
class CannedResponsePolicy
|
|
3
|
+
attr_reader :user, :canned_response
|
|
4
|
+
|
|
5
|
+
def initialize(user, canned_response)
|
|
6
|
+
@user = user
|
|
7
|
+
@canned_response = canned_response
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def index?
|
|
11
|
+
agent? || admin?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create?
|
|
15
|
+
agent? || admin?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def update?
|
|
19
|
+
owner? || admin?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def destroy?
|
|
23
|
+
owner? || admin?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def owner?
|
|
29
|
+
canned_response.created_by == user.id
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def agent?
|
|
33
|
+
user.respond_to?(:escalated_agent?) && user.escalated_agent?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def admin?
|
|
37
|
+
user.respond_to?(:escalated_admin?) && user.escalated_admin?
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Escalated
|
|
2
|
+
class DepartmentPolicy
|
|
3
|
+
attr_reader :user, :department
|
|
4
|
+
|
|
5
|
+
def initialize(user, department)
|
|
6
|
+
@user = user
|
|
7
|
+
@department = department
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def index?
|
|
11
|
+
admin?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def show?
|
|
15
|
+
admin?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def create?
|
|
19
|
+
admin?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def update?
|
|
23
|
+
admin?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def destroy?
|
|
27
|
+
admin?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def admin?
|
|
33
|
+
user.respond_to?(:escalated_admin?) && user.escalated_admin?
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Escalated
|
|
2
|
+
class EscalationRulePolicy
|
|
3
|
+
attr_reader :user, :escalation_rule
|
|
4
|
+
|
|
5
|
+
def initialize(user, escalation_rule)
|
|
6
|
+
@user = user
|
|
7
|
+
@escalation_rule = escalation_rule
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def index?
|
|
11
|
+
admin?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def show?
|
|
15
|
+
admin?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def create?
|
|
19
|
+
admin?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def update?
|
|
23
|
+
admin?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def destroy?
|
|
27
|
+
admin?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def admin?
|
|
33
|
+
user.respond_to?(:escalated_admin?) && user.escalated_admin?
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Escalated
|
|
2
|
+
class SlaPolicyPolicy
|
|
3
|
+
attr_reader :user, :sla_policy
|
|
4
|
+
|
|
5
|
+
def initialize(user, sla_policy)
|
|
6
|
+
@user = user
|
|
7
|
+
@sla_policy = sla_policy
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def index?
|
|
11
|
+
admin?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def show?
|
|
15
|
+
admin?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def create?
|
|
19
|
+
admin?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def update?
|
|
23
|
+
admin?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def destroy?
|
|
27
|
+
admin?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def admin?
|
|
33
|
+
user.respond_to?(:escalated_admin?) && user.escalated_admin?
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Escalated
|
|
2
|
+
class TagPolicy
|
|
3
|
+
attr_reader :user, :tag
|
|
4
|
+
|
|
5
|
+
def initialize(user, tag)
|
|
6
|
+
@user = user
|
|
7
|
+
@tag = tag
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def index?
|
|
11
|
+
agent? || admin?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create?
|
|
15
|
+
admin?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def update?
|
|
19
|
+
admin?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def destroy?
|
|
23
|
+
admin?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def agent?
|
|
29
|
+
user.respond_to?(:escalated_agent?) && user.escalated_agent?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def admin?
|
|
33
|
+
user.respond_to?(:escalated_admin?) && user.escalated_admin?
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
module Escalated
|
|
2
|
+
class TicketPolicy
|
|
3
|
+
attr_reader :user, :ticket
|
|
4
|
+
|
|
5
|
+
def initialize(user, ticket)
|
|
6
|
+
@user = user
|
|
7
|
+
@ticket = ticket
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def index?
|
|
11
|
+
true
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def show?
|
|
15
|
+
owner? || agent? || admin?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def create?
|
|
19
|
+
true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def update?
|
|
23
|
+
agent? || admin?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def destroy?
|
|
27
|
+
admin?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def reply?
|
|
31
|
+
owner? || agent? || admin?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def note?
|
|
35
|
+
agent? || admin?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def assign?
|
|
39
|
+
agent? || admin?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def status?
|
|
43
|
+
agent? || admin?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def priority?
|
|
47
|
+
agent? || admin?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def tags?
|
|
51
|
+
agent? || admin?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def department?
|
|
55
|
+
agent? || admin?
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def close?
|
|
59
|
+
if Escalated.configuration.allow_customer_close
|
|
60
|
+
owner? || agent? || admin?
|
|
61
|
+
else
|
|
62
|
+
agent? || admin?
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def reopen?
|
|
67
|
+
owner? || agent? || admin?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class Scope
|
|
71
|
+
attr_reader :user, :scope
|
|
72
|
+
|
|
73
|
+
def initialize(user, scope)
|
|
74
|
+
@user = user
|
|
75
|
+
@scope = scope
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def resolve
|
|
79
|
+
if admin? || agent?
|
|
80
|
+
scope.all
|
|
81
|
+
else
|
|
82
|
+
scope.where(requester: user)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def admin?
|
|
89
|
+
user.respond_to?(:escalated_admin?) && user.escalated_admin?
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def agent?
|
|
93
|
+
user.respond_to?(:escalated_agent?) && user.escalated_agent?
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
def owner?
|
|
100
|
+
ticket.requester == user
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def agent?
|
|
104
|
+
user.respond_to?(:escalated_agent?) && user.escalated_agent?
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def admin?
|
|
108
|
+
user.respond_to?(:escalated_admin?) && user.escalated_admin?
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
Escalated::Engine.routes.draw do
|
|
2
|
+
# Customer-facing routes
|
|
3
|
+
namespace :customer do
|
|
4
|
+
resources :tickets, only: [:index, :create, :show] do
|
|
5
|
+
member do
|
|
6
|
+
post :reply
|
|
7
|
+
post :close
|
|
8
|
+
post :reopen
|
|
9
|
+
post :rate, to: "satisfaction_ratings#create"
|
|
10
|
+
end
|
|
11
|
+
collection do
|
|
12
|
+
get :new, action: :create, as: :new
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Agent routes
|
|
18
|
+
namespace :agent do
|
|
19
|
+
get "/", to: "dashboard#index", as: :dashboard
|
|
20
|
+
post "tickets/bulk", to: "bulk_actions#create", as: :tickets_bulk
|
|
21
|
+
resources :tickets, only: [:index, :show, :update] do
|
|
22
|
+
member do
|
|
23
|
+
post :reply
|
|
24
|
+
post :note
|
|
25
|
+
post :assign
|
|
26
|
+
post :status
|
|
27
|
+
post :priority
|
|
28
|
+
post :tags
|
|
29
|
+
post :department
|
|
30
|
+
post :macro, action: :apply_macro
|
|
31
|
+
post :follow
|
|
32
|
+
post :presence
|
|
33
|
+
post "replies/:reply_id/pin", action: :pin, as: :reply_pin
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Admin routes
|
|
39
|
+
namespace :admin do
|
|
40
|
+
post "tickets/bulk", to: "bulk_actions#create", as: :tickets_bulk
|
|
41
|
+
resources :tickets, only: [:index, :show] do
|
|
42
|
+
member do
|
|
43
|
+
post :reply
|
|
44
|
+
post :note
|
|
45
|
+
post :assign
|
|
46
|
+
post :status
|
|
47
|
+
post :priority
|
|
48
|
+
post :tags
|
|
49
|
+
post :department
|
|
50
|
+
post :macro, action: :apply_macro
|
|
51
|
+
post :follow
|
|
52
|
+
post :presence
|
|
53
|
+
post "replies/:reply_id/pin", action: :pin, as: :reply_pin
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
resources :departments
|
|
57
|
+
resources :sla_policies
|
|
58
|
+
resources :escalation_rules
|
|
59
|
+
resources :tags, only: [:index, :create, :update, :destroy]
|
|
60
|
+
resources :canned_responses, only: [:index, :create, :update, :destroy]
|
|
61
|
+
resources :macros, only: [:index, :create, :update, :destroy]
|
|
62
|
+
get :reports, to: "reports#index"
|
|
63
|
+
get :settings, to: "settings#index"
|
|
64
|
+
post :settings, to: "settings#update"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Guest routes (no authentication required)
|
|
68
|
+
namespace :guest do
|
|
69
|
+
get "create", to: "tickets#create"
|
|
70
|
+
post "/", to: "tickets#store", as: :tickets
|
|
71
|
+
get ":token", to: "tickets#show", as: :ticket
|
|
72
|
+
post ":token/reply", to: "tickets#reply", as: :ticket_reply
|
|
73
|
+
post ":token/rate", to: "tickets#rate", as: :ticket_rate
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Inbound email webhook (no authentication -- verified by adapter)
|
|
77
|
+
post "inbound/:adapter", to: "inbound#webhook", as: :inbound_webhook
|
|
78
|
+
|
|
79
|
+
# Root redirect to customer tickets
|
|
80
|
+
root to: "customer/tickets#index"
|
|
81
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class CreateEscalatedDepartments < ActiveRecord::Migration[7.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table Escalated.table_name("departments") do |t|
|
|
4
|
+
t.string :name, null: false
|
|
5
|
+
t.string :slug, null: false
|
|
6
|
+
t.text :description
|
|
7
|
+
t.string :email
|
|
8
|
+
t.boolean :is_active, default: true, null: false
|
|
9
|
+
t.bigint :default_sla_policy_id
|
|
10
|
+
|
|
11
|
+
t.timestamps
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
add_index Escalated.table_name("departments"), :slug, unique: true
|
|
15
|
+
add_index Escalated.table_name("departments"), :is_active
|
|
16
|
+
add_index Escalated.table_name("departments"), :name, unique: true
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class CreateEscalatedSlaPolicies < ActiveRecord::Migration[7.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table Escalated.table_name("sla_policies") do |t|
|
|
4
|
+
t.string :name, null: false
|
|
5
|
+
t.text :description
|
|
6
|
+
t.json :first_response_hours, null: false
|
|
7
|
+
t.json :resolution_hours, null: false
|
|
8
|
+
t.boolean :is_active, default: true, null: false
|
|
9
|
+
t.boolean :is_default, default: false, null: false
|
|
10
|
+
|
|
11
|
+
t.timestamps
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
add_index Escalated.table_name("sla_policies"), :name, unique: true
|
|
15
|
+
add_index Escalated.table_name("sla_policies"), :is_active
|
|
16
|
+
add_index Escalated.table_name("sla_policies"), :is_default
|
|
17
|
+
|
|
18
|
+
add_foreign_key Escalated.table_name("departments"),
|
|
19
|
+
Escalated.table_name("sla_policies"),
|
|
20
|
+
column: :default_sla_policy_id,
|
|
21
|
+
on_delete: :nullify
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class CreateEscalatedTags < ActiveRecord::Migration[7.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table Escalated.table_name("tags") do |t|
|
|
4
|
+
t.string :name, null: false
|
|
5
|
+
t.string :slug, null: false
|
|
6
|
+
t.string :color
|
|
7
|
+
t.text :description
|
|
8
|
+
|
|
9
|
+
t.timestamps
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
add_index Escalated.table_name("tags"), :name, unique: true
|
|
13
|
+
add_index Escalated.table_name("tags"), :slug, unique: true
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
class CreateEscalatedTickets < ActiveRecord::Migration[7.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table Escalated.table_name("tickets") do |t|
|
|
4
|
+
t.string :reference, null: false
|
|
5
|
+
t.string :subject, null: false
|
|
6
|
+
t.text :description, null: false
|
|
7
|
+
t.integer :status, default: 0, null: false
|
|
8
|
+
t.integer :priority, default: 1, null: false
|
|
9
|
+
|
|
10
|
+
# Polymorphic requester
|
|
11
|
+
t.string :requester_type, null: false
|
|
12
|
+
t.bigint :requester_id, null: false
|
|
13
|
+
|
|
14
|
+
# Assignee (agent user)
|
|
15
|
+
t.bigint :assigned_to
|
|
16
|
+
|
|
17
|
+
# Department
|
|
18
|
+
t.references :department, foreign_key: { to_table: Escalated.table_name("departments") }, null: true
|
|
19
|
+
|
|
20
|
+
# SLA
|
|
21
|
+
t.references :sla_policy, foreign_key: { to_table: Escalated.table_name("sla_policies") }, null: true
|
|
22
|
+
t.datetime :sla_first_response_due_at
|
|
23
|
+
t.datetime :sla_resolution_due_at
|
|
24
|
+
t.boolean :sla_breached, default: false, null: false
|
|
25
|
+
|
|
26
|
+
# Timestamps
|
|
27
|
+
t.datetime :first_response_at
|
|
28
|
+
t.datetime :resolved_at
|
|
29
|
+
t.datetime :closed_at
|
|
30
|
+
|
|
31
|
+
# Metadata
|
|
32
|
+
t.json :metadata, default: {}
|
|
33
|
+
|
|
34
|
+
t.timestamps
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
add_index Escalated.table_name("tickets"), :reference, unique: true
|
|
38
|
+
add_index Escalated.table_name("tickets"), :status
|
|
39
|
+
add_index Escalated.table_name("tickets"), :priority
|
|
40
|
+
add_index Escalated.table_name("tickets"), [:requester_type, :requester_id]
|
|
41
|
+
add_index Escalated.table_name("tickets"), :assigned_to
|
|
42
|
+
add_index Escalated.table_name("tickets"), :sla_breached
|
|
43
|
+
add_index Escalated.table_name("tickets"), :sla_first_response_due_at
|
|
44
|
+
add_index Escalated.table_name("tickets"), :sla_resolution_due_at
|
|
45
|
+
add_index Escalated.table_name("tickets"), :created_at
|
|
46
|
+
add_index Escalated.table_name("tickets"), :resolved_at
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class CreateEscalatedReplies < ActiveRecord::Migration[7.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table Escalated.table_name("replies") do |t|
|
|
4
|
+
t.references :ticket, null: false, foreign_key: { to_table: Escalated.table_name("tickets") }
|
|
5
|
+
t.text :body, null: false
|
|
6
|
+
|
|
7
|
+
# Polymorphic author
|
|
8
|
+
t.string :author_type
|
|
9
|
+
t.bigint :author_id
|
|
10
|
+
|
|
11
|
+
t.boolean :is_internal, default: false, null: false
|
|
12
|
+
t.boolean :is_system, default: false, null: false
|
|
13
|
+
|
|
14
|
+
t.timestamps
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
add_index Escalated.table_name("replies"), [:author_type, :author_id]
|
|
18
|
+
add_index Escalated.table_name("replies"), :is_internal
|
|
19
|
+
add_index Escalated.table_name("replies"), :created_at
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class CreateEscalatedAttachments < ActiveRecord::Migration[7.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table Escalated.table_name("attachments") do |t|
|
|
4
|
+
# Polymorphic - can attach to tickets or replies
|
|
5
|
+
t.string :attachable_type, null: false
|
|
6
|
+
t.bigint :attachable_id, null: false
|
|
7
|
+
|
|
8
|
+
t.string :filename, null: false
|
|
9
|
+
t.string :content_type, null: false
|
|
10
|
+
t.bigint :byte_size, null: false, default: 0
|
|
11
|
+
|
|
12
|
+
t.timestamps
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
add_index Escalated.table_name("attachments"), [:attachable_type, :attachable_id]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class CreateEscalatedTicketTags < ActiveRecord::Migration[7.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table Escalated.table_name("ticket_tags"), id: false do |t|
|
|
4
|
+
t.references :ticket, null: false, foreign_key: { to_table: Escalated.table_name("tickets") }
|
|
5
|
+
t.references :tag, null: false, foreign_key: { to_table: Escalated.table_name("tags") }
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
add_index Escalated.table_name("ticket_tags"),
|
|
9
|
+
[:ticket_id, :tag_id],
|
|
10
|
+
unique: true,
|
|
11
|
+
name: "idx_escalated_ticket_tags_unique"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
class CreateEscalatedSupportTables < ActiveRecord::Migration[7.0]
|
|
2
|
+
def change
|
|
3
|
+
# Department-Agent join table
|
|
4
|
+
create_table Escalated.table_name("department_agents"), id: false do |t|
|
|
5
|
+
t.bigint :department_id, null: false
|
|
6
|
+
t.bigint :agent_id, null: false
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
add_index Escalated.table_name("department_agents"),
|
|
10
|
+
[:department_id, :agent_id],
|
|
11
|
+
unique: true,
|
|
12
|
+
name: "idx_escalated_dept_agents_unique"
|
|
13
|
+
add_foreign_key Escalated.table_name("department_agents"),
|
|
14
|
+
Escalated.table_name("departments"),
|
|
15
|
+
column: :department_id
|
|
16
|
+
|
|
17
|
+
# Escalation Rules
|
|
18
|
+
create_table Escalated.table_name("escalation_rules") do |t|
|
|
19
|
+
t.string :name, null: false
|
|
20
|
+
t.text :description
|
|
21
|
+
t.json :conditions, null: false
|
|
22
|
+
t.json :actions, null: false
|
|
23
|
+
t.integer :priority, default: 0, null: false
|
|
24
|
+
t.boolean :is_active, default: true, null: false
|
|
25
|
+
|
|
26
|
+
t.timestamps
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
add_index Escalated.table_name("escalation_rules"), :is_active
|
|
30
|
+
add_index Escalated.table_name("escalation_rules"), :priority
|
|
31
|
+
|
|
32
|
+
# Canned Responses
|
|
33
|
+
create_table Escalated.table_name("canned_responses") do |t|
|
|
34
|
+
t.string :title, null: false
|
|
35
|
+
t.text :body, null: false
|
|
36
|
+
t.string :shortcode
|
|
37
|
+
t.string :category
|
|
38
|
+
t.boolean :is_shared, default: false, null: false
|
|
39
|
+
t.bigint :created_by, null: false
|
|
40
|
+
|
|
41
|
+
t.timestamps
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
add_index Escalated.table_name("canned_responses"), :shortcode, unique: true
|
|
45
|
+
add_index Escalated.table_name("canned_responses"), :is_shared
|
|
46
|
+
add_index Escalated.table_name("canned_responses"), :created_by
|
|
47
|
+
add_index Escalated.table_name("canned_responses"), :category
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class CreateEscalatedTicketActivities < ActiveRecord::Migration[7.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table Escalated.table_name("ticket_activities") do |t|
|
|
4
|
+
t.references :ticket, null: false, foreign_key: { to_table: Escalated.table_name("tickets") }
|
|
5
|
+
t.string :action, null: false
|
|
6
|
+
|
|
7
|
+
# Polymorphic causer (user or system)
|
|
8
|
+
t.string :causer_type
|
|
9
|
+
t.bigint :causer_id
|
|
10
|
+
|
|
11
|
+
t.json :details, default: {}
|
|
12
|
+
|
|
13
|
+
t.timestamps
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
add_index Escalated.table_name("ticket_activities"), [:causer_type, :causer_id]
|
|
17
|
+
add_index Escalated.table_name("ticket_activities"), :action
|
|
18
|
+
add_index Escalated.table_name("ticket_activities"), :created_at
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
class CreateEscalatedSettings < ActiveRecord::Migration[7.0]
|
|
2
|
+
def up
|
|
3
|
+
table_name = "#{Escalated.configuration.table_prefix}settings"
|
|
4
|
+
|
|
5
|
+
create_table table_name do |t|
|
|
6
|
+
t.string :key, null: false
|
|
7
|
+
t.text :value
|
|
8
|
+
t.timestamps
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
add_index table_name, :key, unique: true
|
|
12
|
+
|
|
13
|
+
# Seed default settings
|
|
14
|
+
now = Time.current
|
|
15
|
+
execute <<-SQL.squish
|
|
16
|
+
INSERT INTO #{table_name} (#{connection.quote_column_name('key')}, value, created_at, updated_at)
|
|
17
|
+
VALUES
|
|
18
|
+
('guest_tickets_enabled', '1', '#{now.utc.iso8601}', '#{now.utc.iso8601}'),
|
|
19
|
+
('allow_customer_close', '1', '#{now.utc.iso8601}', '#{now.utc.iso8601}'),
|
|
20
|
+
('auto_close_resolved_after_days', '7', '#{now.utc.iso8601}', '#{now.utc.iso8601}'),
|
|
21
|
+
('max_attachments_per_reply', '5', '#{now.utc.iso8601}', '#{now.utc.iso8601}'),
|
|
22
|
+
('max_attachment_size_kb', '10240', '#{now.utc.iso8601}', '#{now.utc.iso8601}')
|
|
23
|
+
SQL
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def down
|
|
27
|
+
drop_table "#{Escalated.configuration.table_prefix}settings"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
class AddGuestFieldsToEscalatedTickets < ActiveRecord::Migration[7.0]
|
|
2
|
+
def up
|
|
3
|
+
table_name = "#{Escalated.configuration.table_prefix}tickets"
|
|
4
|
+
|
|
5
|
+
# Make requester polymorphic fields nullable for guest tickets
|
|
6
|
+
change_column_null table_name, :requester_type, true
|
|
7
|
+
change_column_null table_name, :requester_id, true
|
|
8
|
+
|
|
9
|
+
# Add guest ticket fields
|
|
10
|
+
add_column table_name, :guest_name, :string, null: true
|
|
11
|
+
add_column table_name, :guest_email, :string, null: true
|
|
12
|
+
add_column table_name, :guest_token, :string, limit: 64, null: true
|
|
13
|
+
|
|
14
|
+
add_index table_name, :guest_token, unique: true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def down
|
|
18
|
+
table_name = "#{Escalated.configuration.table_prefix}tickets"
|
|
19
|
+
|
|
20
|
+
remove_column table_name, :guest_name
|
|
21
|
+
remove_column table_name, :guest_email
|
|
22
|
+
remove_index table_name, :guest_token
|
|
23
|
+
remove_column table_name, :guest_token
|
|
24
|
+
|
|
25
|
+
change_column_null table_name, :requester_type, false
|
|
26
|
+
change_column_null table_name, :requester_id, false
|
|
27
|
+
end
|
|
28
|
+
end
|