Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
66 / 66
100.00% covered (success)
100.00%
36 / 36
CRAP
100.00% covered (success)
100.00%
1 / 1
StructureNode
100.00% covered (success)
100.00%
65 / 65
100.00% covered (success)
100.00%
36 / 36
38
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getParentId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getStructureType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDescription
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getLogo
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSortOrder
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isActive
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
3
 getStatus
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSpecialAccess
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCreatedAt
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getUpdatedAt
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCreatedBy
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getOwner
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getOwnerType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCoOwners
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addChild
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getChildren
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setChildren
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setAssignedUsers
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getAssignedUserIds
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAssignedUserNames
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAssignedUsersCount
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasAssignedUsers
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAddressStreet
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAddressBuildingNumber
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAddressApartmentNumber
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAddressPostalCode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAddressCity
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAddressCountry
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAddressLatitude
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAddressLongitude
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toArray
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5/** @license For full copyright and license information, please see the LICENSE.md file. */
6
7namespace App\Modules\Structure\Domain\Model;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11use DateTimeImmutable;
12
13/**
14 * Organizational Structure Node Domain Entity Aggregate.
15 *
16 * Represents an organizational unit, branch, department or role within company structure.
17 *
18 * @package App\Modules\Structure\Domain\Model
19 */
20final class StructureNode
21{
22    /**
23     * @var array<int, StructureNode> Child structure nodes in tree hierarchy.
24     */
25    private array $children = [];
26
27    /**
28     * @var array<int, int> Assigned user IDs.
29     */
30    private array $assignedUserIds = [];
31
32    /**
33     * @var array<int, string> Assigned user full names preview.
34     */
35    private array $assignedUserNames = [];
36
37    /**
38     * StructureNode constructor.
39     *
40     * @param int|null $id Primary record ID.
41     * @param string $name Node name.
42     * @param string|null $code Unique internal code.
43     * @param int|null $parentId Parent node ID.
44     * @param StructureType $structureType Structure type enum.
45     * @param string|null $description Optional description.
46     * @param string|null $logo Optional logo asset path or URL.
47     * @param int $sortOrder Sequence order integer.
48     * @param bool $isActive Active state flag.
49     * @param DateTimeImmutable|null $createdAt Creation timestamp.
50     * @param DateTimeImmutable|null $updatedAt Modification timestamp.
51     * @param int $createdBy Creator user ID.
52     * @param int $owner Owner user ID.
53     * @param string $ownerType Owner entity type ('user' or 'structure').
54     * @param array<int, int> $coOwners Co-owner IDs.
55     * @param string $status Node status.
56     * @param int $specialAccess Special access flag.
57     * @param string|null $addressStreet Street address.
58     * @param string|null $addressBuildingNumber Building number.
59     * @param string|null $addressApartmentNumber Apartment number.
60     * @param string|null $addressPostalCode Postal code.
61     * @param string|null $addressCity City / Locality.
62     * @param string|null $addressCountry Country name.
63     * @param float|null $addressLatitude GPS latitude.
64     * @param float|null $addressLongitude GPS longitude.
65     */
66    public function __construct(
67        private ?int $id,
68        private string $name,
69        private ?string $code = null,
70        private ?int $parentId = null,
71        private StructureType $structureType = StructureType::DEPARTMENT,
72        private ?string $description = null,
73        private ?string $logo = null,
74        private int $sortOrder = 0,
75        private bool $isActive = true,
76        private ?DateTimeImmutable $createdAt = null,
77        private ?DateTimeImmutable $updatedAt = null,
78        private int $createdBy = 1,
79        private int $owner = 1,
80        private string $ownerType = 'user',
81        private array $coOwners = [],
82        private string $status = 'active',
83        private int $specialAccess = 1,
84        private ?string $addressStreet = null,
85        private ?string $addressBuildingNumber = null,
86        private ?string $addressApartmentNumber = null,
87        private ?string $addressPostalCode = null,
88        private ?string $addressCity = null,
89        private ?string $addressCountry = null,
90        private ?float $addressLatitude = null,
91        private ?float $addressLongitude = null,
92    ) {
93    }
94
95    public function getId(): ?int
96    {
97        return $this->id;
98    }
99
100    public function getName(): string
101    {
102        return $this->name;
103    }
104
105    public function getCode(): ?string
106    {
107        return $this->code;
108    }
109
110    public function getParentId(): ?int
111    {
112        return $this->parentId;
113    }
114
115    public function getStructureType(): StructureType
116    {
117        return $this->structureType;
118    }
119
120    /**
121     * Alias for getStructureType.
122     *
123     * @return StructureType
124     */
125    public function getType(): StructureType
126    {
127        return $this->getStructureType();
128    }
129
130    public function getDescription(): ?string
131    {
132        return $this->description;
133    }
134
135    public function getLogo(): ?string
136    {
137        return $this->logo;
138    }
139
140    public function getSortOrder(): int
141    {
142        return $this->sortOrder;
143    }
144
145    public function isActive(): bool
146    {
147        return $this->isActive && $this->status === 'active' && $this->specialAccess === 1;
148    }
149
150    public function getStatus(): string
151    {
152        return $this->status;
153    }
154
155    public function getSpecialAccess(): int
156    {
157        return $this->specialAccess;
158    }
159
160    public function getCreatedAt(): ?DateTimeImmutable
161    {
162        return $this->createdAt;
163    }
164
165    public function getUpdatedAt(): ?DateTimeImmutable
166    {
167        return $this->updatedAt;
168    }
169
170    public function getCreatedBy(): int
171    {
172        return $this->createdBy;
173    }
174
175    public function getOwner(): int
176    {
177        return $this->owner;
178    }
179
180    public function getOwnerType(): string
181    {
182        return $this->ownerType;
183    }
184
185    /**
186     * @return array<int, int>
187     */
188    public function getCoOwners(): array
189    {
190        return $this->coOwners;
191    }
192
193    /**
194     * Adds child node to hierarchy.
195     *
196     * @param StructureNode $child Child node.
197     * @return void
198     */
199    public function addChild(StructureNode $child): void
200    {
201        $this->children[] = $child;
202    }
203
204    /**
205     * Gets child nodes array.
206     *
207     * @return array<int, StructureNode>
208     */
209    public function getChildren(): array
210    {
211        return $this->children;
212    }
213
214    /**
215     * Sets child nodes array.
216     *
217     * @param array<int, StructureNode> $children
218     * @return void
219     */
220    public function setChildren(array $children): void
221    {
222        $this->children = $children;
223    }
224
225    /**
226     * Sets assigned users metadata.
227     *
228     * @param array<int, int> $userIds User IDs.
229     * @param array<int, string> $userNames User full names preview.
230     * @return void
231     */
232    public function setAssignedUsers(array $userIds, array $userNames = []): void
233    {
234        $this->assignedUserIds = array_values(array_unique(array_map('intval', $userIds)));
235        $this->assignedUserNames = $userNames;
236    }
237
238    /**
239     * @return array<int, int>
240     */
241    public function getAssignedUserIds(): array
242    {
243        return $this->assignedUserIds;
244    }
245
246    /**
247     * @return array<int, string>
248     */
249    public function getAssignedUserNames(): array
250    {
251        return $this->assignedUserNames;
252    }
253
254    /**
255     * Gets count of assigned users.
256     *
257     * @return int
258     */
259    public function getAssignedUsersCount(): int
260    {
261        return count($this->assignedUserIds);
262    }
263
264    /**
265     * Checks whether this node has at least one assigned user (Rule 1).
266     *
267     * @return bool True if node has at least one active user.
268     */
269    public function hasAssignedUsers(): bool
270    {
271        return count($this->assignedUserIds) > 0;
272    }
273
274    public function getAddressStreet(): ?string
275    {
276        return $this->addressStreet;
277    }
278
279    public function getAddressBuildingNumber(): ?string
280    {
281        return $this->addressBuildingNumber;
282    }
283
284    public function getAddressApartmentNumber(): ?string
285    {
286        return $this->addressApartmentNumber;
287    }
288
289    public function getAddressPostalCode(): ?string
290    {
291        return $this->addressPostalCode;
292    }
293
294    public function getAddressCity(): ?string
295    {
296        return $this->addressCity;
297    }
298
299    public function getAddressCountry(): ?string
300    {
301        return $this->addressCountry;
302    }
303
304    public function getAddressLatitude(): ?float
305    {
306        return $this->addressLatitude;
307    }
308
309    public function getAddressLongitude(): ?float
310    {
311        return $this->addressLongitude;
312    }
313
314    /**
315     * Serializes node entity to array.
316     *
317     * @return array<string, mixed>
318     */
319    public function toArray(): array
320    {
321        return [
322            'id'                       => $this->id,
323            'name'                     => $this->name,
324            'code'                     => $this->code,
325            'parent_id'                => $this->parentId,
326            'structure_type'           => $this->structureType->value,
327            'structure_type_label'     => $this->structureType->label(),
328            'description'              => $this->description,
329            'logo'                     => $this->logo,
330            'sort_order'               => $this->sortOrder,
331            'status'                   => $this->status,
332            'special_access'           => $this->specialAccess,
333            'is_active'                => $this->isActive(),
334            'address_street'           => $this->addressStreet,
335            'address_building_number'  => $this->addressBuildingNumber,
336            'address_apartment_number' => $this->addressApartmentNumber,
337            'address_postal_code'      => $this->addressPostalCode,
338            'address_city'             => $this->addressCity,
339            'address_country'          => $this->addressCountry,
340            'address_latitude'         => $this->addressLatitude,
341            'address_longitude'        => $this->addressLongitude,
342            'assigned_users_count'     => count($this->assignedUserIds),
343            'assigned_user_names'      => $this->assignedUserNames,
344            'has_assigned_users'       => $this->hasAssignedUsers(),
345            'children'                 => array_map(
346                static fn(StructureNode $c): array => $c->toArray(),
347                $this->children
348            ),
349        ];
350    }
351}