Skip to content

# Receipt Objects

## Sample receipt object

This is an example of the data `global.apis.pos.onReceiptChange` and `global.apis.pos.onTransactionFinished` send to plugins. All data for a PostalPoint transaction is available in its receipt object.

The money-related properties near the bottom of this sample are generated by PostalPoint from the items and payments.

### Notes:

- The `due` property will be a negative number if change is owed to the customer.
- `pendingEmailTo` is set to a suggested email address for the email receipt, and if the receipt should actually be emailed, it will be set in `emailTo`.
- `uuid` is a unique 16-character alphanumeric receipt ID number, which is shown and used in several places. The `uuid` is generated using `global.apis.util.uuid.short()`.
- `customerAccountId` is the UUID for the customer's account, or null if there isn't a customer attached.
- This example happens to be the object used when test printing a receipt from the PostalPoint settings menu, so press that button to see what this is rendered into.

    {
        "items": [
            ReceiptItem.fromJSON({
                "id": "9100123456789012345678",
                "label": "Test Package",
                "text": "Package Details\nTracking # 9100 1234 5678 9012 3456 78\nTo:\nTEST PERSON\nORGANIZATION INC\n123 TEST ROAD\nTESTTOWN TE 99999-0001",
                "priceEach": 8,
                "qty": 1,
                "cost": 0,
                "retail": 8,
                "taxRate": 0,
                "free": false,
                "barcode": "9100123456789012345678",
                "certifiedInfo": false,
                "isMerch": false,
                "surcharge": false,
                "toAddress": {
                    "name": "TEST PERSON",
                    "company": "ORGANIZATION INC",
                    "street1": "123 TEST ROAD",
                    "street2": null,
                    "city": "TESTTOWN",
                    "state": "TE",
                    "zip": "99999-0001",
                    "email": null,
                    "phone": null,
                    "country": "US"
                },
                "fromAddress": {
                    "name": "TEST PERSON",
                    "company": "ORGANIZATION INC",
                    "street1": "123 TEST ROAD",
                    "street2": null,
                    "city": "TESTTOWN",
                    "state": "TE",
                    "zip": "99999-0001",
                    "email": null,
                    "phone": null,
                    "country": "US"
                }
            }),
            ReceiptItem.fromJSON({
                "id": "testitem",
                "label": "Test Item",
                "text": "",
                "priceEach": 2,
                "qty": 1,
                "cost": 0,
                "retail": 2,
                "taxRate": 0.1,
                "free": false,
                "barcode": "",
                "certifiedInfo": false,
                "isMerch": true,
                "surcharge": false
            })
        ],
        "payments": [
            ReceiptPayment.fromJSON({
                "amount": 10,
                "type": "cash",
                "text": "",
                "id": "testcash"
            })
        ],
        "subtotal": 10,
        "subtotalFormatted": "$10.00",
        "tax": 0.2,
        "taxFormatted": "$0.20",
        "grandTotal": 10.2,
        "grandTotalFormatted": "$10.20",
        "paid": 10.2,
        "paidFormatted": "$10.20",
        "due": 0,
        "dueFormatted": "$0.00",
        "emailTo": null,
        "pendingEmailTo": null,
        "uuid": "1234567890abcdef",
        "customerAccountId": null,
        "topTextHTML": "",
        "bottomTextHTML": ""
    }

