Ugrás a tartalomra
kassza

Pénztári nyugta

Egy food truck tabletes pénztárprogramja minden eladás után nyugtát kér. A vevő kérheti e-mailben is. A hálózat itt sem megbízható: ha a kérés elakad, a pénztáros újra megnyomja a gombot, és ebből nem lehet két nyugta.

Nyugtánál a dupla kiállítást a hívásazonosító (callId) akadályozza meg. Ugyanazzal a callId-val a Számlázz.hu nem készít második nyugtát, hanem 338-as hibát ad, amelyet a kassza duplicate kategóriájú hibává alakít. Emiatt a kassza hálózati hiba után a nyugtát magától is újrapróbálja, ha van callId.

A nyugtakészítő függvény#

A nyugta előtagját és alapértelmezett fizetési módját a Közös kliens defaults.receipt beállítása adja.

lib/penztar.ts
import type { Kassza, Receipt, ReceiptItemInput } from 'kassza'
import { kassza as alapKliens } from '@/lib/kassza'

export interface Eladas {
  readonly id: string
  readonly fizetesiMod: 'készpénz' | 'bankkártya'
  readonly tetelek: readonly ReceiptItemInput[]
}

export async function nyugtaEladasrol(
  eladas: Eladas,
  kliens: Kassza = alapKliens,
): Promise<Receipt> {
  const { receipt } = await kliens.receipts.createOnce(
    {
      orderNumber: `POS-${eladas.id}`,
      paymentMethod: eladas.fizetesiMod,
      downloadPdf: false,
      items: eladas.tetelek,
    },
    { lookupFirst: false },
  )
  return receipt
}

A receipts.createOnce() a rendelésszámot hívásazonosítóként is elküldi. Ha a Számlázz.hu 338-as hibát ad, vagy a kimenet bizonytalan (hálózati hiba, időtúllépés), a rendelésszám alapján megkeresi és visszaadja a már elkészült nyugtát. A Számla Agent hívásazonosító alapján nem ad lekérdezést, ezért kell a rendelésszám is.

A lookupFirst: false kihagyja a kiállítás előtti lekérdezést. A pénztárnál minden eladás új, így eladásonként egy kéréssel kevesebb megy el, az ismételt gombnyomást pedig a hívásazonosító így is kiszűri. A részletek a Pontosan egyszer oldalon vannak.

A tételeknél bruttó egységárat adj meg, a nettó és az áfa összegét a kassza számolja ki a nyugtákra vonatkozó kerekítéssel:

lib/penztar-tetelek.ts
import type { ReceiptItemInput } from 'kassza'

export const LANGOS: ReceiptItemInput = { name: 'Lángos', grossUnitPrice: 1_490, vat: 5 }
export const ASVANYVIZ: ReceiptItemInput = { name: 'Ásványvíz 0,5 l', grossUnitPrice: 590, vat: 27 }

Az áfakulcsot a könyvelőddel egyeztesd: vendéglátásban például a helyben készített étel 5%-os, a palackozott ital 27%-os lehet.

Pénztári nyugta, dupla gombnyomás

createOnce hívásazonosítóval: a második gombnyomás a már elkészült nyugtát adja vissza.

Futtatás a sandboxban
penztari-nyugta.ts
import { , type  } from 'kassza'

const  = ({
  : { : { : 'NYGT', : 'bankkártya' } },
})

const : [] = [
  { : 'Lángos', : 2, : 1_490, : 5 },
  { : 'Ásványvíz 0,5 l', : 590, : 27 },
]

async function (: string): <string> {
  const {  } = await ..(
    { : `POS-${}`, : false, :  },
    { : false },
  )
  return .
}

await ('2026-1003-0042')
await ('2026-1003-0042')

Route handler#

A pénztárprogram az eladás mentése után hívja ezt a végpontot, opcionálisan a vevő e-mail címével. Az eladasBetoltese() és a nyugtaszamMentese() a saját adatbázis-függvényeid, az adminKeres() a Nevezés díjbekérővel recept belső token ellenőrzése.

