fix(whatsapp-inbox): name both ways to route receipts for multi-company senders (#1424)
Field feedback after the first live receipt: the link confirmation told a multi-company sender only to set a default company in the panel, so the per-receipt path looked unsupported even though it is the one that actually runs when no default is set. Now it names both: set a default, or send one receipt at a time and answer the company question after each. The guidance also moves to its own paragraph. Run together with the AI disclosure it read as one sentence, which is how a real user came away believing the bot had called itself a "mänsklig AI-assistent". New copy test asserts the promise (both options present, disclosure kept in its own paragraph, single-company message unchanged) rather than the exact wording, so a rewrite stays free but a dropped option fails. Mutation-checked against the previous copy. Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Jakob Wennberg
Claude Fable 5
parent
77d20287b9
commit
c84f951a5c
@@ -0,0 +1,37 @@
|
|||||||
|
import { describe, it, expect } from 'vitest'
|
||||||
|
import { botCopy } from '@/extensions/general/whatsapp-inbox/lib/messages'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy contracts that a rewrite must not quietly break. These assert the
|
||||||
|
* PROMISE the message makes to the user, not its exact wording: rephrasing
|
||||||
|
* is fine, dropping one of the two routing options is not.
|
||||||
|
*/
|
||||||
|
describe('m3Linked, multi-company sender', () => {
|
||||||
|
for (const locale of ['sv', 'en'] as const) {
|
||||||
|
it(`${locale}: names BOTH ways to route receipts`, () => {
|
||||||
|
const msg = botCopy(locale).m3Linked({ companyName: 'Arcim Technology AB', companyCount: 7 })
|
||||||
|
|
||||||
|
// Option 1: a default company set in the panel.
|
||||||
|
expect(msg).toMatch(locale === 'sv' ? /standardföretag/i : /default company/i)
|
||||||
|
// Option 2: per receipt, answered right after each one. Field feedback
|
||||||
|
// 2026-08-05: the old copy named only option 1, which read as if the
|
||||||
|
// per-receipt path did not exist.
|
||||||
|
expect(msg).toMatch(locale === 'sv' ? /ett kvitto i taget/i : /one receipt at a time/i)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`${locale}: keeps the routing guidance out of the AI-disclosure paragraph`, () => {
|
||||||
|
// Run together they read as a single sentence, which is how a real
|
||||||
|
// user mis-read the disclosure. Own paragraph, always.
|
||||||
|
const msg = botCopy(locale).m3Linked({ companyName: 'Arcim Technology AB', companyCount: 7 })
|
||||||
|
const disclosureParagraph = msg.split('\n\n').find((p) => /AI[- ]assistant|AI-assistent/i.test(p))
|
||||||
|
expect(disclosureParagraph).toBeDefined()
|
||||||
|
expect(disclosureParagraph).not.toMatch(/standardföretag|default company/i)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
it('says nothing about company routing when the sender has exactly one company', () => {
|
||||||
|
const msg = botCopy('sv').m3Linked({ companyName: 'Arcim Technology AB', companyCount: 1 })
|
||||||
|
expect(msg).not.toMatch(/standardföretag/i)
|
||||||
|
expect(msg).toMatch(/AI-assistent/)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -77,9 +77,13 @@ const SV = {
|
|||||||
'Två tips: skicka som _dokument_ (gem-ikonen) för bästa skärpa, och en fil per kvitto. Flersidiga kvitton skickas som PDF.'
|
'Två tips: skicka som _dokument_ (gem-ikonen) för bästa skärpa, och en fil per kvitto. Flersidiga kvitton skickas som PDF.'
|
||||||
const outro =
|
const outro =
|
||||||
'Jag är en AI-assistent. Skriv *hjälp* för mänsklig support, *stopp* för att koppla från.'
|
'Jag är en AI-assistent. Skriv *hjälp* för mänsklig support, *stopp* för att koppla från.'
|
||||||
|
// Two real ways to route a receipt, and the old copy named only one,
|
||||||
|
// which read as if the per-receipt path did not exist. Own paragraph:
|
||||||
|
// run together with the outro it read as a single sentence.
|
||||||
const multi =
|
const multi =
|
||||||
companyCount > 1
|
companyCount > 1
|
||||||
? `Du är med i ${companyCount} företag i Accounted. Välj standardföretag i panelen så hamnar kvitton rätt. `
|
? `Du är med i ${companyCount} företag i Accounted. Välj antingen ett standardföretag i panelen, ` +
|
||||||
|
'eller skicka ett kvitto i taget så frågar jag vilket företag det gäller direkt efter varje kvitto.\n\n'
|
||||||
: ''
|
: ''
|
||||||
return `${intro}\n\n${tips}\n\n${multi}${outro}`
|
return `${intro}\n\n${tips}\n\n${multi}${outro}`
|
||||||
},
|
},
|
||||||
@@ -206,7 +210,8 @@ const EN: typeof SV = {
|
|||||||
'I am an AI assistant. Type *hjälp* for human support, *stopp* to disconnect.'
|
'I am an AI assistant. Type *hjälp* for human support, *stopp* to disconnect.'
|
||||||
const multi =
|
const multi =
|
||||||
companyCount > 1
|
companyCount > 1
|
||||||
? `You belong to ${companyCount} companies in Accounted. Pick a default company in the panel so receipts land in the right one. `
|
? `You belong to ${companyCount} companies in Accounted. Either pick a default company in the panel, ` +
|
||||||
|
'or send one receipt at a time and I will ask which company it belongs to right after each one.\n\n'
|
||||||
: ''
|
: ''
|
||||||
return `${intro}\n\n${tips}\n\n${multi}${outro}`
|
return `${intro}\n\n${tips}\n\n${multi}${outro}`
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user