Repository: it-at-m/eappointment
Pull request #992: Feature zmskvr 310 provider specific public visibility
Author: login: manjencic
Created: 2025-04-09T12:16:08Z | Merged: 2025-04-16T06:33:01Z

### Pull Request Checklist (Feature Branch to `next`):

- [ ] Ich habe die neuesten Änderungen aus dem `next` Branch in meinen Feature-Branch gemergt.
- [ ] Das Code-Review wurde abgeschlossen.
- [ ] Fachliche Tests wurden durchgeführt und sind abgeschlossen.


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Added public visibility and maximum quantity attributes for services and requests.
  - Enhanced filtering options allow users to control if unpublished entries are shown.

- **Chores**
  - Updated backend database structures and mapping schemas to support the new visibility and quantity settings.
  - Introduced new properties in the JSON schema for request relations to reflect the public status and maximum quantity.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/zmscitizenapi/src/Zmscitizenapi/Models/OfficeServiceRelation.php b/zmscitizenapi/src/Zmscitizenapi/Models/OfficeServiceRelation.php
index 3c3b708f36..3e415425b9 100644
--- a/zmscitizenapi/src/Zmscitizenapi/Models/OfficeServiceRelation.php
+++ b/zmscitizenapi/src/Zmscitizenapi/Models/OfficeServiceRelation.php
@@ -11,24 +11,29 @@
 class OfficeServiceRelation extends Entity implements JsonSerializable
 {
     public static $schema = 'citizenapi/officeServiceRelation.json';
-/** @var int */
+
     public int $officeId;
-/** @var int */
     public int $serviceId;
-/** @var int */
     public int $slots;
-/**
+    public bool $public;
+    public ?int $maxQuantity;
+
+    /**
      * Constructor.
      *
      * @param int $officeId
      * @param int $serviceId
      * @param int $slots
+     * @param bool $public
+     * @param ?int $maxQuantity
      */
-    public function __construct(int $officeId, int $serviceId, int $slots)
+    public function __construct(int $officeId, int $serviceId, int $slots, bool $public, ?int $maxQuantity)
     {
         $this->officeId = $officeId;
         $this->serviceId = $serviceId;
         $this->slots = $slots;
+        $this->public = $public;
+        $this->maxQuantity = $maxQuantity;
         $this->ensureValid();
     }
 
@@ -50,6 +55,8 @@ public function toArray(): array
             'officeId' => $this->officeId,
             'serviceId' => $this->serviceId,
             'slots' => $this->slots,
+            'public' => $this->public,
+            'maxQuantity' => $this->maxQuantity,
         ];
     }
 
diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/Core/MapperService.php b/zmscitizenapi/src/Zmscitizenapi/Services/Core/MapperService.php
index dd86176dc7..e0780adafc 100644
--- a/zmscitizenapi/src/Zmscitizenapi/Services/Core/MapperService.php
+++ b/zmscitizenapi/src/Zmscitizenapi/Services/Core/MapperService.php
@@ -121,6 +121,10 @@ public static function mapServicesWithCombinations(
         /** @var array<string, array<int>> $servicesProviderIds */
         $servicesProviderIds = [];
         foreach ($relationList as $relation) {
+            if (!$showUnpublished && !$relation->isPublic()) {
+                continue;
+            }
+
             $serviceId = $relation->request->id;
             $servicesProviderIds[$serviceId] ??= [];
             $servicesProviderIds[$serviceId][] = $relation->provider->id;
@@ -159,11 +163,23 @@ public static function mapServicesWithCombinations(
     }
 
 
-    public static function mapRelations(RequestRelationList $relationList): OfficeServiceRelationList
-    {
+    public static function mapRelations(
+        RequestRelationList $relationList,
+        bool $showUnpublished = false
+    ): OfficeServiceRelationList {
         $relations = [];
         foreach ($relationList as $relation) {
-            $relations[] = new OfficeServiceRelation(officeId: (int) $relation->provider->id, serviceId: (int) $relation->request->id, slots: intval($relation->slots));
+            if (!$showUnpublished && !$relation->isPublic()) {
+                continue;
+            }
+
+            $relations[] = new OfficeServiceRelation(
+                officeId: (int) $relation->provider->id,
+                serviceId: (int) $relation->request->id,
+                slots: intval($relation->slots),
+                public: (bool) $relation->isPublic(),
+                maxQuantity: (int) $relation->maxQuantity
+            );
         }
 
         return new OfficeServiceRelationList($relations);
diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php b/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php
index 5b495a24af..165c6a4844 100644
--- a/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php
+++ b/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php
@@ -217,7 +217,7 @@ public static function getServicesAndOffices(bool $showUnpublished = false): Off
             $relationList,
             $showUnpublished
         ) ?? new ServiceList();
-        $relations = MapperService::mapRelations($relationList) ?? new OfficeServiceRelationList();
+        $relations = MapperService::mapRelations($relationList, $showUnpublished) ?? new OfficeServiceRelationList();
 
         $result = new OfficeServiceAndRelationList($offices, $services, $relations);
 
diff --git a/zmscitizenapi/tests/Zmscitizenapi/Controllers/Office/OfficesServicesRelationsControllerTest.php b/zmscitizenapi/tests/Zmscitizenapi/Controllers/Office/OfficesServicesRelationsControllerTest.php
index 65a3c69f06..482e459e6a 100644
--- a/zmscitizenapi/tests/Zmscitizenapi/Controllers/Office/OfficesServicesRelationsControllerTest.php
+++ b/zmscitizenapi/tests/Zmscitizenapi/Controllers/Office/OfficesServicesRelationsControllerTest.php
@@ -136,17 +136,23 @@ public function testRendering()
                 [
                     "officeId" => 9999998,
                     "serviceId" => 1,
-                    "slots" => 2
+                    "slots" => 2,
+                    "public" => true,
+                    "maxQuantity" => null
                 ],
                 [
                     "officeId" => 9999999,
                     "serviceId" => 1,
-                    "slots" => 1
+                    "slots" => 1,
+                    "public" => true,
+                    "maxQuantity" => null
                 ],
                 [
                     "officeId" => 9999999,
                     "serviceId" => 2,
-                    "slots" => 1
+                    "slots" => 1,
+                    "public" => true,
+                    "maxQuantity" => null
                 ]
             ]
         ];
@@ -281,17 +287,23 @@ public function testDisplayNotPublicRequests()
                 [
                     "officeId" => 9999998,
                     "serviceId" => 1,
-                    "slots" => 2
+                    "slots" => 2,
+                    "public" => true,
+                    "maxQuantity" => null
                 ],
                 [
                     "officeId" => 9999999,
                     "serviceId" => 1,
-                    "slots" => 1
+                    "slots" => 1,
+                    "public" => true,
+                    "maxQuantity" => null
                 ],
                 [
                     "officeId" => 9999999,
                     "serviceId" => 2,
-                    "slots" => 1
+                    "slots" => 1,
+                    "public" => true,
+                    "maxQuantity" => null
                 ]
             ]
         ];
diff --git a/zmscitizenapi/tests/Zmscitizenapi/Services/Core/MapperServiceTest.php b/zmscitizenapi/tests/Zmscitizenapi/Services/Core/MapperServiceTest.php
index af4bdf1e3e..fdd591fc11 100644
--- a/zmscitizenapi/tests/Zmscitizenapi/Services/Core/MapperServiceTest.php
+++ b/zmscitizenapi/tests/Zmscitizenapi/Services/Core/MapperServiceTest.php
@@ -518,7 +518,7 @@ public function testMapRelations()
         
         $relationList = new RequestRelationList([$relation1, $relation2]);
         
-        $result = MapperService::mapRelations($relationList);
+        $result = MapperService::mapRelations($relationList, true);
         $this->assertInstanceOf(OfficeServiceRelationList::class, $result);
         
         $relations = $result->toArray()['relations'];
@@ -535,7 +535,7 @@ public function testMapRelations()
     
     public function testMapRelationsWithEmptyList()
     {
-        $result = MapperService::mapRelations(new RequestRelationList());
+        $result = MapperService::mapRelations(new RequestRelationList(), true);
         $this->assertInstanceOf(OfficeServiceRelationList::class, $result);
         $this->assertEmpty($result->toArray()['relations']);
     }