app/api/penztar/eladasok/[id]/nyugta/route.ts
import { isSzamlazzError, type Receipt } from 'kassza'
import { isValidEmail } from 'kassza/validators'
import { adminKeres } from '@/lib/admin'
import { eladasBetoltese, nyugtaszamMentese } from '@/lib/eladasok'
import { kassza } from '@/lib/kassza'
import { nyugtaEladasrol } from '@/lib/penztar'

async function nyugtaKuldese(receiptNumber: string, email: string): Promise<boolean> {
  try {
    await kassza.receipts.send({
      receiptNumber,
      emails: email,
      subject: `Nyugtád: ${receiptNumber}`,
    })
    return true
  } catch (error) {
    if (!isSzamlazzError(error)) throw error
    console.error('A nyugta kiküldése nem sikerült', receiptNumber, error.category)
    return false
  }
}

export async function POST(request: Request, { params }: { params: Promise<{ id: string }> }) {
  if (!adminKeres(request)) return new Response('Nincs jogosultság', { status: 401 })

  const { id } = await params
  const eladas = await eladasBetoltese(id)
  if (!eladas) return Response.json({ hiba: 'Nincs ilyen eladás.' }, { status: 404 })

  const { email } = (await request.json().catch(() => ({}))) as { email?: unknown }
  if (email !== undefined && (typeof email !== 'string' || !isValidEmail(email))) {
    return Response.json({ hiba: 'Érvénytelen e-mail cím.' }, { status: 400 })
  }

  let nyugta: Receipt
  try {
    nyugta = await nyugtaEladasrol(eladas)
  } catch (error) {
    if (!isSzamlazzError(error)) throw error
    return Response.json(
      { hiba: error.message, tipp: error.hint },
      { status: error.retryable ? 503 : 422 },
    )
  }
  await nyugtaszamMentese(eladas.id, nyugta.number)

  const kikuldve = typeof email === 'string' ? await nyugtaKuldese(nyugta.number, email) : false
  return Response.json({
    nyugtaszam: nyugta.number,
    brutto: nyugta.totals.grossAmount,
    kikuldve,
  })
}

Ha a végpont 503-mal válaszol, a pénztáros nyugodtan újrapróbálhatja: ugyanaz a callId megy el, így legfeljebb a már elkészült nyugtát kapja vissza.

Nyugta kiküldése e-mailben

Nyugta elküldése a vevőnek saját tárggyal és válaszcímmel.

Futtatás a sandboxban
nyugta-kikuldes.ts
import {  } from 'kassza'

const  = ({
  : { : { : 'NYGT', : 'bankkártya' } },
})

const  = await ..({
  : 'WEB-2026-0391',
  : [{ : 'Online kurzus, 1 hónap', : 7_990, : 27 }],
})

await ..({
  : .,
  : ['vevo@example.hu', 'konyveles@example.hu'],
  : 'ugyfelszolgalat@example.hu',
  : `Nyugta a vásárlásodról (${.})`,
  : 'Köszönjük a vásárlást! A nyugtát csatolva küldjük.',
})

Ha a Számlázz.hu nem érhető el#

Karbantartás (1-es hiba) vagy hosszabb hálózati hiba alatt az árusítás nem állhat le. A pénztáros ilyenkor a nyomdai nyugtatömbből ad kézi nyugtát, a pénztárprogram pedig rögzíti, melyik eladáshoz melyik tömbnyugta tartozik. Ugyanarról az eladásról később ne állíts ki Agent-nyugtát is.

A kézi nyugtákról a Számlázz.hu nem tud, ezért a napi NAV-összesítőjüket neked kell beküldeni. Ehhez ad eszközt a Kézi nyugtatömb oldal.

Buktatók#

Kapcsolódó#

Oldal szerkesztéseUtoljára frissítve: