Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,17 @@ Vrij en open source onder de EUPL-1.2-licentie.

<repair-steps>
<post-migration>
<!--
MUST stay first. OpenRegister's import matches a schema to an
existing row by SLUG, so a renamed slug in the register JSON
renames nothing: the import finds no match, creates a SECOND
schema, and every stored object keeps pointing at the old one —
data stranded behind a schema nothing reads, which presents as
an app with no records rather than as an error. Renaming the
slug on the existing row first makes the import recognise it.
Ahead of the import trigger, InitializeSettings.
-->
<step>OCA\Procest\Repair\RenameDutchSchemaSlugs</step>
<step>OCA\Procest\Repair\InitializeSettings</step>
<step>OCA\Procest\Repair\LoadDefaultZgwMappings</step>
<step>OCA\Procest\Repair\SeedBezwaarBeroepData</step>
Expand Down
2 changes: 1 addition & 1 deletion lib/Controller/ContactMomentController.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public function create(): JSONResponse {
}

$data = [
'kanaal' => (string)$this->request->getParam('kanaal', ''),
'notificationChannel' => (string)$this->request->getParam('notificationChannel', ''),
'direction' => (string)$this->request->getParam('direction', 'inbound'),
'callerIdentification' => (string)$this->request->getParam('callerIdentification', ''),
'nature' => (string)$this->request->getParam('nature', 'informatieverzoek'),
Expand Down
2 changes: 1 addition & 1 deletion lib/Controller/DwangsomController.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ private function denyUnlessMayAccess(string $calculationId, bool $mutation): ?JS
$caseId = $this->owningCase->resolveVia(
objectId: $calculationId,
schemaKey: 'dwangsom_berekening_schema',
linkField: 'termijnInstance',
linkField: 'deadlineInstance',
viaSchemaKey: 'termijn_instance_schema',
caseField: 'case'
);
Expand Down
2 changes: 1 addition & 1 deletion lib/Controller/NoticeOfDefaultController.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public function register(): JSONResponse {
}

$instanceId = (string)($body['termijnInstanceId'] ?? '');
$channel = (string)($body['kanaal'] ?? '');
$channel = (string)($body['notificationChannel'] ?? '');
$whenStr = (string)($body['receiptDate'] ?? '');
$documentLink = (string)($body['documentLink'] ?? '');
if ($instanceId === '' || $channel === '' || $whenStr === '') {
Expand Down
4 changes: 2 additions & 2 deletions lib/Controller/ParafeerActieController.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public function create(): JSONResponse {

try {
$data = $this->getRequestBody();
$proposalId = (string)($data['voorstel'] ?? '');
$proposalId = (string)($data['proposal'] ?? '');
if ($proposalId === '') {
return new JSONResponse(
['message' => 'voorstel is required'],
Expand Down Expand Up @@ -140,7 +140,7 @@ public function index(): JSONResponse {
}

try {
$proposalId = (string)($this->request->getParam('voorstel') ?? '');
$proposalId = (string)($this->request->getParam('proposal') ?? '');
if ($proposalId === '') {
return new JSONResponse(
['message' => 'voorstel is required'],
Expand Down
4 changes: 2 additions & 2 deletions lib/Controller/ParaferingAuditExportController.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public function export(string $id, string $format = 'json'): JSONResponse {
} catch (Throwable $e) {
$this->logger->error(
'Procest: parafering audit export failed',
['voorstel' => $id, 'exception' => $e->getMessage()],
['proposal' => $id, 'exception' => $e->getMessage()],
);

return new JSONResponse(
Expand Down Expand Up @@ -224,7 +224,7 @@ private function resolveProposalOnderwerp(string $proposalId): ?string {
} catch (Throwable $e) {
$this->logger->warning(
'Procest: failed to resolve voorstel onderwerp for export',
['voorstel' => $proposalId, 'exception' => $e->getMessage()],
['proposal' => $proposalId, 'exception' => $e->getMessage()],
);

return null;
Expand Down
4 changes: 2 additions & 2 deletions lib/Cron/OriDataQualityCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -246,14 +246,14 @@ private function checkRaadsledenReferenceIntegrity(object $objectService): array
}

try {
$politicalGroup = $this->findObjectAsArray(
$fractie = $this->findObjectAsArray(
objectService: $objectService,
register: 'ori',
schema: 'fractie',
id: $politicalGroupRef
);

if ($politicalGroup === null) {
if ($fractie === null) {
$issues[] = [
'schema' => 'raadslid',
'slug' => $rlSlug,
Expand Down
2 changes: 1 addition & 1 deletion lib/Listener/ApprovalStepNotificationListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ private function loadProposal(string $objectUuid): array {
} catch (Throwable $e) {
$this->logger->warning(
'Procest: could not load voorstel for approval notification',
['voorstel' => $objectUuid, 'exception' => $e->getMessage()]
['proposal' => $objectUuid, 'exception' => $e->getMessage()]
);
}

Expand Down
4 changes: 2 additions & 2 deletions lib/Listener/ParaferingAuditListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public function handle(Event $event): void {
if ($object === null) {
$this->logger->warning(
'Procest: ParaferingAuditListener could not resolve voorstel ObjectEntity; audit entry skipped',
['voorstel' => $event->getVoorstelId()],
['proposal' => $event->getVoorstelId()],
);
return;
}
Expand All @@ -104,7 +104,7 @@ public function handle(Event $event): void {
$this->logger->error(
'Procest: ParaferingAuditListener failed',
[
'voorstel' => $event->getVoorstelId(),
'proposal' => $event->getVoorstelId(),
'action' => $event->getAction(),
'exception' => $e->getMessage(),
],
Expand Down
8 changes: 8 additions & 0 deletions lib/Repair/RenameDutchColumns.php
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,14 @@ class RenameDutchColumns implements IRepairStep {
'wachtwoord_kluis_ref' => 'password_vault_ref',
'zaak_identificatie' => 'case_identification',
'zaak_identificatie_strategie' => 'case_identification_strategy',
'avg_classificatie' => 'gdpr_classification',
'catalogus' => 'catalog',
'dwangsom_berekening' => 'penalty_payment_calculation',
'ingebrekestelling' => 'notice_of_default',
'kanaal' => 'notification_channel',
'termijn_definitie' => 'deadline_definition',
'termijn_instance' => 'deadline_instance',
'voorstel' => 'proposal',
];

/**
Expand Down
157 changes: 157 additions & 0 deletions lib/Repair/RenameDutchSchemaSlugDecisions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
<?php

/**
* The pure decisions behind RenameDutchSchemaSlugs.
*
* A collaborator rather than static helpers, because the ruleset forbids static
* access — and an object keeps these testable on their own. They take plain
* arrays and scalars and touch neither database nor logger, which is what makes
* the DECISION unit-testable while the DDL that follows it is not.
*
* Same split as softwarecatalog's RenameDutchSchemaSlugDecisions, for the same reason: a repair step
* that reaches the database cannot be exercised without one, so everything that
* can be decided before touching it is decided here.
*
* @category Repair
* @package OCA\Procest\Repair
* @author Conduction B.V. <info@conduction.nl>
* @copyright 2026 Conduction B.V.
* @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
* @link https://www.conduction.nl
*
* SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>
* SPDX-License-Identifier: EUPL-1.2
*/

declare(strict_types=1);

namespace OCA\Procest\Repair;

/**
* Pure predicates for the Dutch-to-English schema slug migration.
*
* @spec exclude No canonical spec covers the Dutch-to-English vocabulary
* migration. Pointing this at an existing spec would report conformance to a
* requirement that says nothing about it.
*/
class RenameDutchSchemaSlugDecisions {

/**
* Decide which slugs may be renamed, given what the install actually holds.
*
* Returns the renames in the order they must be applied, plus the ones
* refused and why. Two schemas cannot share a slug, so a target that is
* already present means BOTH are left alone: merging them is a decision
* about data, not a rename.
*
* The `$existing` set is updated as it goes, so a rename earlier in the map
* is visible to the collision check of a later one — otherwise two entries
* targeting the same name would both look safe.
*
* @param array<string, string> $map Old slug => new slug.
* @param array<int, string> $existing Slugs currently present.
*
* @return array{renames: array<string, string>, refused: array<string, string>}
*
* @spec exclude No canonical spec covers the Dutch-to-English vocabulary
* migration. Pointing this at an existing spec would report conformance to a
* requirement that says nothing about it.
*/
public function plan(array $map, array $existing): array {
$renames = [];
$refused = [];

foreach ($map as $old => $new) {
if (in_array($old, $existing, true) === false) {
// Not on this install — not a refusal, just nothing to do.
continue;
}

if (in_array($new, $existing, true) === true) {
$refused[$old] = sprintf("target slug '%s' already exists", $new);
continue;
}

$renames[$old] = $new;
$existing[] = $new;
}

return [
'renames' => $renames,
'refused' => $refused,
];
}//end plan()

/**
* Pull the schema ids out of the registers' `schemas` JSON column.
*
* The column is JSON, and a register row can carry null, a malformed value
* or a list with non-numeric entries. Every one of those must yield "no ids"
* rather than a fatal, because this runs inside a repair step where an
* exception aborts the upgrade.
*
* @param array<int, array<string, mixed>> $rows Register rows.
*
* @return array<int, int> Distinct schema ids.
*
* @spec exclude No canonical spec covers the Dutch-to-English vocabulary
* migration. Pointing this at an existing spec would report conformance to a
* requirement that says nothing about it.
*/
public function schemaIdsFrom(array $rows): array {
$ids = [];

foreach ($rows as $row) {
$decoded = json_decode((string)($row['schemas'] ?? '[]'), true);
if (is_array($decoded) === false) {
continue;
}

foreach ($decoded as $id) {
if (is_numeric($id) === true) {
$ids[] = (int)$id;
}
}
}

return array_values(array_unique($ids));
}//end schemaIdsFrom()

/**
* Build the `?,?,?` placeholder list for an IN clause.
*
* Trivial, and here rather than inline because the step builds one three
* times and a mismatch between the placeholder count and the bound
* parameters is the kind of error that only shows up at runtime, inside a
* repair step, on somebody else's install.
*
* @param int $count Number of bound parameters.
*
* @return string The placeholder list.
*
* @spec exclude No canonical spec covers the Dutch-to-English vocabulary
* migration. Pointing this at an existing spec would report conformance to a
* requirement that says nothing about it.
*/
public function placeholders(int $count): string {
return implode(',', array_fill(0, max(0, $count), '?'));
}//end placeholders()
/**
* Pull the slugs out of schema rows.
*
* Sibling of schemaIdsFrom(), and defensive for the same reason: a row with
* a null slug must yield an empty string rather than a TypeError inside a
* repair step, where an exception aborts the upgrade.
*
* @param array<int, array<string, mixed>> $rows Schema rows.
*
* @return array<int, string> The slugs.
*
* @spec exclude No canonical spec covers the Dutch-to-English vocabulary
* migration. Pointing this at an existing spec would report conformance to a
* requirement that says nothing about it.
*/
public function slugsFrom(array $rows): array {
return array_map(static fn (array $row): string => (string)($row['slug'] ?? ''), $rows);
}//end slugsFrom()
}//end class
Loading
Loading