Procházet zdrojové kódy

Count every map check on an inspection as its visit, on screen and on the certificate (#434)

Bernt Christian Egeland před 23 hodinami
rodič
revize
e6c42d566e

+ 33 - 0
e2e/specs/condition-map/inspection.spec.ts

@@ -140,6 +140,39 @@ test('a job’s drop-off mark is grey on the inspection', async ({ page }) => {
   await expect(earlierSection(map)).toContainText('Bonnet')
 })
 
+test('a second map check on the inspection is the same visit, changed on its own check', async ({
+  page,
+}) => {
+  const job = await plantConditionJob(organizationId, userId, `E2E two checks ${stamp}`)
+  const inspection = await plantConditionInspection(organizationId, job.vehicleId, `E2E ${stamp}`, {
+    secondCheck: true,
+  })
+  await plantConditionMark(
+    organizationId,
+    job.vehicleId,
+    { inspectionId: inspection.inspectionId, inspectionItemId: inspection.secondItemId! },
+    { view: 'rear', panel: 'rear_bumper' }
+  )
+
+  await openInspection(page, inspection.inspectionId)
+  const maps = page.getByTestId('condition-map')
+  await expect(maps).toHaveCount(2)
+  const first = maps.first()
+  // On the first check it is this visit, in colour, said to come from the other check.
+  await expect(ownMarks(first)).toHaveCount(1)
+  await expect(earlierSection(first)).toHaveCount(0)
+  await expect(first).toContainText('From another check on this inspection')
+  await ownMarks(first).first().click()
+  const editor = markEditor(page)
+  await expect(editor).toContainText('Drawn on another check of this inspection. Change it there.')
+  await expect(editor.getByRole('button', { name: 'Remove mark' })).toHaveCount(0)
+  await page.keyboard.press('Escape')
+
+  // On its own check it can be changed.
+  await ownMarks(maps.last()).first().click()
+  await expect(editor.getByRole('button', { name: 'Remove mark' })).toBeVisible()
+})
+
 test('a completed inspection’s map cannot be changed until it is reopened', async ({ page }) => {
   const job = await plantConditionJob(organizationId, userId, `E2E completed ${stamp}`)
   const inspection = await plantConditionInspection(organizationId, job.vehicleId, `E2E ${stamp}`)

+ 19 - 5
e2e/support/db.ts

@@ -1647,8 +1647,14 @@ export async function plantConditionInspection(
   organizationId: string,
   vehicleId: string,
   label: string,
-  options: { serviceRecordId?: string; completed?: boolean; startedAt?: Date } = {}
-): Promise<{ inspectionId: string; inspectionItemId: string }> {
+  options: {
+    serviceRecordId?: string
+    completed?: boolean
+    startedAt?: Date
+    /** A second condition map check after the first: a hand-back beside the check-in. */
+    secondCheck?: boolean
+  } = {}
+): Promise<{ inspectionId: string; inspectionItemId: string; secondItemId: string | null }> {
   return withDb(async (db) => {
     const id = () => randomBytes(12).toString('hex')
     const templateId = id()
@@ -1683,17 +1689,25 @@ export async function plantConditionInspection(
       ]
     )
     await db.query(
-      `insert into inspection_items (id, "inspectionId", name, section, "inputType", condition)
-       values ($1, $2, 'Condition map', 'Body', 'condition_map', 'ok')`,
+      `insert into inspection_items (id, "inspectionId", name, section, "inputType", condition, "sortOrder")
+       values ($1, $2, 'Condition map', 'Body', 'condition_map', 'ok', 0)`,
       [inspectionItemId, inspectionId]
     )
+    const secondItemId = options.secondCheck ? id() : null
+    if (secondItemId) {
+      await db.query(
+        `insert into inspection_items (id, "inspectionId", name, section, "inputType", condition, "sortOrder")
+         values ($1, $2, 'Hand-back map', 'Body', 'condition_map', 'ok', 1)`,
+        [secondItemId, inspectionId]
+      )
+    }
     if (options.serviceRecordId) {
       await db.query(`update service_records set "inspectionId" = $2 where id = $1`, [
         options.serviceRecordId,
         inspectionId,
       ])
     }
-    return { inspectionId, inspectionItemId }
+    return { inspectionId, inspectionItemId, secondItemId }
   })
 }
 

+ 2 - 0
messages/de/conditionMap.json

@@ -38,9 +38,11 @@
   "readOnly": "Zum Ändern der Zustandskarte wieder öffnen.",
   "readOnlyLinked": "Auf der verknüpften Inspektion eingezeichnet. Dort ändern.",
   "readOnlyEarlier": "Bei einem früheren Besuch erfasst. Als repariert entfernen, sobald der Schaden behoben ist.",
+  "readOnlyOtherCheck": "Auf einem anderen Prüfpunkt dieser Inspektion eingezeichnet. Dort ändern.",
   "count": "{count, plural, =0 {Keine Markierungen} one {# Markierung} other {# Markierungen}}",
   "previousCount": "{count, plural, one {# von einem früheren Besuch} other {# von früheren Besuchen}}",
   "fromInspection": "Aus der verknüpften Inspektion, dort änderbar",
+  "fromOtherCheck": "Von einem anderen Prüfpunkt dieser Inspektion",
   "elsewhere": "{count, plural, one {# Markierung wurde auf einer anderen Karosserie gesetzt und erscheint nur im Ausdruck} other {# Markierungen wurden auf einer anderen Karosserie gesetzt und erscheinen nur im Ausdruck}}",
   "photoCount": "{count, plural, one {# Foto} other {# Fotos}}",
   "clearBody": "Markierung {n} an {area} wird als repariert vermerkt. Sie bleibt in der Historie und wird nicht mehr gedruckt.",

+ 2 - 0
messages/en/conditionMap.json

@@ -38,9 +38,11 @@
   "readOnly": "Reopen to change the condition map.",
   "readOnlyLinked": "Drawn on the linked inspection. Change it there.",
   "readOnlyEarlier": "Recorded on an earlier visit. Clear it as repaired once it is gone.",
+  "readOnlyOtherCheck": "Drawn on another check of this inspection. Change it there.",
   "count": "{count, plural, =0 {No marks} one {# mark} other {# marks}}",
   "previousCount": "{count, plural, one {# from an earlier visit} other {# from earlier visits}}",
   "fromInspection": "From the linked inspection, changed there",
+  "fromOtherCheck": "From another check on this inspection",
   "views": {
     "top": "Top",
     "left": "Left side",

+ 2 - 0
messages/es/conditionMap.json

@@ -38,9 +38,11 @@
   "readOnly": "Reabra para cambiar el mapa de estado.",
   "readOnlyLinked": "Marcada en la inspección vinculada. Cámbiela allí.",
   "readOnlyEarlier": "Registrada en una visita anterior. Márquela como reparada cuando ya no esté.",
+  "readOnlyOtherCheck": "Marcada en otro punto de esta inspección. Cámbiela allí.",
   "count": "{count, plural, =0 {Sin marcas} one {# marca} other {# marcas}}",
   "previousCount": "{count, plural, one {# de una visita anterior} other {# de visitas anteriores}}",
   "fromInspection": "De la inspección vinculada; se modifica allí",
+  "fromOtherCheck": "De otro punto de esta inspección",
   "elsewhere": "{count, plural, one {# marca se hizo en otra carrocería y solo aparece en la impresión} other {# marcas se hicieron en otra carrocería y solo aparecen en la impresión}}",
   "photoCount": "{count, plural, one {# foto} other {# fotos}}",
   "clearBody": "La marca {n} en {area} quedará registrada como reparada. Se conserva en el historial y deja de imprimirse.",

+ 2 - 0
messages/fr/conditionMap.json

@@ -38,9 +38,11 @@
   "readOnly": "Rouvrez pour modifier la carte d'état.",
   "readOnlyLinked": "Placée sur l'inspection liée. Modifiez-la là-bas.",
   "readOnlyEarlier": "Relevée lors d'une visite précédente. Marquez-la comme réparée une fois disparue.",
+  "readOnlyOtherCheck": "Placée sur un autre point de cette inspection. Modifiez-la là-bas.",
   "count": "{count, plural, =0 {Aucune marque} one {# marque} other {# marques}}",
   "previousCount": "{count, plural, one {# d'une visite précédente} other {# de visites précédentes}}",
   "fromInspection": "De l'inspection liée, à modifier là-bas",
+  "fromOtherCheck": "D'un autre point de cette inspection",
   "elsewhere": "{count, plural, one {# marque a été posée sur une autre carrosserie et n'apparaît que sur l'impression} other {# marques ont été posées sur une autre carrosserie et n'apparaissent que sur l'impression}}",
   "photoCount": "{count, plural, one {# photo} other {# photos}}",
   "clearBody": "La marque {n} sur {area} sera notée comme réparée. Elle reste dans l'historique et ne s'imprime plus.",

+ 2 - 0
messages/it/conditionMap.json

@@ -38,9 +38,11 @@
   "readOnly": "Riapri per modificare la mappa dello stato.",
   "readOnlyLinked": "Segnata sull'ispezione collegata. Modificala lì.",
   "readOnlyEarlier": "Registrata in una visita precedente. Segnala come riparata quando non c'è più.",
+  "readOnlyOtherCheck": "Segnata su un altro controllo di questa ispezione. Modificala lì.",
   "count": "{count, plural, =0 {Nessun segno} one {# segno} other {# segni}}",
   "previousCount": "{count, plural, one {# da una visita precedente} other {# da visite precedenti}}",
   "fromInspection": "Dall'ispezione collegata, si modifica lì",
+  "fromOtherCheck": "Da un altro controllo di questa ispezione",
   "elsewhere": "{count, plural, one {# segno è stato fatto su un'altra carrozzeria e compare solo nella stampa} other {# segni sono stati fatti su un'altra carrozzeria e compaiono solo nella stampa}}",
   "photoCount": "{count, plural, one {# foto} other {# foto}}",
   "clearBody": "Il segno {n} su {area} verrà registrato come riparato. Resta nello storico e non viene più stampato.",

+ 2 - 0
messages/lt/conditionMap.json

@@ -38,9 +38,11 @@
   "readOnly": "Norėdami keisti būklės žemėlapį, atidarykite iš naujo.",
   "readOnlyLinked": "Pažymėta susietoje apžiūroje. Keiskite ten.",
   "readOnlyEarlier": "Užfiksuota ankstesnio apsilankymo metu. Pažymėkite kaip sutaisytą, kai jos nebeliks.",
+  "readOnlyOtherCheck": "Pažymėta kitame šios apžiūros punkte. Keiskite ten.",
   "count": "{count, plural, =0 {Žymų nėra} one {# žyma} few {# žymos} other {# žymų}}",
   "previousCount": "{count, plural, one {# iš ankstesnio vizito} few {# iš ankstesnių vizitų} other {# iš ankstesnių vizitų}}",
   "fromInspection": "Iš susietos apžiūros, keičiama ten",
+  "fromOtherCheck": "Iš kito šios apžiūros punkto",
   "elsewhere": "{count, plural, one {# žyma padėta ant kito kėbulo tipo ir rodoma tik spaudinyje} few {# žymos padėtos ant kito kėbulo tipo ir rodomos tik spaudinyje} other {# žymų padėta ant kito kėbulo tipo ir rodomos tik spaudinyje}}",
   "photoCount": "{count, plural, one {# nuotrauka} few {# nuotraukos} other {# nuotraukų}}",
   "clearBody": "Žyma {n} ant {area} bus pažymėta kaip sutaisyta. Ji lieka istorijoje ir nebespausdinama.",

+ 2 - 0
messages/nb/conditionMap.json

@@ -38,9 +38,11 @@
   "readOnly": "Åpne på nytt for å endre tilstandskartet.",
   "readOnlyLinked": "Tegnet på den koblede inspeksjonen. Endre den der.",
   "readOnlyEarlier": "Registrert ved et tidligere besøk. Fjern som reparert når skaden er borte.",
+  "readOnlyOtherCheck": "Tegnet på et annet kontrollpunkt i denne inspeksjonen. Endre den der.",
   "count": "{count, plural, =0 {Ingen merker} one {# merke} other {# merker}}",
   "previousCount": "{count, plural, one {# fra et tidligere besøk} other {# fra tidligere besøk}}",
   "fromInspection": "Fra den koblede inspeksjonen, endres der",
+  "fromOtherCheck": "Fra et annet kontrollpunkt i denne inspeksjonen",
   "elsewhere": "{count, plural, one {# merke ble satt på et annet karosseri og vises bare på utskriften} other {# merker ble satt på et annet karosseri og vises bare på utskriften}}",
   "photoCount": "{count, plural, one {# bilde} other {# bilder}}",
   "clearBody": "Merke {n} på {area} registreres som reparert. Det blir i historikken og skrives ikke ut lenger.",

+ 2 - 0
messages/nl/conditionMap.json

@@ -38,9 +38,11 @@
   "readOnly": "Heropen om de staatkaart te wijzigen.",
   "readOnlyLinked": "Getekend op de gekoppelde inspectie. Wijzig hem daar.",
   "readOnlyEarlier": "Vastgelegd bij een eerder bezoek. Markeer als gerepareerd zodra de schade weg is.",
+  "readOnlyOtherCheck": "Getekend bij een ander controlepunt van deze inspectie. Wijzig hem daar.",
   "count": "{count, plural, =0 {Geen markeringen} one {# markering} other {# markeringen}}",
   "previousCount": "{count, plural, one {# van een eerder bezoek} other {# van eerdere bezoeken}}",
   "fromInspection": "Uit de gekoppelde inspectie, daar te wijzigen",
+  "fromOtherCheck": "Van een ander controlepunt van deze inspectie",
   "elsewhere": "{count, plural, one {# markering is op een andere carrosserie gezet en staat alleen op de afdruk} other {# markeringen zijn op een andere carrosserie gezet en staan alleen op de afdruk}}",
   "photoCount": "{count, plural, one {# foto} other {# foto's}}",
   "clearBody": "Markering {n} op {area} wordt als hersteld vastgelegd. Hij blijft in de historie en wordt niet meer afgedrukt.",

+ 2 - 0
messages/pl/conditionMap.json

@@ -38,9 +38,11 @@
   "readOnly": "Otwórz ponownie, aby zmienić mapę stanu.",
   "readOnlyLinked": "Zaznaczone na powiązanym przeglądzie. Zmień je tam.",
   "readOnlyEarlier": "Zapisane podczas wcześniejszej wizyty. Oznacz jako naprawione, gdy uszkodzenia już nie ma.",
+  "readOnlyOtherCheck": "Zaznaczone w innym punkcie tego przeglądu. Zmień je tam.",
   "count": "{count, plural, =0 {Brak oznaczeń} one {# oznaczenie} few {# oznaczenia} other {# oznaczeń}}",
   "previousCount": "{count, plural, one {# z wcześniejszej wizyty} few {# z wcześniejszych wizyt} other {# z wcześniejszych wizyt}}",
   "fromInspection": "Z powiązanego przeglądu, zmieniane tam",
+  "fromOtherCheck": "Z innego punktu tego przeglądu",
   "elsewhere": "{count, plural, one {# oznaczenie naniesiono na inne nadwozie i widać je tylko na wydruku} few {# oznaczenia naniesiono na inne nadwozie i widać je tylko na wydruku} other {# oznaczeń naniesiono na inne nadwozie i widać je tylko na wydruku}}",
   "photoCount": "{count, plural, one {# zdjęcie} few {# zdjęcia} other {# zdjęć}}",
   "clearBody": "Oznaczenie {n} na {area} zostanie zapisane jako naprawione. Pozostaje w historii i nie jest już drukowane.",

+ 2 - 0
messages/pt-BR/conditionMap.json

@@ -38,9 +38,11 @@
   "readOnly": "Reabra para alterar o mapa de estado.",
   "readOnlyLinked": "Marcada na inspeção vinculada. Altere lá.",
   "readOnlyEarlier": "Registrada em uma visita anterior. Marque como reparada quando não existir mais.",
+  "readOnlyOtherCheck": "Marcada em outro item desta inspeção. Altere lá.",
   "count": "{count, plural, =0 {Sem marcas} one {# marca} other {# marcas}}",
   "previousCount": "{count, plural, one {# de uma visita anterior} other {# de visitas anteriores}}",
   "fromInspection": "Da inspeção vinculada; altere lá",
+  "fromOtherCheck": "De outro item desta inspeção",
   "elsewhere": "{count, plural, one {# marca foi feita em outra carroceria e aparece só na impressão} other {# marcas foram feitas em outra carroceria e aparecem só na impressão}}",
   "photoCount": "{count, plural, one {# foto} other {# fotos}}",
   "clearBody": "A marca {n} em {area} será registrada como reparada. Ela fica no histórico e deixa de ser impressa.",

+ 2 - 0
messages/pt-PT/conditionMap.json

@@ -38,9 +38,11 @@
   "readOnly": "Reabra para alterar o mapa de estado.",
   "readOnlyLinked": "Marcada na inspeção associada. Altere lá.",
   "readOnlyEarlier": "Registada numa visita anterior. Marque como reparada quando já não existir.",
+  "readOnlyOtherCheck": "Marcada noutro item desta inspeção. Altere lá.",
   "count": "{count, plural, =0 {Sem marcas} one {# marca} other {# marcas}}",
   "previousCount": "{count, plural, one {# de uma visita anterior} other {# de visitas anteriores}}",
   "fromInspection": "Da inspeção associada; altere lá",
+  "fromOtherCheck": "De outro item desta inspeção",
   "elsewhere": "{count, plural, one {# marca foi feita noutra carroçaria e aparece só na impressão} other {# marcas foram feitas noutra carroçaria e aparecem só na impressão}}",
   "photoCount": "{count, plural, one {# fotografia} other {# fotografias}}",
   "clearBody": "A marca {n} em {area} será registada como reparada. Fica no histórico e deixa de ser impressa.",

+ 2 - 0
messages/ru/conditionMap.json

@@ -38,9 +38,11 @@
   "readOnly": "Откройте заново, чтобы изменить карту состояния.",
   "readOnlyLinked": "Отмечено в связанном осмотре. Измените там.",
   "readOnlyEarlier": "Записано при прошлом визите. Отметьте как отремонтированное, когда повреждения не станет.",
+  "readOnlyOtherCheck": "Отмечено в другом пункте этого осмотра. Измените там.",
   "count": "{count, plural, =0 {Нет отметок} one {# отметка} few {# отметки} other {# отметок}}",
   "previousCount": "{count, plural, one {# с прошлого визита} few {# с прошлых визитов} other {# с прошлых визитов}}",
   "fromInspection": "Из связанного осмотра, изменяется там",
+  "fromOtherCheck": "Из другого пункта этого осмотра",
   "elsewhere": "{count, plural, one {# отметка сделана на другом типе кузова и есть только в распечатке} few {# отметки сделаны на другом типе кузова и есть только в распечатке} other {# отметок сделано на другом типе кузова и есть только в распечатке}}",
   "photoCount": "{count, plural, one {# фото} few {# фото} other {# фото}}",
   "clearBody": "Отметка {n} на {area} будет записана как отремонтированная. Она остаётся в истории и больше не печатается.",

+ 2 - 0
messages/tr/conditionMap.json

@@ -38,9 +38,11 @@
   "readOnly": "Durum haritasını değiştirmek için yeniden açın.",
   "readOnlyLinked": "Bağlı muayenede işaretlendi. Orada değiştirin.",
   "readOnlyEarlier": "Önceki bir ziyarette kaydedildi. Hasar giderildiğinde onarıldı olarak kaldırın.",
+  "readOnlyOtherCheck": "Bu muayenenin başka bir kontrolünde işaretlendi. Orada değiştirin.",
   "count": "{count, plural, =0 {İşaret yok} one {# işaret} other {# işaret}}",
   "previousCount": "{count, plural, one {# önceki ziyaretten} other {# önceki ziyaretlerden}}",
   "fromInspection": "Bağlı muayeneden, orada değiştirilir",
+  "fromOtherCheck": "Bu muayenenin başka bir kontrolünden",
   "elsewhere": "{count, plural, one {# işaret başka bir kasa tipine konuldu ve yalnızca çıktıda listelenir} other {# işaret başka bir kasa tipine konuldu ve yalnızca çıktıda listelenir}}",
   "photoCount": "{count, plural, one {# fotoğraf} other {# fotoğraf}}",
   "clearBody": "{area} üzerindeki {n} numaralı işaret onarıldı olarak kaydedilecek. Geçmişte kalır ve artık yazdırılmaz.",

+ 13 - 0
src/__tests__/features/condition-map/marks.test.ts

@@ -53,6 +53,19 @@ describe('condition marks', () => {
     expect(onJob.previous.map((m) => m.id)).toEqual(['a', 'b'])
   })
 
+  it('counts another map check on the same inspection as this visit, drawn on that check', () => {
+    const checkin = mark({ id: 'checkin', inspectionId: 'i1', inspectionItemId: 'c1' })
+    const handback = mark({ id: 'handback', inspectionId: 'i1', inspectionItemId: 'c2' })
+    const scope = { inspectionId: 'i1', inspectionItemId: 'c2' }
+    expect(splitMarks([checkin, handback], scope).own.map((m) => m.id)).toEqual([
+      'checkin',
+      'handback',
+    ])
+    expect(splitMarks([checkin, handback], scope).previous).toEqual([])
+    expect(isDrawnOnSheet(checkin, scope)).toBe(false)
+    expect(isDrawnOnSheet(handback, scope)).toBe(true)
+  })
+
   it("counts the linked inspection's marks as the job's visit, but only the drop-off's as drawn on it", () => {
     const marks = [
       mark({ id: 'checkin', inspectionId: 'i1', inspectionItemId: 'c1' }),

+ 17 - 1
src/__tests__/features/condition-map/print.test.ts

@@ -117,7 +117,12 @@ describe('the printed condition map', () => {
 
   it('numbers earlier marks first, greys them, and can leave them out', () => {
     const marks = [
-      mark({ id: 'new', inspectionItemId: 'c1', recordedAt: '2026-09-05T00:00:00Z' }),
+      mark({
+        id: 'new',
+        inspectionId: 'i',
+        inspectionItemId: 'c1',
+        recordedAt: '2026-09-05T00:00:00Z',
+      }),
       mark({
         id: 'old',
         serviceRecordId: 's0',
@@ -244,6 +249,17 @@ describe('the documents', () => {
     expect(ids(certificate({ conditionMarks: [] }))).not.toContain('condition_map')
   })
 
+  it('counts every condition map check on the inspection as its own', () => {
+    // A mark on a second map check (a hand-back after the check-in) is this
+    // inspection's too: printed in colour, not grey and "recorded earlier".
+    const spec = certificate({
+      conditionMarks: [mark({ id: 'handback', inspectionId: 'insp1', inspectionItemId: 'map2' })],
+    })
+    expect(ids(spec)).toContain('condition_map')
+    const block = spec.blocks.find((b: any) => b.id === 'condition_map').content
+    expect(JSON.stringify(block)).not.toContain(labels.previous)
+  })
+
   it("puts every open mark on the work order, this job's in colour", () => {
     const spec = buildWorkOrderPrintSpec({
       data: {

+ 14 - 0
src/__tests__/features/condition-map/visit-documents.test.ts

@@ -168,6 +168,20 @@ describe('the Vehicle Condition section on an invoice and a quote', () => {
     expect(ids.indexOf('condition_map')).toBeGreaterThan(ids.indexOf('totals'))
   })
 
+  it('goes in front of the signing line in a saved quote design too', () => {
+    // A quote design is merged from the same sections as an invoice, so the
+    // appendix rule reaches it without asking for it by name.
+    const layout = getDefaultLayout('quote')
+    const saved = {
+      ...layout,
+      version: 3,
+      sections: layout.sections.filter((s) => s.id !== 'condition_map'),
+    }
+    const ids = mergeWithDefaults(saved).sections.map((s) => s.id)
+    expect(ids).toContain('condition_map')
+    expect(ids.indexOf('condition_map')).toBe(ids.indexOf('signature') - 1)
+  })
+
   it('keeps the choice a workshop saved', () => {
     const layout = getDefaultLayout('invoice')
     const on = {

+ 9 - 3
src/features/condition-map/Components/ConditionMapCard.tsx

@@ -394,6 +394,7 @@ export function ConditionMapCard({
                       number={numberOf.get(mark.id) ?? 0}
                       previous={false}
                       fromInspection={elsewhereSheet.has(mark.id)}
+                      onInspection={'inspectionItemId' in scope}
                       onOpen={() => setEditingId(mark.id)}
                     />
                   ))}
@@ -496,7 +497,7 @@ export function ConditionMapCard({
           readOnly || !editing || isDrawnOnSheet(editing, scope)
             ? undefined
             : isOwnMark(editing, scope)
-              ? t('readOnlyLinked')
+              ? t('inspectionItemId' in scope ? 'readOnlyOtherCheck' : 'readOnlyLinked')
               : t('readOnlyEarlier')
         }
         busy={pending}
@@ -562,6 +563,7 @@ function LegendRow({
   number,
   previous,
   fromInspection = false,
+  onInspection = false,
   onOpen,
   onClear,
 }: {
@@ -569,8 +571,10 @@ function LegendRow({
   types: readonly MarkType[]
   number: number
   previous: boolean
-  /** Recorded on the inspection linked to this job, and changed there. */
+  /** Drawn on another sheet of this visit (the job's linked inspection, or another check), and changed there. */
   fromInspection?: boolean
+  /** The card is an inspection check, so the other sheet is another check. */
+  onInspection?: boolean
   onOpen: () => void
   onClear?: () => void
 }) {
@@ -611,7 +615,9 @@ function LegendRow({
             <span className="block truncate text-xs text-muted-foreground">{mark.note}</span>
           )}
           {fromInspection && (
-            <span className="block text-[11px] text-muted-foreground">{t('fromInspection')}</span>
+            <span className="block text-[11px] text-muted-foreground">
+              {t(onInspection ? 'fromOtherCheck' : 'fromInspection')}
+            </span>
           )}
           {mark.imageUrls.length > 0 && (
             <span className="block text-[11px] text-muted-foreground">

+ 3 - 1
src/features/condition-map/Lib/marks.ts

@@ -127,7 +127,9 @@ export type MarkScope = (
 
 /** Whether a mark is this visit's rather than one still open from an earlier one. */
 export function isOwnMark(mark: ConditionMarkData, scope: MarkScope): boolean {
-  if ('inspectionItemId' in scope) return mark.inspectionItemId === scope.inspectionItemId
+  // Every map check on one inspection records the same visit: a hand-back
+  // map's marks are the check-in map's visit too, drawn on the other check.
+  if ('inspectionItemId' in scope) return mark.inspectionId === scope.inspectionId
   if (scope.serviceRecordId && mark.serviceRecordId === scope.serviceRecordId) return true
   return Boolean(scope.linkedInspectionId) && mark.inspectionId === scope.linkedInspectionId
 }

+ 4 - 1
src/features/inspections/Pdf/buildCertificatePrint.ts

@@ -310,7 +310,10 @@ export function buildCertificatePrintSpec(input: CertificatePrintInput): Documen
       ? conditionMapForPrint({
           bodyType: input.bodyType,
           marks: marksAsOf(input.conditionMarks, data.completedAt),
-          scope: { inspectionId: data.id, inspectionItemId: mapItem.id, openedAt: data.createdAt },
+          // Every condition map check on the inspection is its own record of
+          // the car, not only the first: an inspection with a check-in map
+          // and a second one at hand-back prints both sets of marks.
+          scope: { linkedInspectionId: data.id, openedAt: data.createdAt },
           requireOwn: true,
           includePrevious:
             mapSection?.fields?.find((f) => f.id === 'previous_marks')?.visible !== false,

+ 1 - 0
src/features/settings/Schema/invoiceLayoutSchema.ts

@@ -1067,6 +1067,7 @@ export function mergeWithDefaults(saved: Partial<InvoiceLayoutConfig>): InvoiceL
     if (seen.has(def.id)) continue
     const defaultIdx = defaultOrder.indexOf(def.id)
     let insertAfterIdx = -1
+    // 'invoice' covers quotes too: they share one section list.
     const closing = documentType === 'invoice' ? INSERTS_BEFORE[def.id] : undefined
     const closingIdx = closing ? merged.findIndex((s) => closing.includes(s.id)) : -1
     if (closingIdx !== -1) {