| @@ -3,8 +3,9 @@ | ||
| 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; |
| 8 | 9 | use FluentSupport\App\Models\AgentGroup; |
| 9 | 10 | use FluentSupport\App\Models\TagPivot; |
| 10 | 11 | use FluentSupport\App\Models\Ticket; |
| @@ -11,9 +12,11 @@ | ||
| 11 | 12 | use FluentSupport\App\Modules\PermissionManager; |
| 12 | 13 | use FluentSupport\App\Models\Person; |
| 13 | 14 | use FluentSupport\App\Modules\Reporting\Reporting; |
| 14 | 15 | use FluentSupport\App\Modules\StatModule; |
| 16 | +use FluentSupport\App\Services\Helper; | |
| 15 | 17 | use FluentSupport\App\Services\TicketHelper; |
| 18 | +use FluentSupport\App\Services\Tickets\AgentTicketAccess; | |
| 16 | 19 | use FluentSupport\Framework\Support\Arr; |
| 17 | 20 | use Exception; |
| 18 | 21 | |
| 19 | 22 | trait AgentTrait |
| @@ -21,10 +24,64 @@ | ||
| 21 | 24 | public function getAgents($search) |
| 22 | 25 | { |
| 23 | 26 | $agents = static::latest() |
| 24 | 27 | ->searchBy($search) |
| 28 | + ->with(['groups' => function ($query) { | |
| 29 | + $query->select(['fs_taggables.id', 'title']); | |
| 30 | + }]) | |
| 25 | 31 | ->paginate(); |
| 26 | 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 | + | |
| 27 | 84 | foreach ($agents as $agent) { |
| 28 | 85 | $agent->permissions = PermissionManager::getUserPermissions($agent->user_id); |
| 29 | 86 | |
| 30 | 87 | if ($agent->user_id) { |
| @@ -30,23 +87,23 @@ | ||
| 30 | 87 | if ($agent->user_id) { |
| 31 | 88 | $agent->user_profile = admin_url('user-edit.php?user_id=' . $agent->user_id); |
| 32 | 89 | } |
| 33 | 90 | |
| 34 | - $agent->replies_count = Conversation::where('person_id', $agent->id)->count(); | |
| 35 | - $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); | |
| 36 | 93 | |
| 37 | - $agent->telegram_chat_id = $agent->getMeta('telegram_chat_id'); | |
| 38 | - $agent->slack_user_id = $agent->getMeta('slack_user_id'); | |
| 39 | - $agent->whatsapp_number = $agent->getMeta('whatsapp_number'); | |
| 40 | - $agent->restrictions = $agent->getMeta('agent_restrictions', [ | |
| 41 | - 'restrictedBusinessBoxes' => [], | |
| 42 | - 'businessBoxRestrictions' => false | |
| 43 | - ]); | |
| 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 | + }; | |
| 44 | 99 | |
| 45 | - $agent->agent_signature = $agent->getMeta('agent_signature', ''); | |
| 46 | - $agent->agent_signature_enabled = $agent->getMeta('agent_signature_enabled', 'no'); | |
| 47 | - | |
| 48 | - $agent->groups = $agent->groups()->select(['fs_taggables.id', 'title'])->get(); | |
| 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'); | |
| 49 | 106 | } |
| 50 | 107 | |
| 51 | 108 | return $agents; |
| 52 | 109 | } |
| @@ -58,9 +115,8 @@ | ||
| 58 | 115 | } |
| 59 | 116 | |
| 60 | 117 | $data = $this->setAgentInfo($data, $user); |
| 61 | 118 | |
| 62 | - // check if another agent has same email address | |
| 63 | 119 | $person = Person::where('email', $data['email'])->first(); |
| 64 | 120 | |
| 65 | 121 | if ($person) { |
| 66 | 122 | throw new \Exception('Sorry, Another agent/person exist with the same email address. Please use a different email address'); |
| @@ -84,11 +140,10 @@ | ||
| 84 | 140 | return $agent; |
| 85 | 141 | } |
| 86 | 142 | |
| 87 | 143 | /** |
| 88 | - * Method to update agent info | |
| 89 | 144 | * @param array $data |
| 90 | - * @param $agent | |
| 145 | + * @param object $agent | |
| 91 | 146 | * @return object |
| 92 | 147 | * @throws Exception |
| 93 | 148 | */ |
| 94 | 149 | public function updateAgent(array $data, object $agent) |
| @@ -112,9 +167,10 @@ | ||
| 112 | 167 | } |
| 113 | 168 | |
| 114 | 169 | |
| 115 | 170 | /** |
| 116 | - * This method will delete an agent by agent id | |
| 171 | + * Delete an agent and move their records to the fallback agent. | |
| 172 | + * | |
| 117 | 173 | * @param int $fallBackAgentId |
| 118 | 174 | * @param int $agentId |
| 119 | 175 | * @return void |
| 120 | 176 | * @throws Exception |
| @@ -126,10 +182,8 @@ | ||
| 126 | 182 | } |
| 127 | 183 | |
| 128 | 184 | $agent = static::findOrFail($agentId); |
| 129 | 185 | |
| 130 | - PermissionManager::detachPermissions($agent->user_id); | |
| 131 | - | |
| 132 | 186 | try { |
| 133 | 187 | $newAgent = static::findOrFail($fallBackAgentId); |
| 134 | 188 | } catch (\Exception $e) { |
| 135 | 189 | throw new \Exception('Fallback agent could not be found'); |
| @@ -134,8 +188,10 @@ | ||
| 134 | 188 | } catch (\Exception $e) { |
| 135 | 189 | throw new \Exception('Fallback agent could not be found'); |
| 136 | 190 | } |
| 137 | 191 | |
| 192 | + PermissionManager::detachPermissions($agent->user_id); | |
| 193 | + | |
| 138 | 194 | $this->assignDataToFallbackAgent($agent->id, $newAgent); |
| 139 | 195 | |
| 140 | 196 | TagPivot::where('source_id', $agent->id) |
| 141 | 197 | ->where('source_type', 'agent_group') |
| @@ -146,9 +202,8 @@ | ||
| 146 | 202 | static::where('id', $agentId)->delete(); |
| 147 | 203 | } |
| 148 | 204 | |
| 149 | 205 | /** |
| 150 | - * This method will assign data to fallback agent | |
| 151 | 206 | * @param int $agentId |
| 152 | 207 | * @param object $newAgent |
| 153 | 208 | * @return void |
| 154 | 209 | */ |
| @@ -188,39 +243,28 @@ | ||
| 188 | 243 | } |
| 189 | 244 | |
| 190 | 245 | private function agentDashboardWidgets($data, $with, $agentId) |
| 191 | 246 | { |
| 192 | - //If the request come with suggested_tickets | |
| 193 | 247 | if (in_array('suggested_tickets', $with)) { |
| 194 | - //Get suggested tickets from ticketHelper | |
| 195 | 248 | $data['suggested_tickets'] = TicketHelper::getSuggestedTickets($agentId); |
| 196 | 249 | } |
| 197 | 250 | |
| 198 | - //If the request come with mentioned_tickets | |
| 199 | 251 | if (defined('FLUENTSUPPORTPRO') && in_array('ticket_to_watch', $with)) { |
| 200 | - //Get the overall statistics by the agent | |
| 201 | 252 | $data['ticket_to_watch'] = TicketHelper::getTicketsToWatch(); |
| 202 | 253 | } |
| 203 | 254 | |
| 204 | - //If the request come with overall_stats | |
| 205 | 255 | if (in_array('overall_stats', $with)) { |
| 206 | - //Get overall status | |
| 207 | 256 | $data['overall_stats'] = (new Reporting())->getActiveStats(); |
| 208 | 257 | } |
| 209 | 258 | |
| 210 | - //If the request come with individual_stat | |
| 211 | 259 | if (in_array('individual_stat', $with)) { |
| 212 | - //get overall statistics by agent id | |
| 213 | 260 | $data['individual_stat'] = (new Reporting())->getActiveStatByAgent($agentId); |
| 214 | 261 | } |
| 215 | - //If the request come with my_overall_stats | |
| 216 | 262 | if (in_array('my_overall_stats', $with)) { |
| 217 | - //Get the overall statistics by the agent | |
| 218 | 263 | $data['my_overall_stats'] = StatModule::getAgentOverallStats($agentId); |
| 219 | 264 | } |
| 220 | 265 | |
| 221 | 266 | if (in_array('tickets_by_products', $with)) { |
| 222 | - //Get tickets by product which are waiting for agent reply | |
| 223 | 267 | $data['tickets_by_product'] = StatModule::getActiveTicketsByProductStats(); |
| 224 | 268 | } |
| 225 | 269 | |
| 226 | 270 | return $data; |
| @@ -226,9 +270,8 @@ | ||
| 226 | 270 | return $data; |
| 227 | 271 | } |
| 228 | 272 | |
| 229 | 273 | /** |
| 230 | - * Method to set agent info | |
| 231 | 274 | * @param array $data |
| 232 | 275 | * @param $user |
| 233 | 276 | * @return array |
| 234 | 277 | */ |
| @@ -247,9 +290,10 @@ | ||
| 247 | 290 | return $data; |
| 248 | 291 | } |
| 249 | 292 | |
| 250 | 293 | /** |
| 251 | - * Method to set agent meta data | |
| 294 | + * Save permissions and agent settings meta. | |
| 295 | + * | |
| 252 | 296 | * @param $user |
| 253 | 297 | * @param array $data |
| 254 | 298 | * @param object $agent |
| 255 | 299 | * @return object |
| @@ -285,8 +329,9 @@ | ||
| 285 | 329 | 'businessBoxRestrictions' => $businessBoxRestrictions |
| 286 | 330 | ]; |
| 287 | 331 | |
| 288 | 332 | $agent->updateMeta('agent_restrictions', $restrictions); |
| 333 | + AgentTicketAccess::resetCache(); | |
| 289 | 334 | $agent->restrictions = $restrictions; |
| 290 | 335 | } |
| 291 | 336 | |
| 292 | 337 | if (isset($data['agent_signature'])) { |