| @@ -3,15 +3,20 @@ | ||
| 3 | 3 | namespace FluentSupport\App\Models\Traits; |
| 4 | 4 | |
| 5 | 5 | use FluentSupport\App\Models\Attachment; |
| 6 | 6 | use FluentSupport\App\Models\Conversation; |
| 7 | +use FluentSupport\App\Models\Meta; | |
| 7 | 8 | use FluentSupport\App\Models\Product; |
| 9 | +use FluentSupport\App\Models\AgentGroup; | |
| 10 | +use FluentSupport\App\Models\TagPivot; | |
| 8 | 11 | use FluentSupport\App\Models\Ticket; |
| 9 | 12 | use FluentSupport\App\Modules\PermissionManager; |
| 10 | 13 | use FluentSupport\App\Models\Person; |
| 11 | 14 | use FluentSupport\App\Modules\Reporting\Reporting; |
| 12 | 15 | use FluentSupport\App\Modules\StatModule; |
| 16 | +use FluentSupport\App\Services\Helper; | |
| 13 | 17 | use FluentSupport\App\Services\TicketHelper; |
| 18 | +use FluentSupport\App\Services\Tickets\AgentTicketAccess; | |
| 14 | 19 | use FluentSupport\Framework\Support\Arr; |
| 15 | 20 | use Exception; |
| 16 | 21 | |
| 17 | 22 | trait AgentTrait |
| @@ -19,10 +24,64 @@ | ||
| 19 | 24 | public function getAgents($search) |
| 20 | 25 | { |
| 21 | 26 | $agents = static::latest() |
| 22 | 27 | ->searchBy($search) |
| 28 | + ->with(['groups' => function ($query) { | |
| 29 | + $query->select(['fs_taggables.id', 'title']); | |
| 30 | + }]) | |
| 23 | 31 | ->paginate(); |
| 24 | 32 | |
| 33 | + $agentIds = $agents->pluck('id')->map('intval')->all(); | |
| 34 | + | |
| 35 | + if (!$agentIds) { | |
| 36 | + return $agents; | |
| 37 | + } | |
| 38 | + | |
| 39 | + // One query per kind of data for the whole page, not per agent. | |
| 40 | + $repliesCount = Conversation::whereIn('person_id', $agentIds) | |
| 41 | + ->groupBy('person_id') | |
| 42 | + ->selectRaw('person_id, COUNT(*) AS total') | |
| 43 | + ->pluck('total', 'person_id') | |
| 44 | + ->all(); | |
| 45 | + | |
| 46 | + $interactionsCount = Conversation::whereIn('person_id', $agentIds) | |
| 47 | + ->groupBy('person_id') | |
| 48 | + ->selectRaw('person_id, COUNT(DISTINCT ticket_id) AS total') | |
| 49 | + ->pluck('total', 'person_id') | |
| 50 | + ->all(); | |
| 51 | + | |
| 52 | + $metaDefaults = [ | |
| 53 | + 'telegram_chat_id' => '', | |
| 54 | + 'slack_user_id' => '', | |
| 55 | + 'whatsapp_number' => '', | |
| 56 | + 'agent_restrictions' => [ | |
| 57 | + 'restrictedBusinessBoxes' => [], | |
| 58 | + 'businessBoxRestrictions' => false | |
| 59 | + ], | |
| 60 | + 'agent_signature' => '', | |
| 61 | + 'agent_signature_enabled' => 'no', | |
| 62 | + ]; | |
| 63 | + | |
| 64 | + $metaRows = Meta::where('object_type', 'person_meta') | |
| 65 | + ->whereIn('object_id', $agentIds) | |
| 66 | + ->whereIn('key', array_keys($metaDefaults)) | |
| 67 | + ->orderBy('id', 'ASC') | |
| 68 | + ->get(['object_id', 'key', 'value']); | |
| 69 | + | |
| 70 | + // First row per agent and key wins, as getMeta()'s first() did. | |
| 71 | + $metaValues = []; | |
| 72 | + foreach ($metaRows as $row) { | |
| 73 | + if (!isset($metaValues[$row->object_id][$row->key])) { | |
| 74 | + $metaValues[$row->object_id][$row->key] = Helper::safeUnserialize($row->value); | |
| 75 | + } | |
| 76 | + } | |
| 77 | + | |
| 78 | + // Prime WordPress's user and user meta caches for the permission lookups. | |
| 79 | + $userIds = array_filter(array_map('intval', $agents->pluck('user_id')->all())); | |
| 80 | + if ($userIds) { | |
| 81 | + cache_users($userIds); | |
| 82 | + } | |
| 83 | + | |
| 25 | 84 | foreach ($agents as $agent) { |
| 26 | 85 | $agent->permissions = PermissionManager::getUserPermissions($agent->user_id); |
| 27 | 86 | |
| 28 | 87 | if ($agent->user_id) { |
| @@ -28,19 +87,23 @@ | ||
| 28 | 87 | if ($agent->user_id) { |
| 29 | 88 | $agent->user_profile = admin_url('user-edit.php?user_id=' . $agent->user_id); |
| 30 | 89 | } |
| 31 | 90 | |
| 32 | - $agent->replies_count = Conversation::where('person_id', $agent->id)->count(); | |
| 33 | - $agent->interactions_count = Conversation::where('person_id', $agent->id)->groupBy('ticket_id')->get()->count(); | |
| 91 | + $agent->replies_count = (int) Arr::get($repliesCount, $agent->id, 0); | |
| 92 | + $agent->interactions_count = (int) Arr::get($interactionsCount, $agent->id, 0); | |
| 34 | 93 | |
| 35 | - $agent->telegram_chat_id = $agent->getMeta('telegram_chat_id'); | |
| 36 | - $agent->slack_user_id = $agent->getMeta('slack_user_id'); | |
| 37 | - $agent->whatsapp_number = $agent->getMeta('whatsapp_number'); | |
| 38 | - $agent->restrictions = $agent->getMeta('agent_restrictions', [ | |
| 39 | - 'restrictedBusinessBoxes' => [], | |
| 40 | - 'businessBoxRestrictions' => false | |
| 41 | - ]); | |
| 94 | + // Same rule as getMeta(): a missing or empty value falls back to the default. | |
| 95 | + $meta = function ($key) use ($metaValues, $metaDefaults, $agent) { | |
| 96 | + $value = $metaValues[$agent->id][$key] ?? null; | |
| 97 | + return $value ? $value : $metaDefaults[$key]; | |
| 98 | + }; | |
| 42 | 99 | |
| 100 | + $agent->telegram_chat_id = $meta('telegram_chat_id'); | |
| 101 | + $agent->slack_user_id = $meta('slack_user_id'); | |
| 102 | + $agent->whatsapp_number = $meta('whatsapp_number'); | |
| 103 | + $agent->restrictions = $meta('agent_restrictions'); | |
| 104 | + $agent->agent_signature = $meta('agent_signature'); | |
| 105 | + $agent->agent_signature_enabled = $meta('agent_signature_enabled'); | |
| 43 | 106 | } |
| 44 | 107 | |
| 45 | 108 | return $agents; |
| 46 | 109 | } |
| @@ -52,9 +115,8 @@ | ||
| 52 | 115 | } |
| 53 | 116 | |
| 54 | 117 | $data = $this->setAgentInfo($data, $user); |
| 55 | 118 | |
| 56 | - // check if another agent has same email address | |
| 57 | 119 | $person = Person::where('email', $data['email'])->first(); |
| 58 | 120 | |
| 59 | 121 | if ($person) { |
| 60 | 122 | throw new \Exception('Sorry, Another agent/person exist with the same email address. Please use a different email address'); |
| @@ -60,20 +122,28 @@ | ||
| 60 | 122 | throw new \Exception('Sorry, Another agent/person exist with the same email address. Please use a different email address'); |
| 61 | 123 | } |
| 62 | 124 | |
| 63 | 125 | |
| 64 | - return $this->setAgentMeta( | |
| 65 | - $user, | |
| 66 | - $data, | |
| 67 | - static::create($data) | |
| 68 | - ); | |
| 126 | + $agent = $this->setAgentMeta($user, $data, static::create($data)); | |
| 69 | 127 | |
| 128 | + $defaultGroup = AgentGroup::whereNotNull('settings')->get()->first(function ($group) { | |
| 129 | + return !empty($group->settings['is_default']); | |
| 130 | + }); | |
| 131 | + | |
| 132 | + if ($defaultGroup) { | |
| 133 | + TagPivot::create([ | |
| 134 | + 'tag_id' => $defaultGroup->id, | |
| 135 | + 'source_id' => $agent->id, | |
| 136 | + 'source_type' => 'agent_group', | |
| 137 | + ]); | |
| 138 | + } | |
| 139 | + | |
| 140 | + return $agent; | |
| 70 | 141 | } |
| 71 | 142 | |
| 72 | 143 | /** |
| 73 | - * Method to update agent info | |
| 74 | 144 | * @param array $data |
| 75 | - * @param $agent | |
| 145 | + * @param object $agent | |
| 76 | 146 | * @return object |
| 77 | 147 | * @throws Exception |
| 78 | 148 | */ |
| 79 | 149 | public function updateAgent(array $data, object $agent) |
| @@ -97,9 +167,10 @@ | ||
| 97 | 167 | } |
| 98 | 168 | |
| 99 | 169 | |
| 100 | 170 | /** |
| 101 | - * This method will delete an agent by agent id | |
| 171 | + * Delete an agent and move their records to the fallback agent. | |
| 172 | + * | |
| 102 | 173 | * @param int $fallBackAgentId |
| 103 | 174 | * @param int $agentId |
| 104 | 175 | * @return void |
| 105 | 176 | * @throws Exception |
| @@ -111,10 +182,8 @@ | ||
| 111 | 182 | } |
| 112 | 183 | |
| 113 | 184 | $agent = static::findOrFail($agentId); |
| 114 | 185 | |
| 115 | - PermissionManager::attachPermissions($agent->user_id, []); | |
| 116 | - | |
| 117 | 186 | try { |
| 118 | 187 | $newAgent = static::findOrFail($fallBackAgentId); |
| 119 | 188 | } catch (\Exception $e) { |
| 120 | 189 | throw new \Exception('Fallback agent could not be found'); |
| @@ -119,10 +188,16 @@ | ||
| 119 | 188 | } catch (\Exception $e) { |
| 120 | 189 | throw new \Exception('Fallback agent could not be found'); |
| 121 | 190 | } |
| 122 | 191 | |
| 192 | + PermissionManager::detachPermissions($agent->user_id); | |
| 193 | + | |
| 123 | 194 | $this->assignDataToFallbackAgent($agent->id, $newAgent); |
| 124 | 195 | |
| 196 | + TagPivot::where('source_id', $agent->id) | |
| 197 | + ->where('source_type', 'agent_group') | |
| 198 | + ->delete(); | |
| 199 | + | |
| 125 | 200 | $agent->deleteAllMeta(); |
| 126 | 201 | |
| 127 | 202 | static::where('id', $agentId)->delete(); |
| 128 | 203 | } |
| @@ -127,9 +202,8 @@ | ||
| 127 | 202 | static::where('id', $agentId)->delete(); |
| 128 | 203 | } |
| 129 | 204 | |
| 130 | 205 | /** |
| 131 | - * This method will assign data to fallback agent | |
| 132 | 206 | * @param int $agentId |
| 133 | 207 | * @param object $newAgent |
| 134 | 208 | * @return void |
| 135 | 209 | */ |
| @@ -169,39 +243,28 @@ | ||
| 169 | 243 | } |
| 170 | 244 | |
| 171 | 245 | private function agentDashboardWidgets($data, $with, $agentId) |
| 172 | 246 | { |
| 173 | - //If the request come with suggested_tickets | |
| 174 | 247 | if (in_array('suggested_tickets', $with)) { |
| 175 | - //Get suggested tickets from ticketHelper | |
| 176 | 248 | $data['suggested_tickets'] = TicketHelper::getSuggestedTickets($agentId); |
| 177 | 249 | } |
| 178 | 250 | |
| 179 | - //If the request come with mentioned_tickets | |
| 180 | 251 | if (defined('FLUENTSUPPORTPRO') && in_array('ticket_to_watch', $with)) { |
| 181 | - //Get the overall statistics by the agent | |
| 182 | 252 | $data['ticket_to_watch'] = TicketHelper::getTicketsToWatch(); |
| 183 | 253 | } |
| 184 | 254 | |
| 185 | - //If the request come with overall_stats | |
| 186 | 255 | if (in_array('overall_stats', $with)) { |
| 187 | - //Get overall status | |
| 188 | 256 | $data['overall_stats'] = (new Reporting())->getActiveStats(); |
| 189 | 257 | } |
| 190 | 258 | |
| 191 | - //If the request come with individual_stat | |
| 192 | 259 | if (in_array('individual_stat', $with)) { |
| 193 | - //get overall statistics by agent id | |
| 194 | 260 | $data['individual_stat'] = (new Reporting())->getActiveStatByAgent($agentId); |
| 195 | 261 | } |
| 196 | - //If the request come with my_overall_stats | |
| 197 | 262 | if (in_array('my_overall_stats', $with)) { |
| 198 | - //Get the overall statistics by the agent | |
| 199 | 263 | $data['my_overall_stats'] = StatModule::getAgentOverallStats($agentId); |
| 200 | 264 | } |
| 201 | 265 | |
| 202 | 266 | if (in_array('tickets_by_products', $with)) { |
| 203 | - //Get tickets by product which are waiting for agent reply | |
| 204 | 267 | $data['tickets_by_product'] = StatModule::getActiveTicketsByProductStats(); |
| 205 | 268 | } |
| 206 | 269 | |
| 207 | 270 | return $data; |
| @@ -207,9 +270,8 @@ | ||
| 207 | 270 | return $data; |
| 208 | 271 | } |
| 209 | 272 | |
| 210 | 273 | /** |
| 211 | - * Method to set agent info | |
| 212 | 274 | * @param array $data |
| 213 | 275 | * @param $user |
| 214 | 276 | * @return array |
| 215 | 277 | */ |
| @@ -228,9 +290,10 @@ | ||
| 228 | 290 | return $data; |
| 229 | 291 | } |
| 230 | 292 | |
| 231 | 293 | /** |
| 232 | - * Method to set agent meta data | |
| 294 | + * Save permissions and agent settings meta. | |
| 295 | + * | |
| 233 | 296 | * @param $user |
| 234 | 297 | * @param array $data |
| 235 | 298 | * @param object $agent |
| 236 | 299 | * @return object |
| @@ -266,9 +329,20 @@ | ||
| 266 | 329 | 'businessBoxRestrictions' => $businessBoxRestrictions |
| 267 | 330 | ]; |
| 268 | 331 | |
| 269 | 332 | $agent->updateMeta('agent_restrictions', $restrictions); |
| 333 | + AgentTicketAccess::resetCache(); | |
| 270 | 334 | $agent->restrictions = $restrictions; |
| 335 | + } | |
| 336 | + | |
| 337 | + if (isset($data['agent_signature'])) { | |
| 338 | + $agent->updateMeta('agent_signature', $data['agent_signature']); | |
| 339 | + $agent->agent_signature = $data['agent_signature']; | |
| 340 | + } | |
| 341 | + | |
| 342 | + if (isset($data['agent_signature_enabled'])) { | |
| 343 | + $agent->updateMeta('agent_signature_enabled', $data['agent_signature_enabled']); | |
| 344 | + $agent->agent_signature_enabled = $data['agent_signature_enabled']; | |
| 271 | 345 | } |
| 272 | 346 | |
| 273 | 347 | return $agent; |
| 274 | 348 | } |