393 lines
12 KiB
Markdown
393 lines
12 KiB
Markdown
# 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.
|
|
|
|
|
|
```javascript
|
|
{
|
|
"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
|
|
|
|
```javascript
|
|
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
|
|
|
|
```javascript
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
``` |