diff --git a/zmsdb/migrations/91744196033-add-max-quantity-in-request-provider-table.sql b/zmsdb/migrations/91744196033-add-max-quantity-in-request-provider-table.sql
new file mode 100644
index 0000000000..82a889ede8
--- /dev/null
+++ b/zmsdb/migrations/91744196033-add-max-quantity-in-request-provider-table.sql
@@ -0,0 +1 @@
+ALTER TABLE `request_provider` ADD COLUMN `max_quantity` TINYINT DEFAULT NULL;
\ No newline at end of file
diff --git a/zmsdb/migrations/91744196033-add-public-visibility-in-request-provider-table.sql b/zmsdb/migrations/91744196033-add-public-visibility-in-request-provider-table.sql
new file mode 100644
index 0000000000..9940a294bd
--- /dev/null
+++ b/zmsdb/migrations/91744196033-add-public-visibility-in-request-provider-table.sql
@@ -0,0 +1 @@
+ALTER TABLE `request_provider` ADD COLUMN `public_visibility` BOOL DEFAULT 1;
\ No newline at end of file
diff --git a/zmsdb/src/Zmsdb/Query/RequestRelation.php b/zmsdb/src/Zmsdb/Query/RequestRelation.php
index 41461c3d6d..5a1daaa530 100644
--- a/zmsdb/src/Zmsdb/Query/RequestRelation.php
+++ b/zmsdb/src/Zmsdb/Query/RequestRelation.php
@@ -14,7 +14,9 @@ public function getEntityMapping()
             'request__id' => self::TABLE . '.request__id',
             'provider__id' => self::TABLE . '.provider__id',
             'source' => self::TABLE . '.source',
-            'slots' => self::TABLE . '.slots'
+            'slots' => self::TABLE . '.slots',
+            'public' => self::TABLE . '.public_visibility',
+            'maxQuantity' => self::TABLE . '.max_quantity'
         ];
     }
 
diff --git a/zmsdb/src/Zmsdb/RequestRelation.php b/zmsdb/src/Zmsdb/RequestRelation.php
index b835b68d51..17fefbb35d 100644
--- a/zmsdb/src/Zmsdb/RequestRelation.php
+++ b/zmsdb/src/Zmsdb/RequestRelation.php
@@ -74,7 +74,9 @@ public function writeListBySource(\BO\Zmsentities\Source $source)
                         'source' => $source->getSource(),
                         'provider' => $provider,
                         'request' => $request,
-                        'slots' => 1
+                        'slots' => 1,
+                        'public' => true,
+                        'maxQuantity' => null,
                     ]);
                     $this->writeEntity($entity);
                 }
@@ -91,7 +93,9 @@ public function writeEntity(Entity $entity)
             'source' => $entity->getSource(),
             'provider__id' => $entity->getProvider()->getId(),
             'request__id' => $entity->getRequest()->getId(),
-            'slots' => $entity->getSlotCount()
+            'slots' => $entity->getSlotCount(),
+            'public_visibility' => $entity->isPublic(),
+            'max_quantity' => $entity->getMaxQuantity()
         ]);
         $this->writeItem($query);
         return $this->readEntity($entity->getRequest()->getId(), $entity->getProvider()->getId());
@@ -109,6 +113,8 @@ public function writeImportList($providerList, $source = 'dldb')
                         'provider__id' => $provider['id'],
                         'request__id' => $reference['service'],
                         'slots' => $reference['appointment']['slots'],
+                        'public_visibility' => isset($reference['public']) && !$reference['public'] ? 0 : 1,
+                        'max_quantity' => $reference['maxQuantity'] ?? null,
                         'bookable' => ($reference['appointment']['allowed'] ? 1 : 0)
                     ]);
                     $this->writeItem($query);
diff --git a/zmsentities/schema/requestrelation.json b/zmsentities/schema/requestrelation.json
index 85e3f1a8a6..dda6f50972 100644
--- a/zmsentities/schema/requestrelation.json
+++ b/zmsentities/schema/requestrelation.json
@@ -39,6 +39,39 @@
             "description": "Number of slots required to fullfill request on provider",
             "default": 1
         },
+        "public": {
+            "oneOf": [
+                {
+                    "type": "null"
+                },
+                {
+                    "type": "number"
+                },
+                {
+                    "type": "boolean"
+                },
+                {
+                    "type": "string",
+                    "pattern": "^[01]$"
+                }
+            ],
+            "description": "Whether request is public available for specific provider",
+            "default": true
+        },
+        "maxQuantity": {
+            "oneOf": [
+                {
+                    "type": "null"
+                },
+                {
+                    "type": "number"
+                },
+                {
+                    "type": "string"
+                }
+            ],
+            "description": "Max quantity of chosen service for specific provider"
+        },
         "source": {
             "type": "string",
             "description": "source of the requestrelation",
diff --git a/zmsentities/schema/source.json b/zmsentities/schema/source.json
index 76b2f51f0c..bf3d89e1ef 100644
--- a/zmsentities/schema/source.json
+++ b/zmsentities/schema/source.json
@@ -42,6 +42,7 @@
                     "group": "Meldewesen und Ordnung"
                 },
                 "slots": 2,
+                "public": "1",
                 "source": "dldb"
             }
         ]
