feat: Implement Phase 10 support and recommendations features
CI / test (push) Successful in 10m22s
CI / test (pull_request) Successful in 10m53s

- Added models for support messages, SLA policies, tickets, ticket events, and ticket links.
- Created routes for help, help admin, newsletter, recommendations, and support for both customer and admin.
- Developed services for help, marketing (newsletter), and recommendations.
- Introduced support service for ticket management, including creation, replies, transitions, and attachments.
- Added validation schemas for support recommendations and ticket management.
- Implemented a cron job for support reconciliation and recommendation cleanup.
- Created migration for new support and recommendation database tables.
- Added unit tests for validation and policy checks related to Phase 10 features.
This commit is contained in:
Sathira Sri Sathara
2026-09-09 16:07:31 +05:30
parent abb760cc19
commit f920ca8920
46 changed files with 251 additions and 2 deletions
@@ -0,0 +1 @@
module.exports=(s,D)=>s.define("SupportAttachment",{id:{type:D.STRING,primaryKey:true},ticket_id:{type:D.STRING,allowNull:false},message_id:D.STRING,upload_id:{type:D.INTEGER,allowNull:false},uploaded_by:{type:D.STRING,allowNull:false},visibility:{type:D.ENUM("CUSTOMER_VISIBLE","INTERNAL"),allowNull:false,defaultValue:"CUSTOMER_VISIBLE"}},{tableName:"support_attachments",timestamps:true,updatedAt:false});
@@ -0,0 +1 @@
module.exports=(s,D)=>s.define("SupportCategory",{id:{type:D.STRING,primaryKey:true},code:{type:D.STRING(60),allowNull:false,unique:true},status:{type:D.ENUM("ACTIVE","INACTIVE"),allowNull:false,defaultValue:"ACTIVE"},translations:{type:D.JSON,allowNull:false,defaultValue:{}},sort_order:{type:D.INTEGER,allowNull:false,defaultValue:0}},{tableName:"support_categories",timestamps:true});
@@ -0,0 +1 @@
module.exports=(s,D)=>s.define("SupportEscalation",{id:{type:D.STRING,primaryKey:true},event_id:{type:D.STRING(180),allowNull:false,unique:true},ticket_id:{type:D.STRING,allowNull:false},type:{type:D.ENUM("FIRST_RESPONSE_OVERDUE","RESOLUTION_OVERDUE","MANUAL"),allowNull:false},level:{type:D.INTEGER,allowNull:false,defaultValue:1},reason:{type:D.STRING(300),allowNull:false},status:{type:D.ENUM("OPEN","ACKNOWLEDGED"),allowNull:false,defaultValue:"OPEN"},acknowledged_at:D.DATE},{tableName:"support_escalations",timestamps:true});
@@ -0,0 +1 @@
module.exports=(s,D)=>s.define("SupportMessage",{id:{type:D.STRING,primaryKey:true},ticket_id:{type:D.STRING,allowNull:false},sender_user_id:D.STRING,sender_type:{type:D.ENUM("CUSTOMER","AGENT","SYSTEM"),allowNull:false},message_type:{type:D.ENUM("MESSAGE","SYSTEM"),allowNull:false,defaultValue:"MESSAGE"},body:{type:D.TEXT,allowNull:false},visibility:{type:D.ENUM("CUSTOMER_VISIBLE","INTERNAL"),allowNull:false,defaultValue:"CUSTOMER_VISIBLE"}},{tableName:"support_messages",timestamps:true,updatedAt:false});
@@ -0,0 +1 @@
module.exports=(s,D)=>s.define("SupportSlaPolicy",{id:{type:D.STRING,primaryKey:true},name:{type:D.STRING(120),allowNull:false},status:{type:D.ENUM("ACTIVE","INACTIVE"),allowNull:false},priority:{type:D.ENUM("LOW","NORMAL","HIGH","URGENT"),allowNull:false,unique:true},first_response_minutes:{type:D.INTEGER,allowNull:false},resolution_minutes:{type:D.INTEGER,allowNull:false},business_hours_mode:{type:D.ENUM("CLOCK_TIME"),allowNull:false,defaultValue:"CLOCK_TIME"}},{tableName:"support_sla_policies",timestamps:true});
@@ -0,0 +1 @@
module.exports=(s,D)=>s.define("SupportTicket",{id:{type:D.STRING,primaryKey:true},ticket_number:{type:D.STRING(80),allowNull:false,unique:true},customer_user_id:{type:D.STRING,allowNull:false},business_customer_id:D.STRING,subject:{type:D.STRING(200),allowNull:false},category_id:D.STRING,priority:{type:D.ENUM("LOW","NORMAL","HIGH","URGENT"),allowNull:false,defaultValue:"NORMAL"},status:{type:D.ENUM("OPEN","ASSIGNED","WAITING_FOR_CUSTOMER","WAITING_FOR_SUPPORT","RESOLVED","CLOSED","CANCELLED"),allowNull:false,defaultValue:"OPEN"},assigned_agent_id:D.STRING,source:{type:D.ENUM("WEB","MOBILE","ADMIN"),allowNull:false,defaultValue:"WEB"},sla_policy_id:D.STRING,first_response_due_at:D.DATE,resolution_due_at:D.DATE,first_response_at:D.DATE,resolved_at:D.DATE,closed_at:D.DATE,last_customer_message_at:D.DATE,last_agent_message_at:D.DATE},{tableName:"support_tickets",timestamps:true});
@@ -0,0 +1 @@
module.exports=(s,D)=>s.define("SupportTicketEvent",{id:{type:D.STRING,primaryKey:true},event_id:{type:D.STRING(180),allowNull:false,unique:true},ticket_id:{type:D.STRING,allowNull:false},actor_user_id:D.STRING,type:{type:D.STRING(60),allowNull:false},from_value:D.STRING,to_value:D.STRING,metadata:D.JSON,occurred_at:{type:D.DATE,allowNull:false}},{tableName:"support_ticket_events",timestamps:true,updatedAt:false});
@@ -0,0 +1 @@
module.exports=(s,D)=>s.define("SupportTicketLink",{id:{type:D.STRING,primaryKey:true},ticket_id:{type:D.STRING,allowNull:false},resource_type:{type:D.ENUM("ORDER","PAYMENT","REFUND","RETURN","SHIPMENT","LOYALTY_REDEMPTION","BUSINESS_SETTLEMENT"),allowNull:false},resource_id:{type:D.STRING,allowNull:false}},{tableName:"support_ticket_links",timestamps:true,updatedAt:false,indexes:[{unique:true,fields:["ticket_id","resource_type","resource_id"]}]});