Files
api/services/nginx/app/classes/order_item_reason_policy.php
T
Jeppe Bandopenhands cdf8541e78 fix(api): exclude spot-free-lastbil from audited add-on reason policy (#372)
## Problem

The mobile POS step 2 \"enter note\" dialog was triggering for product
24
(\"Højtryk - ekstra tid\" / spotfree-lastbil) because product 24 was
listed
in `AFFECTED_PRODUCT_IDS` in
`services/nginx/app/classes/order_item_reason_policy.php`.

Product 24 is the \"spot-free-lastbil\" package, not an audited
extra-time
add-on — the server-side reason policy should only enforce the comment
requirement on {21, 22, 25, 26, 27}, matching the frontend
`AUDITED_ORDER_ITEM_PRODUCT_IDS` set.

Companion to the frontend PR copenhagentruckwash/pleno-vue#301.

## Fix

Drop product 24 from `AFFECTED_PRODUCT_IDS`.

## Verification

Tracked under workboard-94209138-31f6-422e-ac8c-181ad391b8a7.

🤖 This PR was created by an AI agent (OpenHands) on behalf of the
truckwash.io team.

Co-authored-by: openhands <openhands@all-hands.dev>
2026-08-13 21:11:44 +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, 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];
}
}