From 8daeb41ba1ffe9231dab7a89a4a364b079390e35 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 5 Aug 2026 17:41:45 +0000 Subject: [PATCH] =?UTF-8?q?Kr=C3=A4v=20kundens=20besked=20=C3=A4ven=20i=20?= =?UTF-8?q?klienten=20innan=20avslut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grinden på servern har alltid krävt kundbeskedet när arbete faktiskt utförts. Klientens kanAvslutas gjorde inte det, och glappet syntes först när tio ärenden av olika karaktär kördes hela vägen genom gränssnittet: samtliga tio gick att stänga på skärmen med utfört arbete och utan att kunden hade sagt ja. Det är samma sorts fel som slutsatsen hade, på ett annat villkor — och det värre av de två, eftersom ett utfört arbete utan registrerat besked är precis vad en tvist handlar om. Hindertexten namnger nu kundbeskedet, och ett test speglar klientens avslutsvillkor mot grindens hela åtgärdskedja så att nästa villkor som läggs till i grinden inte kan glömmas i klienten. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt --- .../src/felsokning/__tests__/slutsats.test.ts | 50 +++++++++++++++++++ .../app/src/pages/felsokning/ArendeSida.tsx | 11 +++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/felsokning/app/src/felsokning/__tests__/slutsats.test.ts b/felsokning/app/src/felsokning/__tests__/slutsats.test.ts index 8b4f135..ea76ec1 100644 --- a/felsokning/app/src/felsokning/__tests__/slutsats.test.ts +++ b/felsokning/app/src/felsokning/__tests__/slutsats.test.ts @@ -9,6 +9,7 @@ // inte blivit slutsatsen måste bemötas. Utan den är en felsökning en // gissning som råkade stämma — och det är precis den skillnaden en // försäkringsbedömare försöker avgöra. +import { readFileSync } from "node:fs"; import { describe, expect, it } from "vitest"; import { MINSTA_MOTIVERING, @@ -225,3 +226,52 @@ describe("avslutsvillkoret är detsamma i klienten och i grinden", () => { expect(await slutsatsHinder(logg)).not.toHaveLength(0); }); }); + +// ---- Hela avslutskedjan, inte bara slutsatsen --------------------------- +// +// Hittat genom att köra tio ärenden av olika karaktär hela vägen genom +// gränssnittet: samtliga gick att stänga på skärmen med utfört arbete och +// utan kundbesked — men grinden på servern kräver beskedet. Samma glapp +// som slutsatsen hade, på ett annat villkor. +// +// Testet speglar klientens kanAvslutas mot grinden för hela kedjan, så +// att nästa villkor som läggs till i grinden inte kan glömmas i klienten. +describe("klientens avslutsvillkor täcker hela grindens åtgärdskedja", () => { + const KEDJA = [ + { typ: "reproducering", status: "ja", beskrivning: "Reproducerad vid provkörning i 90 km/h på plan väg." }, + { + typ: "felorsak", + avvikelse: "Höger framhjul har 38 g obalans mot gränsen 10 g.", + orsaker: ["Normalt slitage"], + underlag: ["Mätresultat"], + sakerhet: "hog", + atgard: "Balansera om höger framhjul.", + }, + { typ: "atgard_utford", beskrivning: "Balanserade om höger framhjul.", utford: true }, + { typ: "kvalitetskontroll", resultat: "symptomet_borta", beskrivning: "Ny provkörning — ingen vibration." }, + ]; + + const gateHinder = async (handelser: unknown[]) => { + const { grinda } = await import("../../../../services/gemensam/grind.mjs"); + const { ALLA_METODIKER } = await import("../../../../services/gemensam/metodiker.mjs"); + return grinda(handelser, ALLA_METODIKER.at(-1)).map((h) => h.id); + }; + + it("utfört arbete utan kundbesked spärras av grinden", async () => { + expect(await gateHinder(KEDJA)).toContain("kundbeslut"); + }); + + it("med kundbesked försvinner det hindret", async () => { + const med = [...KEDJA, { typ: "kundbeslut", beslut: "godkant", kanal: "telefon" }]; + expect(await gateHinder(med)).not.toContain("kundbeslut"); + }); + + it("klienten kräver samma sak — kundbeslut ingår i underlagKlart", () => { + const kod = readFileSync("src/pages/felsokning/ArendeSida.tsx", "utf8"); + const rad = kod.match(/const underlagKlart =[\s\S]*?;/)?.[0] ?? ""; + expect(rad).toContain("reproducering(arende)"); + expect(rad).toContain("felorsaker(arende)"); + expect(rad).toContain("kvalitetskontroll(arende)"); + expect(rad).toContain("kundbeslut(arende)"); + }); +}); diff --git a/felsokning/app/src/pages/felsokning/ArendeSida.tsx b/felsokning/app/src/pages/felsokning/ArendeSida.tsx index 8ae42f2..e93598c 100644 --- a/felsokning/app/src/pages/felsokning/ArendeSida.tsx +++ b/felsokning/app/src/pages/felsokning/ArendeSida.tsx @@ -1257,11 +1257,17 @@ function GuideFlik({ // Underlaget: det som gör att en slutsats över huvud taget går att // motivera. Innan detta är på plats vore varför-frågan en begäran om // en gissning. + // + // Kundbeskedet hör till kedjan när arbete faktiskt utförts. Grinden på + // servern har alltid krävt det; klienten gjorde det inte, med följden + // att ett ärende gick att stänga på skärmen och nekas först vid synk. + // Samma sorts glapp som slutsatsen hade — hittat genom att köra tio + // ärenden hela vägen och jämföra mot grinden. const underlagKlart = !!reproducering(arende) && felorsaker(arende).length > 0 && atgardsposter.length > 0 && - (!utfordAtgard || !!kvalitetskontroll(arende)); + (!utfordAtgard || (!!kvalitetskontroll(arende) && !!kundbeslut(arende))); // ALVA-RULE-200. Samma granskning som kvalitetsgrinden på servern kör // — avsiktligt samma funktion, inte en klientkopia av regeln. En @@ -1443,7 +1449,8 @@ function Avslutshinder({ underlagKlart, brister }: { underlagKlart: boolean; bri {!underlagKlart && (

Underlaget är ofullständigt: avslut kräver symptomverifiering, felorsaksanalys, dokumenterad åtgärd och — - vid utförd åtgärd — kvalitetskontroll av att symptomet är borta. Se panelerna nedan. + vid utförd åtgärd — kvalitetskontroll av att symptomet är borta samt kundens besked på åtgärdsförslaget. + Se panelerna nedan.

)} {brister.length > 0 && (