@kreiseck/kasseneck-api 1.13.0 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/README.md +71 -2
- package/dist/cjs/client/api.d.ts +8 -1
- package/dist/cjs/client/api.js +4 -0
- package/dist/cjs/client/aufrufe.d.ts +6 -6
- package/dist/cjs/client/aufrufe.js +11 -2
- package/dist/cjs/client/index.d.ts +2 -1
- package/dist/cjs/client/index.js +13 -1
- package/dist/cjs/client/list-receipts.d.ts +211 -0
- package/dist/cjs/client/list-receipts.js +290 -0
- package/dist/cjs/client/receipts.d.ts +85 -2
- package/dist/cjs/client/receipts.js +164 -5
- package/dist/cjs/enums/voucher-type.d.ts +1 -1
- package/dist/cjs/enums/voucher-type.js +1 -1
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.js +24 -3
- package/dist/cjs/invoice/typen.d.ts +1 -1
- package/dist/cjs/invoice/vertrag.d.ts +1 -1
- package/dist/cjs/invoice/vertrag.js +2 -0
- package/dist/cjs/models/index.d.ts +2 -1
- package/dist/cjs/models/index.js +12 -1
- package/dist/cjs/models/receipt-errors.d.ts +1 -1
- package/dist/cjs/models/receipt-errors.js +3 -0
- package/dist/cjs/models/receipt-output.d.ts +79 -0
- package/dist/cjs/models/receipt-output.js +143 -0
- package/dist/cjs/models/receipt.d.ts +2 -2
- package/dist/cjs/models/receipt.js +2 -2
- package/dist/cjs/models/voucher.d.ts +24 -2
- package/dist/cjs/models/voucher.js +2 -2
- package/dist/cjs/partner/typen.d.ts +1 -1
- package/dist/cjs/pos/index.d.ts +2 -1
- package/dist/cjs/pos/index.js +6 -1
- package/dist/cjs/pos/schicht-typen.d.ts +2 -2
- package/dist/cjs/pos/schicht-typen.js +2 -2
- package/dist/cjs/pos/settings.d.ts +1 -1
- package/dist/cjs/pos/settings.js +5 -0
- package/dist/cjs/pos/texte.d.ts +114 -0
- package/dist/cjs/pos/texte.js +64 -0
- package/dist/cjs/receipt/due.d.ts +4 -2
- package/dist/cjs/receipt/due.js +22 -5
- package/dist/cjs/receipt/duplikat.d.ts +35 -0
- package/dist/cjs/receipt/duplikat.js +76 -0
- package/dist/cjs/receipt/gutschein-plan.d.ts +2 -0
- package/dist/cjs/receipt/gutschein-plan.js +5 -0
- package/dist/cjs/receipt/index.d.ts +1 -0
- package/dist/cjs/receipt/index.js +4 -2
- package/dist/cjs/receipt/layout.js +44 -21
- package/dist/cjs/register/pairing.d.ts +6 -2
- package/dist/cjs/register/pairing.js +1 -1
- package/dist/cjs/stored/bon-layout.js +3 -4
- package/dist/cjs/stored/vokabular.d.ts +1 -1
- package/dist/cjs/stored/vokabular.js +47 -3
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/client/api.d.ts +8 -1
- package/dist/esm/client/api.js +5 -1
- package/dist/esm/client/aufrufe.d.ts +6 -6
- package/dist/esm/client/aufrufe.js +11 -2
- package/dist/esm/client/index.d.ts +2 -1
- package/dist/esm/client/index.js +2 -1
- package/dist/esm/client/list-receipts.d.ts +211 -0
- package/dist/esm/client/list-receipts.js +284 -0
- package/dist/esm/client/receipts.d.ts +85 -2
- package/dist/esm/client/receipts.js +164 -6
- package/dist/esm/enums/voucher-type.d.ts +1 -1
- package/dist/esm/enums/voucher-type.js +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/esm/invoice/typen.d.ts +1 -1
- package/dist/esm/invoice/vertrag.d.ts +1 -1
- package/dist/esm/invoice/vertrag.js +2 -0
- package/dist/esm/models/index.d.ts +2 -1
- package/dist/esm/models/index.js +1 -0
- package/dist/esm/models/receipt-errors.d.ts +1 -1
- package/dist/esm/models/receipt-errors.js +3 -0
- package/dist/esm/models/receipt-output.d.ts +79 -0
- package/dist/esm/models/receipt-output.js +135 -0
- package/dist/esm/models/receipt.d.ts +2 -2
- package/dist/esm/models/receipt.js +2 -2
- package/dist/esm/models/voucher.d.ts +24 -2
- package/dist/esm/models/voucher.js +2 -2
- package/dist/esm/partner/typen.d.ts +1 -1
- package/dist/esm/pos/index.d.ts +2 -1
- package/dist/esm/pos/index.js +3 -1
- package/dist/esm/pos/schicht-typen.d.ts +2 -2
- package/dist/esm/pos/schicht-typen.js +2 -2
- package/dist/esm/pos/settings.d.ts +1 -1
- package/dist/esm/pos/settings.js +5 -0
- package/dist/esm/pos/texte.d.ts +114 -0
- package/dist/esm/pos/texte.js +63 -0
- package/dist/esm/receipt/due.d.ts +4 -2
- package/dist/esm/receipt/due.js +23 -6
- package/dist/esm/receipt/duplikat.d.ts +35 -0
- package/dist/esm/receipt/duplikat.js +73 -0
- package/dist/esm/receipt/gutschein-plan.d.ts +2 -0
- package/dist/esm/receipt/gutschein-plan.js +4 -0
- package/dist/esm/receipt/index.d.ts +1 -0
- package/dist/esm/receipt/index.js +1 -0
- package/dist/esm/receipt/layout.js +44 -21
- package/dist/esm/register/pairing.d.ts +6 -2
- package/dist/esm/register/pairing.js +1 -1
- package/dist/esm/stored/bon-layout.js +3 -4
- package/dist/esm/stored/vokabular.d.ts +1 -1
- package/dist/esm/stored/vokabular.js +47 -3
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/fixtures/code-table-receipts.json +1 -1
- package/fixtures/duplicate-cases.json +5616 -0
- package/fixtures/expected/cancellation-discount.grid32.txt +1 -1
- package/fixtures/expected/cancellation-discount.grid48.txt +1 -1
- package/fixtures/expected/cancellation-discount.lines.json +1 -1
- package/fixtures/expected/cancellation-discount.sheet32.json +1 -1
- package/fixtures/expected/cancellation-discount.sheet48.json +1 -1
- package/fixtures/expected/cancellation-full.grid32.txt +1 -1
- package/fixtures/expected/cancellation-full.grid48.txt +1 -1
- package/fixtures/expected/cancellation-full.lines.json +1 -1
- package/fixtures/expected/cancellation-full.sheet32.json +1 -1
- package/fixtures/expected/cancellation-full.sheet48.json +1 -1
- package/fixtures/expected/cancellation-partial.grid32.txt +1 -1
- package/fixtures/expected/cancellation-partial.grid48.txt +1 -1
- package/fixtures/expected/cancellation-partial.lines.json +1 -1
- package/fixtures/expected/cancellation-partial.sheet32.json +1 -1
- package/fixtures/expected/cancellation-partial.sheet48.json +1 -1
- package/fixtures/expected/cancellation-split-full.grid32.txt +1 -1
- package/fixtures/expected/cancellation-split-full.grid48.txt +1 -1
- package/fixtures/expected/cancellation-split-full.lines.json +1 -1
- package/fixtures/expected/cancellation-split-full.sheet32.json +1 -1
- package/fixtures/expected/cancellation-split-full.sheet48.json +1 -1
- package/fixtures/expected/cancellation-split-partial.grid32.txt +1 -1
- package/fixtures/expected/cancellation-split-partial.grid48.txt +1 -1
- package/fixtures/expected/cancellation-split-partial.lines.json +1 -1
- package/fixtures/expected/cancellation-split-partial.sheet32.json +1 -1
- package/fixtures/expected/cancellation-split-partial.sheet48.json +1 -1
- package/fixtures/expected/card-custom.grid32.txt +1 -1
- package/fixtures/expected/card-custom.grid48.txt +1 -1
- package/fixtures/expected/card-custom.lines.json +1 -1
- package/fixtures/expected/card-custom.sheet32.json +1 -1
- package/fixtures/expected/card-custom.sheet48.json +1 -1
- package/fixtures/expected/card-gptom-ios.grid32.txt +1 -1
- package/fixtures/expected/card-gptom-ios.grid48.txt +1 -1
- package/fixtures/expected/card-gptom-ios.lines.json +1 -1
- package/fixtures/expected/card-gptom-ios.sheet32.json +1 -1
- package/fixtures/expected/card-gptom-ios.sheet48.json +1 -1
- package/fixtures/expected/card-gptom.grid32.txt +1 -1
- package/fixtures/expected/card-gptom.grid48.txt +1 -1
- package/fixtures/expected/card-gptom.lines.json +1 -1
- package/fixtures/expected/card-gptom.sheet32.json +1 -1
- package/fixtures/expected/card-gptom.sheet48.json +1 -1
- package/fixtures/expected/card-hobex-cloud.grid32.txt +1 -1
- package/fixtures/expected/card-hobex-cloud.grid48.txt +1 -1
- package/fixtures/expected/card-hobex-cloud.lines.json +1 -1
- package/fixtures/expected/card-hobex-cloud.sheet32.json +1 -1
- package/fixtures/expected/card-hobex-cloud.sheet48.json +1 -1
- package/fixtures/expected/card-hobex-hps.grid32.txt +1 -1
- package/fixtures/expected/card-hobex-hps.grid48.txt +1 -1
- package/fixtures/expected/card-hobex-hps.lines.json +1 -1
- package/fixtures/expected/card-hobex-hps.sheet32.json +1 -1
- package/fixtures/expected/card-hobex-hps.sheet48.json +1 -1
- package/fixtures/expected/card-mypos.grid32.txt +1 -1
- package/fixtures/expected/card-mypos.grid48.txt +1 -1
- package/fixtures/expected/card-mypos.lines.json +1 -1
- package/fixtures/expected/card-mypos.sheet32.json +1 -1
- package/fixtures/expected/card-mypos.sheet48.json +1 -1
- package/fixtures/expected/card-stripe-eps.grid32.txt +1 -1
- package/fixtures/expected/card-stripe-eps.grid48.txt +1 -1
- package/fixtures/expected/card-stripe-eps.lines.json +1 -1
- package/fixtures/expected/card-stripe-eps.sheet32.json +1 -1
- package/fixtures/expected/card-stripe-eps.sheet48.json +1 -1
- package/fixtures/expected/card-stripe.grid32.txt +1 -1
- package/fixtures/expected/card-stripe.grid48.txt +1 -1
- package/fixtures/expected/card-stripe.lines.json +1 -1
- package/fixtures/expected/card-stripe.sheet32.json +1 -1
- package/fixtures/expected/card-stripe.sheet48.json +1 -1
- package/fixtures/expected/card-sumup.grid32.txt +1 -1
- package/fixtures/expected/card-sumup.grid48.txt +1 -1
- package/fixtures/expected/card-sumup.lines.json +1 -1
- package/fixtures/expected/card-sumup.sheet32.json +1 -1
- package/fixtures/expected/card-sumup.sheet48.json +1 -1
- package/fixtures/expected/code-table-receipt.long-item-name.iso8859_15.hex +1 -1
- package/fixtures/expected/code-table-receipt.long-item-name.pc437.hex +1 -1
- package/fixtures/expected/code-table-receipt.long-item-name.pc850.hex +1 -1
- package/fixtures/expected/code-table-receipt.long-item-name.pc858.hex +1 -1
- package/fixtures/expected/code-table-receipt.long-item-name.replacement.hex +1 -1
- package/fixtures/expected/code-table-receipt.long-item-name.wpc1252.hex +1 -1
- package/fixtures/expected/code-table-receipt.sale-cash.iso8859_15.hex +1 -1
- package/fixtures/expected/code-table-receipt.sale-cash.pc437.hex +1 -1
- package/fixtures/expected/code-table-receipt.sale-cash.pc850.hex +1 -1
- package/fixtures/expected/code-table-receipt.sale-cash.pc858.hex +1 -1
- package/fixtures/expected/code-table-receipt.sale-cash.replacement.hex +1 -1
- package/fixtures/expected/code-table-receipt.sale-cash.wpc1252.hex +1 -1
- package/fixtures/expected/code-table-receipt.special-characters.iso8859_15.hex +1 -1
- package/fixtures/expected/code-table-receipt.special-characters.pc437.hex +1 -1
- package/fixtures/expected/code-table-receipt.special-characters.pc850.hex +1 -1
- package/fixtures/expected/code-table-receipt.special-characters.pc858.hex +1 -1
- package/fixtures/expected/code-table-receipt.special-characters.replacement.hex +1 -1
- package/fixtures/expected/code-table-receipt.special-characters.wpc1252.hex +1 -1
- package/fixtures/expected/code-table-receipt.test-cashregister-sale.iso8859_15.hex +1 -1
- package/fixtures/expected/code-table-receipt.test-cashregister-sale.pc437.hex +1 -1
- package/fixtures/expected/code-table-receipt.test-cashregister-sale.pc850.hex +1 -1
- package/fixtures/expected/code-table-receipt.test-cashregister-sale.pc858.hex +1 -1
- package/fixtures/expected/code-table-receipt.test-cashregister-sale.replacement.hex +1 -1
- package/fixtures/expected/code-table-receipt.test-cashregister-sale.wpc1252.hex +1 -1
- package/fixtures/expected/discount-lines.grid32.txt +1 -1
- package/fixtures/expected/discount-lines.grid48.txt +1 -1
- package/fixtures/expected/discount-lines.lines.json +1 -1
- package/fixtures/expected/discount-lines.sheet32.json +1 -1
- package/fixtures/expected/discount-lines.sheet48.json +1 -1
- package/fixtures/expected/discount-owner-tip.grid32.txt +1 -1
- package/fixtures/expected/discount-owner-tip.grid48.txt +1 -1
- package/fixtures/expected/discount-owner-tip.lines.json +1 -1
- package/fixtures/expected/discount-owner-tip.sheet32.json +1 -1
- package/fixtures/expected/discount-owner-tip.sheet48.json +1 -1
- package/fixtures/expected/discount-simple.grid32.txt +1 -1
- package/fixtures/expected/discount-simple.grid48.txt +1 -1
- package/fixtures/expected/discount-simple.lines.json +1 -1
- package/fixtures/expected/discount-simple.sheet32.json +1 -1
- package/fixtures/expected/discount-simple.sheet48.json +1 -1
- package/fixtures/expected/discount-tip.grid32.txt +1 -1
- package/fixtures/expected/discount-tip.grid48.txt +1 -1
- package/fixtures/expected/discount-tip.lines.json +1 -1
- package/fixtures/expected/discount-tip.sheet32.json +1 -1
- package/fixtures/expected/discount-tip.sheet48.json +1 -1
- package/fixtures/expected/discount-value-voucher.grid32.txt +1 -1
- package/fixtures/expected/discount-value-voucher.grid48.txt +1 -1
- package/fixtures/expected/discount-value-voucher.lines.json +1 -1
- package/fixtures/expected/discount-value-voucher.sheet32.json +1 -1
- package/fixtures/expected/discount-value-voucher.sheet48.json +1 -1
- package/fixtures/expected/duplicate.cancellation-full.hex +44 -0
- package/fixtures/expected/duplicate.cancellation-partial.hex +33 -0
- package/fixtures/expected/duplicate.sale-cash-mm58.hex +28 -0
- package/fixtures/expected/duplicate.sale-cash-twice.hex +35 -0
- package/fixtures/expected/duplicate.sale-cash.hex +35 -0
- package/fixtures/expected/duplicate.shift-close.hex +43 -0
- package/fixtures/expected/duplicate.summer-time-last-second.hex +28 -0
- package/fixtures/expected/duplicate.summer-time-start.hex +35 -0
- package/fixtures/expected/duplicate.test-cashregister-sale-logo.hex +189 -0
- package/fixtures/expected/duplicate.voucher-multi-purpose-redeem.hex +37 -0
- package/fixtures/expected/duplicate.voucher-service-redeem-logo.hex +119 -0
- package/fixtures/expected/duplicate.winter-time-first-second.hex +28 -0
- package/fixtures/expected/duplicate.zero-start.hex +40 -0
- package/fixtures/expected/long-item-name.grid32.txt +1 -1
- package/fixtures/expected/long-item-name.grid48.txt +1 -1
- package/fixtures/expected/long-item-name.lines.json +1 -1
- package/fixtures/expected/long-item-name.sheet32.json +1 -1
- package/fixtures/expected/long-item-name.sheet48.json +1 -1
- package/fixtures/expected/sale-card.grid32.txt +1 -1
- package/fixtures/expected/sale-card.grid48.txt +1 -1
- package/fixtures/expected/sale-card.lines.json +1 -1
- package/fixtures/expected/sale-card.sheet32.json +1 -1
- package/fixtures/expected/sale-card.sheet48.json +1 -1
- package/fixtures/expected/sale-cash.grid32.txt +1 -1
- package/fixtures/expected/sale-cash.grid48.txt +1 -1
- package/fixtures/expected/sale-cash.lines.json +1 -1
- package/fixtures/expected/sale-cash.sheet32.json +1 -1
- package/fixtures/expected/sale-cash.sheet48.json +1 -1
- package/fixtures/expected/sale-small-business.grid32.txt +1 -1
- package/fixtures/expected/sale-small-business.grid48.txt +1 -1
- package/fixtures/expected/sale-small-business.lines.json +1 -1
- package/fixtures/expected/sale-small-business.sheet32.json +1 -1
- package/fixtures/expected/sale-small-business.sheet48.json +1 -1
- package/fixtures/expected/signature-outage-sale.grid32.txt +1 -1
- package/fixtures/expected/signature-outage-sale.grid48.txt +1 -1
- package/fixtures/expected/signature-outage-sale.lines.json +1 -1
- package/fixtures/expected/signature-outage-sale.sheet32.json +1 -1
- package/fixtures/expected/signature-outage-sale.sheet48.json +1 -1
- package/fixtures/expected/split-card-card-cash.grid32.txt +1 -1
- package/fixtures/expected/split-card-card-cash.grid48.txt +1 -1
- package/fixtures/expected/split-card-card-cash.lines.json +1 -1
- package/fixtures/expected/split-card-card-cash.sheet32.json +1 -1
- package/fixtures/expected/split-card-card-cash.sheet48.json +1 -1
- package/fixtures/expected/split-cash-change.grid32.txt +1 -1
- package/fixtures/expected/split-cash-change.grid48.txt +1 -1
- package/fixtures/expected/split-cash-change.lines.json +1 -1
- package/fixtures/expected/split-cash-change.sheet32.json +1 -1
- package/fixtures/expected/split-cash-change.sheet48.json +1 -1
- package/fixtures/expected/split-long-amount.grid32.txt +1 -1
- package/fixtures/expected/split-long-amount.grid48.txt +1 -1
- package/fixtures/expected/split-long-amount.lines.json +1 -1
- package/fixtures/expected/split-long-amount.sheet32.json +1 -1
- package/fixtures/expected/split-long-amount.sheet48.json +1 -1
- package/fixtures/expected/split-one-card-tip.grid32.txt +1 -1
- package/fixtures/expected/split-one-card-tip.grid48.txt +1 -1
- package/fixtures/expected/split-one-card-tip.lines.json +1 -1
- package/fixtures/expected/split-one-card-tip.sheet32.json +1 -1
- package/fixtures/expected/split-one-card-tip.sheet48.json +1 -1
- package/fixtures/expected/split-table-round-tip.grid32.txt +1 -1
- package/fixtures/expected/split-table-round-tip.grid48.txt +1 -1
- package/fixtures/expected/split-table-round-tip.lines.json +1 -1
- package/fixtures/expected/split-table-round-tip.sheet32.json +1 -1
- package/fixtures/expected/split-table-round-tip.sheet48.json +1 -1
- package/fixtures/expected/split-tip-card.grid32.txt +1 -1
- package/fixtures/expected/split-tip-card.grid48.txt +1 -1
- package/fixtures/expected/split-tip-card.lines.json +1 -1
- package/fixtures/expected/split-tip-card.sheet32.json +1 -1
- package/fixtures/expected/split-tip-card.sheet48.json +1 -1
- package/fixtures/expected/split-two-cards-same-provider.grid32.txt +1 -1
- package/fixtures/expected/split-two-cards-same-provider.grid48.txt +1 -1
- package/fixtures/expected/split-two-cards-same-provider.lines.json +1 -1
- package/fixtures/expected/split-two-cards-same-provider.sheet32.json +1 -1
- package/fixtures/expected/split-two-cards-same-provider.sheet48.json +1 -1
- package/fixtures/expected/test-cashregister-sale.grid32.txt +1 -1
- package/fixtures/expected/test-cashregister-sale.grid48.txt +1 -1
- package/fixtures/expected/test-cashregister-sale.lines.json +1 -1
- package/fixtures/expected/test-cashregister-sale.sheet32.json +1 -1
- package/fixtures/expected/test-cashregister-sale.sheet48.json +1 -1
- package/fixtures/expected/test-signature-sale.grid32.txt +1 -1
- package/fixtures/expected/test-signature-sale.grid48.txt +1 -1
- package/fixtures/expected/test-signature-sale.lines.json +1 -1
- package/fixtures/expected/test-signature-sale.sheet32.json +1 -1
- package/fixtures/expected/test-signature-sale.sheet48.json +1 -1
- package/fixtures/expected/training.grid32.txt +1 -1
- package/fixtures/expected/training.grid48.txt +1 -1
- package/fixtures/expected/training.lines.json +1 -1
- package/fixtures/expected/training.sheet32.json +1 -1
- package/fixtures/expected/training.sheet48.json +1 -1
- package/fixtures/expected/voucher-multi-purpose-sale.grid32.txt +1 -2
- package/fixtures/expected/voucher-multi-purpose-sale.grid48.txt +1 -1
- package/fixtures/expected/voucher-multi-purpose-sale.lines.json +1 -1
- package/fixtures/expected/voucher-multi-purpose-sale.sheet32.json +1 -7
- package/fixtures/expected/voucher-multi-purpose-sale.sheet48.json +1 -1
- package/fixtures/expected/voucher-single-purpose-redeem-exempt.grid32.txt +1 -1
- package/fixtures/expected/voucher-single-purpose-redeem-exempt.grid48.txt +1 -1
- package/fixtures/expected/voucher-single-purpose-redeem-exempt.lines.json +1 -1
- package/fixtures/expected/voucher-single-purpose-redeem-exempt.sheet32.json +1 -1
- package/fixtures/expected/voucher-single-purpose-redeem-exempt.sheet48.json +1 -1
- package/fixtures/expected/voucher-single-purpose-redeem.grid32.txt +1 -1
- package/fixtures/expected/voucher-single-purpose-redeem.grid48.txt +1 -1
- package/fixtures/expected/voucher-single-purpose-redeem.lines.json +1 -1
- package/fixtures/expected/voucher-single-purpose-redeem.sheet32.json +1 -1
- package/fixtures/expected/voucher-single-purpose-redeem.sheet48.json +1 -1
- package/fixtures/expected/voucher-single-purpose-sale.grid32.txt +2 -3
- package/fixtures/expected/voucher-single-purpose-sale.grid48.txt +2 -2
- package/fixtures/expected/voucher-single-purpose-sale.lines.json +2 -2
- package/fixtures/expected/voucher-single-purpose-sale.sheet32.json +2 -8
- package/fixtures/expected/voucher-single-purpose-sale.sheet48.json +2 -2
- package/fixtures/expected/zero-annual.grid32.txt +1 -1
- package/fixtures/expected/zero-annual.grid48.txt +1 -1
- package/fixtures/expected/zero-annual.lines.json +1 -1
- package/fixtures/expected/zero-annual.sheet32.json +1 -1
- package/fixtures/expected/zero-annual.sheet48.json +1 -1
- package/fixtures/expected/zero-final.grid32.txt +1 -1
- package/fixtures/expected/zero-final.grid48.txt +1 -1
- package/fixtures/expected/zero-final.lines.json +1 -1
- package/fixtures/expected/zero-final.sheet32.json +1 -1
- package/fixtures/expected/zero-final.sheet48.json +1 -1
- package/fixtures/expected/zero-manual.grid32.txt +1 -1
- package/fixtures/expected/zero-manual.grid48.txt +1 -1
- package/fixtures/expected/zero-manual.lines.json +1 -1
- package/fixtures/expected/zero-manual.sheet32.json +1 -1
- package/fixtures/expected/zero-manual.sheet48.json +1 -1
- package/fixtures/expected/zero-monthly.grid32.txt +1 -1
- package/fixtures/expected/zero-monthly.grid48.txt +1 -1
- package/fixtures/expected/zero-monthly.lines.json +1 -1
- package/fixtures/expected/zero-monthly.sheet32.json +1 -1
- package/fixtures/expected/zero-monthly.sheet48.json +1 -1
- package/fixtures/expected/zero-outage-end.grid32.txt +1 -1
- package/fixtures/expected/zero-outage-end.grid48.txt +1 -1
- package/fixtures/expected/zero-outage-end.lines.json +1 -1
- package/fixtures/expected/zero-outage-end.sheet32.json +1 -1
- package/fixtures/expected/zero-outage-end.sheet48.json +1 -1
- package/fixtures/expected/zero-start.grid32.txt +1 -1
- package/fixtures/expected/zero-start.grid48.txt +1 -1
- package/fixtures/expected/zero-start.lines.json +1 -1
- package/fixtures/expected/zero-start.sheet32.json +1 -1
- package/fixtures/expected/zero-start.sheet48.json +1 -1
- package/fixtures/hobex-hps-codes.json +1 -1
- package/fixtures/invoice-api.schema.json +3 -2
- package/fixtures/invoice-texts.json +1 -1
- package/fixtures/manifest.json +260 -260
- package/fixtures/pos-texts.json +118 -1
- package/fixtures/receipt-due-voucher-generated.json +2 -2
- package/fixtures/receipt-outputs-cases.json +2778 -0
- package/fixtures/receipts/cancellation-discount.json +1 -1
- package/fixtures/receipts/cancellation-full.json +1 -1
- package/fixtures/receipts/cancellation-partial.json +1 -1
- package/fixtures/receipts/cancellation-split-full.json +1 -1
- package/fixtures/receipts/cancellation-split-partial.json +1 -1
- package/fixtures/receipts/card-custom.json +1 -1
- package/fixtures/receipts/card-gptom-ios.json +1 -1
- package/fixtures/receipts/card-gptom.json +1 -1
- package/fixtures/receipts/card-hobex-cloud.json +1 -1
- package/fixtures/receipts/card-hobex-hps.json +1 -1
- package/fixtures/receipts/card-mypos.json +1 -1
- package/fixtures/receipts/card-stripe-eps.json +1 -1
- package/fixtures/receipts/card-stripe.json +1 -1
- package/fixtures/receipts/card-sumup.json +1 -1
- package/fixtures/receipts/discount-lines.json +1 -1
- package/fixtures/receipts/discount-owner-tip.json +1 -1
- package/fixtures/receipts/discount-simple.json +1 -1
- package/fixtures/receipts/discount-tip.json +1 -1
- package/fixtures/receipts/discount-value-voucher.json +1 -1
- package/fixtures/receipts/long-item-name.json +1 -1
- package/fixtures/receipts/sale-card.json +1 -1
- package/fixtures/receipts/sale-cash.json +1 -1
- package/fixtures/receipts/sale-small-business.json +1 -1
- package/fixtures/receipts/signature-outage-sale.json +1 -1
- package/fixtures/receipts/split-card-card-cash.json +1 -1
- package/fixtures/receipts/split-cash-change.json +1 -1
- package/fixtures/receipts/split-long-amount.json +1 -1
- package/fixtures/receipts/split-one-card-tip.json +1 -1
- package/fixtures/receipts/split-table-round-tip.json +1 -1
- package/fixtures/receipts/split-tip-card.json +1 -1
- package/fixtures/receipts/split-two-cards-same-provider.json +1 -1
- package/fixtures/receipts/test-cashregister-sale.json +1 -1
- package/fixtures/receipts/test-signature-sale.json +1 -1
- package/fixtures/receipts/training.json +1 -1
- package/fixtures/receipts/zero-annual.json +1 -1
- package/fixtures/receipts/zero-final.json +1 -1
- package/fixtures/receipts/zero-manual.json +1 -1
- package/fixtures/receipts/zero-monthly.json +1 -1
- package/fixtures/receipts/zero-outage-end.json +1 -1
- package/fixtures/receipts/zero-start.json +1 -1
- package/fixtures/surface.json +30 -7
- package/fixtures/v3/README.md +10 -3
- package/fixtures/v3/antworten/belege.json +623 -125
- package/fixtures/v3/antworten/belegmail.json +2 -2
- package/fixtures/v3/antworten/kasse-belege.json +622 -122
- package/fixtures/v3/antworten/kasse.json +479 -38
- package/fixtures/v3/antworten/lager.json +1 -1
- package/fixtures/v3/antworten/rechnungen.json +4 -4
- package/fixtures/v3/antworten/storno.json +211 -3
- package/fixtures/v3/stored/belege.json +2 -2
- package/fixtures/v3/stored/kasse.json +262 -382
- package/fixtures/v3/stored/rechnungen.json +157 -17
- package/fixtures/v3/v3-vokabular.json +311 -28
- package/fixtures/v3/zahlbetrag-faelle.json +1070 -4
- package/package.json +3 -1
- package/dist/cjs/stored/gutscheine.d.ts +0 -3
- package/dist/cjs/stored/gutscheine.js +0 -60
- package/dist/esm/stored/gutscheine.d.ts +0 -3
- package/dist/esm/stored/gutscheine.js +0 -56
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { KeckPaymentMethod, ReceiptType } from '../enums/index.js';
|
|
2
|
+
import { CANCELLATION_STATUSES, fromReceiptPayload, fromReceiptPaymentPayload, } from '../models/index.js';
|
|
3
|
+
import { readEnumKey } from '../models/enum-payload.js';
|
|
4
|
+
import { KasseneckValidationError } from './errors.js';
|
|
5
|
+
/**
|
|
6
|
+
* Belege einer Kasse auflisten (`listReceipts`, nur unter `/v3`): ein Tag oder
|
|
7
|
+
* ein Zeitraum von hoechstens 31 Kalendertagen, Seite fuer Seite ueber einen
|
|
8
|
+
* undurchsichtigen Cursor. Nachfolger von `getReportV2` fuer Listen;
|
|
9
|
+
* `getReportV2` bleibt fuer Berichte.
|
|
10
|
+
*
|
|
11
|
+
* **Anmeldung wie `getReportV2`:** API-Schluessel plus `cashregister-token`
|
|
12
|
+
* (`apiKeyAuth`); die Kasse kommt aus dem Token, nie aus einem Parameter.
|
|
13
|
+
* Kassen-Bediener (Geraet + PIN) weist der Server ab; die Kasse hat dafuer
|
|
14
|
+
* `listMyReceipts` am Kassenweg. Dieses Paket bildet das nicht nach, wer darf,
|
|
15
|
+
* entscheidet allein das Backend.
|
|
16
|
+
*
|
|
17
|
+
* Zeiten sind die **Wiener Wanduhr** als Text, wie `timeStamp` am Beleg; der
|
|
18
|
+
* Server vergleicht sie als Text. Ein Tag ist ein Kalendertag, auch an den
|
|
19
|
+
* Tagen der Zeitumstellung, und die Grenze von 31 Tagen zaehlt Kalendertage,
|
|
20
|
+
* nicht 31 x 24 Stunden. Die Pruefung hier rechnet darum ohne Zeitzone, genau
|
|
21
|
+
* wie der Server (`beleg-liste-core.js`).
|
|
22
|
+
*/
|
|
23
|
+
/** Ansicht einer Seite: `summary` eine schlanke Zeile je Beleg, `full` das gespeicherte Belegdokument. */
|
|
24
|
+
export const LIST_RECEIPTS_VIEWS = Object.freeze(['summary', 'full']);
|
|
25
|
+
/** Reihenfolge: `desc` neueste zuerst (Vorgabe des Servers), `asc` aelteste zuerst. */
|
|
26
|
+
export const LIST_RECEIPTS_ORDERS = Object.freeze(['desc', 'asc']);
|
|
27
|
+
/** Groesste Seite, die der Server liefert (`limit`); ohne Angabe nimmt er 100. */
|
|
28
|
+
export const LIST_RECEIPTS_LIMIT_MAX = 500;
|
|
29
|
+
/** Laengster Zeitraum in Kalendertagen (`to` hoechstens `from` + 31 Tage). */
|
|
30
|
+
export const LIST_RECEIPTS_DAYS_MAX = 31;
|
|
31
|
+
/**
|
|
32
|
+
* So viele Seiten holt [listAllReceipts] hoechstens, dann bricht er mit
|
|
33
|
+
* Fehler ab. Schutz gegen einen Cursor, der nie endet; mit der Seitengroesse
|
|
34
|
+
* des Helfers (500) sind das 50.000 Belege, mehr als jede Kasse in 31 Tagen
|
|
35
|
+
* ausstellt.
|
|
36
|
+
*/
|
|
37
|
+
export const LIST_ALL_RECEIPTS_MAX_PAGES = 100;
|
|
38
|
+
/**
|
|
39
|
+
* Fehlercodes von `listReceipts` (`code` des [KasseneckApiError]), dazu die
|
|
40
|
+
* gemeinsamen Codes von Anmeldung und Rand (Kassen-Token, Live-Gate,
|
|
41
|
+
* API-Freigabe). Ein Code ausserhalb bleibt ueber `KasseneckApiError.code`
|
|
42
|
+
* lesbar. Die Regeln der ersten sieben prueft dieses Paket schon vor dem
|
|
43
|
+
* Senden (ausser `invalid_cursor`: ob ein Cursor zur Abfrage passt, weiss nur
|
|
44
|
+
* der Server).
|
|
45
|
+
*/
|
|
46
|
+
export const LIST_RECEIPTS_ERROR_CODES = Object.freeze([
|
|
47
|
+
'invalid_date', // kein Datum JJJJ-MM-TT bzw. Zeitpunkt JJJJ-MM-TTThh:mm:ss, oder unmoeglich (2026-02-30)
|
|
48
|
+
'invalid_range', // nicht genau eine Form (date ODER from + to), oder to nicht nach from
|
|
49
|
+
'range_too_long', // mehr als 31 Kalendertage
|
|
50
|
+
'invalid_limit', // keine ganze Zahl von 1 bis 500 (der Server kappt nie still)
|
|
51
|
+
'invalid_order', // weder desc noch asc
|
|
52
|
+
'invalid_view', // weder summary noch full
|
|
53
|
+
'invalid_cursor', // veraendert, fremde Kasse, oder Zeitfenster/order/view anders als auf Seite 1
|
|
54
|
+
'server_error',
|
|
55
|
+
]);
|
|
56
|
+
const TAG = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
57
|
+
const ZEIT = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})$/;
|
|
58
|
+
/** Kalendertag als UTC-Mitternacht (nur zum Rechnen mit Tagen; auch fuer Jahre unter 100). */
|
|
59
|
+
function utcTag(jahr, monat, tag) {
|
|
60
|
+
const d = new Date(Date.UTC(2000, 0, 1));
|
|
61
|
+
d.setUTCFullYear(jahr, monat - 1, tag);
|
|
62
|
+
return d;
|
|
63
|
+
}
|
|
64
|
+
/** Ein echtes Kalenderdatum (2026-02-30 nicht)? */
|
|
65
|
+
function kalendertag(jahr, monat, tag) {
|
|
66
|
+
const d = utcTag(jahr, monat, tag);
|
|
67
|
+
return d.getUTCFullYear() === jahr && d.getUTCMonth() === monat - 1 && d.getUTCDate() === tag;
|
|
68
|
+
}
|
|
69
|
+
const zwei = (n) => String(n).padStart(2, '0');
|
|
70
|
+
/** Wanduhr-Text `YYYY-MM-DDTHH:mm:ss` aus einem Tag oder Zeitpunkt; sonst `null`. */
|
|
71
|
+
function wanduhr(wert) {
|
|
72
|
+
if (typeof wert !== 'string')
|
|
73
|
+
return null;
|
|
74
|
+
const t = TAG.exec(wert);
|
|
75
|
+
if (t)
|
|
76
|
+
return kalendertag(+t[1], +t[2], +t[3]) ? `${wert}T00:00:00` : null;
|
|
77
|
+
const z = ZEIT.exec(wert);
|
|
78
|
+
if (z && kalendertag(+z[1], +z[2], +z[3]) && +z[4] <= 23 && +z[5] <= 59 && +z[6] <= 59)
|
|
79
|
+
return wert;
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
/** Wanduhr-Text plus ganze Kalendertage, Uhrzeit bleibt (ohne Zeitzone gerechnet). */
|
|
83
|
+
function plusTage(zeit, tage) {
|
|
84
|
+
const [datum, uhr] = zeit.split('T');
|
|
85
|
+
const [j, m, t] = datum.split('-').map(Number);
|
|
86
|
+
const d = utcTag(j, m, t + tage);
|
|
87
|
+
return `${String(d.getUTCFullYear()).padStart(4, '0')}-${zwei(d.getUTCMonth() + 1)}-${zwei(d.getUTCDate())}T${uhr}`;
|
|
88
|
+
}
|
|
89
|
+
const gesetzt = (v) => v !== undefined && v !== null;
|
|
90
|
+
/** Fehler in der Eingabe des Aufrufers: es geht keine Anfrage raus. */
|
|
91
|
+
function eingabefehler(grund) {
|
|
92
|
+
return new KasseneckValidationError('listReceipts', grund, 'request');
|
|
93
|
+
}
|
|
94
|
+
/** Antwort meldet Erfolg, traegt aber nicht, was der Aufruf zusagt. */
|
|
95
|
+
function antwortfehler(grund) {
|
|
96
|
+
return new KasseneckValidationError('listReceipts', grund, 'response');
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Prueft die Eingabe nach den Regeln des Servers und gibt die Parameter
|
|
100
|
+
* zurueck, die hinausgehen (nur gesetzte Felder, Werte unveraendert, damit
|
|
101
|
+
* Folgeseiten dasselbe Fenster senden).
|
|
102
|
+
*/
|
|
103
|
+
function parameter(options) {
|
|
104
|
+
if (options == null || typeof options !== 'object')
|
|
105
|
+
throw eingabefehler('Optionen fehlen (date oder from und to)');
|
|
106
|
+
const o = options;
|
|
107
|
+
const raus = {};
|
|
108
|
+
if (gesetzt(o.date)) {
|
|
109
|
+
if (gesetzt(o.from) || gesetzt(o.to))
|
|
110
|
+
throw eingabefehler('entweder date oder from und to angeben, nicht beides');
|
|
111
|
+
if (typeof o.date !== 'string' || !TAG.test(o.date) || wanduhr(o.date) === null) {
|
|
112
|
+
throw eingabefehler(`date muss ein Datum YYYY-MM-DD sein, war "${String(o.date)}"`);
|
|
113
|
+
}
|
|
114
|
+
raus['date'] = o.date;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
if (!gesetzt(o.from) || !gesetzt(o.to))
|
|
118
|
+
throw eingabefehler('Zeitraum fehlt: date oder from und to angeben');
|
|
119
|
+
const von = wanduhr(o.from);
|
|
120
|
+
if (von === null)
|
|
121
|
+
throw eingabefehler(`from muss YYYY-MM-DD oder YYYY-MM-DDTHH:mm:ss sein, war "${String(o.from)}"`);
|
|
122
|
+
const bis = wanduhr(o.to);
|
|
123
|
+
if (bis === null)
|
|
124
|
+
throw eingabefehler(`to muss YYYY-MM-DD oder YYYY-MM-DDTHH:mm:ss sein, war "${String(o.to)}"`);
|
|
125
|
+
if (bis <= von)
|
|
126
|
+
throw eingabefehler('to muss nach from liegen (to ist ausschliesslich)');
|
|
127
|
+
if (bis > plusTage(von, LIST_RECEIPTS_DAYS_MAX)) {
|
|
128
|
+
throw eingabefehler(`Zeitraum hoechstens ${LIST_RECEIPTS_DAYS_MAX} Kalendertage (to hoechstens from + ${LIST_RECEIPTS_DAYS_MAX} Tage)`);
|
|
129
|
+
}
|
|
130
|
+
raus['from'] = o.from;
|
|
131
|
+
raus['to'] = o.to;
|
|
132
|
+
}
|
|
133
|
+
if (gesetzt(o.limit)) {
|
|
134
|
+
if (!Number.isInteger(o.limit) || o.limit < 1 || o.limit > LIST_RECEIPTS_LIMIT_MAX) {
|
|
135
|
+
throw eingabefehler(`limit muss eine ganze Zahl von 1 bis ${LIST_RECEIPTS_LIMIT_MAX} sein, war "${String(o.limit)}"`);
|
|
136
|
+
}
|
|
137
|
+
raus['limit'] = o.limit;
|
|
138
|
+
}
|
|
139
|
+
if (gesetzt(o.order)) {
|
|
140
|
+
if (!LIST_RECEIPTS_ORDERS.includes(o.order))
|
|
141
|
+
throw eingabefehler(`order muss desc oder asc sein, war "${String(o.order)}"`);
|
|
142
|
+
raus['order'] = o.order;
|
|
143
|
+
}
|
|
144
|
+
if (gesetzt(o.view)) {
|
|
145
|
+
if (!LIST_RECEIPTS_VIEWS.includes(o.view))
|
|
146
|
+
throw eingabefehler(`view muss summary oder full sein, war "${String(o.view)}"`);
|
|
147
|
+
raus['view'] = o.view;
|
|
148
|
+
}
|
|
149
|
+
if (gesetzt(o.cursor)) {
|
|
150
|
+
if (typeof o.cursor !== 'string' || o.cursor === '')
|
|
151
|
+
throw eingabefehler('cursor muss der nextCursor der vorigen Seite sein (nicht leerer Text)');
|
|
152
|
+
raus['cursor'] = o.cursor;
|
|
153
|
+
}
|
|
154
|
+
return raus;
|
|
155
|
+
}
|
|
156
|
+
const istObjekt = (w) => w != null && typeof w === 'object' && !Array.isArray(w);
|
|
157
|
+
const text = (w) => (typeof w === 'string' ? w : null);
|
|
158
|
+
// Ganze Cent; ein einzelner unbrauchbarer Betrag laesst die Liste nicht scheitern.
|
|
159
|
+
const cent = (w) => (typeof w === 'number' && Number.isInteger(w) ? w : 0);
|
|
160
|
+
function istStornoStand(w) {
|
|
161
|
+
return typeof w === 'string' && CANCELLATION_STATUSES.includes(w);
|
|
162
|
+
}
|
|
163
|
+
/** Zeile `view: summary` vom Draht. */
|
|
164
|
+
export function fromListReceiptsSummaryPayload(payload) {
|
|
165
|
+
const brutto = istObjekt(payload['grossByRateCents']) ? payload['grossByRateCents'] : {};
|
|
166
|
+
const bediener = payload['operator'];
|
|
167
|
+
const bezug = payload['cancellationOf'];
|
|
168
|
+
return {
|
|
169
|
+
receiptId: text(payload['receiptId']),
|
|
170
|
+
counter: typeof payload['counter'] === 'number' ? payload['counter'] : null,
|
|
171
|
+
receiptType: typeof payload['receiptType'] === 'string' ? readEnumKey(ReceiptType, payload['receiptType']) : null,
|
|
172
|
+
zeroKind: text(payload['zeroKind']),
|
|
173
|
+
timeStamp: text(payload['timeStamp']),
|
|
174
|
+
totalCents: cent(payload['totalCents']),
|
|
175
|
+
grossByRateCents: {
|
|
176
|
+
standard: cent(brutto['standard']),
|
|
177
|
+
reduced1: cent(brutto['reduced1']),
|
|
178
|
+
reduced2: cent(brutto['reduced2']),
|
|
179
|
+
zero: cent(brutto['zero']),
|
|
180
|
+
special: cent(brutto['special']),
|
|
181
|
+
},
|
|
182
|
+
paymentMethod: typeof payload['paymentMethod'] === 'string' ? readEnumKey(KeckPaymentMethod, payload['paymentMethod']) : null,
|
|
183
|
+
...(Array.isArray(payload['payments'])
|
|
184
|
+
? { payments: payload['payments'].filter(istObjekt).map((z) => fromReceiptPaymentPayload(z)) }
|
|
185
|
+
: {}),
|
|
186
|
+
...(typeof payload['tipCents'] === 'number' ? { tipCents: payload['tipCents'] } : {}),
|
|
187
|
+
operator: istObjekt(bediener) ? { name: typeof bediener['name'] === 'string' ? bediener['name'] : '' } : null,
|
|
188
|
+
cancellationOf: istObjekt(bezug) ? { receiptId: text(bezug['receiptId']), timeStamp: text(bezug['timeStamp']) } : null,
|
|
189
|
+
cancellationReason: text(payload['cancellationReason']),
|
|
190
|
+
cancellationStatus: istStornoStand(payload['cancellationStatus']) ? payload['cancellationStatus'] : 'unknown',
|
|
191
|
+
signatureOk: payload['signatureOk'] !== false,
|
|
192
|
+
customProjectId: text(payload['customProjectId']),
|
|
193
|
+
fullReceiptId: text(payload['fullReceiptId']),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function tagessumme(roh) {
|
|
197
|
+
if (roh === null)
|
|
198
|
+
return null;
|
|
199
|
+
if (!istObjekt(roh))
|
|
200
|
+
throw antwortfehler('daySummary ist weder ein Objekt noch null');
|
|
201
|
+
const saetze = Array.isArray(roh['revenueByRate']) ? roh['revenueByRate'] : [];
|
|
202
|
+
return {
|
|
203
|
+
date: typeof roh['date'] === 'string' ? roh['date'] : '',
|
|
204
|
+
receiptCount: cent(roh['receiptCount']),
|
|
205
|
+
revenueCents: cent(roh['revenueCents']),
|
|
206
|
+
revenueByRate: saetze.filter(istObjekt).map((z) => ({ rateBp: cent(z['rateBp']), grossCents: cent(z['grossCents']) })),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Eine Seite Belege der angemeldeten Kasse (`listReceipts`). Die Eingabe wird
|
|
211
|
+
* vor dem Senden nach den Regeln des Servers geprueft (genau eine Form des
|
|
212
|
+
* Zeitfensters, Datumsformat, hoechstens 31 Kalendertage, `limit` 1-500,
|
|
213
|
+
* `order`, `view`); ein Verstoss wirft [KasseneckValidationError] mit
|
|
214
|
+
* `scope: 'request'`, ohne dass etwas hinausgeht.
|
|
215
|
+
*
|
|
216
|
+
* Mit `view: 'full'` sind die Belege das gespeicherte Belegdokument
|
|
217
|
+
* ([Receipt], dieselbe Form wie `getReportV2`), sonst [ListReceiptsSummary].
|
|
218
|
+
* Alle Seiten auf einmal: [listAllReceipts].
|
|
219
|
+
*/
|
|
220
|
+
export async function listReceipts(transport, options) {
|
|
221
|
+
const params = parameter(options);
|
|
222
|
+
const voll = params['view'] === 'full';
|
|
223
|
+
const daten = await transport('listReceipts', params);
|
|
224
|
+
if (!istObjekt(daten))
|
|
225
|
+
throw antwortfehler('Antwort enthaelt keine Daten (data fehlt)');
|
|
226
|
+
const liste = daten['receipts'];
|
|
227
|
+
if (!Array.isArray(liste) || liste.some((b) => !istObjekt(b))) {
|
|
228
|
+
throw antwortfehler('Antwort enthaelt keine Belegliste (data.receipts fehlt)');
|
|
229
|
+
}
|
|
230
|
+
if (typeof daten['hasMore'] !== 'boolean')
|
|
231
|
+
throw antwortfehler('Antwort ohne hasMore');
|
|
232
|
+
const weiter = daten['nextCursor'];
|
|
233
|
+
// Ein `hasMore` ohne Cursor liesse den Aufrufer ratlos (oder einen Blaetterer im Kreis laufen).
|
|
234
|
+
if (daten['hasMore'] ? typeof weiter !== 'string' || weiter === '' : weiter !== null) {
|
|
235
|
+
throw antwortfehler('nextCursor passt nicht zu hasMore (Text bei hasMore, sonst null)');
|
|
236
|
+
}
|
|
237
|
+
const receipts = (voll
|
|
238
|
+
? liste.map((b) => fromReceiptPayload(b))
|
|
239
|
+
: liste.map((b) => fromListReceiptsSummaryPayload(b)));
|
|
240
|
+
return {
|
|
241
|
+
receipts,
|
|
242
|
+
hasMore: daten['hasMore'],
|
|
243
|
+
nextCursor: daten['hasMore'] ? weiter : null,
|
|
244
|
+
...('daySummary' in daten ? { daySummary: tagessumme(daten['daySummary']) } : {}),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Alle Belege eines Tages oder Zeitraums, Seite fuer Seite: holt mit
|
|
249
|
+
* [listReceipts] die erste Seite, dann mit `cursor: nextCursor` und
|
|
250
|
+
* demselben Zeitfenster, `order` und `view` jede weitere, und liefert jeden
|
|
251
|
+
* Beleg einzeln, in der Reihenfolge des Servers. Jeder Beleg des Fensters
|
|
252
|
+
* kommt genau einmal.
|
|
253
|
+
*
|
|
254
|
+
* Ohne `limit` holt der Helfer Seiten zu 500 (weniger Aufrufe als die
|
|
255
|
+
* Vorgabe 100 des Servers). Mit `cursor` setzt er an dieser Stelle fort.
|
|
256
|
+
*
|
|
257
|
+
* Ein Fehler auf irgendeiner Seite bricht ab (der Fehler kommt im
|
|
258
|
+
* `for await` an); die bis dahin gelieferten Belege bleiben gueltig. Nach
|
|
259
|
+
* [LIST_ALL_RECEIPTS_MAX_PAGES] Seiten, oder wenn der Server denselben
|
|
260
|
+
* Cursor zweimal nennt, bricht er mit [KasseneckValidationError]
|
|
261
|
+
* (`scope: 'response'`) ab, statt endlos zu laufen.
|
|
262
|
+
*
|
|
263
|
+
* `daySummary` liefert der Helfer nicht; wer sie braucht, ruft die erste
|
|
264
|
+
* Seite mit [listReceipts].
|
|
265
|
+
*/
|
|
266
|
+
export async function* listAllReceipts(transport, options) {
|
|
267
|
+
// Frueh pruefen: ein Eingabefehler kommt beim ersten Schritt an, vor jedem Aufruf.
|
|
268
|
+
parameter(options);
|
|
269
|
+
const grund = { ...options, limit: options.limit ?? LIST_RECEIPTS_LIMIT_MAX };
|
|
270
|
+
let cursor = options.cursor;
|
|
271
|
+
const gesehen = new Set();
|
|
272
|
+
for (let seite = 1; seite <= LIST_ALL_RECEIPTS_MAX_PAGES; seite++) {
|
|
273
|
+
const page = await listReceipts(transport, { ...grund, cursor });
|
|
274
|
+
yield* page.receipts;
|
|
275
|
+
if (!page.hasMore)
|
|
276
|
+
return;
|
|
277
|
+
const weiter = page.nextCursor;
|
|
278
|
+
if (gesehen.has(weiter) || weiter === cursor)
|
|
279
|
+
throw antwortfehler('Server nennt denselben Cursor erneut; Abbruch statt Endlosschleife');
|
|
280
|
+
gesehen.add(weiter);
|
|
281
|
+
cursor = weiter;
|
|
282
|
+
}
|
|
283
|
+
throw antwortfehler(`mehr als ${LIST_ALL_RECEIPTS_MAX_PAGES} Seiten; Zeitfenster kleiner waehlen`);
|
|
284
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ReceiptType, type ReceiptTypeKey, KeckPaymentMethod, type KeckPaymentMethodKey } from '../enums/index.js';
|
|
2
2
|
import { type Receipt, type ReceiptCompany, type ReceiptItem, type Voucher, type ReceiptSummary, type ReportMonth, type CancellationItem, type CancellationOf, type CancellationReason, type TipRefundSource, type ReturnDisposition, type ReceiptPaymentInput, type RegistrationInfo, type ReceiptEmailVia } from '../models/index.js';
|
|
3
|
+
import { type ReceiptOutput, type ReceiptOutputEntryErrorCode, type ReceiptOutputLogKind, type ReceiptOutputResult } from '../models/receipt-output.js';
|
|
3
4
|
import type { InternerTransport } from './aufrufe.js';
|
|
4
5
|
import { type ReceiptLayout } from '../receipt/layout.js';
|
|
5
6
|
import type { PosPaperSize } from '../printing/escpos.js';
|
|
@@ -140,8 +141,9 @@ export type CancelReceiptOptions = {
|
|
|
140
141
|
/**
|
|
141
142
|
* Rueckzahlung je Zahlung (Betraege negativ, `refundOf` = `id` der
|
|
142
143
|
* Originalzahlung). Ohne Angabe spiegelt der Server die Restbetraege jeder
|
|
143
|
-
* Originalzahlung
|
|
144
|
-
*
|
|
144
|
+
* Originalzahlung (Ausnahme Storno einer Gutschein-Auszahlung: genau eine
|
|
145
|
+
* positive Barzahlung ohne `refundOf`); ein Teilstorno eines Belegs mit
|
|
146
|
+
* mehreren Zahlungen braucht sie (`cancellation_payments_required`).
|
|
145
147
|
*
|
|
146
148
|
* **Karten-Storno:** `provider`, `providerPaymentId` und `providerData`
|
|
147
149
|
* beschreiben die ERSTATTUNG am Terminal, nie die Originalzahlung. Die
|
|
@@ -216,6 +218,13 @@ export interface ReceiptWithCompany {
|
|
|
216
218
|
* setzen das Logo in genau dieser Stufe; fehlt der Wert, gilt `M`.
|
|
217
219
|
*/
|
|
218
220
|
logoScale: SheetLogoSize;
|
|
221
|
+
/**
|
|
222
|
+
* Ausgaben des Belegs (`data.outputs`, nur `getReceipt` unter `/v3`): die
|
|
223
|
+
* neuesten 20, neueste zuerst. Fehlt, wenn die Antwort keine Liste traegt
|
|
224
|
+
* (Verkauf, `/api`). Gelesen nach [ReceiptOutput]; ein kaputter Eintrag
|
|
225
|
+
* faellt weg, der Beleg bleibt lesbar.
|
|
226
|
+
*/
|
|
227
|
+
outputs?: ReceiptOutput[];
|
|
219
228
|
}
|
|
220
229
|
/**
|
|
221
230
|
* Das Zeilenmodell zum Drucken und Anzeigen eines Belegs aus einer Antwort.
|
|
@@ -402,6 +411,80 @@ export interface SendReceiptEmailResult {
|
|
|
402
411
|
* eine zweite Mail und zaehlt auf die Schleuse.
|
|
403
412
|
*/
|
|
404
413
|
export declare function sendReceiptEmail(transport: InternerTransport, options: SendReceiptEmailOptions): Promise<SendReceiptEmailResult>;
|
|
414
|
+
/**
|
|
415
|
+
* Eine Ausgabe, wie die Kasse sie puffert und meldet: ein Druck oder ein
|
|
416
|
+
* Teilen des Belegs. Siehe [logReceiptOutputs].
|
|
417
|
+
*/
|
|
418
|
+
export interface ReceiptOutputEntry {
|
|
419
|
+
/**
|
|
420
|
+
* Idempotenzschluessel des Eintrags, am Geraet beim Puffern vergeben und
|
|
421
|
+
* beim Nachsenden unveraendert: ein zweites Melden legt keinen zweiten
|
|
422
|
+
* Eintrag an. 8–64 Zeichen `[A-Za-z0-9_-]` (Regel des Servers); ein anderer
|
|
423
|
+
* Schluessel wirft vor dem Senden.
|
|
424
|
+
*/
|
|
425
|
+
key: string;
|
|
426
|
+
/** Beleg in der Kasse der Anmeldung; ein Beleg einer anderen Kasse ist `receipt_not_found`. */
|
|
427
|
+
receiptId: string;
|
|
428
|
+
/** `print` (Erstdruck), `reprint` (Duplikat, gedruckt mit `markAsDuplicate`), `link_shared`, `pdf_shared`. */
|
|
429
|
+
kind: ReceiptOutputLogKind;
|
|
430
|
+
/**
|
|
431
|
+
* Zeitpunkt am Geraet: Millisekunden seit 1970 oder ISO 8601 **mit Zone**
|
|
432
|
+
* (`new Date().toISOString()`); Text ohne Zone wirft vor dem Senden. Eine
|
|
433
|
+
* falsch gehende Geraeteuhr lehnt der Server nicht ab: er klemmt die Zeit auf
|
|
434
|
+
* Belegzeit bis Eingang.
|
|
435
|
+
*/
|
|
436
|
+
at: number | string;
|
|
437
|
+
/**
|
|
438
|
+
* Ergebnis eines Drucks: `ok`, `failed` (sicher nicht gedruckt) oder
|
|
439
|
+
* `unclear` (Ausgang offen). Pflicht bei `print` und `reprint` (sonst lehnt
|
|
440
|
+
* der Server den Eintrag mit `validation` ab); beim Teilen ohne Angabe `ok`.
|
|
441
|
+
*/
|
|
442
|
+
result?: ReceiptOutputResult;
|
|
443
|
+
}
|
|
444
|
+
/** Eingabe von [logReceiptOutputs]. Die Kasse bindet die Anmeldung (wie bei [sendReceiptEmail]). */
|
|
445
|
+
export interface LogReceiptOutputsOptions {
|
|
446
|
+
/** 1–50 Eintraege ([RECEIPT_OUTPUT_ENTRIES_MAX]); ein groesserer Puffer geht in mehreren Aufrufen hinaus. */
|
|
447
|
+
entries: ReceiptOutputEntry[];
|
|
448
|
+
}
|
|
449
|
+
/** Ein abgelehnter Eintrag: dauerhaft, aus dem Puffer nehmen und nicht noch einmal senden. */
|
|
450
|
+
export interface RejectedReceiptOutput {
|
|
451
|
+
key: string;
|
|
452
|
+
/** Aus [RECEIPT_OUTPUT_ENTRY_ERROR_CODES]; ein Code, den das Paket nicht kennt, bleibt lesbar. */
|
|
453
|
+
code: ReceiptOutputEntryErrorCode | (string & {});
|
|
454
|
+
}
|
|
455
|
+
/** Antwort von [logReceiptOutputs]. */
|
|
456
|
+
export interface LogReceiptOutputsResult {
|
|
457
|
+
/** Angenommene Eintraege, auch die schon frueher gemeldeten (gleicher Schluessel). */
|
|
458
|
+
accepted: number;
|
|
459
|
+
rejected: RejectedReceiptOutput[];
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Meldet Drucke und das Teilen eines Belegs an das Protokoll „Ausgaben“
|
|
463
|
+
* (Endpunkt `logReceiptOutputs`, nur ueber den Kassenweg `/api/v3`; mit
|
|
464
|
+
* `registerUserAuth` und mit `apiKeyAuth`). Registrierkassen-Erlass
|
|
465
|
+
* BMF-AV Nr. 2/2026, Abschnitt 4.2: die Belegerteilung ist zu dokumentieren.
|
|
466
|
+
* Der Beleg selbst bleibt unberuehrt; das Protokoll steht nie im DEP.
|
|
467
|
+
*
|
|
468
|
+
* **Ein Eintrag scheitert nie die anderen.** Der Server nimmt jeden Eintrag
|
|
469
|
+
* einzeln an oder lehnt ihn mit Code ab (`rejected`, Codes
|
|
470
|
+
* [RECEIPT_OUTPUT_ENTRY_ERROR_CODES]); nach einer Erfolgsantwort ist jeder
|
|
471
|
+
* gesendete Eintrag erledigt. Die Kasse puffert die Eintraege (offline,
|
|
472
|
+
* Neustart) und sendet sie spaeter; der Puffer blockiert nie Druck oder Teilen.
|
|
473
|
+
*
|
|
474
|
+
* **Wiederholen ist gefahrlos:** jeder Eintrag traegt seinen Schluessel. Bei
|
|
475
|
+
* unklarem Ausgang (`outcome: 'unknown'`) dieselben Eintraege mit denselben
|
|
476
|
+
* Schluesseln noch einmal senden.
|
|
477
|
+
*
|
|
478
|
+
* **Vor dem Senden abgewiesen** wird nur, was die ganze Anfrage scheitern
|
|
479
|
+
* liesse oder ohne Netz sicher falsch ist: keine oder mehr als 50 Eintraege,
|
|
480
|
+
* ein unbekanntes Feld (oben oder im Eintrag), ein fehlender Beleg, ein
|
|
481
|
+
* Schluessel ausserhalb der Regel des Servers, eine Art oder ein Ergebnis
|
|
482
|
+
* ausserhalb des Katalogs, eine Zeit, die weder ganze Millisekunden noch
|
|
483
|
+
* ISO 8601 mit Zone ist. `email` und `opened` gehen hinaus und kommen als
|
|
484
|
+
* `invalid_output_kind` zurueck: so verwirft der Puffer genau diesen Eintrag.
|
|
485
|
+
* Fehler der ganzen Anfrage: [RECEIPT_OUTPUT_ERROR_CODES].
|
|
486
|
+
*/
|
|
487
|
+
export declare function logReceiptOutputs(transport: InternerTransport, options: LogReceiptOutputsOptions): Promise<LogReceiptOutputsResult>;
|
|
405
488
|
/**
|
|
406
489
|
* Prueft die Gutschein-Kombination eines Belegs und liefert den ersten
|
|
407
490
|
* Regelverstoss als Text (oder `null`) — Zwilling von
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { ReceiptType, KeckPaymentMethod, CreditCardProvider, VoucherAction, VoucherType, } from '../enums/index.js';
|
|
2
2
|
import { fromReceiptCompanyPayload, fromReceiptPayload, toReceiptItemPayload, toVoucherPayload, receiptItemIsValid, voucherIsValid, isNewFormVoucher, voucherServerFields, fromReceiptSummaryPayload, isCancellationReason, isReturnDisposition, RETURN_DISPOSITIONS, isTipRefundSource, TIP_REFUND_SOURCES, RECEIPT_EMAIL_VIAS, readRegistrationInfo, } from '../models/index.js';
|
|
3
|
+
import { RECEIPT_OUTPUT_ENTRIES_MAX, RECEIPT_OUTPUT_KINDS, RECEIPT_OUTPUT_RESULTS, receiptOutputsFromWire, } from '../models/receipt-output.js';
|
|
4
|
+
import { VoucherCode } from '@kreiseck/validator';
|
|
3
5
|
import { parseServerTimeStamp, toViennaWallClock } from '../vienna-time.js';
|
|
4
6
|
import { euroToCents } from '../money.js';
|
|
5
|
-
import { KasseneckValidationError, isKasseneckApiError, signiertGelesen } from './errors.js';
|
|
7
|
+
import { KasseneckApiError, KasseneckValidationError, isKasseneckApiError, signiertGelesen } from './errors.js';
|
|
6
8
|
import { buildReceiptLayout } from '../receipt/layout.js';
|
|
7
9
|
/**
|
|
8
10
|
* Das Zeilenmodell zum Drucken und Anzeigen eines Belegs aus einer Antwort.
|
|
@@ -38,14 +40,44 @@ export function receiptLayoutFromResult(result, options = {}) {
|
|
|
38
40
|
* die Hand des Aufrufers, sondern zu einem der benannten Aufrufe darunter.
|
|
39
41
|
*/
|
|
40
42
|
export async function createReceipt(rufen, options) {
|
|
41
|
-
const daten = await rufen
|
|
43
|
+
const daten = await createReceiptRufen(rufen, options);
|
|
42
44
|
return signiertGelesen('createReceipt', () => belegAusHuelle(daten, 'createReceipt'));
|
|
43
45
|
}
|
|
44
46
|
/** Wie [createReceipt], liest aus derselben Antwort zusaetzlich die Firmendaten. */
|
|
45
47
|
async function createReceiptWithCompany(rufen, options) {
|
|
46
|
-
const daten = await rufen
|
|
48
|
+
const daten = await createReceiptRufen(rufen, options);
|
|
47
49
|
return signiertGelesen('createReceipt', () => belegMitFirmaAusHuelle(daten, 'createReceipt'));
|
|
48
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Sendet `createReceipt`; die Gutschein-Codes der Nutzlast gelten als Geheimnis
|
|
53
|
+
* (normiert und formatiert): der Transport siebt Ursachen und Details, ein
|
|
54
|
+
* Servertext, der den Code wiederholt, wird hier geschwaerzt.
|
|
55
|
+
*/
|
|
56
|
+
async function createReceiptRufen(rufen, options) {
|
|
57
|
+
// Der Code geht so hinaus, wie er getippt wurde: die getippte Schreibweise (auch ungueltig,
|
|
58
|
+
// etwa mit falscher Pruefziffer) zaehlt ebenso als Geheimnis wie normiert und formatiert.
|
|
59
|
+
// Kurze Reste (Endung mit vier Zeichen) werden nie eingetragen.
|
|
60
|
+
const geheim = [...new Set((options.vouchers ?? []).flatMap((v) => {
|
|
61
|
+
if (typeof v.code !== 'string')
|
|
62
|
+
return [];
|
|
63
|
+
const roh = v.code.trim();
|
|
64
|
+
const geprueft = VoucherCode.validate(v.code);
|
|
65
|
+
const liste = geprueft.ok ? [geprueft.normalized, VoucherCode.format(geprueft.normalized)] : [];
|
|
66
|
+
if (roh.length >= 8)
|
|
67
|
+
liste.push(roh);
|
|
68
|
+
return liste;
|
|
69
|
+
}))].sort((a, b) => b.length - a.length);
|
|
70
|
+
try {
|
|
71
|
+
return await rufen('createReceipt', createReceiptParams(options), undefined, geheim);
|
|
72
|
+
}
|
|
73
|
+
catch (fehler) {
|
|
74
|
+
if (fehler instanceof KasseneckApiError && geheim.some((g) => fehler.serverMessage.includes(g))) {
|
|
75
|
+
const text = geheim.reduce((t, g) => t.split(g).join('****'), fehler.serverMessage);
|
|
76
|
+
throw new KasseneckApiError('createReceipt', text, fehler.details, fehler.code);
|
|
77
|
+
}
|
|
78
|
+
throw fehler;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
49
81
|
/**
|
|
50
82
|
* Baut die Nutzlast von `createReceipt` und prueft die Eingabe. Wirft, bevor
|
|
51
83
|
* irgendetwas rausgeht — ein Beleg ist nicht folgenlos wiederholbar.
|
|
@@ -171,6 +203,24 @@ function pruefeRueckgabeWahl(wert, pfad) {
|
|
|
171
203
|
throw new KasseneckValidationError('cancelReceipt', `${pfad}: erlaubt sind ${RETURN_DISPOSITIONS.join(', ')}`, 'request');
|
|
172
204
|
}
|
|
173
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Storno einer Gutschein-Auszahlung: genau eine positive Barzahlung ohne `refundOf`
|
|
208
|
+
* (das Geld kommt in die Lade zurueck). Alles andere bleibt Rueckzahlung mit negativem Betrag;
|
|
209
|
+
* ob der Beleg eine Auszahlung war, entscheidet der Server.
|
|
210
|
+
*/
|
|
211
|
+
function istStornoAuszahlung(payments, original) {
|
|
212
|
+
if (!Array.isArray(payments) || payments.length !== 1)
|
|
213
|
+
return false;
|
|
214
|
+
// Kennt der Aufrufer das Original und es hat keine Auszahlung, bleibt es bei negativen Rueckzahlungen.
|
|
215
|
+
if (original != null && !(original.vouchers ?? []).some((v) => v.action === VoucherAction.payout))
|
|
216
|
+
return false;
|
|
217
|
+
const z = payments[0];
|
|
218
|
+
if (z == null || typeof z !== 'object')
|
|
219
|
+
return false;
|
|
220
|
+
const m = z['method'];
|
|
221
|
+
const art = typeof m === 'object' && m !== null ? m.value : m;
|
|
222
|
+
return art === 'cash' && typeof z['amountCents'] === 'number' && z['amountCents'] > 0 && z['refundOf'] === undefined;
|
|
223
|
+
}
|
|
174
224
|
/**
|
|
175
225
|
* Storno-Beleg zu einem bestehenden Beleg — voll oder in Teilen. Prueft die
|
|
176
226
|
* Eingabe, bevor etwas hinausgeht; der Server haelt die Restmengen und die
|
|
@@ -218,7 +268,7 @@ export async function cancelReceipt(transport, options) {
|
|
|
218
268
|
throw new KasseneckValidationError('cancelReceipt', `${altfeld} gibt es unter /v3 nicht mehr: Rueckzahlungen gehen als payments hinaus.`, 'request');
|
|
219
269
|
}
|
|
220
270
|
// `payments: null` gilt wie im Backend als nicht angegeben.
|
|
221
|
-
const zahlungen = options.payments != null ? gepruefteZahlungen(options.payments, true, 'cancelReceipt') : undefined;
|
|
271
|
+
const zahlungen = options.payments != null ? gepruefteZahlungen(options.payments, true, 'cancelReceipt', false, istStornoAuszahlung(options.payments, options.receipt)) : undefined;
|
|
222
272
|
if (zahlungen !== undefined)
|
|
223
273
|
pruefeKartenRueckbuchung(zahlungen, options.receipt);
|
|
224
274
|
const params = { cashregisterId, originalReceiptId, reason: options.reason };
|
|
@@ -450,6 +500,111 @@ export async function sendReceiptEmail(transport, options) {
|
|
|
450
500
|
via,
|
|
451
501
|
};
|
|
452
502
|
}
|
|
503
|
+
const AUSGABEN_FELDER = ['entries'];
|
|
504
|
+
const AUSGABE_FELDER = ['key', 'receiptId', 'kind', 'at', 'result'];
|
|
505
|
+
/** Idempotenzschluessel eines Eintrags, wie der Server ihn annimmt. */
|
|
506
|
+
const AUSGABE_SCHLUESSEL = /^[A-Za-z0-9_-]{8,64}$/;
|
|
507
|
+
/** Zeitpunkt als Text: RFC 3339 mit Zone, bis zu sechs Nachkommastellen (wie der Server). */
|
|
508
|
+
const AUSGABE_ZEIT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,6})?(Z|[+-]\d{2}:\d{2})$/;
|
|
509
|
+
/**
|
|
510
|
+
* Meldet Drucke und das Teilen eines Belegs an das Protokoll „Ausgaben“
|
|
511
|
+
* (Endpunkt `logReceiptOutputs`, nur ueber den Kassenweg `/api/v3`; mit
|
|
512
|
+
* `registerUserAuth` und mit `apiKeyAuth`). Registrierkassen-Erlass
|
|
513
|
+
* BMF-AV Nr. 2/2026, Abschnitt 4.2: die Belegerteilung ist zu dokumentieren.
|
|
514
|
+
* Der Beleg selbst bleibt unberuehrt; das Protokoll steht nie im DEP.
|
|
515
|
+
*
|
|
516
|
+
* **Ein Eintrag scheitert nie die anderen.** Der Server nimmt jeden Eintrag
|
|
517
|
+
* einzeln an oder lehnt ihn mit Code ab (`rejected`, Codes
|
|
518
|
+
* [RECEIPT_OUTPUT_ENTRY_ERROR_CODES]); nach einer Erfolgsantwort ist jeder
|
|
519
|
+
* gesendete Eintrag erledigt. Die Kasse puffert die Eintraege (offline,
|
|
520
|
+
* Neustart) und sendet sie spaeter; der Puffer blockiert nie Druck oder Teilen.
|
|
521
|
+
*
|
|
522
|
+
* **Wiederholen ist gefahrlos:** jeder Eintrag traegt seinen Schluessel. Bei
|
|
523
|
+
* unklarem Ausgang (`outcome: 'unknown'`) dieselben Eintraege mit denselben
|
|
524
|
+
* Schluesseln noch einmal senden.
|
|
525
|
+
*
|
|
526
|
+
* **Vor dem Senden abgewiesen** wird nur, was die ganze Anfrage scheitern
|
|
527
|
+
* liesse oder ohne Netz sicher falsch ist: keine oder mehr als 50 Eintraege,
|
|
528
|
+
* ein unbekanntes Feld (oben oder im Eintrag), ein fehlender Beleg, ein
|
|
529
|
+
* Schluessel ausserhalb der Regel des Servers, eine Art oder ein Ergebnis
|
|
530
|
+
* ausserhalb des Katalogs, eine Zeit, die weder ganze Millisekunden noch
|
|
531
|
+
* ISO 8601 mit Zone ist. `email` und `opened` gehen hinaus und kommen als
|
|
532
|
+
* `invalid_output_kind` zurueck: so verwirft der Puffer genau diesen Eintrag.
|
|
533
|
+
* Fehler der ganzen Anfrage: [RECEIPT_OUTPUT_ERROR_CODES].
|
|
534
|
+
*/
|
|
535
|
+
export async function logReceiptOutputs(transport, options) {
|
|
536
|
+
const name = 'logReceiptOutputs';
|
|
537
|
+
const anfrage = (grund) => new KasseneckValidationError(name, grund, 'request');
|
|
538
|
+
if (options === null || typeof options !== 'object' || Array.isArray(options))
|
|
539
|
+
throw anfrage('Anfrage muss ein Objekt sein');
|
|
540
|
+
// Ein unbekanntes Feld (etwa eine zweite cashregisterId) ginge sonst still verloren.
|
|
541
|
+
const fremd = Object.keys(options).find((k) => !AUSGABEN_FELDER.includes(k));
|
|
542
|
+
if (fremd != null)
|
|
543
|
+
throw anfrage(`unbekanntes Feld "${fremd}"`);
|
|
544
|
+
const roh = options.entries;
|
|
545
|
+
if (!Array.isArray(roh) || roh.length === 0 || roh.length > RECEIPT_OUTPUT_ENTRIES_MAX) {
|
|
546
|
+
throw anfrage(`entries muss 1–${RECEIPT_OUTPUT_ENTRIES_MAX} Eintraege haben`);
|
|
547
|
+
}
|
|
548
|
+
const entries = roh.map((e, i) => {
|
|
549
|
+
const wo = `entries[${i}]`;
|
|
550
|
+
if (e === null || typeof e !== 'object' || Array.isArray(e))
|
|
551
|
+
throw anfrage(`${wo} muss ein Objekt sein`);
|
|
552
|
+
const eintrag = {};
|
|
553
|
+
for (const [feld, w] of Object.entries(e)) {
|
|
554
|
+
if (!AUSGABE_FELDER.includes(feld))
|
|
555
|
+
throw anfrage(`${wo}: unbekanntes Feld "${feld}"`);
|
|
556
|
+
if (w !== undefined)
|
|
557
|
+
eintrag[feld] = w;
|
|
558
|
+
}
|
|
559
|
+
// Schluessel und Beleg nie getrimmt: ein veraenderter Schluessel waere ein anderer.
|
|
560
|
+
for (const feld of ['key', 'receiptId']) {
|
|
561
|
+
const w = eintrag[feld];
|
|
562
|
+
if (typeof w !== 'string' || w.trim() === '')
|
|
563
|
+
throw anfrage(`${wo}.${feld} fehlt`);
|
|
564
|
+
}
|
|
565
|
+
// Dieselbe Regel wie der Server: so traegt jede Antwort (`rejected[].key`)
|
|
566
|
+
// genau den Schluessel, unter dem die Kasse den Eintrag puffert.
|
|
567
|
+
if (!AUSGABE_SCHLUESSEL.test(eintrag.key))
|
|
568
|
+
throw anfrage(`${wo}.key muss 8–64 Zeichen [A-Za-z0-9_-] haben`);
|
|
569
|
+
// Ein Wert ausserhalb des Katalogs liesse den Rand die ganze Anfrage abweisen.
|
|
570
|
+
if (!RECEIPT_OUTPUT_KINDS.includes(eintrag.kind)) {
|
|
571
|
+
throw anfrage(`${wo}.kind muss ${RECEIPT_OUTPUT_KINDS.join(', ')} sein`);
|
|
572
|
+
}
|
|
573
|
+
if (eintrag.result !== undefined && !RECEIPT_OUTPUT_RESULTS.includes(eintrag.result)) {
|
|
574
|
+
throw anfrage(`${wo}.result muss ${RECEIPT_OUTPUT_RESULTS.join(', ')} sein`);
|
|
575
|
+
}
|
|
576
|
+
const at = eintrag.at;
|
|
577
|
+
// Text nur als ISO 8601 mit Zone (Regel des Servers): ohne Zone waere die
|
|
578
|
+
// Zeit je nach Geraet eine andere; das ist ein Fehler der Kasse, kein Fall
|
|
579
|
+
// fuer den Puffer.
|
|
580
|
+
const zeitOk = (typeof at === 'number' && Number.isSafeInteger(at) && at >= 0) || (typeof at === 'string' && AUSGABE_ZEIT.test(at));
|
|
581
|
+
if (!zeitOk)
|
|
582
|
+
throw anfrage(`${wo}.at muss Millisekunden seit 1970 oder ISO 8601 mit Zone sein`);
|
|
583
|
+
return eintrag;
|
|
584
|
+
});
|
|
585
|
+
return ausgabenGemeldet(name, await transport(name, { entries }));
|
|
586
|
+
}
|
|
587
|
+
/** Antwort von `logReceiptOutputs`, streng: `accepted` ganze Zahl ab 0, `rejected` Liste aus Schluessel und Code. */
|
|
588
|
+
function ausgabenGemeldet(name, daten) {
|
|
589
|
+
if (daten === null || typeof daten !== 'object' || Array.isArray(daten))
|
|
590
|
+
throw antwortfehler(name, 'Antwort ist kein Objekt');
|
|
591
|
+
const { accepted, rejected } = daten;
|
|
592
|
+
if (typeof accepted !== 'number' || !Number.isSafeInteger(accepted) || accepted < 0) {
|
|
593
|
+
throw antwortfehler(name, 'Antwort enthaelt keine Zahl accepted');
|
|
594
|
+
}
|
|
595
|
+
if (!Array.isArray(rejected))
|
|
596
|
+
throw antwortfehler(name, 'Antwort enthaelt keine Liste rejected');
|
|
597
|
+
return {
|
|
598
|
+
accepted,
|
|
599
|
+
rejected: rejected.map((r, i) => {
|
|
600
|
+
const { key, code } = (r ?? {});
|
|
601
|
+
if (typeof key !== 'string' || typeof code !== 'string' || code === '') {
|
|
602
|
+
throw antwortfehler(name, `rejected[${i}] ohne Schluessel oder Code`);
|
|
603
|
+
}
|
|
604
|
+
return { key, code };
|
|
605
|
+
}),
|
|
606
|
+
};
|
|
607
|
+
}
|
|
453
608
|
/**
|
|
454
609
|
* Prueft die Gutschein-Kombination eines Belegs und liefert den ersten
|
|
455
610
|
* Regelverstoss als Text (oder `null`) — Zwilling von
|
|
@@ -643,14 +798,15 @@ const MAX_ZAHLUNGEN = 20;
|
|
|
643
798
|
* (Summe, Anbieter-Pflicht und Trinkgeld prueft der Server). Wirft, bevor
|
|
644
799
|
* etwas hinausgeht.
|
|
645
800
|
*/
|
|
646
|
-
function gepruefteZahlungen(roh, storno, functionName, auszahlung = false) {
|
|
801
|
+
function gepruefteZahlungen(roh, storno, functionName, auszahlung = false, stornoAuszahlung = false) {
|
|
647
802
|
const fehler = (grund) => new KasseneckValidationError(functionName, grund, 'request');
|
|
648
803
|
if (!Array.isArray(roh))
|
|
649
804
|
throw fehler('payments muss eine Liste sein.');
|
|
650
805
|
if (roh.length > MAX_ZAHLUNGEN)
|
|
651
806
|
throw fehler(`payments: es sind hoechstens ${MAX_ZAHLUNGEN} Eintraege erlaubt.`);
|
|
652
807
|
// Auszahlung eines Gutscheins: der Betrag geht bar hinaus, ohne gegeben und ohne Trinkgeld (Server pruefeZahlungen).
|
|
653
|
-
|
|
808
|
+
// Storno einer Auszahlung: der Kunde gibt das Geld zurueck, die eine Barzahlung ist positiv (zahlungen-core stornoAuszahlung).
|
|
809
|
+
const negativ = (storno && !stornoAuszahlung) || auszahlung;
|
|
654
810
|
if (auszahlung) {
|
|
655
811
|
const e = roh[0];
|
|
656
812
|
if (roh.length !== 1)
|
|
@@ -823,6 +979,7 @@ export function belegAusHuelle(daten, functionName) {
|
|
|
823
979
|
export function belegMitFirmaAusHuelle(daten, functionName) {
|
|
824
980
|
const receipt = belegAusHuelle(daten, functionName);
|
|
825
981
|
const d = (daten ?? {});
|
|
982
|
+
const outputs = receiptOutputsFromWire(d.outputs);
|
|
826
983
|
const layout = d.layout && typeof d.layout === 'object' && Array.isArray(d.layout.lines) ? d.layout : null;
|
|
827
984
|
const angaben = d.registrationInfo && typeof d.registrationInfo === 'object' ? readRegistrationInfo(d.registrationInfo) : null;
|
|
828
985
|
return {
|
|
@@ -834,6 +991,7 @@ export function belegMitFirmaAusHuelle(daten, functionName) {
|
|
|
834
991
|
layout,
|
|
835
992
|
registrationInfo: angaben,
|
|
836
993
|
logoScale: d.logo_scale === 'S' || d.logo_scale === 'M' || d.logo_scale === 'L' || d.logo_scale === 'XL' ? d.logo_scale : 'M',
|
|
994
|
+
...(outputs !== undefined ? { outputs } : {}),
|
|
837
995
|
};
|
|
838
996
|
}
|
|
839
997
|
/**
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
export declare const VoucherType: {
|
|
5
5
|
/** Altform: Wertgutschein (Wert im Cent, Code frei). */
|
|
6
6
|
readonly value: "value";
|
|
7
|
-
/** Altform: Rabatt-/
|
|
7
|
+
/** Altform: Rabatt-/Aktionsgutschein. */
|
|
8
8
|
readonly promo: "promo";
|
|
9
9
|
/** Neue Form: Mehrzweckgutschein (Zahlungsmittel, Steuer erst bei der Einloesung). */
|
|
10
10
|
readonly multi_purpose: "multi_purpose";
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
export const VoucherType = {
|
|
5
5
|
/** Altform: Wertgutschein (Wert im Cent, Code frei). */
|
|
6
6
|
value: 'value',
|
|
7
|
-
/** Altform: Rabatt-/
|
|
7
|
+
/** Altform: Rabatt-/Aktionsgutschein. */
|
|
8
8
|
promo: 'promo',
|
|
9
9
|
/** Neue Form: Mehrzweckgutschein (Zahlungsmittel, Steuer erst bei der Einloesung). */
|
|
10
10
|
multi_purpose: 'multi_purpose',
|