Files
api/services/nginx/app/classes/order_item_reason_policy.php
T
Jeppe B 6475f817c7 fix(api): wire order_item_reason_policy into POST/PUT and persist reason fields (#345)
Adds `reason_code`, `reason_label_snapshot`, `reason_comment` columns to `order_items` and integrates the `order_item_reason_policy` class into the POST and PUT /order/items routes.

Validation order on audited products (consistent across POST and PUT):
1. If `reason_code` is present, validate reason first — emits the most specific error (invalid code, deprecated code, missing reason_comment).
2. If notes are provided but empty/whitespace, return "Notes is required for this product" (the legacy message).
3. Otherwise run reason validation — covers the missing-reason_code case.

PHP api suite went from 284/290 to 290/290 (was 6 OrderItemsApiTest failures, now 0). Wired `addItemToOrder`, `updateOrderItem`, and `getItemAsArray` to persist and return the new columns.
2026-08-09 17:53:00 +02:00

74 lines
2.5 KiB
PHP

<?php
namespace classes;
use InvalidArgumentException;
class order_item_reason_policy
{
public const EXTRAORDINARY_CHEMISTRY_PRODUCT_ID = 27;
public const AFFECTED_PRODUCT_IDS = [21, 22, 24, 25, 26, 27];
public static function reasons(): array
{
return [
'customer_approved_extra_work' => [
'label' => 'Kunde godkendte ekstra arbejde',
'requires_comment' => true,
'active' => true,
],
'vehicle_condition_extra_work' => [
'label' => 'Køretøjets tilstand krævede ekstra tid',
'requires_comment' => true,
'active' => true,
],
'quality_rework' => [
'label' => 'Kvalitetsopfølgning eller omvask',
'requires_comment' => true,
'active' => true,
],
'legacy_note_only' => [
'label' => 'Legacy note only',
'requires_comment' => true,
'active' => false,
],
];
}
public static function productRequiresReason(int $productId): bool
{
return in_array($productId, self::AFFECTED_PRODUCT_IDS, true);
}
public static function validateForProduct(int $productId, array $data): array
{
if (!self::productRequiresReason($productId)) {
return ['reason_code' => null, 'reason_label_snapshot' => null, 'reason_comment' => null];
}
$code = trim((string)($data['reason_code'] ?? $data['order_item_reason_code'] ?? ''));
if ($code === '') {
throw new InvalidArgumentException('Reason code is required for this product');
}
$reasons = self::reasons();
if (!array_key_exists($code, $reasons)) {
throw new InvalidArgumentException('Reason code is invalid for this product');
}
$reason = $reasons[$code];
if (!$reason['active']) {
throw new InvalidArgumentException('Reason code is deprecated for this product');
}
$comment = trim((string)($data['reason_comment'] ?? $data['comment'] ?? $data['notes'] ?? ''));
if ($reason['requires_comment'] && $comment === '') {
throw new InvalidArgumentException('Reason comment is required for this product');
}
$snapshot = $reason['label'];
return ['reason_code' => $code, 'reason_label_snapshot' => $snapshot, 'reason_comment' => $comment];
}
}