Ugrás a tartalomra
kassza

Pénztári nyugta

Egy bolt vagy büfé 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 ezt a hívásazonosító (callId) oldja 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 { isSzamlazzError, type Kassza, type Receipt, type 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 azonosito = `POS-${eladas.id}`
  try {
    return await kliens.receipts.create({
      callId: azonosito,
      orderNumber: azonosito,
      paymentMethod: eladas.fizetesiMod,
      downloadPdf: false,
      items: eladas.tetelek,
    })
  } catch (error) {
    if (!isSzamlazzError(error) || !error.isDuplicate) throw error
    const meglevo = await kliens.receipts.find({ orderNumber: azonosito, downloadPdf: false })
    if (meglevo) return meglevo
    throw error
  }
}

A callId és a rendelésszám ugyanaz. A Számla Agent hívásazonosító alapján nem ad lekérdezést, ezért a dupla nyugta hibája után a rendelésszám alapján keressük meg a már elkészült nyugtát.

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 KAVE: ReceiptItemInput = { name: 'Kávé', grossUnitPrice: 890, vat: 27 }
export const KIFLI: ReceiptItemInput = { name: 'Kifli', grossUnitPrice: 250, vat: 5 }

Nyugta kiállítása

Pénztári nyugta hívásazonosítóval, két áfakulccsal, alapbeállításokkal.

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

const  = ({
  : { : { : 'NYGT', : 'készpénz' } },
})

const  = await ..({
  : 'PENZTAR-2026-0001',
  : 'PENZTAR-2026-0001',
  : [
    { : 'Kávé', : 2, : 890, : 27 },
    { : 'Kifli', : 250, : 5 },
  ],
})

.(., ., .)
.(.)
.(
  ..(
    () => `${.}: ${.} + ${.} = ${.}`,
  ),
)

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'
import {  } from 'kassza-sandbox'

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.',
})

.('Címzettek:', .account().receipts[0]?.sentTo)

Buktatók#

Kapcsolódó#

Oldal szerkesztéseUtoljára frissítve: