Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
63.64% covered (warning)
63.64%
77 / 121
16.67% covered (danger)
16.67%
1 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
UpdateUserService
63.64% covered (warning)
63.64%
77 / 121
16.67% covered (danger)
16.67%
1 / 6
158.79
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 update
78.95% covered (warning)
78.95%
15 / 19
0.00% covered (danger)
0.00%
0 / 1
6.34
 processUserLog
88.24% covered (warning)
88.24%
15 / 17
0.00% covered (danger)
0.00%
0 / 1
4.03
 mountUserModel
53.85% covered (warning)
53.85%
7 / 13
0.00% covered (danger)
0.00%
0 / 1
7.46
 verifyUserPermission
54.41% covered (warning)
54.41%
37 / 68
0.00% covered (danger)
0.00%
0 / 1
122.05
 fetchUser
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace Domain\User\Services;
4
5use App\Events\ServiceProcessed;
6use App\Models\Team\Team;
7use App\Models\User;
8
9use Domain\Shared\Integrations\Senior\Exceptions\IntegrationSoapSeniorException;
10use Domain\User\DTOs\UserDTO;
11use Domain\User\Enums\UserLogOperationEnum;
12use Domain\User\Exceptions\UserSectorDoesNotMatchException;
13use Domain\User\Exceptions\UserNotAllowedToUpdateException;
14
15readonly class UpdateUserService
16{
17    private UserDTO $userDTO;
18    private User $userToUpdate;
19    private User $user;
20
21    /**
22     * @param UserDTO $userDTO
23     * @param User $userToUpdate
24     * @param User $user
25     */
26    public function __construct(
27        UserDTO $userDTO,
28        User $userToUpdate,
29        User $user
30    ) {
31        $this->userDTO      = $userDTO;
32        $this->userToUpdate = $userToUpdate;
33        $this->user         = $user;
34    }
35
36    /**
37     * @return User
38     * @throws UserSectorDoesNotMatchException
39     * @throws UserNotAllowedToUpdateException
40     * @throws IntegrationSoapSeniorException
41     */
42    public function update(): User
43    {
44        $data = $this->userDTO->toArray();
45        if (isset($data['access_level'])) {
46            $current = $this->user->currentAccessToken()->abilities;
47            $new = $data['access_level'];
48
49            if ($current !== $new && !$this->user->isAdmin() && !$this->user->isMaster()) {
50                throw new UserNotAllowedToUpdateException(
51                    __('This user has no access to update access level.')
52                );
53            }
54        }
55
56        (new UserAccessValidatorService())->validate($this->userDTO->accessLevel);
57
58        $original = $this->userToUpdate->getOriginal();
59
60        $this->verifyUserPermission();
61
62        $this->mountUserModel();
63
64        $changes = $this->userToUpdate->getDirty();
65        $this->userToUpdate->save();
66
67        $this->processUserLog($original, $changes);
68
69        if ($this->userDTO->erp_user) {
70            $this->fetchUser($this->userDTO->erp_user, $this->userToUpdate);
71        }
72
73        ServiceProcessed::dispatch(self::class, $this->user);
74        return $this->userToUpdate;
75    }
76
77    /**
78     * @param $original
79     * @param $changes
80     * @return void
81     */
82    private function processUserLog($original, $changes): void
83    {
84        if(!empty($changes)){
85            $detail = [];
86
87            foreach ($changes as $field => $newValue) {
88                $oldValue = $original[$field] ?? null;
89
90                if($field === 'access_level'){
91                    $this->userToUpdate->tokens()->delete();
92
93                    $oldValue = json_encode($oldValue);
94                }
95
96                $detail[$field] = [
97                    'old_value' => $oldValue,
98                    'new_value' => $newValue,
99                ];
100            }
101
102            (new StoreUserLogService())->store(
103                $this->userToUpdate,
104                $this->user,
105                UserLogOperationEnum::UPDATE,
106                $detail
107            );
108        }
109    }
110
111    /**
112     * @return void
113     */
114    private function mountUserModel(): void
115    {
116        if(!is_null($this->userDTO->name)){
117            $this->userToUpdate->name = $this->userDTO->name;
118        }
119
120        if(!is_null($this->userDTO->email)){
121            $this->userToUpdate->email = $this->userDTO->email;
122        }
123
124        if(!is_null($this->userDTO->badge)){
125            $this->userToUpdate->badge = $this->userDTO->badge;
126        }
127
128        if(!is_null($this->userDTO->erp_user)){
129            $this->userToUpdate->integrations = [
130                'senior' => [
131                    'user' => $this->userDTO->erp_user,
132                    'password' => null
133                ]
134            ];
135        }
136    }
137
138    /**
139     * @return void
140     * @throws UserSectorDoesNotMatchException
141     */
142    private function verifyUserPermission(): void
143    {
144        if(!empty($this->userDTO->accessLevel)){
145            $changedKeys = [];
146            $addKey = [];
147            $removeKey = [];
148            $newValues = [];
149
150            $allKeys = array_unique(array_merge(
151                array_keys($this->userDTO->accessLevel),
152                array_keys($this->userToUpdate->access_level)
153            ));
154
155            foreach ($allKeys as $key) {
156                $oldValues = $this->userToUpdate->access_level[$key] ?? null;
157
158                if (array_key_exists($key, $this->userDTO->accessLevel)) {
159                    foreach ($this->userDTO->accessLevel[$key] as $newValue) {
160                        $newValues[] = $newValue->value;
161                    }
162                } else {
163                    $newValues = null;
164                }
165
166                if ($oldValues === null && $newValues !== null) {
167                    $addKey[] = $key;
168                    continue;
169                }
170
171                if ($oldValues !== null && $newValues === null) {
172                    $removeKey[] = $key;
173                    continue;
174                }
175
176                if ($oldValues === null || $newValues === null) {
177                    $changedKeys[] = $key;
178                    continue;
179                }
180
181                if ((array_diff($oldValues, $newValues) || array_diff($newValues, $oldValues)) &&
182                    empty($addKey) && empty($removeKey)) {
183                    $changedKeys[] = $key;
184                }
185
186                $newValues = [];
187            }
188
189            if(!empty($changedKeys)){
190                $keysIntersect = array_intersect(
191                    array_keys($this->user->currentAccessToken()->abilities),
192                    $changedKeys
193                );
194
195                if(empty($keysIntersect)){
196                    throw new UserSectorDoesNotMatchException(__('Cannot change the access level or the sector information from a usar of different sector'));
197                }
198            }
199
200        if (!empty($addKey) || !empty($removeKey)) {
201            $adminSectors = array_keys($this->user->currentAccessToken()->abilities);
202
203            foreach ($removeKey as $removedSector) {
204                if (!in_array($removedSector, $adminSectors)) {
205                    throw new UserSectorDoesNotMatchException(__('Cannot change the access level or the sector information from a usar of different sector'));
206                }
207            }
208
209            foreach ($addKey as $addedSector) {
210                if (!in_array($addedSector, $adminSectors)) {
211                    throw new UserSectorDoesNotMatchException(__('Cannot change the access level or the sector information from a usar of different sector'));
212                }
213            }
214
215            if (!empty($addKey) && !empty($removeKey)) {
216                throw new UserSectorDoesNotMatchException(__('Cannot change the access level or the sector information from a usar of different sector'));
217            }
218        }
219
220            $this->userToUpdate->access_level = $this->userDTO->accessLevel;
221
222            if (!empty($addKey)) {
223                foreach ($addKey as $addSector) {
224                    $team = Team::query()
225                        ->where('sector', $addSector)
226                        ->where('default', true)
227                        ->first();
228
229                    if ($team) {
230                        (new StoreTeamUserService($this->userToUpdate, $team))->store();
231                    }
232                }
233            }
234        }
235
236        $keysIntersect = array_intersect(
237            array_keys($this->user->currentAccessToken()->abilities),
238            array_keys($this->userToUpdate->access_level)
239        );
240
241        $keysIntersectAdd = array_intersect(
242            array_keys($this->user->currentAccessToken()->abilities),
243            $addKey
244        );
245
246        $keysIntersectRemove = array_intersect(
247            array_keys($this->user->currentAccessToken()->abilities),
248            $removeKey
249        );
250
251        if(empty($keysIntersect) && empty($keysIntersectAdd) && empty($keysIntersectRemove)){
252            throw new UserSectorDoesNotMatchException(__('Cannot change a user from a different sector'));
253        }
254    }
255
256    /**
257     * @param string $erp_user
258     * @param User $user
259     * @return void
260     * @throws IntegrationSoapSeniorException
261     */
262    private function fetchUser(string $erp_user, User $user): void
263    {
264        (new ImportSeniorUSerService())->import($erp_user, 'username', $user);
265    }
266}