## global.apis.pos.ReceiptItem

    export class ReceiptItem {
        /**
         *
         * @param {string|number} id Unique ID number for this item (UPC code, inventory number, etc). Used to deduplicate line items. Unique items (like shipping labels) should be random or empty.
         * @param {string} label One-line item information.
         * @param {string} text Extra item information.
         * @param {number} priceEach Price per unit
         * @param {number} quantity Number of units
         * @param {number} cost Cost per unit. Used for automatic expense tracking.
         * @param {number} taxrate Examples: 0 (for 0%), 0.05 (for 5%), etc
         * @param {string} taxableAmount The part of the sale price that's taxable. "" for default (all), "markup" for only taxing profit.
         * @returns {ReceiptItem}
         */
        constructor(id, label, text, priceEach, quantity, cost, taxrate = 0.0, taxableAmount = "") {
            this.id = id;
            this.label = label;
            if (text == null) {
                this.txt == "";
            } else {
                this.txt = text;
            }
            this.priceEach = num(priceEach);
            this.qty = num(quantity);
            this.cost = num(cost);
            if (isNaN(taxrate)) {
                this.taxRate = 0;
            } else {
                this.taxRate = num(taxrate);
            }
            this.taxableAmount = taxableAmount;
            this.merch = false;
            this.merchid = null;
            this.surcharge = false;
            this.retail = 0; // For ensuring PostalPoint fee collection on office mode shipments
            this.mailboxNumber = null;
            this.mailboxDays = 0;
            this.mailboxMonths = 0;
            this.category = ""; // merch category
            this.electronicReturnReceipt = false;
        }

        static fromJSON(obj) {
            var item = new ReceiptItem(obj.id, obj.label, obj.text, obj.priceEach, obj.qty, obj.cost, obj.taxRate, obj.taxableAmount ?? "");
            item.free = obj.free;
            item.barcode = obj.barcode;
            item.certifiedInfo = obj.certifiedInfo;
            item.toAddress = obj.toAddress;
            item.fromAddress = obj.fromAddress;
            item.merch = obj.isMerch == true || (typeof obj.merchid == "string" && obj.merchid.length > 0);
            item.merchid = obj.merchid ?? null;
            item.mailboxNumber = obj.mailboxNumber ?? null;
            item.mailboxDays = obj.mailboxDays ?? 0;
            item.mailboxMonths = obj.mailboxMonths ?? 0;
            item.surcharge = obj.surcharge;
            item.retailPrice = obj.retail;
            item.carrier = obj.carrier ?? null;
            item.service = obj.service ?? null;
            item.category = obj.category ?? "";
            item.electronicReturnReceipt = obj.electronicReturnReceipt ?? false;
            return item;
        }

        toJSON() {
            return {
                id: this.id,
                label: this.label,
                text: this.text,
                priceEach: num(this.priceEach),
                qty: num(this.qty),
                cost: num(this.cost),
                retail: num(this.retail),
                taxRate: num(this.taxRate),
                taxableAmount: this.taxableAmount,
                taxTotal: this.taxAmount,
                free: this.free,
                barcode: this.barcode,
                certifiedInfo: this.certifiedInfo,
                isMerch: this.merch,
                merchid: this.merchid,
                surcharge: this.surcharge,
                toAddress: this.toAddress,
                fromAddress: this.fromAddress,
                mailboxNumber: this.mailboxNumber,
                mailboxDays: this.mailboxDays,
                mailboxMonths: this.mailboxMonths,
                carrier: this.carrier,
                service: this.service,
                category: this.category,
                electronicReturnReceipt: this.electronicReturnReceipt
            };
        }

        get text() {
            if (typeof this.txt == "string") {
                return this.txt;
            }
            return "";
        }

        set text(t) {
            if (typeof t == "string") {
                this.txt = t;
            } else {
                this.txt = "";
            }
        }

        get certifiedInfo() {
            if (typeof this.certified == "undefined") {
                return false;
            }
            return this.certified;
        }

        set certifiedInfo(info) {
            this.certified = info;
        }

        setCertifiedInfo(tracking, certfee, extrafees, postage, date, location, toaddress) {
            this.certified = {
                tracking: tracking,
                certifiedFee: num(certfee),
                extraFees: extrafees,
                postage: num(postage),
                date: date,
                location: location,
                to: toaddress
            };
        }

        setQuantity(q) {
            this.qty = num(q);
        }

        get free() {
            return this.isFree == true;
        }

        set free(free) {
            this.isFree = free == true;
        }

        get barcode() {
            if (typeof this.barcodeData != "string") {
                return "";
            }
            return this.barcodeData;
        }

        set barcode(data) {
            this.barcodeData = data;
        }

        get linePrice() {
            return round(m(this.priceEach, this.qty), 2);
        }

        get priceEachFormatted() {
            return getCurrencySymbol() + round(num(this.priceEach), 2).toFixed(2);
        }

        get linePriceFormatted() {
            return getCurrencySymbol() + round(num(this.linePrice), 2).toFixed(2);
        }

        get texthtml() {
            if (typeof this.text != "string") {
                return "";
            }
            var lines = this.text.split("\n");
            for (var i = 0; i < lines.length; i++) {
                if (lines[i].startsWith("Tracking # ")) {
                    // Allow copying tracking number
                    lines[i] = "Tracking # <span class=\"usall\">" + lines[i].replace("Tracking # ", "") + "</span>";
                }
            }
            return lines.join("<br />");
        }

        get taxAmount() {
            if (this.taxableAmount == "markup") {
                var lineCost = m(this.cost, this.qty);
                var margin = s(this.linePrice, lineCost);
                if (margin <= 0) {
                    return 0;
                }
                return round(m(margin, this.taxRate), 2);
            } else {
                return round(m(this.linePrice, this.taxRate), 2);
            }
        }

        get retailPrice() {
            if (typeof this.retail == "number") {
                return this.retail;
            }
            return this.priceEach * this.qty;
        }

        set retailPrice(price) {
            this.retail = num(price);
        }

    }

## global.apis.pos.ReceiptPayment

    export class ReceiptPayment {

        /**
         *
         * @param {number} amount amount paid
         * @param {string} type payment type
         * @param {string} text extra data (credit card info, etc)
         * @returns {ReceiptPayment}
         */
        constructor(amount, type, text) {
            this.id = (Math.random() * 100000000) + "_" + type + "_" + amount;
            this.text = (typeof text != "string" ? "" : text);
            this.type = type;
            this.amount = amount;
        }

        static fromJSON(obj) {
            var item = new ReceiptPayment(obj.amount, obj.type, obj.text);
            item.id = obj.id;
            return item;
        }

        toJSON() {
            return {
                amount: round(this.amount, 2),
                type: this.type,
                text: this.text,
                id: this.id
            };
        }

        get texthtml() {
            if (typeof this.text != "string") {
                return "";
            }
            return this.text.replaceAll("\n", "<br />");
        }

        get amountFormatted() {
            return getCurrencySymbol() + this.amount.toFixed(2);
        }

        get label() {
            if (typeof this.type != "string") {
                return "Payment";
            }
            switch (this.type) {
                case "cash":
                    return "Cash";
                case "check":
                    return "Check";
                case "card":
                    return "Card";
                case "card_manual":
                    return "Card";
                case "account":
                    return "Account";
                case "free":
                    return "Free";
                case "discount":
                    return "Discount";
                case "crypto":
                    return "Cryptocurrency";
                case "ach":
                    return "ACH Debit";
                case "rounding":
                    return "Cash Rounding"; // Used in penniless countries to balance a cash-only transaction
                default:
                    return this.type;
            }
        }
    }