diff --git a/zmsentities/src/Zmsentities/RequestRelation.php b/zmsentities/src/Zmsentities/RequestRelation.php
index 0fbe0853e3..00bf13a3a3 100644
--- a/zmsentities/src/Zmsentities/RequestRelation.php
+++ b/zmsentities/src/Zmsentities/RequestRelation.php
@@ -13,6 +13,8 @@ public function getDefaults()
             'request' => new Request(),
             'source' => null,
             'slots' => '1',
+            'public' => true,
+            'maxQuantity' => null,
         ];
     }
 
@@ -31,6 +33,16 @@ public function getSlotCount()
         return $this->toProperty()->slots->get();
     }
 
+    public function getMaxQuantity()
+    {
+        return $this->toProperty()->maxQuantity->get();
+    }
+
+    public function isPublic()
+    {
+        return (bool) $this->toProperty()->public->get();
+    }
+
     public function getSource()
     {
         return $this->toProperty()->source->get();


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 5**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>zmscitizenapi/src/Zmscitizenapi/Models/OfficeServiceRelation.php (1)</summary><blockquote>

`11-72`: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Replace error_log in referenced code**

There's an issue in the `OfficeServiceRelationList` constructor which uses `error_log`. According to the coding guidelines, this should be replaced with a proper logging mechanism.


Run this script to locate the `error_log` call in the `OfficeServiceRelationList` class:

---

🏁 Script executed:

```shell
#!/bin/bash
# Verify error_log usage in OfficeServiceRelationList class
rg "error_log" zmscitizenapi/src/Zmscitizenapi/Models/Collections/OfficeServiceRelationList.php
```

Length of output: 188

---



</details>

**Action Required: Replace primitive `error_log` with a proper logging mechanism**

- In the file `zmscitizenapi/src/Zmscitizenapi/Models/Collections/OfficeServiceRelationList.php`, the following line is found:
  ```php
  error_log("Invalid OfficeServiceRelation encountered: " . $e->getMessage());
  ```
- According to our coding guidelines, this usage should be replaced with a more robust logging mechanism (for example, using a dedicated logger via dependency injection such as one implementing `Psr\Log\LoggerInterface`).
- Please update the constructor of `OfficeServiceRelationList` to utilize the approved logging solution instead of `error_log`.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>zmsentities/schema/source.json (1)</summary><blockquote>

`45-45`: **Schema value type discrepancy.**

The `public` property is defined with a string value "1" in this example, but in the actual schema (requestrelation.json), it's defined as a boolean type. Consider using a boolean value instead for consistency:

```diff
-                "public": "1",
+                "public": true,
```

</blockquote></details>
<details>
<summary>zmsentities/schema/requestrelation.json (1)</summary><blockquote>

`34-38`: **Fix typo in property description.**

There is a typo in the description for the `public` property.

```diff
-            "description": "Weather request is public available for specific provider",
+            "description": "Whether request is public available for specific provider",
```

Additionally, consider improving the wording slightly:

```diff
-            "description": "Whether request is public available for specific provider",
+            "description": "Whether request is publicly available for specific provider",
```

</blockquote></details>
<details>
<summary>zmscitizenapi/src/Zmscitizenapi/Services/Core/MapperService.php (3)</summary><blockquote>

`177-183`: **Ensure type safety for maxQuantity.**

You're passing `$relation->maxQuantity` directly to the constructor without any type checking or conversion, which could lead to issues if the value isn't an integer.


Consider adding an explicit conversion for better type safety:

```diff
  $relations[] = new OfficeServiceRelation(
      officeId: (int) $relation->provider->id,
      serviceId: (int) $relation->request->id,
      slots: intval($relation->slots),
      public: $relation->public,
-     maxQuantity: $relation->maxQuantity
+     maxQuantity: intval($relation->maxQuantity)
  );
```

---

`124-175`: **Consider refactoring to DRY up filtering logic.**

Both `mapServicesWithCombinations` and `mapRelations` methods contain similar logic for filtering unpublished relations. This violates the DRY (Don't Repeat Yourself) principle.


Consider extracting the filtering logic into a separate private static method:

```php
private static function shouldIncludeRelation($relation, bool $showUnpublished): bool
{
    return $showUnpublished || $relation->isPublic();
}
```

Then use it in both methods:

```php
if (!self::shouldIncludeRelation($relation, $showUnpublished)) {
    continue;
}
```

---

`8-38`: **Consider breaking down this large class.**

The class is marked with `@SuppressWarnings(PHPMD.ExcessiveClassComplexity)` and has a TODO comment stating it exceeds complexity metrics (101 vs 100). This PR adds more complexity to an already complex class.


Consider breaking this service down into smaller, focused mappers as suggested in the TODO comment on line 33. You could create separate mapper classes for each entity type (e.g., `OfficeMapper`, `ServiceMapper`, `RelationMapper`, etc.) that implement a common interface.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 42a33d8721db7f13b4de54623109453e35d31148 and 2c9a7009ad402e959caed5970383322110830bc8.

</details>

<details>
<summary>📒 Files selected for processing (10)</summary>

* `zmscitizenapi/src/Zmscitizenapi/Models/OfficeServiceRelation.php` (2 hunks)
* `zmscitizenapi/src/Zmscitizenapi/Services/Core/MapperService.php` (2 hunks)
* `zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php` (1 hunks)
* `zmsdb/migrations/91744196033-add-max-quantity-in-request-provider-table.sql` (1 hunks)
* `zmsdb/migrations/91744196033-add-public-visibility-in-request-provider-table.sql` (1 hunks)
* `zmsdb/src/Zmsdb/Query/RequestRelation.php` (1 hunks)
* `zmsdb/src/Zmsdb/RequestRelation.php` (3 hunks)
* `zmsentities/schema/requestrelation.json` (1 hunks)
* `zmsentities/schema/source.json` (1 hunks)
* `zmsentities/src/Zmsentities/RequestRelation.php` (2 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (1)</summary>

<details>
<summary>`**/*.php`: Flag any usage of error_log() as it should be replaced with proper logging mechanisms:
1. For error handling: Use a proper logging framework with error levels (PSR-3 Lo...</summary>

> `**/*.php`: Flag any usage of error_log() as it should be replaced with proper logging mechanisms:
> 1. For error handling: Use a proper logging framework with error levels (PSR-3 LoggerInterface)
> 2. For debugging: Use a dedicated debug logger or remove debug statements
> 3. For CLI output: Use a CLI output handler or symfony/console
> 4. For application events: Use structured logging with proper log levels
> 
> Example replacement:
> ```php
> // Instead of:
> error_log("Import failed - " . $e->getMessage());
> 
> // Use:
> $logger->error("Import failed", ['error' => $e->getMessage()]);
> ```
> 

- `zmsdb/src/Zmsdb/Query/RequestRelation.php`
- `zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php`
- `zmscitizenapi/src/Zmscitizenapi/Services/Core/MapperService.php`
- `zmscitizenapi/src/Zmscitizenapi/Models/OfficeServiceRelation.php`
- `zmsdb/src/Zmsdb/RequestRelation.php`
- `zmsentities/src/Zmsentities/RequestRelation.php`

</details>

</details><details>
<summary>🧬 Code Graph Analysis (4)</summary>

<details>
<summary>zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php (2)</summary><blockquote>

<details>
<summary>zmscitizenapi/src/Zmscitizenapi/Services/Core/MapperService.php (2)</summary>

* `MapperService` (35-382)
* `mapRelations` (169-187)

</details>
<details>
<summary>zmscitizenapi/src/Zmscitizenapi/Models/Collections/OfficeServiceRelationList.php (1)</summary>

* `OfficeServiceRelationList` (12-53)

</details>

</blockquote></details>
<details>
<summary>zmscitizenapi/src/Zmscitizenapi/Services/Core/MapperService.php (3)</summary><blockquote>

<details>
<summary>zmsentities/src/Zmsentities/RequestRelation.php (1)</summary>

* `isPublic` (41-44)

</details>
<details>
<summary>zmscitizenapi/src/Zmscitizenapi/Models/Collections/OfficeServiceRelationList.php (1)</summary>

* `OfficeServiceRelationList` (12-53)

</details>
<details>
<summary>zmscitizenapi/src/Zmscitizenapi/Models/OfficeServiceRelation.php (1)</summary>

* `OfficeServiceRelation` (11-72)

</details>

</blockquote></details>
<details>
<summary>zmscitizenapi/src/Zmscitizenapi/Models/OfficeServiceRelation.php (2)</summary><blockquote>

<details>
<summary>zmscitizenapi/src/Zmscitizenapi/Models/Collections/OfficeServiceRelationList.php (1)</summary>

* `__construct` (17-33)

</details>
<details>
<summary>zmscitizenapi/src/Zmscitizenapi/Models/Service.php (1)</summary>

* `__construct` (35-42)

</details>

</blockquote></details>
<details>
<summary>zmsdb/src/Zmsdb/RequestRelation.php (1)</summary><blockquote>

<details>
<summary>zmsentities/src/Zmsentities/RequestRelation.php (3)</summary>

* `getSlotCount` (31-34)
* `isPublic` (41-44)
* `getMaxQuantity` (36-39)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (8)</summary>

* GitHub Check: Analyze (javascript-typescript)
* GitHub Check: call-owasp-security-checks / security-scan (zmsticketprinter, 8.0)
* GitHub Check: call-owasp-security-checks / security-scan (zmsdb, 8.0)
* GitHub Check: call-owasp-security-checks / security-scan (zmsentities, 8.0)
* GitHub Check: call-owasp-security-checks / security-scan (zmsdldb, 8.0)
* GitHub Check: call-owasp-security-checks / security-scan (zmscalldisplay, 8.0)
* GitHub Check: call-owasp-security-checks / security-scan (zmsadmin, 8.0)
* GitHub Check: call-owasp-security-checks / security-scan (zmscitizenapi, 8.0)

</details>

<details>
<summary>🔇 Additional comments (11)</summary><blockquote>

<details>
<summary>zmsdb/migrations/91744196033-add-max-quantity-in-request-provider-table.sql (1)</summary>

`1-1`: **LGTM! Migration adds max_quantity column correctly.**

The migration correctly adds a `max_quantity` column of type TINYINT with NULL as the default value to the `request_provider` table. This data type is appropriate for storing small integer values that would represent quantity limits.

</details>
<details>
<summary>zmsdb/migrations/91744196033-add-public-visibility-in-request-provider-table.sql (1)</summary>

`1-1`: **LGTM! Migration adds public_visibility column correctly.**

The migration correctly adds a `public_visibility` column of type BOOL with a default value of 1 (true) to the `request_provider` table. Setting the default to 1 ensures that existing records will be publicly visible by default, which seems appropriate.

</details>
<details>
<summary>zmsdb/src/Zmsdb/Query/RequestRelation.php (1)</summary>

`17-19`: **LGTM! Entity mapping extended correctly.**

The entity mapping has been correctly extended to include the new `public` and `maxQuantity` properties, mapping them to their respective database columns (`public_visibility` and `max_quantity`). The property naming convention (camelCase) is consistent with the rest of the codebase.

</details>
<details>
<summary>zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php (1)</summary>

`220-220`: **Added `$showUnpublished` parameter to control relation visibility**

The parameter `$showUnpublished` is now correctly passed to the `mapRelations` method, ensuring that relations marked as non-public are filtered out when `$showUnpublished` is false. This is consistent with the same pattern used in other methods in this class.

</details>
<details>
<summary>zmsentities/src/Zmsentities/RequestRelation.php (2)</summary>

`16-17`: **Added new public and maxQuantity properties**

The `public` and `maxQuantity` properties are properly initialized with sensible defaults: `public` defaults to `true` (visible) and `maxQuantity` to `null` (no limit).

---

`36-44`: **Added accessor methods for new properties**

The methods `getMaxQuantity()` and `isPublic()` follow the same pattern as existing getter methods, ensuring consistency in the codebase.

</details>
<details>
<summary>zmsdb/src/Zmsdb/RequestRelation.php (3)</summary>

`77-79`: **Added public and maxQuantity to writeListBySource entity creation**

The default values used here (`public` = true, `maxQuantity` = null) are consistent with the defaults in the `RequestRelation` class.

---

`96-98`: **Updated writeEntity to include new properties in database query**

The values are properly retrieved using the new accessor methods from the entity.

---

`116-117`: **Added public and maxQuantity to writeImportList database query**

The `maxQuantity` property uses the null coalescing operator to provide a fallback value of `null` when the value is not present in the reference array. This is good defensive programming.

</details>
<details>
<summary>zmscitizenapi/src/Zmscitizenapi/Models/OfficeServiceRelation.php (2)</summary>

`27-30`: **Constructor signature and docblock updated correctly**

The constructor signature and docblock have been properly updated to include the new parameters.

---

`58-59`: **Updated toArray method to include new properties**

The `toArray` method properly includes the new properties in the returned array.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T12:18:56Z
user: login: ThomasAFink
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-04-11T07:18:32Z
user: login: Fabinatix97
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-04-11T11:20:50Z

Review comments:
diff_hunk: @@ -11,24 +11,29 @@
 class OfficeServiceRelation extends Entity implements JsonSerializable
 {
     public static $schema = 'citizenapi/officeServiceRelation.json';
-/** @var int */
+
     public int $officeId;
-/** @var int */
     public int $serviceId;
-/** @var int */
     public int $slots;
-/**
+    public bool $public;
+    public int $maxQuantity;
path: zmscitizenapi/src/Zmscitizenapi/Models/OfficeServiceRelation.php
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Type declaration for maxQuantity should accommodate null values**

The `maxQuantity` property is declared as `public int $maxQuantity`, which means it cannot be null. However, in the `RequestRelation` class, this property can be null. Consider changing the type to `public ?int $maxQuantity` to allow null values.


```diff
- public int $maxQuantity;
+ public ?int $maxQuantity;
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    public bool $public;
    public ?int $maxQuantity;
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-09T12:18:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -11,24 +11,29 @@
 class OfficeServiceRelation extends Entity implements JsonSerializable
 {
     public static $schema = 'citizenapi/officeServiceRelation.json';
-/** @var int */
+
     public int $officeId;
-/** @var int */
     public int $serviceId;
-/** @var int */
     public int $slots;
-/**
+    public bool $public;
+    public int $maxQuantity;
+
+    /**
      * Constructor.
      *
      * @param int $officeId
      * @param int $serviceId
      * @param int $slots
+     * @param bool $public
+     * @param int $maxQuantity
      */
-    public function __construct(int $officeId, int $serviceId, int $slots)
+    public function __construct(int $officeId, int $serviceId, int $slots, bool $public, int $maxQuantity)
     {
         $this->officeId = $officeId;
         $this->serviceId = $serviceId;
         $this->slots = $slots;
+        $this->public = $public;
+        $this->maxQuantity = $maxQuantity;
path: zmscitizenapi/src/Zmscitizenapi/Models/OfficeServiceRelation.php
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Update constructor type handling for maxQuantity**

In addition to fixing the property declaration, ensure the constructor type checks are consistent with handling nullable integers for `maxQuantity`.


```diff
 public function __construct(int $officeId, int $serviceId, int $slots, bool $public, int $maxQuantity)
+public function __construct(int $officeId, int $serviceId, int $slots, bool $public, ?int $maxQuantity)
```


> Committable suggestion skipped: line range outside the PR's diff.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-09T12:18:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -159,11 +166,21 @@ public static function mapServicesWithCombinations(
     }
 
 
-    public static function mapRelations(RequestRelationList $relationList): OfficeServiceRelationList
+    public static function mapRelations(RequestRelationList $relationList, bool $showUnpublished): OfficeServiceRelationList
path: zmscitizenapi/src/Zmscitizenapi/Services/Core/MapperService.php
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add default value for `$showUnpublished` parameter.**

The `$showUnpublished` parameter lacks a default value, unlike in `mapServicesWithCombinations` method where it defaults to `false`. This inconsistency could lead to unexpected behavior or developer confusion.


Add a default value for consistency:

```diff
- public static function mapRelations(RequestRelationList $relationList, bool $showUnpublished): OfficeServiceRelationList
+ public static function mapRelations(RequestRelationList $relationList, bool $showUnpublished = false): OfficeServiceRelationList
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    public static function mapRelations(RequestRelationList $relationList, bool $showUnpublished = false): OfficeServiceRelationList
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-09T12:18:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -159,11 +166,21 @@
     }
 
 
-    public static function mapRelations(RequestRelationList $relationList): OfficeServiceRelationList
+    public static function mapRelations(RequestRelationList $relationList, bool $showUnpublished): OfficeServiceRelationList
     {
         $relations = [];
         foreach ($relationList as $relation) {
-            $relations[] = new OfficeServiceRelation(officeId: (int) $relation->provider->id, serviceId: (int) $relation->request->id, slots: intval($relation->slots));
+            if (!$showUnpublished && !$relation->public) {
+                continue;
+            }
path: zmscitizenapi/src/Zmscitizenapi/Services/Core/MapperService.php
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Use the `isPublic()` method for consistency.**

As mentioned earlier, you're directly accessing the `public` property here instead of using the `isPublic()` method that's used in `mapServicesWithCombinations`.


Replace direct property access with the method call:

```diff
- if (!$showUnpublished && !$relation->public) {
+ if (!$showUnpublished && !$relation->isPublic()) {
    continue;
}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
            if (!$showUnpublished && !$relation->isPublic()) {
                continue;
            }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-09T12:18:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -121,6 +121,13 @@
         /** @var array<string, array<int>> $servicesProviderIds */
         $servicesProviderIds = [];
         foreach ($relationList as $relation) {
+            if (
+                !$showUnpublished
+                && !$relation->isPublic()
+            ) {
+                continue;
+            }
path: zmscitizenapi/src/Zmscitizenapi/Services/Core/MapperService.php
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Ensure consistent approach for checking public visibility.**

There's an inconsistency between how public visibility is checked here and in the `mapRelations` method. Here you're using `$relation->isPublic()` method, but in `mapRelations` (lines 173-175) you directly access the `$relation->public` property.


Choose one consistent approach for better maintainability. Consider using the `isPublic()` method in both places, as it provides a cleaner abstraction:

```diff
- if (!$showUnpublished && !$relation->public) {
+ if (!$showUnpublished && !$relation->isPublic()) {
    continue;
}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
            if (!$showUnpublished && !$relation->isPublic()) {
                continue;
            }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-09T12:18:54Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-09T12:16:17Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
This pull request extends several service and data mapping classes by introducing two new properties—`public` (bool) and `maxQuantity` (int). The changes update constructors, mapping methods, and JSON schemas to incorporate these attributes. Additionally, new SQL migration files add corresponding columns to the database. These alterations ensure that various layers, from API models to database queries, support enhanced filtering and data serialization based on public visibility and quantity limits.

## Changes

| File(s) | Change Summary |
|---------|----------------|
| `zmscitizenapi/src/.../OfficeServiceRelation.php` | Added properties `public` (bool) and `maxQuantity` (int); updated constructor and `toArray` method to include new attributes. |
| `zmscitizenapi/src/.../MapperService.php`<br>`zmscitizenapi/src/.../ZmsApiFacadeService.php` | Modified mapping functions to include a `showUnpublished` flag for filtering; updated method signatures and related calls accordingly. |
| `zmsdb/migrations/91744196033-add-max-quantity...sql`<br>`zmsdb/migrations/91744196033-add-public-visibility...sql` | Introduced two SQL migration files adding `max_quantity` (TINYINT, default NULL) and `public_visibility` (BOOL, default 1) columns to the `request_provider` table. |
| `zmsdb/src/.../Query/RequestRelation.php`<br>`zmsdb/src/.../RequestRelation.php` | Updated mapping and write methods to include new keys/fields: `public` and `maxQuantity` in entity arrays and queries. |
| `zmsentities/schema/requestrelation.json`<br>`zmsentities/schema/source.json` | Updated JSON schemas by adding a boolean `public` property and a `maxQuantity` property to the request relation definitions. |
| `zmsentities/src/.../RequestRelation.php` | Added new default values for `public` (true) and `maxQuantity` (null) in the defaults array; introduced accessor methods `isPublic` and `getMaxQuantity`. |
| `zmscitizenapi/tests/.../MapperServiceTest.php`<br>`zmscitizenapi/tests/.../OfficesServicesRelationsControllerTest.php` | Updated test methods to include new parameters and fields for `public` and `maxQuantity` in assertions. |

## Sequence Diagram(s)
```mermaid
sequenceDiagram
    participant C as Client
    participant F as ZmsApiFacadeService
    participant M as MapperService
    participant R as OfficeServiceRelation

    C->>F: getServicesAndOffices(showUnpublished)
    F->>M: mapRelations(relationList, showUnpublished)
    M->>M: Iterate over relationList
    alt Relation is public or showUnpublished true
        M->>R: Instantiate OfficeServiceRelation(relation, public, maxQuantity)
    end
    M-->>F: Return OfficeServiceRelationList
    F-->>C: Return services & offices result
```

## Poem
> I'm a rabbit in the coding glade,  
> Hoping through changes where new fields are laid.  
> Public flags and quantities now shine so bright,  
> Guiding each relation through day and night.  
> With swift hops through code and schema anew,  
> I cheer for these changes—hip, hip, hooray to you!  
> 🐇✨

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Free**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between ad152a60734f0ed4eca2fa5b78fe91d6d6a7c688 and 37d5830da30f307eb09e9f9e89a4e0a74400e634.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `zmscitizenapi/src/Zmscitizenapi/Services/Core/MapperService.php` (2 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* zmscitizenapi/src/Zmscitizenapi/Services/Core/MapperService.php

</details>

</details>
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- tips_start -->

---

> [!NOTE]
> <details>
> <summary>🎁 Summarized by CodeRabbit Free</summary>
> 
> Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting <https://app.coderabbit.ai/login>.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.
> 
> </details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=it-at-m/eappointment&utm_content=992):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: manjencic
created_at: 2025-04-16T06:32:55Z
body: Approved zusammen.
author_association: CONTRIBUTOR