Betalarspåret: beviskraven följer den som betalar — ALVA 3.6
Garantistandarden FGS-1.0 (fabriksgaranti, vagnskadegaranti, försäkring, extern garantigivare, leasing/fleet, goodwill) inarbetad i regelmotorn och bokförd i docs/GARANTISTANDARD-FGS.md (ALVA-DOC-0011): vad som redan var uppfyllt, vad som införs nu, och den ärliga gränsen — ODIS/DISS/SAGA2 är tillverkarsystem; ALVA bär bevispaketet och eskaleringsspåret, den ersätter dem inte. Tre nya händelsetyper i det stängda schemat: betalare (spår, namn, claim-/skadereferens, godkännande), eskalering (öppnad/besvarad, kanal, referens) och reservdel (artikelnummer, serienummer, batch, sparad del). Grinden kräver fastställd betalare för de ärendetyper där någon annan än kunden betalar, godkännande för goodwill och externa garantigivare, och spärrar avslut när en eskalering är öppnad utan dokumenterat svar — matchad per referens. Claim- och skadenummer kan komma ur betalaren, inte bara ur skannad arbetsorder. Tre nya ärendetyper med regelpaketskrav: Body damage warranty, Extended warranty, Leasing or fleet. Betalare, eskalering och reservdel är interna i delningsfiltret — kunden ser åtgärden, inte förhandlingen. Fyndet på vägen, åtgärdat med regressionstest: serverns ärendetypskrav var en tyst no-op. Servern matade den distribuerade paketformen (arendetypRegler) in i en grindfunktion som bara läste den slimmade (arendetyper[typ].krav) — garanti- och försäkringskraven upprätthölls bara som råd i klienten. C-2-mönstret, återuppstånden genom ett filformat. Grinden läser nu båda formerna, och regressionstestet låser att den distribuerade formen spärrar på servern. UI: betalar- och eskaleringspaneler i ärendevyn (panelen visas när ärendetypen kräver den), grindnyckeln grind.eskalering på alla tio språken, regelpaket 2.1 speglat klient/server med likhetstestet intakt. 781 enhetstester, genomgång 4/4 inom budget, portalspärr, integrationstest mot Postgres, typkontroll, lint på baslinjen, artefaktmätning och språkrunda gröna. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
This commit is contained in:
@@ -181,7 +181,7 @@ describe("Traceability Engine + ärendeidentitet", () => {
|
|||||||
it("spårbarhetspaketet innehåller version, grindstatus och evidensposter", () => {
|
it("spårbarhetspaketet innehåller version, grindstatus och evidensposter", () => {
|
||||||
const arende = byggArende([OBJEKT, { typ: "foto", beskrivning: "x", dataUrl: "data:" }]);
|
const arende = byggArende([OBJEKT, { typ: "foto", beskrivning: "x", dataUrl: "data:" }]);
|
||||||
const paket = sparbarhetspaket(arende, VIBRATION_METODIK);
|
const paket = sparbarhetspaket(arende, VIBRATION_METODIK);
|
||||||
expect(paket.ecmVersion).toBe("2.0");
|
expect(paket.ecmVersion).toBe("2.1");
|
||||||
expect(paket.arendetyp).toBe("Private customer");
|
expect(paket.arendetyp).toBe("Private customer");
|
||||||
expect(paket.evidensposter).toHaveLength(1);
|
expect(paket.evidensposter).toHaveLength(1);
|
||||||
expect(paket.kvalitetsgrind.some((r) => r.id === "objekt" && r.ok)).toBe(true);
|
expect(paket.kvalitetsgrind.some((r) => r.id === "objekt" && r.ok)).toBe(true);
|
||||||
|
|||||||
@@ -572,3 +572,74 @@ describe("mätarställningen ska vara fotograferad, inte bara inskriven", () =>
|
|||||||
expect(grinda(tomt, GENERISK).map((h) => h.id)).toContain("matarstallning_ingaende");
|
expect(grinda(tomt, GENERISK).map((h) => h.id)).toContain("matarstallning_ingaende");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ---- FGS-1.0: betalarspåret och eskaleringen ----------------------------
|
||||||
|
//
|
||||||
|
// Betalaren avgör beviskraven, och en öppnad teknisk eskalering utan
|
||||||
|
// dokumenterat svar spärrar avslutet — bulletinernas "invänta svar innan
|
||||||
|
// ytterligare åtgärder" som grindregel, inte som råd.
|
||||||
|
describe("FGS · betalare och eskalering", () => {
|
||||||
|
it("en öppnad eskalering spärrar avslutet, ett dokumenterat svar släpper", () => {
|
||||||
|
const oppen = [...komplettLogg(), { typ: "eskalering", status: "oppnad", beskrivning: "DISS query", referens: "D-1" }];
|
||||||
|
expect(grinda(oppen, GENERISK).map((h) => h.id)).toContain("eskalering");
|
||||||
|
const besvarad = [...oppen, { typ: "eskalering", status: "besvarad", beskrivning: "Factory answer", referens: "D-1" }];
|
||||||
|
expect(grinda(besvarad, GENERISK)).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("svaret matchas per referens — ett svar på fel förfrågan släpper inte", () => {
|
||||||
|
const logg = [
|
||||||
|
...komplettLogg(),
|
||||||
|
{ typ: "eskalering", status: "oppnad", beskrivning: "Battery query", referens: "D-1" },
|
||||||
|
{ typ: "eskalering", status: "oppnad", beskrivning: "HV query", referens: "D-2" },
|
||||||
|
{ typ: "eskalering", status: "besvarad", beskrivning: "Answer", referens: "D-1" },
|
||||||
|
];
|
||||||
|
const hinder = grinda(logg, GENERISK).filter((h) => h.id === "eskalering");
|
||||||
|
expect(hinder).toHaveLength(1);
|
||||||
|
expect(hinder[0].detalj).toContain("D-2");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("betalarkravet uppfylls av en registrerad betalare med spår och namn", () => {
|
||||||
|
const paket = { arendetypRegler: { Warranty: [{ id: "w_betalare", rubrik: "Payer established", krav: "betalare", detaljVidBrist: "Record the payer." }] } };
|
||||||
|
const utan = [{ typ: "arendetyp_satt", arendetyp: "Warranty" }];
|
||||||
|
expect(grindaArendetyp(utan, paket).map((h) => h.id)).toEqual(["arendetyp_betalare"]);
|
||||||
|
const med = [...utan, { typ: "betalare", spar: "fabriksgaranti", namn: "Volkswagen AG" }];
|
||||||
|
expect(grindaArendetyp(med, paket)).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("claim-referensen kan komma ur betalaren, inte bara ur skannad arbetsorder", () => {
|
||||||
|
const paket = { arendetyper: { Warranty: { krav: ["claim"] } } };
|
||||||
|
const utan = [{ typ: "arendetyp_satt", arendetyp: "Warranty" }];
|
||||||
|
expect(grindaArendetyp(utan, paket).map((h) => h.id)).toEqual(["arendetyp_claim"]);
|
||||||
|
const med = [...utan, { typ: "betalare", spar: "fabriksgaranti", namn: "VW", referens: "CL-4711" }];
|
||||||
|
expect(grindaArendetyp(med, paket)).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("goodwill kräver godkännande — en betalare utan klartecken räcker inte", () => {
|
||||||
|
const paket = { arendetypRegler: { Goodwill: [{ id: "g", rubrik: "Approval", krav: "godkannande", detaljVidBrist: "Approval required." }] } };
|
||||||
|
const utanKlartecken = [
|
||||||
|
{ typ: "arendetyp_satt", arendetyp: "Goodwill" },
|
||||||
|
{ typ: "betalare", spar: "goodwill", namn: "Importören" },
|
||||||
|
];
|
||||||
|
expect(grindaArendetyp(utanKlartecken, paket).map((h) => h.id)).toEqual(["arendetyp_godkannande"]);
|
||||||
|
const med = [...utanKlartecken, { typ: "betalare", spar: "goodwill", namn: "Importören", godkannande: "GW-9" }];
|
||||||
|
expect(grindaArendetyp(med, paket)).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("den distribuerade paketformen (arendetypRegler) utvärderas på servern", () => {
|
||||||
|
// Regression: servern matade in filens rika form i en funktion som
|
||||||
|
// bara läste den slimmade — varje ärendetypskrav blev en tyst no-op
|
||||||
|
// och spärren fanns bara som råd i klienten. C-2, återuppstånden
|
||||||
|
// genom ett format.
|
||||||
|
const rikt = {
|
||||||
|
arendetypRegler: {
|
||||||
|
Warranty: [{ id: "garanti_miltal", rubrik: "Mileage documented", krav: "miltal", detaljVidBrist: "Odometer reading required." }],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const utan = [{ typ: "arendetyp_satt", arendetyp: "Warranty" }];
|
||||||
|
const hinder = grindaArendetyp(utan, rikt);
|
||||||
|
expect(hinder.map((h) => h.id)).toEqual(["arendetyp_miltal"]);
|
||||||
|
expect(hinder[0].rubrik).toBe("Mileage documented");
|
||||||
|
expect(hinder[0].detalj).toBe("Odometer reading required.");
|
||||||
|
expect(grindaArendetyp([...utan, { typ: "matarstallning", lage: "ingaende", varde: "12000" }], rikt)).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -87,6 +87,27 @@ export interface Bilaga {
|
|||||||
dataUrl?: string;
|
dataUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FGS-1.0 §3: betalarspåren. Datavärdena är invarianta (svenska tokens i
|
||||||
|
// loggen, precis som "godkant"/"ingaende"); etiketterna är gränssnitt.
|
||||||
|
export type Betalarspar =
|
||||||
|
| "fabriksgaranti"
|
||||||
|
| "vagnskadegaranti"
|
||||||
|
| "forsakring"
|
||||||
|
| "extern_garanti"
|
||||||
|
| "leasing"
|
||||||
|
| "goodwill"
|
||||||
|
| "kund";
|
||||||
|
|
||||||
|
export const BETALARSPAR_LABEL: Record<Betalarspar, string> = {
|
||||||
|
fabriksgaranti: "Factory warranty",
|
||||||
|
vagnskadegaranti: "Body damage warranty",
|
||||||
|
forsakring: "Insurance",
|
||||||
|
extern_garanti: "External warranty",
|
||||||
|
leasing: "Leasing or fleet",
|
||||||
|
goodwill: "Goodwill",
|
||||||
|
kund: "Customer pays",
|
||||||
|
};
|
||||||
|
|
||||||
export type Handelse =
|
export type Handelse =
|
||||||
| { typ: "objekt_identifierat"; objekt: Objekt }
|
| { typ: "objekt_identifierat"; objekt: Objekt }
|
||||||
| ({ typ: "arbetsorder_skannad"; falt: ArbetsorderFalt[] } & Bilaga)
|
| ({ typ: "arbetsorder_skannad"; falt: ArbetsorderFalt[] } & Bilaga)
|
||||||
@@ -137,6 +158,19 @@ export type Handelse =
|
|||||||
// Ärendetypen styr vilka dokumentationskrav ECM ställer (garanti,
|
// Ärendetypen styr vilka dokumentationskrav ECM ställer (garanti,
|
||||||
// försäkring, reklamation …).
|
// försäkring, reklamation …).
|
||||||
| { typ: "arendetyp_satt"; arendetyp: string }
|
| { typ: "arendetyp_satt"; arendetyp: string }
|
||||||
|
// FGS-1.0: betalarspåret. Vem som ersätter arbetet avgör beviskraven —
|
||||||
|
// fabrik, vagnskadegaranti, försäkringsbolag, extern garantigivare,
|
||||||
|
// leasing/fleet, goodwill eller kunden själv. Referensen är claim-,
|
||||||
|
// skade- eller policynummer; godkännandet är betalarens klartecken.
|
||||||
|
| { typ: "betalare"; spar: Betalarspar; namn: string; referens?: string; godkannande?: string }
|
||||||
|
// Teknisk eskalering (DISS-mönstret): öppnad förfrågan till tillverkare
|
||||||
|
// eller garantigivare. En öppnad eskalering utan besvarad spärrar
|
||||||
|
// avslutet — svaret ska inväntas och dokumenteras.
|
||||||
|
| { typ: "eskalering"; status: "oppnad" | "besvarad"; beskrivning: string; referens?: string; kanal?: string }
|
||||||
|
// Reservdelsdokumentation: artikelnummer och spårbarhet för utbytt del.
|
||||||
|
// `sparad` betyder att den demonterade delen behålls i väntan på
|
||||||
|
// garantibeslut eller revision.
|
||||||
|
| { typ: "reservdel"; artikelnummer: string; beskrivning: string; serienummer?: string; batch?: string; sparad?: boolean }
|
||||||
// Pre-diagnostik: fordonshistoriken kontrollerad — eller motiverat
|
// Pre-diagnostik: fordonshistoriken kontrollerad — eller motiverat
|
||||||
// varför inte (kvalitetsvarning).
|
// varför inte (kvalitetsvarning).
|
||||||
| { typ: "historik_kontrollerad"; kontrollerad: boolean; kommentar?: string }
|
| { typ: "historik_kontrollerad"; kontrollerad: boolean; kommentar?: string }
|
||||||
@@ -312,6 +346,14 @@ export function handelseRubrik(post: LoggPost): string {
|
|||||||
return `Ansvarig tekniker: ${h.ansvarig}`;
|
return `Ansvarig tekniker: ${h.ansvarig}`;
|
||||||
case "arendetyp_satt":
|
case "arendetyp_satt":
|
||||||
return `Case type: ${h.arendetyp}`;
|
return `Case type: ${h.arendetyp}`;
|
||||||
|
case "betalare":
|
||||||
|
return `Payer: ${BETALARSPAR_LABEL[h.spar]} — ${h.namn}${h.referens ? ` (ref ${h.referens})` : ""}${h.godkannande ? ` — approval ${h.godkannande}` : ""}`;
|
||||||
|
case "eskalering":
|
||||||
|
return h.status === "oppnad"
|
||||||
|
? `Escalation opened${h.kanal ? ` via ${h.kanal}` : ""}${h.referens ? ` (${h.referens})` : ""}: ${h.beskrivning}`
|
||||||
|
: `Escalation answered${h.referens ? ` (${h.referens})` : ""}: ${h.beskrivning}`;
|
||||||
|
case "reservdel":
|
||||||
|
return `Part documented: ${h.artikelnummer} — ${h.beskrivning}${h.serienummer ? `, s/n ${h.serienummer}` : ""}${h.batch ? `, batch ${h.batch}` : ""}${h.sparad ? " — old part retained" : ""}`;
|
||||||
case "historik_kontrollerad":
|
case "historik_kontrollerad":
|
||||||
return h.kontrollerad
|
return h.kontrollerad
|
||||||
? `Fordonshistorik kontrollerad${h.kommentar ? `: ${h.kommentar}` : ""}`
|
? `Fordonshistorik kontrollerad${h.kommentar ? `: ${h.kommentar}` : ""}`
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import type { Arende, LoggPost } from "./domain";
|
|||||||
import type { Metodik } from "./metodik";
|
import type { Metodik } from "./metodik";
|
||||||
import { arendetypNu } from "../../../services/gemensam/sprak/index.mjs";
|
import { arendetypNu } from "../../../services/gemensam/sprak/index.mjs";
|
||||||
|
|
||||||
export const ECM_VERSION = "2.0";
|
export const ECM_VERSION = "2.1";
|
||||||
|
|
||||||
// ---- 1. Evidence Engine -----------------------------------------------
|
// ---- 1. Evidence Engine -----------------------------------------------
|
||||||
|
|
||||||
@@ -345,6 +345,9 @@ export const ARENDETYPER = [
|
|||||||
"Insurance",
|
"Insurance",
|
||||||
"Complaint",
|
"Complaint",
|
||||||
"Used-vehicle warranty",
|
"Used-vehicle warranty",
|
||||||
|
"Body damage warranty",
|
||||||
|
"Extended warranty",
|
||||||
|
"Leasing or fleet",
|
||||||
"Internal quality check",
|
"Internal quality check",
|
||||||
"Technical investigation",
|
"Technical investigation",
|
||||||
] as const;
|
] as const;
|
||||||
@@ -372,7 +375,7 @@ export function arendetyp(arende: Arende): Arendetyp {
|
|||||||
// Klienten cachar senast hämtade paket och faller tillbaka till det
|
// Klienten cachar senast hämtade paket och faller tillbaka till det
|
||||||
// inbyggda standardpaketet offline/i lokalt läge.
|
// inbyggda standardpaketet offline/i lokalt läge.
|
||||||
|
|
||||||
export type ComplianceKravTyp = "miltal" | "historik" | "claim" | "skadenummer" | "foto";
|
export type ComplianceKravTyp = "miltal" | "historik" | "claim" | "skadenummer" | "foto" | "betalare" | "godkannande";
|
||||||
|
|
||||||
export interface ComplianceRegel {
|
export interface ComplianceRegel {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -403,11 +406,21 @@ const KRAV_KONTROLLER: Record<ComplianceKravTyp, (arende: Arende) => boolean> =
|
|||||||
a.handelser.some((p) => p.handelse.typ === "matarstallning" && !p.handelse.undantag) ||
|
a.handelser.some((p) => p.handelse.typ === "matarstallning" && !p.handelse.undantag) ||
|
||||||
!!objektFalt(a, "miltal"),
|
!!objektFalt(a, "miltal"),
|
||||||
historik: (a) => a.handelser.some((p) => p.handelse.typ === "historik_kontrollerad" && p.handelse.kontrollerad),
|
historik: (a) => a.handelser.some((p) => p.handelse.typ === "historik_kontrollerad" && p.handelse.kontrollerad),
|
||||||
claim: (a) => !!objektFalt(a, "claim"),
|
// Referensen kan komma ur den skannade arbetsordern ELLER ur en
|
||||||
skadenummer: (a) => !!objektFalt(a, "skadenummer"),
|
// registrerad betalare — alla verkstäder skannar inte (FGS-1.0 §4).
|
||||||
|
claim: (a) => !!objektFalt(a, "claim") || betalarReferens(a),
|
||||||
|
skadenummer: (a) => !!objektFalt(a, "skadenummer") || betalarReferens(a),
|
||||||
foto: (a) => a.handelser.some((p) => p.handelse.typ === "foto"),
|
foto: (a) => a.handelser.some((p) => p.handelse.typ === "foto"),
|
||||||
|
betalare: (a) =>
|
||||||
|
a.handelser.some((p) => p.handelse.typ === "betalare" && p.handelse.namn.trim() !== "" && p.handelse.spar.trim() !== ""),
|
||||||
|
godkannande: (a) =>
|
||||||
|
a.handelser.some((p) => p.handelse.typ === "betalare" && (p.handelse.godkannande ?? "").trim() !== ""),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function betalarReferens(a: Arende): boolean {
|
||||||
|
return a.handelser.some((p) => p.handelse.typ === "betalare" && (p.handelse.referens ?? "").trim() !== "");
|
||||||
|
}
|
||||||
|
|
||||||
// Inbyggt standardpaket = fallback när servern inte nåtts ännu.
|
// Inbyggt standardpaket = fallback när servern inte nåtts ännu.
|
||||||
export const STANDARD_REGELPAKET: RegelPaket = {
|
export const STANDARD_REGELPAKET: RegelPaket = {
|
||||||
version: ECM_VERSION,
|
version: ECM_VERSION,
|
||||||
@@ -415,15 +428,18 @@ export const STANDARD_REGELPAKET: RegelPaket = {
|
|||||||
"Warranty": [
|
"Warranty": [
|
||||||
{ id: "garanti_miltal", rubrik: "Mileage documented", krav: "miltal", detaljVidBrist: "Warranty cases require a documented odometer reading." },
|
{ id: "garanti_miltal", rubrik: "Mileage documented", krav: "miltal", detaljVidBrist: "Warranty cases require a documented odometer reading." },
|
||||||
{ id: "garanti_historik", rubrik: "Service history checked", krav: "historik", detaljVidBrist: "Warranty cases require a checked service history." },
|
{ id: "garanti_historik", rubrik: "Service history checked", krav: "historik", detaljVidBrist: "Warranty cases require a checked service history." },
|
||||||
{ id: "garanti_claim", rubrik: "Claim or warranty number recorded", krav: "claim", detaljVidBrist: "State the claim or warranty number (read from the work order)." },
|
{ id: "garanti_claim", rubrik: "Claim or warranty number recorded", krav: "claim", detaljVidBrist: "State the claim or warranty number — from the scanned work order or the recorded payer." },
|
||||||
|
{ id: "garanti_betalare", rubrik: "Payer established", krav: "betalare", detaljVidBrist: "Record the payer — track, name and reference — before closing." },
|
||||||
],
|
],
|
||||||
Goodwill: [
|
Goodwill: [
|
||||||
{ id: "goodwill_miltal", rubrik: "Mileage documented", krav: "miltal", detaljVidBrist: "Goodwill cases require a documented odometer reading." },
|
{ id: "goodwill_miltal", rubrik: "Mileage documented", krav: "miltal", detaljVidBrist: "Goodwill cases require a documented odometer reading." },
|
||||||
{ id: "goodwill_historik", rubrik: "Service history checked", krav: "historik", detaljVidBrist: "Goodwill cases require a checked service history." },
|
{ id: "goodwill_historik", rubrik: "Service history checked", krav: "historik", detaljVidBrist: "Goodwill cases require a checked service history." },
|
||||||
|
{ id: "goodwill_godkannande", rubrik: "Grantor's approval recorded", krav: "godkannande", detaljVidBrist: "Goodwill requires the grantor's documented approval before closing." },
|
||||||
],
|
],
|
||||||
"Insurance": [
|
"Insurance": [
|
||||||
{ id: "forsakring_skadenummer", rubrik: "Claim number recorded", krav: "skadenummer", detaljVidBrist: "Insurance cases require a claim number (read from the work order)." },
|
{ id: "forsakring_skadenummer", rubrik: "Claim number recorded", krav: "skadenummer", detaljVidBrist: "Insurance cases require a claim number (read from the work order)." },
|
||||||
{ id: "forsakring_bildbevis", rubrik: "Photographic evidence present", krav: "foto", detaljVidBrist: "Insurance cases require photographic documentation." },
|
{ id: "forsakring_bildbevis", rubrik: "Photographic evidence present", krav: "foto", detaljVidBrist: "Insurance cases require photographic documentation." },
|
||||||
|
{ id: "forsakring_betalare", rubrik: "Payer established", krav: "betalare", detaljVidBrist: "Record the insurer — name and claim reference — before closing." },
|
||||||
],
|
],
|
||||||
"Complaint": [
|
"Complaint": [
|
||||||
{ id: "reklamation_historik", rubrik: "History and previous attempts checked", krav: "historik", detaljVidBrist: "Complaint cases require a checked history (previous repairs and attempts)." },
|
{ id: "reklamation_historik", rubrik: "History and previous attempts checked", krav: "historik", detaljVidBrist: "Complaint cases require a checked history (previous repairs and attempts)." },
|
||||||
@@ -431,6 +447,21 @@ export const STANDARD_REGELPAKET: RegelPaket = {
|
|||||||
"Used-vehicle warranty": [
|
"Used-vehicle warranty": [
|
||||||
{ id: "begagnat_miltal", rubrik: "Mileage documented", krav: "miltal", detaljVidBrist: "Used-vehicle warranty cases require a documented odometer reading." },
|
{ id: "begagnat_miltal", rubrik: "Mileage documented", krav: "miltal", detaljVidBrist: "Used-vehicle warranty cases require a documented odometer reading." },
|
||||||
],
|
],
|
||||||
|
"Body damage warranty": [
|
||||||
|
{ id: "vagnskada_betalare", rubrik: "Payer established", krav: "betalare", detaljVidBrist: "Record the warranty administrator — name and reference — before closing." },
|
||||||
|
{ id: "vagnskada_miltal", rubrik: "Mileage documented", krav: "miltal", detaljVidBrist: "Body damage warranty cases require a documented odometer reading." },
|
||||||
|
{ id: "vagnskada_bildbevis", rubrik: "Photographic evidence present", krav: "foto", detaljVidBrist: "Body damage warranty cases require photographs of the damage." },
|
||||||
|
],
|
||||||
|
"Extended warranty": [
|
||||||
|
{ id: "forlangd_betalare", rubrik: "Payer established", krav: "betalare", detaljVidBrist: "Record the warranty provider — name and reference — before closing." },
|
||||||
|
{ id: "forlangd_claim", rubrik: "Claim authorization recorded", krav: "claim", detaljVidBrist: "External warranties require a claim or authorization number before repair is reimbursed." },
|
||||||
|
{ id: "forlangd_miltal", rubrik: "Mileage documented", krav: "miltal", detaljVidBrist: "Extended warranty cases require a documented odometer reading." },
|
||||||
|
{ id: "forlangd_historik", rubrik: "Service history checked", krav: "historik", detaljVidBrist: "Extended warranty terms often condition cover on service history — check and document it." },
|
||||||
|
],
|
||||||
|
"Leasing or fleet": [
|
||||||
|
{ id: "leasing_betalare", rubrik: "Payer established", krav: "betalare", detaljVidBrist: "Record the leasing or fleet company — name and reference — before closing." },
|
||||||
|
{ id: "leasing_miltal", rubrik: "Mileage documented", krav: "miltal", detaljVidBrist: "Leasing and fleet cases require a documented odometer reading." },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
undantagsorsaker: UNDANTAGSORSAKER,
|
undantagsorsaker: UNDANTAGSORSAKER,
|
||||||
orsakskategorier: ORSAKSKATEGORIER,
|
orsakskategorier: ORSAKSKATEGORIER,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { Link, useParams } from "react-router-dom";
|
import { Link, useParams } from "react-router-dom";
|
||||||
import type { Arende, Handelse, TidKategori, Tillforlitlighet } from "@/felsokning/domain";
|
import type { Arende, Betalarspar, Handelse, TidKategori, Tillforlitlighet } from "@/felsokning/domain";
|
||||||
import { KUNDBESLUT_LABEL, KVALITETSKONTROLL_LABEL, TIDKATEGORI_LABEL, TILLFORLITLIGHET_LABEL, handelseRubrik } from "@/felsokning/domain";
|
import { BETALARSPAR_LABEL, KUNDBESLUT_LABEL, KVALITETSKONTROLL_LABEL, TIDKATEGORI_LABEL, TILLFORLITLIGHET_LABEL, handelseRubrik } from "@/felsokning/domain";
|
||||||
import type { Metodik, NastaSteg } from "@/felsokning/metodik";
|
import type { Metodik, NastaSteg } from "@/felsokning/metodik";
|
||||||
import { nastaSteg } from "@/felsokning/metodik";
|
import { nastaSteg } from "@/felsokning/metodik";
|
||||||
import { fasFor, klaraFaser } from "../../../../services/gemensam/faser.mjs";
|
import { fasFor, klaraFaser } from "../../../../services/gemensam/faser.mjs";
|
||||||
@@ -1444,6 +1444,19 @@ function GuideFlik({
|
|||||||
</Panel>
|
</Panel>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* FGS-1.0: betalaren avgör beviskraven — grinden kräver spår, namn
|
||||||
|
och referens för de ärendetyper där någon annan än kunden
|
||||||
|
betalar. Panelen visas när ärendetypen kräver den eller när en
|
||||||
|
betalare redan registrerats. */}
|
||||||
|
{(BETALARTYPER.includes(arendetyp(arende)) ||
|
||||||
|
arende.handelser.some((p) => p.handelse.typ === "betalare")) && (
|
||||||
|
<BetalarePanel arende={arende} skicka={skicka} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Teknisk eskalering (DISS-mönstret): en öppnad förfrågan utan
|
||||||
|
dokumenterat svar spärrar avslutet. */}
|
||||||
|
<EskaleringPanel arende={arende} skicka={skicka} />
|
||||||
|
|
||||||
{!kanAvslutas && <Avslutshinder hinder={hinder} />}
|
{!kanAvslutas && <Avslutshinder hinder={hinder} />}
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-2 gap-2">
|
||||||
<StorKnapp variant="sekundar" onClick={() => setVisaOverlamning(true)}>
|
<StorKnapp variant="sekundar" onClick={() => setVisaOverlamning(true)}>
|
||||||
@@ -1461,6 +1474,125 @@ function GuideFlik({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ärendetyper där någon annan än kunden ersätter arbetet — betalaren ska
|
||||||
|
// vara fastställd innan grinden släpper avslutet (FGS-1.0 §1/§18).
|
||||||
|
const BETALARTYPER: readonly string[] = [
|
||||||
|
"Warranty",
|
||||||
|
"Goodwill",
|
||||||
|
"Insurance",
|
||||||
|
"Used-vehicle warranty",
|
||||||
|
"Body damage warranty",
|
||||||
|
"Extended warranty",
|
||||||
|
"Leasing or fleet",
|
||||||
|
];
|
||||||
|
|
||||||
|
function BetalarePanel({ arende, skicka }: { arende: Arende; skicka: (h: Handelse) => void }) {
|
||||||
|
const [spar, setSpar] = useState<Betalarspar>("fabriksgaranti");
|
||||||
|
const [namn, setNamn] = useState("");
|
||||||
|
const [referens, setReferens] = useState("");
|
||||||
|
const [godkannande, setGodkannande] = useState("");
|
||||||
|
const senaste = [...arende.handelser].reverse().find((p) => p.handelse.typ === "betalare")?.handelse;
|
||||||
|
return (
|
||||||
|
<Panel rubrik="Payer and claim — who reimburses the work">
|
||||||
|
{senaste?.typ === "betalare" && (
|
||||||
|
<p className="mb-2 text-[13px]">
|
||||||
|
<span className="font-semibold text-[#4D5662]">Recorded:</span> {BETALARSPAR_LABEL[senaste.spar]} — {senaste.namn}
|
||||||
|
{senaste.referens ? ` (ref ${senaste.referens})` : ""}
|
||||||
|
{senaste.godkannande ? ` — approval ${senaste.godkannande}` : ""}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<div className="mb-2 grid gap-2 sm:grid-cols-2">
|
||||||
|
<label className="block">
|
||||||
|
<span className="mb-2 block text-[11px] font-semibold uppercase tracking-[0.08em] text-[#4D5662]">Track</span>
|
||||||
|
<select
|
||||||
|
value={spar}
|
||||||
|
onChange={(e) => setSpar(e.target.value as Betalarspar)}
|
||||||
|
className="w-full border border-[#4D5662] bg-white px-2 py-2 text-[13px] focus:outline-none"
|
||||||
|
>
|
||||||
|
{(Object.keys(BETALARSPAR_LABEL) as Betalarspar[]).map((k) => (
|
||||||
|
<option key={k} value={k}>
|
||||||
|
{BETALARSPAR_LABEL[k]}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<TextFalt label="Payer name" varde={namn} satt={setNamn} platshallare="e.g. the manufacturer, insurer or warranty provider" />
|
||||||
|
<TextFalt label="Claim / policy / damage number" varde={referens} satt={setReferens} platshallare="Claim, policy or damage reference" />
|
||||||
|
<TextFalt label="Approval reference" varde={godkannande} satt={setGodkannande} platshallare="Authorization — goodwill and external warranties" />
|
||||||
|
</div>
|
||||||
|
<StorKnapp
|
||||||
|
variant="sekundar"
|
||||||
|
disabled={!namn.trim()}
|
||||||
|
onClick={() => {
|
||||||
|
skicka({
|
||||||
|
typ: "betalare",
|
||||||
|
spar,
|
||||||
|
namn: namn.trim(),
|
||||||
|
...(referens.trim() ? { referens: referens.trim() } : {}),
|
||||||
|
...(godkannande.trim() ? { godkannande: godkannande.trim() } : {}),
|
||||||
|
});
|
||||||
|
setNamn("");
|
||||||
|
setReferens("");
|
||||||
|
setGodkannande("");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Record payer
|
||||||
|
</StorKnapp>
|
||||||
|
</Panel>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function EskaleringPanel({ arende, skicka }: { arende: Arende; skicka: (h: Handelse) => void }) {
|
||||||
|
const [text, setText] = useState("");
|
||||||
|
const [kanal, setKanal] = useState("");
|
||||||
|
const [referens, setReferens] = useState("");
|
||||||
|
// Samma matchning som grinden: öppnade per referens, besvarade tar bort.
|
||||||
|
const oppna = new Map<string, string>();
|
||||||
|
for (const post of arende.handelser) {
|
||||||
|
const h = post.handelse;
|
||||||
|
if (h.typ !== "eskalering") continue;
|
||||||
|
const nyckel = (h.referens ?? "").trim();
|
||||||
|
if (h.status === "oppnad") oppna.set(nyckel, h.beskrivning);
|
||||||
|
else if (oppna.has(nyckel)) oppna.delete(nyckel);
|
||||||
|
else if (nyckel === "" && oppna.size === 1) oppna.clear();
|
||||||
|
}
|
||||||
|
const skickaMed = (status: "oppnad" | "besvarad") => {
|
||||||
|
skicka({
|
||||||
|
typ: "eskalering",
|
||||||
|
status,
|
||||||
|
beskrivning: text.trim(),
|
||||||
|
...(referens.trim() ? { referens: referens.trim() } : {}),
|
||||||
|
...(kanal.trim() ? { kanal: kanal.trim() } : {}),
|
||||||
|
});
|
||||||
|
setText("");
|
||||||
|
setKanal("");
|
||||||
|
setReferens("");
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Panel rubrik="Technical escalation — manufacturer or warranty provider">
|
||||||
|
{oppna.size > 0 && (
|
||||||
|
<p className="mb-2 border-l-2 border-[#8A5A00] pl-4 text-[13px]">
|
||||||
|
<span className="font-semibold text-[#8A5A00]">Open, awaiting answer:</span>{" "}
|
||||||
|
{[...oppna.entries()].map(([r, b]) => (r ? `${r} — ${b}` : b)).join(" · ")}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<div className="mb-2 grid gap-2 sm:grid-cols-3">
|
||||||
|
<TextFalt label="Description" varde={text} satt={setText} platshallare="What was asked, or what the answer says" />
|
||||||
|
<TextFalt label="Channel" varde={kanal} satt={setKanal} platshallare="e.g. DISS, technical support" />
|
||||||
|
<TextFalt label="Reference" varde={referens} satt={setReferens} platshallare="Ticket or query number" />
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-2">
|
||||||
|
<StorKnapp variant="sekundar" disabled={!text.trim()} onClick={() => skickaMed("oppnad")}>
|
||||||
|
Open escalation
|
||||||
|
</StorKnapp>
|
||||||
|
<StorKnapp variant="sekundar" disabled={!text.trim() || oppna.size === 0} onClick={() => skickaMed("besvarad")}>
|
||||||
|
Document the answer
|
||||||
|
</StorKnapp>
|
||||||
|
</div>
|
||||||
|
</Panel>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Varför avslut är spärrat — i klartext, på skärmen, medan arbetet pågår.
|
* Varför avslut är spärrat — i klartext, på skärmen, medan arbetet pågår.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# Garantistandarden FGS-1.0 kartlagd mot plattformen
|
||||||
|
|
||||||
|
**ALVA 3.6 · 2026-08-06 · ALVA-DOC-0011**
|
||||||
|
|
||||||
|
Underlaget är den sammanställda Felsöknings- och Garantistandarden
|
||||||
|
FGS-1.0: ett gemensamt flöde för fyra betalande parter — fabriksgaranti,
|
||||||
|
vagnskade-/bilskadegaranti, försäkringsbolag och externa garantigivare —
|
||||||
|
byggt på offentligt verifierbara källor (EU:s konsument- och
|
||||||
|
konkurrensregler, Volkswagen Groups offentliga garanti- och
|
||||||
|
revisionsunderlag kring ODIS/GFF/DISS/SAGA2, svenska försäkringsbolags
|
||||||
|
och garantiadministratörers publicerade flöden).
|
||||||
|
|
||||||
|
Detta dokument bokför hur standarden förhåller sig till plattformen:
|
||||||
|
vad som redan var uppfyllt, vad som infördes med ALVA 3.6, och var
|
||||||
|
gränsen mot tillverkarnas egna system går. Grundhållningen är densamma
|
||||||
|
som FGS §18: ett ärende som inte kan granskas i efterhand utan
|
||||||
|
kompletteringar är inte klart.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Uppfyllt före denna utgåva
|
||||||
|
|
||||||
|
| FGS | Plattformens motsvarighet |
|
||||||
|
| --- | --- |
|
||||||
|
| §4 Mottagning (VIN, regnr, miltal, datum, servicehistorik) | Objektidentifiering med registerslagning, mätarställning in/ut med fotokrav, historikkontroll med motiveringstvång vid nej — allt grindat |
|
||||||
|
| §5 Kundens beskrivning ordagrant | Felbeskrivningen är en egen händelse; symptomsteget frågar vad/när/var och skriver aldrig om kundens ord |
|
||||||
|
| §6 Reproducering före diagnos | Symptomverifiering med ja/delvis/nej och motivering — grindkrav sedan ALVA 1.x |
|
||||||
|
| §7 Diagnos, §8 teknisk verifiering | Metodikbiblioteket (16 metodiker, 176 kontroller efter fackgranskningen i ALVA-DOC-0009), evidenskrav på varje kontroll |
|
||||||
|
| §10 Complaint–Cause–Correction | Felbeskrivning → felorsak (avvikelse, orsaker, underlag, konfidens) → åtgärd + kvalitetskontroll "symptomet verifierat avhjälpt" — och slutsatsen (ALVA-RULE-200) tvingar fram *varför* |
|
||||||
|
| §12/15 Provkörning, slutkontroll | Provkörningssteg med dokumentationskrav; kvalitetskontrollen är grindad |
|
||||||
|
| §17 Revisionssäkring | Append-only-logg, hashkedja, förseglat avslut, spårbarhetspaket — varje fråga i FGS §17 besvaras ur loggen |
|
||||||
|
| Miltal/historik/claim/skadenummer/foto per ärendetyp | Datadrivet regelpaket (ECM Knowledge Library), signerat vid extern distribution |
|
||||||
|
|
||||||
|
## 2. Infört med ALVA 3.6
|
||||||
|
|
||||||
|
| FGS | Åtgärd |
|
||||||
|
| --- | --- |
|
||||||
|
| §1/§3 Ärendeklassificering per betalare | Händelsen `betalare`: spår (fabriksgaranti, vagnskadegaranti, försäkring, extern garanti, leasing/fleet, goodwill, kund), namn, referens, godkännande. Grindkravet `betalare` för alla spår där någon annan än kunden betalar |
|
||||||
|
| §3 Typ B/C/D saknade | Tre nya ärendetyper med egna regelpaketskrav: Body damage warranty, Extended warranty, Leasing or fleet |
|
||||||
|
| §8 DISS-mönstret | Händelsen `eskalering` (öppnad/besvarad, kanal, referens). En öppnad eskalering utan dokumenterat svar spärrar avslutet, matchad per referens — "invänta svar" är en grindregel, inte ett råd |
|
||||||
|
| §11/§12 Claim-/godkännandenummer | Claim- och skadenummer kan komma ur den registrerade betalaren, inte bara ur skannad arbetsorder. Goodwill och externa garantigivare kräver dokumenterat godkännande (`godkannande`-kravet) före avslut — FGS §12: "förhandsbesked innan reparation fortsätter" |
|
||||||
|
| §13 Reservdelar | Händelsen `reservdel`: artikelnummer, serienummer, batch, beskrivning och markering att den demonterade delen sparas i väntan på garantibeslut |
|
||||||
|
| Delningsminimering | Betalare, eskalering och reservdel är interna i delningsfiltret: kunden ser åtgärden, inte förhandlingen med betalaren |
|
||||||
|
|
||||||
|
**Fynd under arbetet, åtgärdat och regressionstestat:** serverns
|
||||||
|
ärendetypskrav var en tyst no-op. Servern matade den distribuerade
|
||||||
|
paketformen (`arendetypRegler`) in i en grindfunktion som bara läste
|
||||||
|
den slimmade formen (`arendetyper[typ].krav`) — garanti- och
|
||||||
|
försäkringskraven upprätthölls därmed bara som råd i klienten. Det är
|
||||||
|
C-2-mönstret från revision 1, återuppstånden genom ett filformat.
|
||||||
|
Grinden läser nu båda formerna, och regressionstestet låser att den
|
||||||
|
distribuerade formen faktiskt spärrar på servern.
|
||||||
|
|
||||||
|
## 3. Utanför plattformen — med avsikt
|
||||||
|
|
||||||
|
| FGS | Gräns |
|
||||||
|
| --- | --- |
|
||||||
|
| ODIS/GFF-körning, verktygsversioner inom 14 dagar | Tillverkarens verktyg och versionspolicy. ALVA dokumenterar diagnosens resultat (loggar, foton, mätvärden som evidens) men kör inte tillverkardiagnos. Verktygsversion kan antecknas i mätvärdets beskrivning; ett eget versionsfält bokförs som kandidat till nästa schemarevision |
|
||||||
|
| DISS-registrering, SAGA2-claimskapande | Tillverkarsystem bakom återförsäljaravtal. ALVA bär eskaleringsspåret och det kompletta bevispaket en SAGA2-claim (eller en extern garantigivares claim) kräver — den skapar inte claimen. Det är samma gräns som fakturamodellen: underlag, inte betalningsflöde |
|
||||||
|
| §15 Avslagsorsakerna | Var och en motsvarar ett grindkrav eller en evidensregel som redan spärrar; listan är med andra ord en beskrivning av vad grinden finns till för |
|
||||||
|
| Juridiska ramar (EU-garantiregler, reklamationsrätt) | Reglerar avtal mellan parter, inte verkstadens dokumentation. Noterbart: att garantin inte får villkoras av märkesbunden service är ett skäl att historikkravet formuleras som *kontrollerad*, aldrig som *utförd hos auktoriserad verkstad* |
|
||||||
|
|
||||||
|
## 4. Verifiering
|
||||||
|
|
||||||
|
Betalar- och eskaleringskraven mutationsprövade i grindtesterna
|
||||||
|
(öppnad spärrar/besvarad släpper, referensmatchning, betalare med och
|
||||||
|
utan namn/godkännande, claim ur betalare respektive arbetsorder, den
|
||||||
|
distribuerade paketformens regression). Klientens och serverns
|
||||||
|
regelpaket låsta mot varandra av befintligt likhetstest;
|
||||||
|
delningsfiltrets tillåtelselista täcker de nya händelsetyperna via
|
||||||
|
schematestet. Full kontrollslinga i commit-historiken.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*ALVA-DOC-0011 · FGS-1.0 ⇄ ALVA · ALVA bär bevispaketet — tillverkarsystemen ersätts inte*
|
||||||
@@ -210,6 +210,24 @@ export function grinda(handelser, metodik, sprak = STANDARD) {
|
|||||||
krav(`sparr_${frageId}`, nyckel, arJakande(svaret?.svar), "grind.sparr.ej_uppfyllt");
|
krav(`sparr_${frageId}`, nyckel, arJakande(svaret?.svar), "grind.sparr.ej_uppfyllt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Teknisk eskalering (FGS-1.0 §8, DISS-mönstret): en öppnad förfrågan
|
||||||
|
// till tillverkare eller garantigivare utan dokumenterat svar spärrar
|
||||||
|
// avslutet. Bulletinerna kräver att svaret inväntas innan ytterligare
|
||||||
|
// åtgärder görs — ett ärende som stängs med öppen eskalering har inte
|
||||||
|
// hela orsakskedjan dokumenterad, och garantianspråket kan ogiltigas.
|
||||||
|
{
|
||||||
|
const oppna = new Map();
|
||||||
|
for (const h of av(handelser, "eskalering")) {
|
||||||
|
const nyckel = String(h.referens ?? "").trim();
|
||||||
|
if (h.status === "oppnad") oppna.set(nyckel, h);
|
||||||
|
else if (oppna.has(nyckel)) oppna.delete(nyckel);
|
||||||
|
else if (nyckel === "" && oppna.size === 1) oppna.clear();
|
||||||
|
}
|
||||||
|
krav("eskalering", "grind.eskalering", oppna.size === 0, "grind.eskalering.detalj", {
|
||||||
|
referens: [...oppna.keys()].map((r) => r || "—").join(", "),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Evidensnivån måste överstiga E0 — annars avslutas ett ärende utan
|
// Evidensnivån måste överstiga E0 — annars avslutas ett ärende utan
|
||||||
// att någonting alls har dokumenterats.
|
// att någonting alls har dokumenterats.
|
||||||
krav(
|
krav(
|
||||||
@@ -231,15 +249,33 @@ export function grindaArendetyp(handelser, regelpaket, sprak = STANDARD) {
|
|||||||
// när typerna hette "Garanti" ska få garantins extra krav även efter
|
// när typerna hette "Garanti" ska få garantins extra krav även efter
|
||||||
// att de bytt språk — se ARENDETYP_ARV.
|
// att de bytt språk — se ARENDETYP_ARV.
|
||||||
const typ = arendetypNu(av(handelser, "arendetyp_satt").at(-1)?.arendetyp);
|
const typ = arendetypNu(av(handelser, "arendetyp_satt").at(-1)?.arendetyp);
|
||||||
const regler = regelpaket?.arendetyper?.[typ]?.krav ?? [];
|
// Paketet finns i två former: den distribuerade filens rika form
|
||||||
|
// (arendetypRegler med id/rubrik/detaljVidBrist) och den slimmade
|
||||||
|
// (arendetyper[typ].krav). Servern matade tidigare in den rika formen
|
||||||
|
// i en funktion som bara läste den slimmade — varje ärendetypskrav
|
||||||
|
// blev en tyst no-op på servern, och spärren fanns bara som råd i
|
||||||
|
// klienten. Det är exakt C-2-mönstret, återuppstått genom ett format.
|
||||||
|
const regler = [
|
||||||
|
...(regelpaket?.arendetyper?.[typ]?.krav ?? []),
|
||||||
|
...(regelpaket?.arendetypRegler?.[typ] ?? []).map((r) => ({ typ: r.krav, rubrik: r.rubrik, detalj: r.detaljVidBrist })),
|
||||||
|
];
|
||||||
const hinder = [];
|
const hinder = [];
|
||||||
|
|
||||||
|
const betalareReferens = () => av(handelser, "betalare").some((h) => String(h.referens ?? "").trim());
|
||||||
const uppfyllt = {
|
const uppfyllt = {
|
||||||
miltal: () => av(handelser, "matarstallning").length > 0,
|
miltal: () => av(handelser, "matarstallning").length > 0,
|
||||||
historik: () => av(handelser, "historik_kontrollerad").some((h) => h.kontrollerad),
|
historik: () => av(handelser, "historik_kontrollerad").some((h) => h.kontrollerad),
|
||||||
foto: () => av(handelser, "foto").length > 0,
|
foto: () => av(handelser, "foto").length > 0,
|
||||||
claim: () => harReferens(handelser, ["ao_claim", "claim"]),
|
// Referensen kan komma ur den skannade arbetsordern ELLER ur en
|
||||||
skadenummer: () => harReferens(handelser, ["ao_skadenummer", "skadenummer"]),
|
// registrerad betalare — alla verkstäder skannar inte (FGS-1.0 §4).
|
||||||
|
claim: () => harReferens(handelser, ["ao_claim", "claim"]) || betalareReferens(),
|
||||||
|
skadenummer: () => harReferens(handelser, ["ao_skadenummer", "skadenummer"]) || betalareReferens(),
|
||||||
|
// FGS-1.0 §1/§18: betalaren fastställd — spår och namn — innan
|
||||||
|
// ärendet kan stängas i de spår där någon annan än kunden betalar.
|
||||||
|
betalare: () => av(handelser, "betalare").some((h) => String(h.namn ?? "").trim() && String(h.spar ?? "").trim()),
|
||||||
|
// Goodwill och externa garantigivare kräver godkännande/claim
|
||||||
|
// authorization INNAN reparationen ersätts (FGS-1.0 §12).
|
||||||
|
godkannande: () => av(handelser, "betalare").some((h) => String(h.godkannande ?? "").trim()),
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const regel of regler) {
|
for (const regel of regler) {
|
||||||
@@ -260,6 +296,7 @@ export function grindaArendetyp(handelser, regelpaket, sprak = STANDARD) {
|
|||||||
id: `arendetyp_${krav}`,
|
id: `arendetyp_${krav}`,
|
||||||
nyckel: regel.rubrik ? undefined : "grind.arendetyp.krav",
|
nyckel: regel.rubrik ? undefined : "grind.arendetyp.krav",
|
||||||
rubrik: regel.rubrik ?? t(sprak, "grind.arendetyp.krav", { krav }),
|
rubrik: regel.rubrik ?? t(sprak, "grind.arendetyp.krav", { krav }),
|
||||||
|
...(regel.detalj ? { detalj: regel.detalj } : {}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,14 @@ export const HÄNDELSESCHEMA = {
|
|||||||
resultat: ["symptomet_borta", "kvarstar", "delvis", "ej_verifierbar"],
|
resultat: ["symptomet_borta", "kvarstar", "delvis", "ej_verifierbar"],
|
||||||
beskrivning: "text",
|
beskrivning: "text",
|
||||||
},
|
},
|
||||||
|
// FGS-1.0: betalarspåret, eskaleringen och reservdelen är händelser i
|
||||||
|
// loggen — inte fritext i en kommentar — så att grinden kan räkna på dem.
|
||||||
|
betalare: {
|
||||||
|
spar: ["fabriksgaranti", "vagnskadegaranti", "forsakring", "extern_garanti", "leasing", "goodwill", "kund"],
|
||||||
|
namn: "text",
|
||||||
|
},
|
||||||
|
eskalering: { status: ["oppnad", "besvarad"], beskrivning: "text" },
|
||||||
|
reservdel: { artikelnummer: "text", beskrivning: "text" },
|
||||||
kommentar: { text: "text" },
|
kommentar: { text: "text" },
|
||||||
kategori_byte: { kategori: "text" },
|
kategori_byte: { kategori: "text" },
|
||||||
inaktivitet_forklarad: { text: "text", minuter: "tal" },
|
inaktivitet_forklarad: { text: "text", minuter: "tal" },
|
||||||
@@ -91,6 +99,9 @@ export const VALFRIA_FÄLT = {
|
|||||||
matarstallning: ["undantag", "bilagaId", "bilagaHash", "dataUrl"],
|
matarstallning: ["undantag", "bilagaId", "bilagaHash", "dataUrl"],
|
||||||
|
|
||||||
kontroll_utford: ["resultat", "undantag"],
|
kontroll_utford: ["resultat", "undantag"],
|
||||||
|
betalare: ["referens", "godkannande"],
|
||||||
|
eskalering: ["referens", "kanal"],
|
||||||
|
reservdel: ["serienummer", "batch", "sparad"],
|
||||||
// `kalla` bär härkomsten för värden som kommit in via ett
|
// `kalla` bär härkomsten för värden som kommit in via ett
|
||||||
// diagnosprotokoll (ALVA-SPEC-020). Fältet måste vara deklarerat:
|
// diagnosprotokoll (ALVA-SPEC-020). Fältet måste vara deklarerat:
|
||||||
// integrationen fungerade tidigare bara därför att schemat var öppet.
|
// integrationen fungerade tidigare bara därför att schemat var öppet.
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ export const DE = {
|
|||||||
"grind.arendetyp.krav": "Anforderung für diese Vorgangsart: {krav}",
|
"grind.arendetyp.krav": "Anforderung für diese Vorgangsart: {krav}",
|
||||||
"grind.sakerhet": "Konfidenzniveau innerhalb dessen, was die Evidenz trägt",
|
"grind.sakerhet": "Konfidenzniveau innerhalb dessen, was die Evidenz trägt",
|
||||||
"grind.sakerhet.detalj": "Die angegebene Konfidenz ({niva}) übersteigt, was die Evidenz trägt ({tak}). Evidenz ergänzen oder das Niveau senken — ehrliche Unsicherheit ist Information.",
|
"grind.sakerhet.detalj": "Die angegebene Konfidenz ({niva}) übersteigt, was die Evidenz trägt ({tak}). Evidenz ergänzen oder das Niveau senken — ehrliche Unsicherheit ist Information.",
|
||||||
|
"grind.eskalering": "Technische Eskalationen beantwortet",
|
||||||
|
"grind.eskalering.detalj": "Eine Eskalation ist offen ({referens}). Die Antwort abwarten und dokumentieren, bevor der Vorgang abgeschlossen wird.",
|
||||||
|
|
||||||
|
|
||||||
// ---- Abschlussfeststellung (ALVA-RULE-200) ------------------------------
|
// ---- Abschlussfeststellung (ALVA-RULE-200) ------------------------------
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ export const EN = {
|
|||||||
"grind.arendetyp.krav": "Requirement for this case type: {krav}",
|
"grind.arendetyp.krav": "Requirement for this case type: {krav}",
|
||||||
"grind.sakerhet": "Confidence level within what the evidence supports",
|
"grind.sakerhet": "Confidence level within what the evidence supports",
|
||||||
"grind.sakerhet.detalj": "The stated confidence ({niva}) exceeds what the evidence supports ({tak}). Add supporting evidence, or lower the confidence — honest uncertainty is information.",
|
"grind.sakerhet.detalj": "The stated confidence ({niva}) exceeds what the evidence supports ({tak}). Add supporting evidence, or lower the confidence — honest uncertainty is information.",
|
||||||
|
"grind.eskalering": "Technical escalations answered",
|
||||||
|
"grind.eskalering.detalj": "An escalation is open ({referens}). Await the answer and document it before the case is closed.",
|
||||||
|
|
||||||
|
|
||||||
// ---- The closing statement (ALVA-RULE-200) ------------------------------
|
// ---- The closing statement (ALVA-RULE-200) ------------------------------
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ export const ES = {
|
|||||||
"grind.arendetyp.krav": "Requisito para este tipo de expediente: {krav}",
|
"grind.arendetyp.krav": "Requisito para este tipo de expediente: {krav}",
|
||||||
"grind.sakerhet": "Nivel de confianza dentro de lo que sustenta la evidencia",
|
"grind.sakerhet": "Nivel de confianza dentro de lo que sustenta la evidencia",
|
||||||
"grind.sakerhet.detalj": "La confianza indicada ({niva}) supera lo que sustenta la evidencia ({tak}). Añadir evidencia que la sustente, o bajar el nivel — la incertidumbre honesta es información.",
|
"grind.sakerhet.detalj": "La confianza indicada ({niva}) supera lo que sustenta la evidencia ({tak}). Añadir evidencia que la sustente, o bajar el nivel — la incertidumbre honesta es información.",
|
||||||
|
"grind.eskalering": "Escalaciones técnicas respondidas",
|
||||||
|
"grind.eskalering.detalj": "Hay una escalación abierta ({referens}). Esperar la respuesta y documentarla antes de cerrar el expediente.",
|
||||||
|
|
||||||
|
|
||||||
// ---- La conclusión (ALVA-RULE-200) --------------------------------------
|
// ---- La conclusión (ALVA-RULE-200) --------------------------------------
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ export const FR = {
|
|||||||
"grind.arendetyp.krav": "Exigence pour ce type de dossier : {krav}",
|
"grind.arendetyp.krav": "Exigence pour ce type de dossier : {krav}",
|
||||||
"grind.sakerhet": "Niveau de confiance dans les limites de ce que les preuves soutiennent",
|
"grind.sakerhet": "Niveau de confiance dans les limites de ce que les preuves soutiennent",
|
||||||
"grind.sakerhet.detalj": "La confiance indiquée ({niva}) dépasse ce que les preuves soutiennent ({tak}). Compléter les preuves, ou abaisser le niveau — une incertitude honnête est une information.",
|
"grind.sakerhet.detalj": "La confiance indiquée ({niva}) dépasse ce que les preuves soutiennent ({tak}). Compléter les preuves, ou abaisser le niveau — une incertitude honnête est une information.",
|
||||||
|
"grind.eskalering": "Escalades techniques traitées",
|
||||||
|
"grind.eskalering.detalj": "Une escalade est ouverte ({referens}). Attendre la réponse et la documenter avant de clôturer le dossier.",
|
||||||
|
|
||||||
|
|
||||||
// ---- La conclusion (ALVA-RULE-200) --------------------------------------
|
// ---- La conclusion (ALVA-RULE-200) --------------------------------------
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ export const IT = {
|
|||||||
"grind.arendetyp.krav": "Requisito per questo tipo di pratica: {krav}",
|
"grind.arendetyp.krav": "Requisito per questo tipo di pratica: {krav}",
|
||||||
"grind.sakerhet": "Livello di confidenza entro quanto sostiene l’evidenza",
|
"grind.sakerhet": "Livello di confidenza entro quanto sostiene l’evidenza",
|
||||||
"grind.sakerhet.detalj": "La confidenza indicata ({niva}) supera quanto sostiene l’evidenza ({tak}). Integrare l’evidenza, oppure abbassare il livello — l’incertezza onesta è informazione.",
|
"grind.sakerhet.detalj": "La confidenza indicata ({niva}) supera quanto sostiene l’evidenza ({tak}). Integrare l’evidenza, oppure abbassare il livello — l’incertezza onesta è informazione.",
|
||||||
|
"grind.eskalering": "Escalation tecniche risolte",
|
||||||
|
"grind.eskalering.detalj": "Un’escalation è aperta ({referens}). Attendere la risposta e documentarla prima di chiudere la pratica.",
|
||||||
|
|
||||||
|
|
||||||
// ---- La conclusione (ALVA-RULE-200) -------------------------------------
|
// ---- La conclusione (ALVA-RULE-200) -------------------------------------
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ export const NL = {
|
|||||||
"grind.arendetyp.krav": "Eis voor dit zaaktype: {krav}",
|
"grind.arendetyp.krav": "Eis voor dit zaaktype: {krav}",
|
||||||
"grind.sakerhet": "Betrouwbaarheidsniveau binnen wat het bewijs draagt",
|
"grind.sakerhet": "Betrouwbaarheidsniveau binnen wat het bewijs draagt",
|
||||||
"grind.sakerhet.detalj": "De opgegeven betrouwbaarheid ({niva}) overstijgt wat het bewijs draagt ({tak}). Het bewijs aanvullen, of het niveau verlagen — eerlijke onzekerheid is informatie.",
|
"grind.sakerhet.detalj": "De opgegeven betrouwbaarheid ({niva}) overstijgt wat het bewijs draagt ({tak}). Het bewijs aanvullen, of het niveau verlagen — eerlijke onzekerheid is informatie.",
|
||||||
|
"grind.eskalering": "Technische escalaties beantwoord",
|
||||||
|
"grind.eskalering.detalj": "Er staat een escalatie open ({referens}). Wacht het antwoord af en documenteer het voordat de zaak wordt afgesloten.",
|
||||||
|
|
||||||
|
|
||||||
// ---- De eindconclusie (ALVA-RULE-200) -----------------------------------
|
// ---- De eindconclusie (ALVA-RULE-200) -----------------------------------
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ export const PL = {
|
|||||||
"grind.arendetyp.krav": "Wymóg dla tego rodzaju sprawy: {krav}",
|
"grind.arendetyp.krav": "Wymóg dla tego rodzaju sprawy: {krav}",
|
||||||
"grind.sakerhet": "Poziom pewności w granicach tego, co potwierdzają dowody",
|
"grind.sakerhet": "Poziom pewności w granicach tego, co potwierdzają dowody",
|
||||||
"grind.sakerhet.detalj": "Podany poziom pewności ({niva}) przekracza to, co potwierdzają dowody ({tak}). Uzupełnić dowody lub obniżyć poziom — uczciwa niepewność to informacja.",
|
"grind.sakerhet.detalj": "Podany poziom pewności ({niva}) przekracza to, co potwierdzają dowody ({tak}). Uzupełnić dowody lub obniżyć poziom — uczciwa niepewność to informacja.",
|
||||||
|
"grind.eskalering": "Eskalacje techniczne z odpowiedzią",
|
||||||
|
"grind.eskalering.detalj": "Eskalacja jest otwarta ({referens}). Poczekać na odpowiedź i udokumentować ją przed zamknięciem sprawy.",
|
||||||
|
|
||||||
|
|
||||||
// ---- Wniosek końcowy (ALVA-RULE-200) ------------------------------------
|
// ---- Wniosek końcowy (ALVA-RULE-200) ------------------------------------
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ export const PT = {
|
|||||||
"grind.arendetyp.krav": "Requisito para este tipo de processo: {krav}",
|
"grind.arendetyp.krav": "Requisito para este tipo de processo: {krav}",
|
||||||
"grind.sakerhet": "Nível de confiança dentro do que a evidência sustenta",
|
"grind.sakerhet": "Nível de confiança dentro do que a evidência sustenta",
|
||||||
"grind.sakerhet.detalj": "A confiança indicada ({niva}) excede o que a evidência sustenta ({tak}). Completar a evidência, ou baixar o nível — a incerteza honesta é informação.",
|
"grind.sakerhet.detalj": "A confiança indicada ({niva}) excede o que a evidência sustenta ({tak}). Completar a evidência, ou baixar o nível — a incerteza honesta é informação.",
|
||||||
|
"grind.eskalering": "Escalonamentos técnicos respondidos",
|
||||||
|
"grind.eskalering.detalj": "Há um escalonamento em aberto ({referens}). Aguardar a resposta e documentá-la antes de encerrar o processo.",
|
||||||
|
|
||||||
|
|
||||||
// ---- A conclusão (ALVA-RULE-200) ----------------------------------------
|
// ---- A conclusão (ALVA-RULE-200) ----------------------------------------
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ export const RO = {
|
|||||||
"grind.arendetyp.krav": "Cerință pentru acest tip de caz: {krav}",
|
"grind.arendetyp.krav": "Cerință pentru acest tip de caz: {krav}",
|
||||||
"grind.sakerhet": "Nivel de încredere în limitele a ceea ce susțin dovezile",
|
"grind.sakerhet": "Nivel de încredere în limitele a ceea ce susțin dovezile",
|
||||||
"grind.sakerhet.detalj": "Încrederea indicată ({niva}) depășește ceea ce susțin dovezile ({tak}). Completează dovezile sau coboară nivelul — incertitudinea onestă este informație.",
|
"grind.sakerhet.detalj": "Încrederea indicată ({niva}) depășește ceea ce susțin dovezile ({tak}). Completează dovezile sau coboară nivelul — incertitudinea onestă este informație.",
|
||||||
|
"grind.eskalering": "Escaladări tehnice cu răspuns",
|
||||||
|
"grind.eskalering.detalj": "O escaladare este deschisă ({referens}). Așteaptă răspunsul și documentează-l înainte de închiderea cazului.",
|
||||||
|
|
||||||
|
|
||||||
// ---- Concluzia finală (ALVA-RULE-200) -----------------------------------
|
// ---- Concluzia finală (ALVA-RULE-200) -----------------------------------
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ export const SV = {
|
|||||||
"grind.arendetyp.krav": "Krav för ärendetypen: {krav}",
|
"grind.arendetyp.krav": "Krav för ärendetypen: {krav}",
|
||||||
"grind.sakerhet": "Konfidensnivån inom vad underlaget bär",
|
"grind.sakerhet": "Konfidensnivån inom vad underlaget bär",
|
||||||
"grind.sakerhet.detalj": "Angiven konfidens ({niva}) överstiger vad underlaget bär ({tak}). Komplettera underlaget, eller sänk nivån — ärlig osäkerhet är information.",
|
"grind.sakerhet.detalj": "Angiven konfidens ({niva}) överstiger vad underlaget bär ({tak}). Komplettera underlaget, eller sänk nivån — ärlig osäkerhet är information.",
|
||||||
|
"grind.eskalering": "Tekniska eskaleringar besvarade",
|
||||||
|
"grind.eskalering.detalj": "En eskalering är öppen ({referens}). Invänta svaret och dokumentera det innan ärendet avslutas.",
|
||||||
|
|
||||||
|
|
||||||
// ---- Slutsatsen (ALVA-RULE-200) -----------------------------------------
|
// ---- Slutsatsen (ALVA-RULE-200) -----------------------------------------
|
||||||
|
|||||||
@@ -21,6 +21,20 @@
|
|||||||
// TÜV-härdningen ändrade vad loggen garanterar.
|
// TÜV-härdningen ändrade vad loggen garanterar.
|
||||||
|
|
||||||
export const UTGAVOR = [
|
export const UTGAVOR = [
|
||||||
|
{
|
||||||
|
version: "3.6",
|
||||||
|
datum: "2026-08-06",
|
||||||
|
commit: "4dea6d2",
|
||||||
|
rubrik: "Betalarspåret: beviskraven följer den som betalar",
|
||||||
|
andringar: [
|
||||||
|
"FGS-1.0 (garantistandarden) inarbetad: betalaren är en händelse i loggen — spår, namn, claim-/skadereferens och godkännande — och grinden kräver den för de ärendetyper där någon annan än kunden betalar. Tre nya ärendetyper: vagnskadegaranti, förlängd garanti och leasing/fleet, med egna regelpaketskrav.",
|
||||||
|
"Eskaleringsspärren (DISS-mönstret): en öppnad teknisk förfrågan till tillverkare eller garantigivare utan dokumenterat svar spärrar avslutet, matchad per referens. Bulletinernas \"invänta svar\" är nu en grindregel, inte ett råd.",
|
||||||
|
"Ett verkligt fynd på vägen: serverns ärendetypskrav var en tyst no-op — servern matade in den distribuerade paketformen i en funktion som bara läste den slimmade, så garanti- och försäkringskraven fanns bara som råd i klienten. C-2-mönstret, återuppstånden genom ett format. Grinden läser nu båda formerna, med regressionstest.",
|
||||||
|
"Claim- och skadenummer kan komma ur den registrerade betalaren, inte bara ur skannad arbetsorder. Goodwill och externa garantigivare kräver dokumenterat godkännande före avslut.",
|
||||||
|
"Reservdelshändelsen: artikelnummer, serienummer, batch och markering att den demonterade delen sparas i väntan på garantibeslut. Betalare, eskalering och reservdel är interna i delningsfiltret — kunden ser åtgärden, inte förhandlingen.",
|
||||||
|
"Kartläggningen av hela FGS-1.0 mot plattformen bokförd i ALVA-DOC-0011, med ärlig gräns: ODIS/DISS/SAGA2 är tillverkarsystem — ALVA bär bevispaketet och eskaleringsspåret, den ersätter dem inte.",
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
version: "3.5",
|
version: "3.5",
|
||||||
datum: "2026-08-06",
|
datum: "2026-08-06",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "2.0",
|
"version": "2.1",
|
||||||
"beskrivning": "ECM Knowledge Library — serverdistribuerat regelpaket. Uppdateras utan appändring: redigera filen (eller montera en ConfigMap över den i klustret) och starta om tjänsten. Klienten hämtar paketet vid inloggning och faller tillbaka till sitt inbyggda standardpaket offline.",
|
"beskrivning": "ECM Knowledge Library — serverdistribuerat regelpaket. Uppdateras utan appändring: redigera filen (eller montera en ConfigMap över den i klustret) och starta om tjänsten. Klienten hämtar paketet vid inloggning och faller tillbaka till sitt inbyggda standardpaket offline.",
|
||||||
"arendetypRegler": {
|
"arendetypRegler": {
|
||||||
"Warranty": [
|
"Warranty": [
|
||||||
@@ -19,7 +19,13 @@
|
|||||||
"id": "garanti_claim",
|
"id": "garanti_claim",
|
||||||
"rubrik": "Claim or warranty number recorded",
|
"rubrik": "Claim or warranty number recorded",
|
||||||
"krav": "claim",
|
"krav": "claim",
|
||||||
"detaljVidBrist": "State the claim or warranty number (read from the work order)."
|
"detaljVidBrist": "State the claim or warranty number — from the scanned work order or the recorded payer."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "garanti_betalare",
|
||||||
|
"rubrik": "Payer established",
|
||||||
|
"krav": "betalare",
|
||||||
|
"detaljVidBrist": "Record the payer — track, name and reference — before closing."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Goodwill": [
|
"Goodwill": [
|
||||||
@@ -34,6 +40,12 @@
|
|||||||
"rubrik": "Service history checked",
|
"rubrik": "Service history checked",
|
||||||
"krav": "historik",
|
"krav": "historik",
|
||||||
"detaljVidBrist": "Goodwill cases require a checked service history."
|
"detaljVidBrist": "Goodwill cases require a checked service history."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "goodwill_godkannande",
|
||||||
|
"rubrik": "Grantor's approval recorded",
|
||||||
|
"krav": "godkannande",
|
||||||
|
"detaljVidBrist": "Goodwill requires the grantor's documented approval before closing."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Insurance": [
|
"Insurance": [
|
||||||
@@ -48,6 +60,12 @@
|
|||||||
"rubrik": "Photographic evidence present",
|
"rubrik": "Photographic evidence present",
|
||||||
"krav": "foto",
|
"krav": "foto",
|
||||||
"detaljVidBrist": "Insurance cases require photographic documentation."
|
"detaljVidBrist": "Insurance cases require photographic documentation."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "forsakring_betalare",
|
||||||
|
"rubrik": "Payer established",
|
||||||
|
"krav": "betalare",
|
||||||
|
"detaljVidBrist": "Record the insurer — name and claim reference — before closing."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Complaint": [
|
"Complaint": [
|
||||||
@@ -65,6 +83,66 @@
|
|||||||
"krav": "miltal",
|
"krav": "miltal",
|
||||||
"detaljVidBrist": "Used-vehicle warranty cases require a documented odometer reading."
|
"detaljVidBrist": "Used-vehicle warranty cases require a documented odometer reading."
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"Body damage warranty": [
|
||||||
|
{
|
||||||
|
"id": "vagnskada_betalare",
|
||||||
|
"rubrik": "Payer established",
|
||||||
|
"krav": "betalare",
|
||||||
|
"detaljVidBrist": "Record the warranty administrator — name and reference — before closing."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "vagnskada_miltal",
|
||||||
|
"rubrik": "Mileage documented",
|
||||||
|
"krav": "miltal",
|
||||||
|
"detaljVidBrist": "Body damage warranty cases require a documented odometer reading."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "vagnskada_bildbevis",
|
||||||
|
"rubrik": "Photographic evidence present",
|
||||||
|
"krav": "foto",
|
||||||
|
"detaljVidBrist": "Body damage warranty cases require photographs of the damage."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Extended warranty": [
|
||||||
|
{
|
||||||
|
"id": "forlangd_betalare",
|
||||||
|
"rubrik": "Payer established",
|
||||||
|
"krav": "betalare",
|
||||||
|
"detaljVidBrist": "Record the warranty provider — name and reference — before closing."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "forlangd_claim",
|
||||||
|
"rubrik": "Claim authorization recorded",
|
||||||
|
"krav": "claim",
|
||||||
|
"detaljVidBrist": "External warranties require a claim or authorization number before repair is reimbursed."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "forlangd_miltal",
|
||||||
|
"rubrik": "Mileage documented",
|
||||||
|
"krav": "miltal",
|
||||||
|
"detaljVidBrist": "Extended warranty cases require a documented odometer reading."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "forlangd_historik",
|
||||||
|
"rubrik": "Service history checked",
|
||||||
|
"krav": "historik",
|
||||||
|
"detaljVidBrist": "Extended warranty terms often condition cover on service history — check and document it."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Leasing or fleet": [
|
||||||
|
{
|
||||||
|
"id": "leasing_betalare",
|
||||||
|
"rubrik": "Payer established",
|
||||||
|
"krav": "betalare",
|
||||||
|
"detaljVidBrist": "Record the leasing or fleet company — name and reference — before closing."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "leasing_miltal",
|
||||||
|
"rubrik": "Mileage documented",
|
||||||
|
"krav": "miltal",
|
||||||
|
"detaljVidBrist": "Leasing and fleet cases require a documented odometer reading."
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"undantagsorsaker": [
|
"undantagsorsaker": [
|
||||||
|
|||||||
@@ -214,8 +214,8 @@ kontroll "statistiken är organisationsknuten data" "$(echo "$STAT" | falt '.ors
|
|||||||
|
|
||||||
# 10b. ECM Knowledge Library: regelpaketet serveras till inloggade klienter
|
# 10b. ECM Knowledge Library: regelpaketet serveras till inloggade klienter
|
||||||
REGLER=$(curl -s "$BAS/api/ecm/regler" -H "Authorization: Bearer $TOKEN_J")
|
REGLER=$(curl -s "$BAS/api/ecm/regler" -H "Authorization: Bearer $TOKEN_J")
|
||||||
kontroll "regelpaketet serveras" "$(echo "$REGLER" | falt .version)" "2.0"
|
kontroll "regelpaketet serveras" "$(echo "$REGLER" | falt .version)" "2.1"
|
||||||
kontroll "regelpaketet innehåller garantiregler" "$(echo "$REGLER" | falt '.arendetypRegler.Warranty.length')" "3"
|
kontroll "regelpaketet innehåller garantiregler" "$(echo "$REGLER" | falt '.arendetypRegler.Warranty.length')" "4"
|
||||||
|
|
||||||
# 10c. Publikt kundgodkännande — den enda skrivande publika vägen
|
# 10c. Publikt kundgodkännande — den enda skrivande publika vägen
|
||||||
# Ärendets ursprungliga delningskod saknar registrerad nivå och får
|
# Ärendets ursprungliga delningskod saknar registrerad nivå och får
|
||||||
|
|||||||
@@ -386,7 +386,10 @@ export const DELBART_PARTNER = [...DELBART_KUND, "hypotes"];
|
|||||||
|
|
||||||
// Aldrig utanför organisationen: arbetsledning, arbetsmaterial och
|
// Aldrig utanför organisationen: arbetsledning, arbetsmaterial och
|
||||||
// underlag som kan läsas som konstateranden.
|
// underlag som kan läsas som konstateranden.
|
||||||
export const ENDAST_INTERNT = ["kategori_byte", "hypotes", "ai_svar", "ansvarig_satt", "arbetsorder_skannad"];
|
// Betalare, eskalering och reservdel är interna: claimreferenser,
|
||||||
|
// fabrikskorrespondens och serienummer hör till garantiakten, inte till
|
||||||
|
// kundens delningslänk — kunden ser åtgärden, inte förhandlingen (FGS-1.0).
|
||||||
|
export const ENDAST_INTERNT = ["kategori_byte", "hypotes", "ai_svar", "ansvarig_satt", "arbetsorder_skannad", "betalare", "eskalering", "reservdel"];
|
||||||
|
|
||||||
export function synligaTyper(niva) {
|
export function synligaTyper(niva) {
|
||||||
if (niva === "intern") return null; // full insyn — ingen filtrering
|
if (niva === "intern") return null; // full insyn — ingen filtrering
|
||||||
|
|||||||
Reference in New Issue
Block a user