Build site with mkdocs and auto-generated API docs
This commit is contained in:
parent
89fb64ca12
commit
2aa4cef22d
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/site
|
||||
/.cache
|
||||
478
README.md
478
README.md
@ -1,479 +1,5 @@
|
||||
# PostalPoint Plugins
|
||||
|
||||
PostalPoint® supports JavaScript plugin extensions. Plugins can hook into PostalPoint to add features and integrations.
|
||||
This repository is the source for the PostalPoint plugin development site.
|
||||
|
||||
## What plugins can do
|
||||
|
||||
* Process card payments and handle saved payment methods
|
||||
* Process cryptocurrency payments
|
||||
* Add additional carriers, providing shipping rates and labels
|
||||
* Print to label and receipt printers, letting PostalPoint handle hardware support and drivers
|
||||
* Extend support for prepaid label acceptance, prepaid barcode recognition, and carrier dropoff QR codes
|
||||
* Install pages in the Tools menu, creating new interfaces and features
|
||||
* Receive transaction receipts for ingestion into third-party accounting or business software
|
||||
* Display interactive HTML5 content on the customer-facing screen
|
||||
* Run both Node.JS and browser code.
|
||||
|
||||
## Plugin Package Structure
|
||||
|
||||
A plugin is distributed as a simple ZIP file, containing a folder. The folder then has at least one file, named `plugin.js`.
|
||||
The `exports.init` function in `plugin.js` is executed when PostalPoint launches, allowing the plugin
|
||||
to request involvement with various events in PostalPoint.
|
||||
|
||||
PostalPoint installs plugin packages by unzipping their contents into a plugins folder.
|
||||
Plugins are uninstalled by deleting their folder.
|
||||
|
||||
## Minimal Plugin Code
|
||||
|
||||
```javascript
|
||||
// plugin-name/plugin.js
|
||||
exports.init = function () {
|
||||
global.apis.alert("This message appears when PostalPoint launches.", "Hello!");
|
||||
};
|
||||
```
|
||||
|
||||
Yes, the smallest plugin really is just two lines of code, and accessing PostalPoint features
|
||||
really is that easy.
|
||||
|
||||
## Plugin Metadata File
|
||||
|
||||
While not strictly required, a `package.json` is encouraged, and allows specifying the plugin's
|
||||
display name, PostalPoint version compatibility, and using a filename other than `plugin.js`
|
||||
for the main plugin script.
|
||||
|
||||
Sample:
|
||||
|
||||
```js
|
||||
{
|
||||
"name": "plugin-id-here",
|
||||
"main": "plugin.js",
|
||||
"description": "Human-readable description of the plugin",
|
||||
"version": "1.0.0",
|
||||
"author": "Your Name",
|
||||
"license": "Code license name",
|
||||
"postalpoint": {
|
||||
"pluginname": "Display Name for Plugin",
|
||||
"minVersion": "000034",
|
||||
"maxVersion": "001000"
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PostalPoint version codes are MMMnnn where MMM is the major version and nnn is the minor version, zero-padded.
|
||||
So version 0.35 is "000035", and 1.23 is "001023".
|
||||
|
||||
## Example Plugins
|
||||
|
||||
Check out the `examples` folder for sample plugins demonstrating the API features.
|
||||
|
||||
## PostalPoint Plugin API
|
||||
|
||||
The PostalPoint plugin API is a globally-available object named `global.apis`.
|
||||
|
||||
### API List
|
||||
|
||||
Note: API functions shown here are available in the
|
||||
[latest testing build](https://build.netsyms.net/job/PostalPoint_Retail/),
|
||||
but may not yet be present in the most recent release.
|
||||
|
||||
Functions listed as `async ...` return a Promise.
|
||||
|
||||
#### Core
|
||||
|
||||
`global.apis.`:
|
||||
|
||||
* `alert(text, title)`: Display an alert dialog.
|
||||
* `getAppFolder()`: Get the filesystem path to the PostalPoint installation folder.
|
||||
* `getPluginFolder(pluginName)`: Get the filesystem path to a plugin's folder.
|
||||
If used without arguments, gets the root plugin storage folder.
|
||||
* `f7`: The Framework7 app instance for PostalPoint's entire UI, created by `new Framework7()`. See https://framework7.io/docs/app for details. Be very careful.
|
||||
* `eventbus`: A [Framework7 event bus](https://framework7.io/docs/events#events-bus) that parts of PostalPoint broadcast on. See docs/Events.md for details.
|
||||
|
||||
#### Barcode
|
||||
|
||||
`global.apis.barcode.`:
|
||||
|
||||
* `TrackingBarcode`: A class defining a prepaid barcode and related information. See docs/TrackingBarcode.md for details.
|
||||
* `onPrepaidScan(function (codeString) {})`: The function passed to onPrepaidScan is run
|
||||
when a barcode is scanned on the Prepaid page. The function is passed one argument, a string
|
||||
containing the raw barcode data. The function shall return boolean `false` if unable or unwilling
|
||||
to handle the barcode. If the barcode is handled by this function, it shall return a TrackingBarcode object.
|
||||
* `addPrepaidBarcode(trackingBarcodeData)`: Add a TrackingBarcode object to the transaction receipt at any time other than `onPrepaidScan`.
|
||||
* `inject(barcodeData)`: Pass data to the internal barcode event subsystem. The data is handled as if it were just received from a physical barcode scanner.
|
||||
|
||||
#### Database
|
||||
|
||||
PostalPoint supports multiple SQL databases, currently SQLite and MariaDB.
|
||||
|
||||
`global.apis.database.`:
|
||||
|
||||
* `async getConnection()`: Returns a database driver. See docs/Database.md for details.
|
||||
|
||||
|
||||
#### Graphics
|
||||
|
||||
PostalPoint uses the Jimp library version 1.6 for creating and manipulating images and shipping labels.
|
||||
|
||||
`global.apis.graphics.`:
|
||||
|
||||
* `Jimp()`: The [JavaScript Image Manipulation Program](https://jimp-dev.github.io/jimp/). Access like so: `const {Jimp} = global.apis.graphics.Jimp();`.
|
||||
* `async loadFont(filename)`: Replacement for [Jimp's loadFont](https://jimp-dev.github.io/jimp/api/jimp/functions/loadfont/), which gets very confused about our JS environment and ends up crashing everything.
|
||||
|
||||
#### International and Currency
|
||||
|
||||
PostalPoint only officially supports the US and Canada, but does not prohibit use in other countries.
|
||||
It's recommended to use these functions instead of assuming the US dollar.
|
||||
|
||||
`global.apis.i18n.`:
|
||||
|
||||
* `country()`: Returns the 2-character country code that PostalPoint is running in (examples: "US", "CA").
|
||||
* `currency()`: Returns the 3-character currency code (examples: "usd", "cad").
|
||||
* `symbol()`: Returns the currency symbol (example: "$").
|
||||
* `decimals()`: Returns the number of decimal places for the currency (2 for USD 1.23, 3 for TND 1.234, etc)
|
||||
* `async convertCurrency(amount = 1.0, fromCurrency = "usd", toCurrency = global.apis.i18n.currency())`: Convert an amount of money to a different currency. Conversion rate is retrieved from the cloud service and cached for 4 hours.
|
||||
* `moneyToFixed(amount)`: Returns the amount as a string with the correct number of decimal places. Same as running `amount.toFixed(global.apis.i18n.decimals())`.
|
||||
* `moneyString(amount)`: Returns the money amount formatted as a string with currency symbol. Same as running `global.apis.i18n.symbol() + amount.toFixed(global.apis.i18n.decimals())`.
|
||||
|
||||
#### Kiosk
|
||||
|
||||
`global.apis.kiosk.`:
|
||||
|
||||
* `isKiosk()`: Returns a boolean to indicate if PostalPoint is running in kiosk mode.
|
||||
|
||||
#### Mailboxes
|
||||
|
||||
Note: Functions will throw an error if a mailbox number is not valid (must be alphanumeric).
|
||||
Use `boxNumberValid(number)` to validate user input and avoid errors.
|
||||
|
||||
`global.apis.mailboxes.`:
|
||||
|
||||
* `FormPS1583`: PS Form 1583 object. See docs/FormPS1583.md
|
||||
* `boxNumberValid(number)`: Returns true if the mailbox number is an acceptable format, false if it isn't. Does not check if the box actually exists, merely if the number is acceptable to use as a mailbox number.
|
||||
* `async getList()`: Get the list of mailboxes and boxholders as an array of objects (see below)
|
||||
* `async addDaysToMailbox(boxNumber, days = 0, months = 0)`: Add a number of days or months to a mailbox's expiration.
|
||||
* `async setMailboxExpirationDate(boxNumber, date)`: Set the box expiration to a specific JavaScript Date object, or a UNIX timestamp (in seconds).
|
||||
* `async createMailbox(number, size, notes)`: Create a new mailbox number with the specified box size. Throws an error if the box number is already in use.
|
||||
* `async editMailbox(oldNumber, newNumber, newSize = null)`: Change the number and/or size of a mailbox while preserving the boxholders and packages associated. If only changing size, set oldNumber and newNumber to the same value.
|
||||
* `async deleteMailbox(number)`: Delete a mailbox. Throws an error if the mailbox has boxholders attached.
|
||||
* `async closeMailbox(number)`: Close a mailbox by removing the boxholders and marking it as vacant. Boxholder PS Form 1583 records are archived per USPS regulations.
|
||||
* `async mailboxExists(number)`: Returns true if the mailbox number exists, false if it doesn't.
|
||||
* `async addOrUpdateBoxholder(boxNumber, info)`: Modify or add a boxholder to a mailbox. `info` is the boxholder structure below. If the `uuid` given already belongs to a boxholder, their info is updated with what you supply. Otherwise, the info is added as a new boxholder.
|
||||
* `async removeBoxholder(boxNumber, uuid)`: Remove a boxholder by their UUID, and archive their PS Form 1583 data per USPS regulations.
|
||||
* `async get1583(boxNumber, uuid, archiveNumber = false)`: Get the FormPS1583 object for a boxholder by UUID. If `archiveNumber` is true, returns the form for a deleted boxholder from the archive.
|
||||
* `async set1583(boxNumber, uuid, formps1583)`: Set the FormPS1583 object for a boxholder by UUID.
|
||||
* `async getMailboxProducts()`: Get a list of merchandise items that are usable for mailbox renewals. See below.
|
||||
|
||||
|
||||
##### Mailbox getList() output
|
||||
|
||||
```js
|
||||
[{
|
||||
num: "123", // Box number as string
|
||||
expires: 1234567890, // UNIX timestamp (in seconds) or false if box vacant
|
||||
size: "2", // Box size, 1-10
|
||||
notes: "", // Notes for mailbox, not currently shown in Mailbox Manager UI but may be used in the future
|
||||
barcode: "", // Unique barcode for the mailbox, for future use
|
||||
renewalMerchID: "", // Merchandise item ID used for autorenewing this mailbox
|
||||
isBusiness: false, // True if the box is for a business, false if for personal use
|
||||
names: [], // Array of boxholders, see Boxholder structure below
|
||||
packages: [], // Array of packages awaiting pickup, see below
|
||||
vacant: false // True if the box is currently vacant, else false
|
||||
}]
|
||||
```
|
||||
|
||||
##### Boxholder structure
|
||||
|
||||
Unless noted, all fields are strings and default to an empty string if data not available.
|
||||
|
||||
```js
|
||||
{
|
||||
name: [bizname, fname, mname, lname].filter(Boolean).join(" "),
|
||||
fname: "", // First name
|
||||
mname: "", // Middle name
|
||||
lname: "", // Last name
|
||||
email: "", // Email
|
||||
phone: "", // Phone
|
||||
uuid: "", // Customer UUID
|
||||
bizname: "", // Business name
|
||||
street1: "", // Street address
|
||||
city: "", // City
|
||||
state: "", // Two-character state
|
||||
zipcode: "", // ZIP or postal code
|
||||
country: "", // Two-character country code
|
||||
primary: true // True if the primary (first) boxholder, false if an additional authorized mail recipient
|
||||
}
|
||||
```
|
||||
|
||||
##### Mailbox getList()[].packages format
|
||||
|
||||
```js
|
||||
{
|
||||
tracking: tracking ?? "[Untracked]", // Package tracking number
|
||||
finalized: true, // True if package check-in is finished and shelf tag/mailbox slips printed, false if not finalized
|
||||
available_date: Date(), // The date and time the package was checked in
|
||||
tag: "" // Unique number assigned to the package and printed on shelf tags, scanned by employee when customer picks up package
|
||||
}
|
||||
```
|
||||
|
||||
##### Mailbox merchandise item format (getMailboxProducts)
|
||||
|
||||
These fields are returned as entered in the Merchandise Admin user interface or as shown in the merchandise CSV export.
|
||||
|
||||
```js
|
||||
{
|
||||
id: "", // Unique ID for this entry in the merchandise table
|
||||
name: "", // Merch item name
|
||||
category: "", // Merch item category
|
||||
price: 0.0, // Sale price in dollars
|
||||
cost: 0.0, // Merchandise cost in dollars (likely not used for mailboxes)
|
||||
barcode: "", // Barcode/UPC (likely not used for mailboxes)
|
||||
tax: 0.0, // Sales tax rate
|
||||
rentaldays: 30, // Number of days this item adds to a mailbox (mutually exclusive with rentalmonths)
|
||||
rentalmonths: 1, // Number of months (mutually exclusive with rentaldays)
|
||||
boxsize: "1" // Mailbox size tier, 1-10
|
||||
}
|
||||
```
|
||||
|
||||
#### Point of Sale
|
||||
|
||||
`global.apis.pos.`:
|
||||
|
||||
* `addReceiptItem(item)`: Add a `ReceiptItem` to the current transaction.
|
||||
* `addReceiptPayment(item)`: Add a `ReceiptPayment` to the current transaction.
|
||||
* `addOnscreenPaymentLog(string)`: Append a line of text to the onscreen log displayed during credit card processing. Not shown in kiosk mode.
|
||||
* `getReceiptID()`: Get the unique alphanumeric ID for the current transaction/receipt. This is the same code printed on receipts and used in digital receipt URLs.
|
||||
* `onReceiptChange(function (receipt) {})`: Add a function to be called whenever the transaction data/receipt is changed.
|
||||
* `onTransactionFinished(function (receipt) {})`: Same as `onReceiptChange` except run when a transaction is completed.
|
||||
* `registerCardProcessor(...)`: Register the plugin as a credit card processor. See examples/payment-processor for details.
|
||||
* `registerCryptoProcessor(...)`: Register the plugin as a cryptocurrency payment provider. See examples/crypto-processor for details.
|
||||
* `ReceiptItem`: A class representing a sale item in the current transaction. See docs/Receipt.md for details.
|
||||
* `ReceiptPayment`: A class representing a payment entry for the current transaction. See docs/Receipt.md for details.
|
||||
|
||||
#### Printing
|
||||
|
||||
`global.apis.print.`:
|
||||
|
||||
* `async printLabelImage(image)`: Print a 300 DPI image to the configured shipping label printer. centered on a 4x6 shipping label.
|
||||
If the printer is 200/203 DPI, the image is automatically scaled down to keep the same real size.
|
||||
Accepts a Jimp image, or the raw image file data as a Node Buffer, ArrayBuffer, or Uint8Array.
|
||||
Also accepts an http/https image URL, but it is recommended you fetch the image yourself and handle any network errors.
|
||||
Recommended image size is 1200x1800, which is 4x6 inches at 300 DPI. 800x1200 images are assumed to be 4x6 but at 200 DPI.
|
||||
For compatibility reasons, the bottom 200 pixels on an 800x1400 image is cropped off.
|
||||
Images wider than they are tall will be rotated 90 degrees. Images that are 1200 or 800 pixels
|
||||
wide and multiples of 1800 or 1200 pixels tall, respectively, are automatically split and printed
|
||||
onto multiple 1200x1800 or 800x1200 labels.
|
||||
* `async getReceiptPrinter()`: Get the receipt printer interface. See docs/ReceiptPrinter.md for available printer functions and example code.
|
||||
* `async printReceiptData(data)`: Print the raw data generated by the receipt printer interface.
|
||||
* `imageToBitmap(jimpImage, dpiFrom = 300, dpiTo = 300)`: Convert a Jimp image to monochrome 1-bit
|
||||
pixel data for printing on a receipt printer. Converts color to grayscale, and grayscale is dithered to monochrome.
|
||||
Optionally changes the image DPI. Use this to get image data for sending to a receipt printer.
|
||||
Returns an object such as {width: 300, height: 200, img: Uint8Array}. Pass the img value to `drawImage`.
|
||||
|
||||
|
||||
#### Reports
|
||||
|
||||
Plugins can supply reports which the PostalPoint user can run. Reports can be exported to a CSV file.
|
||||
|
||||
`global.apis.reports.`:
|
||||
|
||||
* `registerReport(name, onload, date = true)`: Add a report to the Reports UI in PostalPoint.
|
||||
`name` is the user-visible name of the report in the list. `onload` is an async function that
|
||||
returns the report (see below for format). It is passed two arguments consisting of UNIX timestamps
|
||||
for the start and end date selected by the user. Setting `date` to `false` turns off the date range
|
||||
selection for reports that don't need a date range.
|
||||
|
||||
##### Report format:
|
||||
|
||||
Single table report:
|
||||
|
||||
```js
|
||||
{
|
||||
table: {
|
||||
header: ["Column 1", "Column 2"],
|
||||
datatypes: ["string", "string"],
|
||||
rows: [
|
||||
["Row 1 Col 1", "Row 1 Col 2"],
|
||||
["Row 2 Col 1", "Row 2 Col 2"]
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Multiple table report: (several tables in one report):
|
||||
|
||||
```js
|
||||
{
|
||||
multitable: true,
|
||||
table: {
|
||||
titles: ["Report 1 Title", "Report 2 Title"],
|
||||
header: [["Report 1 Column 1", "Report 1 Column 2"], ["Report 2 Column 1", ...]],
|
||||
datatypes: [["string", "string"], ["string", "string"]],
|
||||
rows: [
|
||||
[
|
||||
["Report 1 Row 1 Col 1", "Report 1 Row 1 Col 2"],
|
||||
["Report 1 Row 2 Col 1", "Report 1 Row 2 Col 2"]
|
||||
],
|
||||
[
|
||||
["Report 2 Row 1 Col 1", "Report 2 Row 1 Col 2"],
|
||||
["Report 2 Row 2 Col 1", "Report 2 Row 2 Col 2"]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### Report datatypes/column values:
|
||||
|
||||
With a few exceptions, report columns must be strings.
|
||||
If the datatype provided is not recognized, it is treated as `string`.
|
||||
Strings are displayed as-is, except newlines are replaced with markup
|
||||
so multi-line data displays as expected.
|
||||
|
||||
Special datatype values:
|
||||
|
||||
* `datetime`: Column is a UNIX timestamp (in seconds). It is displayed as a formatted date and time string.
|
||||
* `receiptid`: Column is a PostalPoint receipt ID number.
|
||||
Displayed as a link. Clicking the ID will fetch and open the receipt in a popup.
|
||||
* `userid`: Column contains an employee ID number from the PostalPoint database. It is queried in
|
||||
the database and replaced with the employee's name, or with an empty string if the ID lookup has
|
||||
no results.
|
||||
|
||||
Other datatypes may be defined in the future, so always use `string` when no special behavior is desired.
|
||||
|
||||
#### Settings/Configuration Storage
|
||||
|
||||
PostalPoint provides a UI for user-configurable plugin settings. See `exports.config` in examples/basic-demo/plugin.js for details.
|
||||
|
||||
Settings are typically very short strings. Do not store data in settings. For data storage, see **Storing Data**.
|
||||
Non-string settings values are transparently converted to/from JSON objects.
|
||||
|
||||
Use a unique key name prefix for your plugin to prevent key name conflicts.
|
||||
Reverse domain style is recommended (i.e. `"com.example.pluginname.keyname"`).
|
||||
|
||||
`global.apis.settings.`:
|
||||
|
||||
* `get(key, defaultValue)`: Get a setting value, or the `defaultValue` if the setting is not set.
|
||||
* `set(key, value)`: Set a setting value.
|
||||
|
||||
|
||||
#### Storing Data
|
||||
|
||||
There are three key/value stores available in PostalPoint.
|
||||
|
||||
1. `setSmall` and `getSmall` currently use
|
||||
[localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) as a backend,
|
||||
but this may change in the future.
|
||||
2. `setBig` and `getBig` store the data to disk as a JSON file.
|
||||
3. `setDB` and `getDB` store the data in the PostalPoint database, making stored data
|
||||
available on the network to other PostalPoint installations. Depending on the backend in use, data
|
||||
is stored either as a SQLite TEXT column or a MariaDB/MySQL LONGTEXT.
|
||||
|
||||
`*Big` and `*Small` functions perform synchronous disk access while `*Database` functions are
|
||||
asynchronous and return a Promise.
|
||||
|
||||
String values are stored and fetched as-is; all other datatypes are encoded to JSON before saving,
|
||||
and decoded back when fetched.
|
||||
|
||||
Storing a value of `null` causes the key to be deleted. Fetching a key that doesn't exist without
|
||||
specifying a default value returns `null`.
|
||||
|
||||
Behavior if the host machine is out of disk space or has a drive failure is undefined.
|
||||
|
||||
`global.apis.storage.`:
|
||||
|
||||
* `getBig(key, defaultValue)`
|
||||
* `setBig(key, value)`
|
||||
* `getSmall(key, defaultValue)`
|
||||
* `setSmall(key, value)`
|
||||
* `async getDB(key, defaultValue)`
|
||||
* `async setDB(key, value)`
|
||||
|
||||
|
||||
#### Shipping and Rates
|
||||
|
||||
`global.apis.shipping.`:
|
||||
|
||||
* `Address`: A class representing an address. See docs/Address.md.
|
||||
* `getZIPCode(zip)`: Get data for a 5-digit US ZIP Code, such as
|
||||
`{"city": "HELENA", "state": "MT", "type": "STANDARD"}`.
|
||||
Returns `false` if the ZIP Code isn't in the database.
|
||||
* `getCarrierName(carrierId)`: Converts the carrier ID string into a consistent and human-readable name. Useful with registerMarkupCalculator.
|
||||
See docs/Carrier_Service.md for info.
|
||||
* `getServiceName(serviceId, carrier = "USPS")`: Converts the service ID string into a consistent and human-readable name.
|
||||
Set the carrier ID for better results. Useful with registerMarkupCalculator.
|
||||
See docs/Carrier_Service.md for info.
|
||||
* `registerRateEndpoint(getRate, purchase, idPrefix)`: Register the plugin as a shipping rate and label provider.
|
||||
See examples/shipping/plugin.js for example usage.
|
||||
* `registerMarkupCalculator(function (cost, retail, suggested, carrier, service) {})`:
|
||||
Register the plugin to modify PostalPoint's shipping markup calculation during shipment rating.
|
||||
The function shall return either `false` (to opt out of making the decision) or a numeric retail price for the shipment rate.
|
||||
Only one plugin may register with this function at a time. If a plugin attempts to register later, it will throw an `Error`.
|
||||
See examples/shipping/plugin.js for details.
|
||||
|
||||
#### UI
|
||||
|
||||
`global.apis.ui.`:
|
||||
|
||||
* `addToolsPage(page, title, id = "", description = "", cardTitle = "", icon = "", type = "page")`: Add a page to the Tools screen. See examples/basic-demo for example usage. If `type` is set to "function", the `page` argument will be run as a function and will not be expected to return a page component.
|
||||
* `showProgressSpinner(title, text = "", subtitle = "")`: Show a Framework7 notification with a loading icon.
|
||||
* `hideProgressSpinner()`: hide the UI element created by `showProgressSpinner`.
|
||||
* `openInternalWebBrowser(url)`: Open a web browser UI, navigating to the URL. The browser has forward/back/close buttons.
|
||||
* `openSystemWebBrowser(url)`: Open the native OS default browser to the URL given.
|
||||
* `getCustomerDisplayInfo()`: Describes if the customer-facing display is currently enabled, and if it supports customer touch interaction: `{"enabled": true, "touch": true}`.
|
||||
* `clearCustomerScreen()`: Clear any custom content on the customer-facing display, defaulting back to blank/receipt/shipping rates, as applicable.
|
||||
* `setCustomerScreen(content, type = "html", displayInCard = false, cardSize = [300, 300])`: Encodes `content` as a data URI (example: `` `data:text/html;charset=utf-8,${content}` ``) and renders on the customer-facing display. If `type` is `html`, renders the string as HTML. If `type` is `pdf`, displays a PDF viewer. If `type` is `raw`, functions like setting an iframe's src to `content`. All other `type` values are rendered as `text/plain`. To display the iframe in a card centered on the screen, set displayInCard to true and pass the desired dimensions (w, h) of the card in px. If the requested size is larger than the available screen space, the card will instead fill the available space. Warning: Do not load third-party websites, this is a security risk. Wrap it in a `<webview src="..."></webview>` tag if you need to display one.
|
||||
* `collectSignatureFromCustomerScreen()`: Show a signature pad on the customer-facing display. When the customer indicates the signature is finished, the `customerSignatureCollected` event is emitted with the data `{"svg": "data:image/svg+xml;base64,...", "png": "data:image/png;base64,..."}`
|
||||
* `cancelSignatureCollection()`: Cancels customer signature collection and returns the customer-facing display to normal operation.
|
||||
* `clearSignaturePad()`: Erase the signature on the customer-facing display. Note that the customer is also provided a button to do this.
|
||||
|
||||
|
||||
#### Utilities
|
||||
|
||||
Various useful helper functions.
|
||||
|
||||
`global.apis.util.`:
|
||||
|
||||
* `async barcode.getBuffer(data, type = "code128", height = 10, scale = 2, includetext = false)`: Get a PNG image buffer of a barcode. Uses library "bwip-js".
|
||||
* `async barcode.getBase64`: Same as `barcode.getBuffer` except returns a base64 image URL.
|
||||
* `clipboard.copy(text, showNotification = false)`: Copy a string to the system clipboard, optionally showing a "copied" notification to the user.
|
||||
* `async delay(ms = 1000)`: Pause execution for some amount of time in an async function, i.e., returns a Promise that resolves in some number of milliseconds.
|
||||
* `async http.fetch(url, responseType = "text", timeout = 15)`: Fetch a URL. `responseType` can be "text", "blob", "buffer", or "json". Timeout is in seconds.
|
||||
* `async http.post(url, data, responseType = "text", headers = {"Content-Type": "application/json"}, method = "POST", continueOnBadStatusCode = false)`: POST to a URL. `data` is sent as a JSON body.
|
||||
* `objectEquals(a, b)`: Compare two objects for equality.
|
||||
* `string.chunk(str, chunksize)`: Split a string into an array of strings of length `chunksize`.
|
||||
* `string.split(input, separator, limit)`: Split a string with a RegExp separator an optionally limited number of times.
|
||||
* `time.diff(compareto)`: Get the number of seconds between now and the given UNIX timestamp.
|
||||
* `time.format(format, timestamp = time.now())`: Take a UNIX timestamp in seconds and format it to a string. (Mostly) compatible with PHP's date() function.
|
||||
* `time.now()`: Get the current UNIX timestamp in seconds.
|
||||
* `time.strtotime(str)`: Parse a string date and return a UNIX timestamp.
|
||||
* `time.toDateString(timestamp)`: Get a localized date string for a UNIX timestamp.
|
||||
* `time.toTimeString(timestamp)`: Get a time string for a UNIX timestamp, for example, "2:01 PM".
|
||||
* `uuid.v4()`: Generate a version 4 UUID string, for example, "fcca5b12-6a11-46eb-96e4-5ed6365de977".
|
||||
* `uuid.short()`: Generate a 16-character random alphanumeric string, for example, "4210cd8f584e6f6c".
|
||||
* `async http.webhook.geturl(sourcename)`: Returns a URL that can be used as a webhook target/endpoint for third-party integrations. The `sourcename` is a unique identifier for the webhook. There is a limit on webhook payload size; large payloads over 500KB will be rejected by the server.
|
||||
* `async http.webhook.poll(sourcename)`: Returns an array of webhook payloads received by the webhook URL generated by `geturl(sourcename)`.
|
||||
* `async http.webhook.ack(webhookid)`: Acknowledge a webhook payload, clearing it from the list returned by `poll`.
|
||||
|
||||
##### Webhook poll result format:
|
||||
|
||||
```javascript
|
||||
[
|
||||
{
|
||||
// Unique ID. Used for ack(webhookid).
|
||||
id: 123,
|
||||
|
||||
// UNIX timestamp (in seconds) of when the data was received by the webhook URL.
|
||||
timestamp: 1234567890,
|
||||
|
||||
// Source name set in geturl()
|
||||
source: "sourcename",
|
||||
|
||||
// JSON string of all the HTTP headers sent to the webhook URL.
|
||||
headers: "{'Content-Type': 'application/json'}",
|
||||
|
||||
// Entire HTTP request body sent to the webhook URL.
|
||||
body: ""
|
||||
}
|
||||
]
|
||||
```
|
||||
[Click here to go there.](https://dev.postalpoint.app)
|
||||
|
||||
7
build.sh
Executable file
7
build.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
rm -r docs/Plugin\ API
|
||||
mkdir docs/Plugin\ API
|
||||
|
||||
PLUGINAPISRC="$HOME/Documents/Projects/Sources/PostalPoint_Retail/src/js/pluginapi/*.js"
|
||||
node jsdoc2md.js "$PLUGINAPISRC"
|
||||
@ -1,4 +1,4 @@
|
||||
# SQL Database Drivers
|
||||
# Database Drivers
|
||||
|
||||
`global.apis.database.getConnection()` returns one of these, depending on which database is in use.
|
||||
|
||||
@ -66,6 +66,63 @@ export class SQLiteAdapter {
|
||||
}
|
||||
```
|
||||
|
||||
## Remote host/master
|
||||
|
||||
```javascript
|
||||
export class RemoteDatabaseAdapter {
|
||||
constructor() {
|
||||
this.type = "remote";
|
||||
}
|
||||
|
||||
async apirequest(args) {
|
||||
var resp = await sendToPostalPointHTTPServer(args, "database");
|
||||
if (typeof resp.status == "string" && resp.status == "OK") {
|
||||
return resp.result;
|
||||
} else if (typeof resp.status == "string" && resp.status == "ERR") {
|
||||
if (typeof resp.message == "string") {
|
||||
throw new Error(resp.message);
|
||||
} else {
|
||||
throw new Error(resp);
|
||||
}
|
||||
} else {
|
||||
throw new Error(resp);
|
||||
}
|
||||
}
|
||||
|
||||
async query(query, replace = []) {
|
||||
return await this.apirequest({type: "query", query: query, replace: replace});
|
||||
}
|
||||
|
||||
async run(statement, replace = []) {
|
||||
return await this.apirequest({type: "run", query: statement, replace: replace});
|
||||
}
|
||||
|
||||
async exec(statement) {
|
||||
return await this.apirequest({type: "exec", query: statement});
|
||||
}
|
||||
|
||||
async exists(table, where, replace = []) {
|
||||
return await this.apirequest({type: "exists", table: table, where: where, replace: replace});
|
||||
}
|
||||
|
||||
async close() {
|
||||
// NOOP: We don't care about this
|
||||
}
|
||||
|
||||
async tableExists(table) {
|
||||
return await this.apirequest({type: "tableExists", table: table});
|
||||
}
|
||||
|
||||
async getSchemaVersion() {
|
||||
return await this.apirequest({type: "getSchemaVersion"});
|
||||
}
|
||||
|
||||
async setSchemaVersion(version) {
|
||||
// NOOP: Don't upgrade server's installation, it can do that itself
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## MariaDB/MySQL
|
||||
|
||||
```javascript
|
||||
@ -141,4 +198,4 @@ export class MariaDBAdapter {
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
@ -14,6 +14,7 @@ Plugins can use `global.apis.eventbus` to receive events when certain actions ha
|
||||
* `settingsSaved`: Emitted when PostalPoint's settings are saved. Usually this means the user changed a setting in the UI.
|
||||
* `pluginSettingsSaved`: Emitted when the user saves a plugin's settings. The plugin ID string is sent as the event data.
|
||||
* `packageCheckIn`: Emitted when a package is checked in to a mailbox or for Hold At Location. See below for event data details.
|
||||
* `mailboxMailDelivered`: Emitted when mail delivery notifications are sent by the user. Data is an array of the mailbox numbers notifications are being sent for.
|
||||
|
||||
## Example Code
|
||||
|
||||
@ -36,7 +37,7 @@ For events that return an object instead of a single value.
|
||||
|
||||
### transactionFinished
|
||||
|
||||
See Receipt.md
|
||||
See [Receipt](./Receipt.md)
|
||||
|
||||
### customerSignatureCollected
|
||||
|
||||
@ -60,4 +61,4 @@ See Receipt.md
|
||||
toLocker: "5", // Parcel locker number, or false if not going to a parcel locker.
|
||||
shelfLabelPrinted: true // Indicates if a shelf label was printed for this package. Will be false if going to a locker, if the user requested no label, or if the label failed to print.
|
||||
}
|
||||
```
|
||||
```
|
||||
10
docs/Examples/01Minimal.md
Normal file
10
docs/Examples/01Minimal.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Minimal Plugin
|
||||
|
||||
This is the smallest possible valid plugin.
|
||||
|
||||
|
||||
```js title="plugin-name/plugin.js"
|
||||
exports.init = function () {
|
||||
global.apis.alert("This message appears when PostalPoint launches.", "Hello!");
|
||||
};
|
||||
```
|
||||
15
docs/Examples/02Basic.md
Normal file
15
docs/Examples/02Basic.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Basic Plugin
|
||||
|
||||
This sample plugin showcases some of the features many plugins will want to use.
|
||||
|
||||
[Download JavaScript](basic-demo/plugin.js)
|
||||
|
||||
```js title="plugin.js"
|
||||
--8<-- "Examples/basic-demo/plugin.js"
|
||||
```
|
||||
|
||||
[Download HTML](basic-demo/uipluginpage.f7)
|
||||
|
||||
```html title="uipluginpage.f7"
|
||||
--8<-- "Examples/basic-demo/uipluginpage.f7"
|
||||
```
|
||||
9
docs/Examples/03Shipping.md
Normal file
9
docs/Examples/03Shipping.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Shipping Plugin
|
||||
|
||||
This sample plugin demonstrates how to add support for a custom shipping carrier.
|
||||
|
||||
[Download JavaScript](shipping/plugin.js)
|
||||
|
||||
```js title="plugin.js"
|
||||
--8<-- "Examples/shipping/plugin.js"
|
||||
```
|
||||
11
docs/Examples/04CardProcessor.md
Normal file
11
docs/Examples/04CardProcessor.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Card Payments
|
||||
|
||||
This plugin template shows how to implement a card payment processor,
|
||||
allowing PostalPoint to interface with your card reader/terminal hardware,
|
||||
as well as saving, retrieving, and charging cards on file.
|
||||
|
||||
[Download JavaScript](payment-processor/plugin.js)
|
||||
|
||||
```js title="plugin-name/plugin.js"
|
||||
--8<-- "Examples/payment-processor/plugin.js"
|
||||
```
|
||||
10
docs/Examples/05CryptoProcessor.md
Normal file
10
docs/Examples/05CryptoProcessor.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Cryptocurrency Payments
|
||||
|
||||
This plugin template shows how to implement a cryptocurrency payment processor,
|
||||
using the customer-facing display to show a payment QR code webpage.
|
||||
|
||||
[Download JavaScript](crypto-processor/plugin.js)
|
||||
|
||||
```js title="plugin-name/plugin.js"
|
||||
--8<-- "Examples/crypto-processor/plugin.js"
|
||||
```
|
||||
@ -4,7 +4,10 @@ var rateCache = [];
|
||||
var parcelCache = {};
|
||||
|
||||
exports.init = function () {
|
||||
// Add support for shipping rating and label purchasing
|
||||
global.apis.shipping.registerRateEndpoint(getRates, purchase, "uniqueprefixhere_");
|
||||
|
||||
// Add support for prepaid drop-offs
|
||||
global.apis.barcode.onPrepaidScan(function (barcode) {
|
||||
if (barcode.startsWith("mycarrierbarcode")) { // Replace this with your checks for barcode validity
|
||||
var data = new global.apis.barcode.TrackingBarcode(barcode);
|
||||
@ -14,46 +17,6 @@ exports.init = function () {
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Plugins can override PostalPoint's built-in shipment markup calculation.
|
||||
* Only one plugin on an instance of PostalPoint can register, on a first-come basis.
|
||||
* This function throws an Error if another plugin has already registered.
|
||||
*/
|
||||
/**
|
||||
* Add your code for calculating shipping markup.
|
||||
* @param {number} cost Cost to shipper
|
||||
* @param {number} retail Carrier-suggested retail price
|
||||
* @param {number} suggested PostalPoint-suggested retail (default margin calc)
|
||||
* @param {string} carrier Shipping carrier name
|
||||
* @param {string} service Shipping service code
|
||||
* @param {number|null} weightOz The weight of the shipment in ounces, or null if not available.
|
||||
* @param {string} packaging An empty string if not available, or "Letter", "FlatRateEnvelope", etc. See https://docs.easypost.com/docs/parcels#predefined-packages
|
||||
* @returns {Boolean|number} false if opting out of setting markup, otherwise the retail price
|
||||
*/
|
||||
global.apis.shipping.registerMarkupCalculator(
|
||||
function (cost, retail, suggested, carrier, service, weightOz, packaging) {
|
||||
if (carrier == "USPS") {
|
||||
if (service == "First-Class Mail") {
|
||||
// Handle First-Class Mail differently if it's a 1oz letter (i.e. Forever stamp)
|
||||
if (weightOz <= 1 && packaging == "Letter") {
|
||||
return retail + 0.05;
|
||||
} else {
|
||||
return retail + 0.25;
|
||||
}
|
||||
}
|
||||
// Handle flat rate envelopes differently
|
||||
if (global.apis.shipping.getServiceName(service, carrier) == "Priority Mail" && packaging == "FlatRateEnvelope") {
|
||||
return retail + 1.0;
|
||||
}
|
||||
return suggested + 2.0; // Charge the PostalPoint-calculated amount plus $2
|
||||
} else {
|
||||
return cost * 2; // Charges the customer double the shipment's cost.
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function purchase(rateid) {
|
||||
@ -116,4 +79,4 @@ async function getRates(parcel) {
|
||||
parcelCache = parcel;
|
||||
|
||||
return rates;
|
||||
}
|
||||
}
|
||||
86
docs/Plugin API/barcode.md
Normal file
86
docs/Plugin API/barcode.md
Normal file
@ -0,0 +1,86 @@
|
||||
<a name="barcode"></a>
|
||||
|
||||
## barcode : <code>object</code>
|
||||
Handle tracking barcodes
|
||||
|
||||
**Kind**: global namespace
|
||||
|
||||
* [barcode](#barcode) : <code>object</code>
|
||||
* [.TrackingBarcode](#barcode.TrackingBarcode)
|
||||
* [new TrackingBarcode(code)](#new_barcode.TrackingBarcode_new)
|
||||
* [.addPrepaidBarcode(trackingBarcodeData)](#barcode.addPrepaidBarcode)
|
||||
* [.inject(barcodeData)](#barcode.inject)
|
||||
* [.onPrepaidScan(f)](#barcode.onPrepaidScan)
|
||||
|
||||
<a name="barcode.TrackingBarcode"></a>
|
||||
|
||||
### barcode.TrackingBarcode
|
||||
**Kind**: static class of [<code>barcode</code>](#barcode)
|
||||
**Properties**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| tracking | <code>string</code> | Tracking number |
|
||||
| barcode | <code>string</code> | Original barcode data this was created from |
|
||||
| toZip | <code>string</code> | Destination ZIP Code, for domestic shipments. The city and state are automatically added. If toAddress is specified, toZip is ignored in favor of it. |
|
||||
| toCountry | <code>string</code> | Two-letter destination country code. If it doesn't match the country PostalPoint is running in, the full country name is appended to the displayed address information. |
|
||||
| toAddress | <code>string</code> | Destination mailing/shipping address. |
|
||||
| carrier | <code>string</code> | Shipping carrier name. |
|
||||
| service | <code>string</code> | Shipping service/mail class name. Example: "Priority Mail". |
|
||||
| dropoff | <code>boolean</code> | If set to false, the barcode will be rejected with a suitable message when PostalPoint is running in self-serve kiosk mode. |
|
||||
| confidentCarrier | <code>boolean</code> | If false, PostalPoint may prompt user to specify the shipping carrier. |
|
||||
| extraInfo | <code>Array.<string></code> | Extra description strings, like "Signature Required". |
|
||||
| message | <code>string</code> | If not empty, the barcode will NOT be added and the contents of `message` will be displayed to the user. |
|
||||
| warning | <code>string</code> | If not empty, the barcode WILL be added and the contents of `warning` will be displayed to the user. |
|
||||
| destString | <code>string</code> | (read only) Get the destination information as a human-presentable multiline string. |
|
||||
| serviceString | <code>string</code> | (read only) Get the carrier and service. |
|
||||
| toString() | <code>function</code> | Get the package information in a format suitable for display on a receipt. |
|
||||
| toString(false) | <code>function</code> | Get the package information in a format suitable for display on a receipt, suppressing the tracking number. |
|
||||
|
||||
<a name="new_barcode.TrackingBarcode_new"></a>
|
||||
|
||||
#### new TrackingBarcode(code)
|
||||
A Tracking barcode object.
|
||||
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| code | <code>string</code> | Barcode data |
|
||||
|
||||
<a name="barcode.addPrepaidBarcode"></a>
|
||||
|
||||
### barcode.addPrepaidBarcode(trackingBarcodeData)
|
||||
Add a TrackingBarcode object to the transaction receipt at any time other than `onPrepaidScan`.
|
||||
|
||||
**Kind**: static method of [<code>barcode</code>](#barcode)
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| trackingBarcodeData | <code>TrackingBarcode</code> |
|
||||
|
||||
<a name="barcode.inject"></a>
|
||||
|
||||
### barcode.inject(barcodeData)
|
||||
Pass data to the internal barcode event subsystem. The data is handled as if it
|
||||
were just received from a physical barcode scanner.
|
||||
|
||||
**Kind**: static method of [<code>barcode</code>](#barcode)
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| barcodeData | <code>string</code> |
|
||||
|
||||
<a name="barcode.onPrepaidScan"></a>
|
||||
|
||||
### barcode.onPrepaidScan(f)
|
||||
The function passed to onPrepaidScan is run when a barcode is scanned on the Prepaid page.
|
||||
The function is passed one argument, a string containing the raw barcode data.
|
||||
The function shall return boolean false if unable or unwilling to handle the barcode.
|
||||
If the barcode is handled by this function, it shall return a TrackingBarcode object.
|
||||
|
||||
**Kind**: static method of [<code>barcode</code>](#barcode)
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| f | <code>function</code> |
|
||||
|
||||
12
docs/Plugin API/database.md
Normal file
12
docs/Plugin API/database.md
Normal file
@ -0,0 +1,12 @@
|
||||
<a name="database"></a>
|
||||
|
||||
## database : <code>object</code>
|
||||
Database connection
|
||||
|
||||
**Kind**: global namespace
|
||||
<a name="database.getConnection"></a>
|
||||
|
||||
### database.getConnection() ⇒ <code>Promise.<DatabaseAdapter></code>
|
||||
Return a database connection object to run SQL against the store database. See the Database docs for details.
|
||||
|
||||
**Kind**: static method of [<code>database</code>](#database)
|
||||
81
docs/Plugin API/fs.md
Normal file
81
docs/Plugin API/fs.md
Normal file
@ -0,0 +1,81 @@
|
||||
<a name="fs"></a>
|
||||
|
||||
## fs : <code>object</code>
|
||||
Basic filesystem access utility functions, wrapping Node.JS and/or NW.JS code.
|
||||
|
||||
**Kind**: global namespace
|
||||
|
||||
* [fs](#fs) : <code>object</code>
|
||||
* [.openFileSaveDialog(suggestedFilename, fileExtensions)](#fs.openFileSaveDialog) ⇒ <code>Promise.<(string\|null)></code>
|
||||
* [.openFileBrowseDialog(chooseFolder, accept, dialogTitle)](#fs.openFileBrowseDialog) ⇒ <code>string</code> \| <code>null</code>
|
||||
* [.writeFile(filename, data, encoding, flag)](#fs.writeFile) ⇒ <code>Promise</code>
|
||||
* [.readFile(filename, encoding, flag)](#fs.readFile) ⇒ <code>Promise.<(string\|Buffer)></code>
|
||||
* [.fileExists(filename)](#fs.fileExists) ⇒ <code>boolean</code>
|
||||
|
||||
<a name="fs.openFileSaveDialog"></a>
|
||||
|
||||
### fs.openFileSaveDialog(suggestedFilename, fileExtensions) ⇒ <code>Promise.<(string\|null)></code>
|
||||
Open a file save as dialog prompting the user to save a file.
|
||||
Opens to the user's Documents folder, with sane fallbacks if it cannot be located.
|
||||
|
||||
**Kind**: static method of [<code>fs</code>](#fs)
|
||||
**Returns**: <code>Promise.<(string\|null)></code> - The full file path the user selected, or null if they cancelled.
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| suggestedFilename | <code>string</code> | The filename string to pre-fill in the dialog. |
|
||||
| fileExtensions | <code>string</code> | The file type filter to show. Examples: ".csv", ".csv,.html" |
|
||||
|
||||
<a name="fs.openFileBrowseDialog"></a>
|
||||
|
||||
### fs.openFileBrowseDialog(chooseFolder, accept, dialogTitle) ⇒ <code>string</code> \| <code>null</code>
|
||||
Open a file browse/file open dialog prompting the user to select a file or folder.
|
||||
Opens to the user's Documents folder, with sane fallbacks if it cannot be located.
|
||||
|
||||
**Kind**: static method of [<code>fs</code>](#fs)
|
||||
**Returns**: <code>string</code> \| <code>null</code> - The selected file/folder path, or null if cancelled.
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| chooseFolder | <code>boolean</code> | <code>false</code> | Set to true to choose a folder instead of a file. |
|
||||
| accept | <code>string</code> | | File filter. ".csv,.html", "image/*", etc. |
|
||||
| dialogTitle | <code>string</code> \| <code>null</code> | <code>null</code> | Title of the file open dialog. |
|
||||
|
||||
<a name="fs.writeFile"></a>
|
||||
|
||||
### fs.writeFile(filename, data, encoding, flag) ⇒ <code>Promise</code>
|
||||
Write a file to disk.
|
||||
|
||||
**Kind**: static method of [<code>fs</code>](#fs)
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| filename | <code>string</code> | | The path and filename to write to. |
|
||||
| data | <code>string</code> \| <code>Buffer</code> \| <code>ArrayBuffer</code> \| <code>Uint8Array</code> | | Data to write to the file. |
|
||||
| encoding | <code>string</code> \| <code>null</code> | <code>"utf8"</code> | Text encoding. Set to empty if not passing string data. |
|
||||
| flag | <code>string</code> \| <code>null</code> | <code>"w+"</code> | Filesystem flag. |
|
||||
|
||||
<a name="fs.readFile"></a>
|
||||
|
||||
### fs.readFile(filename, encoding, flag) ⇒ <code>Promise.<(string\|Buffer)></code>
|
||||
Read a file from disk and return its contents.
|
||||
|
||||
**Kind**: static method of [<code>fs</code>](#fs)
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| filename | <code>string</code> | | The path and filename to read from. |
|
||||
| encoding | <code>string</code> | <code>"utf8"</code> | File encoding. Set to null or empty string when reading binary data. |
|
||||
| flag | <code>string</code> | <code>"r+"</code> | Filesystem flag. |
|
||||
|
||||
<a name="fs.fileExists"></a>
|
||||
|
||||
### fs.fileExists(filename) ⇒ <code>boolean</code>
|
||||
Check if a file exists.
|
||||
|
||||
**Kind**: static method of [<code>fs</code>](#fs)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| filename | <code>string</code> | Path and filename to check. |
|
||||
|
||||
38
docs/Plugin API/global functions.md
Normal file
38
docs/Plugin API/global functions.md
Normal file
@ -0,0 +1,38 @@
|
||||
<a name="f7"></a>
|
||||
|
||||
## f7
|
||||
The Framework7 app instance for PostalPoint's entire UI, created by new Framework7().
|
||||
See https://framework7.io/docs/app for details.
|
||||
|
||||
**Kind**: global constant
|
||||
<a name="getPluginFolder"></a>
|
||||
|
||||
## getPluginFolder([id]) ⇒ <code>string</code>
|
||||
Get the filesystem path to a plugin's installation folder.
|
||||
|
||||
**Kind**: global function
|
||||
**Returns**: <code>string</code> - "/home/user/.config/postalpoint-retail/Default/storage/plugins/...", "C:\Users\user\AppData\...", etc
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [id] | <code>string</code> | Plugin ID. If omitted or empty, will return the parent folder plugins are installed within. |
|
||||
|
||||
<a name="getAppFolder"></a>
|
||||
|
||||
## getAppFolder() ⇒ <code>string</code>
|
||||
Get the filesystem path to the PostalPoint installation folder.
|
||||
|
||||
**Kind**: global function
|
||||
<a name="alert"></a>
|
||||
|
||||
## alert(text, title, [callback])
|
||||
Display a simple alert-style dialog box.
|
||||
|
||||
**Kind**: global function
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| text | <code>string</code> | | Body text of the dialog. |
|
||||
| title | <code>string</code> | | Dialog title. |
|
||||
| [callback] | <code>function</code> | <code></code> | Function to call when the alert is closed. |
|
||||
|
||||
33
docs/Plugin API/graphics.md
Normal file
33
docs/Plugin API/graphics.md
Normal file
@ -0,0 +1,33 @@
|
||||
<a name="graphics"></a>
|
||||
|
||||
## graphics : <code>object</code>
|
||||
PostalPoint uses the Jimp library version 1.6 for creating and manipulating images and shipping labels.
|
||||
|
||||
**Kind**: global namespace
|
||||
|
||||
* [graphics](#graphics) : <code>object</code>
|
||||
* [.Jimp()](#graphics.Jimp) ⇒ <code>Jimp</code>
|
||||
* [.loadFont(filename)](#graphics.loadFont) ⇒ <code>Promise</code>
|
||||
|
||||
<a name="graphics.Jimp"></a>
|
||||
|
||||
### graphics.Jimp() ⇒ <code>Jimp</code>
|
||||
The [JavaScript Image Manipulation Program](https://jimp-dev.github.io/jimp/).
|
||||
|
||||
**Kind**: static method of [<code>graphics</code>](#graphics)
|
||||
**Example**
|
||||
```js
|
||||
const {Jimp} = global.apis.graphics.Jimp();
|
||||
```
|
||||
<a name="graphics.loadFont"></a>
|
||||
|
||||
### graphics.loadFont(filename) ⇒ <code>Promise</code>
|
||||
Replacement for [Jimp's loadFont function](https://jimp-dev.github.io/jimp/api/jimp/functions/loadfont/),
|
||||
which gets very confused about our JS environment and ends up crashing everything.
|
||||
|
||||
**Kind**: static method of [<code>graphics</code>](#graphics)
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| filename | <code>string</code> |
|
||||
|
||||
107
docs/Plugin API/i18n.md
Normal file
107
docs/Plugin API/i18n.md
Normal file
@ -0,0 +1,107 @@
|
||||
<a name="i18n"></a>
|
||||
|
||||
## i18n : <code>object</code>
|
||||
Functions to help support multiple currencies and countries.
|
||||
|
||||
**Kind**: global namespace
|
||||
|
||||
* [i18n](#i18n) : <code>object</code>
|
||||
* [.country()](#i18n.country) ⇒ <code>string</code>
|
||||
* [.currency()](#i18n.currency) ⇒ <code>string</code>
|
||||
* [.symbol()](#i18n.symbol) ⇒ <code>string</code>
|
||||
* [.decimals()](#i18n.decimals) ⇒ <code>number</code>
|
||||
* [.convertCurrency(amount, fromCurrency, [toCurrency])](#i18n.convertCurrency) ⇒ <code>Promise.<number></code>
|
||||
* [.moneyToFixed(amount)](#i18n.moneyToFixed) ⇒ <code>string</code>
|
||||
* [.moneyString(amount)](#i18n.moneyString) ⇒ <code>string</code>
|
||||
* [.currencyMinorToMajor(amount)](#i18n.currencyMinorToMajor) ⇒ <code>number</code>
|
||||
* [.currencyMajorToMinor(amount)](#i18n.currencyMajorToMinor) ⇒ <code>number</code>
|
||||
|
||||
<a name="i18n.country"></a>
|
||||
|
||||
### i18n.country() ⇒ <code>string</code>
|
||||
Get the 2-character ISO country code that PostalPoint is running in.
|
||||
|
||||
**Kind**: static method of [<code>i18n</code>](#i18n)
|
||||
**Returns**: <code>string</code> - "US", "CA", etc.
|
||||
<a name="i18n.currency"></a>
|
||||
|
||||
### i18n.currency() ⇒ <code>string</code>
|
||||
Get the 3-character currency code in use.
|
||||
|
||||
**Kind**: static method of [<code>i18n</code>](#i18n)
|
||||
**Returns**: <code>string</code> - "usd", "cad", etc.
|
||||
<a name="i18n.symbol"></a>
|
||||
|
||||
### i18n.symbol() ⇒ <code>string</code>
|
||||
Get the currency symbol.
|
||||
|
||||
**Kind**: static method of [<code>i18n</code>](#i18n)
|
||||
**Returns**: <code>string</code> - "$", "€", "₤", etc
|
||||
<a name="i18n.decimals"></a>
|
||||
|
||||
### i18n.decimals() ⇒ <code>number</code>
|
||||
Get the number of decimal places for the currency: for example, USD has 2 ($x.00), KRW has 0 (x), UYW has 4 (x.0000).
|
||||
|
||||
**Kind**: static method of [<code>i18n</code>](#i18n)
|
||||
**Returns**: <code>number</code> - 0, 2, 3, or 4
|
||||
<a name="i18n.convertCurrency"></a>
|
||||
|
||||
### i18n.convertCurrency(amount, fromCurrency, [toCurrency]) ⇒ <code>Promise.<number></code>
|
||||
Convert an amount of money to a different currency. Conversion rate is retrieved from the internet and cached for 4 hours.
|
||||
|
||||
**Kind**: static method of [<code>i18n</code>](#i18n)
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| amount | <code>number</code> | | Amount of money in the "wrong" currency |
|
||||
| fromCurrency | <code>string</code> | | The currency code for the "wrong" currency that needs conversion |
|
||||
| [toCurrency] | <code>string</code> | <code>"getCurrencyCode()"</code> | The "correct" currency we want the amount to be in. |
|
||||
|
||||
<a name="i18n.moneyToFixed"></a>
|
||||
|
||||
### i18n.moneyToFixed(amount) ⇒ <code>string</code>
|
||||
Returns the amount as a string formatted with the correct number of decimal places for the currency in use.
|
||||
|
||||
**Kind**: static method of [<code>i18n</code>](#i18n)
|
||||
**Returns**: <code>string</code> - "1.23", "1.2345", etc
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| amount | <code>number</code> |
|
||||
|
||||
<a name="i18n.moneyString"></a>
|
||||
|
||||
### i18n.moneyString(amount) ⇒ <code>string</code>
|
||||
Returns the amount as a string, with the correct decimal places, and the local currency symbol.
|
||||
|
||||
**Kind**: static method of [<code>i18n</code>](#i18n)
|
||||
**Returns**: <code>string</code> - "$1.23", etc
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| amount | <code>number</code> |
|
||||
|
||||
<a name="i18n.currencyMinorToMajor"></a>
|
||||
|
||||
### i18n.currencyMinorToMajor(amount) ⇒ <code>number</code>
|
||||
Convert an amount in cents to dollars (or the local equivalent currency units).
|
||||
|
||||
**Kind**: static method of [<code>i18n</code>](#i18n)
|
||||
**Returns**: <code>number</code> - Dollars, etc
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| amount | <code>number</code> | Cents, etc |
|
||||
|
||||
<a name="i18n.currencyMajorToMinor"></a>
|
||||
|
||||
### i18n.currencyMajorToMinor(amount) ⇒ <code>number</code>
|
||||
Convert an amount in dollars to cents (or the local equivalent currency units).
|
||||
|
||||
**Kind**: static method of [<code>i18n</code>](#i18n)
|
||||
**Returns**: <code>number</code> - Cents, etc
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| amount | <code>number</code> | Dollars, etc |
|
||||
|
||||
13
docs/Plugin API/kiosk.md
Normal file
13
docs/Plugin API/kiosk.md
Normal file
@ -0,0 +1,13 @@
|
||||
<a name="kiosk"></a>
|
||||
|
||||
## kiosk : <code>object</code>
|
||||
Self-serve kiosk mode
|
||||
|
||||
**Kind**: global namespace
|
||||
<a name="kiosk.isKiosk"></a>
|
||||
|
||||
### kiosk.isKiosk() ⇒ <code>boolean</code>
|
||||
Check if PostalPoint is running in kiosk mode.
|
||||
|
||||
**Kind**: static method of [<code>kiosk</code>](#kiosk)
|
||||
**Returns**: <code>boolean</code> - - True if system is in kiosk mode, else false.
|
||||
258
docs/Plugin API/mailboxes.md
Normal file
258
docs/Plugin API/mailboxes.md
Normal file
@ -0,0 +1,258 @@
|
||||
<a name="mailboxes"></a>
|
||||
|
||||
## mailboxes : <code>object</code>
|
||||
Add, modify, and delete mailboxes and mailbox customers.
|
||||
|
||||
**Kind**: global namespace
|
||||
|
||||
* [mailboxes](#mailboxes) : <code>object</code>
|
||||
* [.FormPS1583](#mailboxes.FormPS1583)
|
||||
* [new FormPS1583()](#new_mailboxes.FormPS1583_new)
|
||||
* [.getList(filter)](#mailboxes.getList) ⇒ <code>Promise.<Array></code>
|
||||
* [.addDaysToMailbox(boxNumber, days, months)](#mailboxes.addDaysToMailbox) ⇒ <code>Promise</code>
|
||||
* [.setMailboxExpirationDate(boxNumber, date)](#mailboxes.setMailboxExpirationDate) ⇒ <code>Promise</code>
|
||||
* [.createMailbox(number, size, notes, barcode)](#mailboxes.createMailbox) ⇒ <code>Promise</code>
|
||||
* [.editMailbox(oldNumber, newNumber, newSize, barcode)](#mailboxes.editMailbox) ⇒ <code>Promise</code>
|
||||
* [.deleteMailbox(number)](#mailboxes.deleteMailbox) ⇒ <code>Promise</code>
|
||||
* [.closeMailbox(number)](#mailboxes.closeMailbox) ⇒ <code>Promise</code>
|
||||
* [.mailboxExists(number)](#mailboxes.mailboxExists) ⇒ <code>Promise.<boolean></code>
|
||||
* [.addOrUpdateBoxholder(boxNumber, info)](#mailboxes.addOrUpdateBoxholder) ⇒ <code>Promise</code>
|
||||
* [.removeBoxholder(boxNumber, uuid)](#mailboxes.removeBoxholder) ⇒ <code>Promise</code>
|
||||
* [.get1583(boxNumber, uuid, archiveNumber)](#mailboxes.get1583) ⇒ <code>Promise.<FormPS1583></code>
|
||||
* [.set1583(boxNumber, uuid, formps1583)](#mailboxes.set1583) ⇒ <code>Promise</code>
|
||||
* [.boxNumberValid()](#mailboxes.boxNumberValid) ⇒ <code>boolean</code>
|
||||
* [.getMailboxProducts()](#mailboxes.getMailboxProducts) ⇒ <code>Promise.<Array></code>
|
||||
|
||||
<a name="mailboxes.FormPS1583"></a>
|
||||
|
||||
### mailboxes.FormPS1583
|
||||
**Kind**: static class of [<code>mailboxes</code>](#mailboxes)
|
||||
<a name="new_mailboxes.FormPS1583_new"></a>
|
||||
|
||||
#### new FormPS1583()
|
||||
USPS Form PS1583 object, with all the fields needed by USPS for CMRA customers.
|
||||
|
||||
<a name="mailboxes.getList"></a>
|
||||
|
||||
### mailboxes.getList(filter) ⇒ <code>Promise.<Array></code>
|
||||
Get the list of mailboxes and boxholders as an array of objects, see example.
|
||||
|
||||
**Kind**: static method of [<code>mailboxes</code>](#mailboxes)
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| filter | <code>null</code> \| <code>object</code> | <code></code> | Filter to mailboxes matching a column condition, such as `getList({number: "102"})` or `getList({"size >=": "2"})`. Supported filter names include "number" (string, box number), "expires" (expiration date), "size" (number 1-10), and "barcode" (string) SQL injection warning: Filter names are inserted directly into query strings without sanitization. Only the values are safe for user input. |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
[{
|
||||
num: "123", // Box number as string
|
||||
expires: 1234567890, // UNIX timestamp (in seconds) or false if box vacant
|
||||
size: "2", // Box size, 1-10
|
||||
notes: "", // Notes for mailbox, not currently shown in Mailbox Manager UI but may be used in the future
|
||||
barcode: "", // Unique barcode for the mailbox, for future use
|
||||
renewalMerchID: "", // Merchandise item ID used for autorenewing this mailbox
|
||||
isBusiness: false, // True if the box is for a business, false if for personal use
|
||||
names: [], // Array of boxholders. See addOrUpdateBoxholder for the format.
|
||||
packages: [], // Array of packages awaiting pickup, see below
|
||||
vacant: false // True if the box is currently vacant, else false
|
||||
}]
|
||||
```
|
||||
**Example**
|
||||
```js
|
||||
// Data objects in the packages array:
|
||||
{
|
||||
tracking: tracking ?? "[Untracked]", // Package tracking number
|
||||
finalized: true, // True if package check-in is finished and shelf tag/mailbox slips printed, false if not finalized
|
||||
available_date: Date(), // The date and time the package was checked in
|
||||
tag: "" // Unique number assigned to the package and printed on shelf tags, scanned by employee when customer picks up package
|
||||
}
|
||||
```
|
||||
<a name="mailboxes.addDaysToMailbox"></a>
|
||||
|
||||
### mailboxes.addDaysToMailbox(boxNumber, days, months) ⇒ <code>Promise</code>
|
||||
Add a number of days or months to a mailbox's expiration. Use either days or months, not both.
|
||||
|
||||
**Kind**: static method of [<code>mailboxes</code>](#mailboxes)
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| boxNumber | <code>string</code> | | Mailbox number. |
|
||||
| days | <code>number</code> | <code>0</code> | Days to add. |
|
||||
| months | <code>number</code> | <code>0</code> | Months to add. |
|
||||
|
||||
<a name="mailboxes.setMailboxExpirationDate"></a>
|
||||
|
||||
### mailboxes.setMailboxExpirationDate(boxNumber, date) ⇒ <code>Promise</code>
|
||||
Set the box expiration to a specific JavaScript Date object, or a UNIX timestamp (in seconds).
|
||||
|
||||
**Kind**: static method of [<code>mailboxes</code>](#mailboxes)
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| boxNumber | <code>string</code> |
|
||||
| date | <code>number</code> \| <code>Date</code> |
|
||||
|
||||
<a name="mailboxes.createMailbox"></a>
|
||||
|
||||
### mailboxes.createMailbox(number, size, notes, barcode) ⇒ <code>Promise</code>
|
||||
Create a new mailbox number with the specified box size. Throws an error if the box number is already in use.
|
||||
|
||||
**Kind**: static method of [<code>mailboxes</code>](#mailboxes)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| number | <code>string</code> | Mailbox number |
|
||||
| size | <code>number</code> | Box size (1 - 10) |
|
||||
| notes | <code>string</code> | Arbitrary string with human-readable notes about the box. |
|
||||
| barcode | <code>null</code> \| <code>string</code> | A barcode value representing this mailbox, typically a sticker on the the physical box visible when delivering mail. |
|
||||
|
||||
<a name="mailboxes.editMailbox"></a>
|
||||
|
||||
### mailboxes.editMailbox(oldNumber, newNumber, newSize, barcode) ⇒ <code>Promise</code>
|
||||
Change the number and/or size of a mailbox while preserving the boxholders
|
||||
and packages associated. If only changing size, set oldNumber and newNumber to the same value.
|
||||
|
||||
**Kind**: static method of [<code>mailboxes</code>](#mailboxes)
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| oldNumber | <code>string</code> | | Currently assigned box number. |
|
||||
| newNumber | <code>string</code> | | New box number. Must not exist yet. |
|
||||
| newSize | <code>number</code> \| <code>null</code> | <code></code> | Box size (1 - 10), if changing the size. |
|
||||
| barcode | <code>null</code> \| <code>string</code> | | A barcode value representing this mailbox, typically a sticker on the the physical box visible when delivering mail. |
|
||||
|
||||
<a name="mailboxes.deleteMailbox"></a>
|
||||
|
||||
### mailboxes.deleteMailbox(number) ⇒ <code>Promise</code>
|
||||
Delete a mailbox. Throws an Error if the mailbox has boxholders attached.
|
||||
|
||||
**Kind**: static method of [<code>mailboxes</code>](#mailboxes)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| number | <code>string</code> | Mailbox number to delete. |
|
||||
|
||||
<a name="mailboxes.closeMailbox"></a>
|
||||
|
||||
### mailboxes.closeMailbox(number) ⇒ <code>Promise</code>
|
||||
Close a mailbox by removing the boxholders and marking it as vacant.
|
||||
Boxholder PS Form 1583 records are automatically archived per USPS regulations.
|
||||
|
||||
**Kind**: static method of [<code>mailboxes</code>](#mailboxes)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| number | <code>string</code> | Mailbox number to close. |
|
||||
|
||||
<a name="mailboxes.mailboxExists"></a>
|
||||
|
||||
### mailboxes.mailboxExists(number) ⇒ <code>Promise.<boolean></code>
|
||||
Returns true if the mailbox number exists, false if it doesn't.
|
||||
|
||||
**Kind**: static method of [<code>mailboxes</code>](#mailboxes)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| number | <code>string</code> | Mailbox number to check. |
|
||||
|
||||
<a name="mailboxes.addOrUpdateBoxholder"></a>
|
||||
|
||||
### mailboxes.addOrUpdateBoxholder(boxNumber, info) ⇒ <code>Promise</code>
|
||||
Modify or add a boxholder to a mailbox. info is the boxholder structure below.
|
||||
If the uuid given already belongs to a boxholder, their info is updated with what you supply.
|
||||
Otherwise, the info is added as a new boxholder.
|
||||
|
||||
**Kind**: static method of [<code>mailboxes</code>](#mailboxes)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| boxNumber | <code>string</code> | Mailbox number |
|
||||
| info | <code>Object</code> | Boxholder information. |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
// Unless noted, all fields are strings and default to an empty string.
|
||||
{
|
||||
name: [bizname, fname, mname, lname].filter(Boolean).join(" "),
|
||||
fname: "", // First name
|
||||
mname: "", // Middle name
|
||||
lname: "", // Last name
|
||||
email: "", // Email
|
||||
phone: "", // Phone
|
||||
uuid: "", // Customer UUID
|
||||
bizname: "", // Business name
|
||||
street1: "", // Street address
|
||||
city: "", // City
|
||||
state: "", // Two-character state
|
||||
zipcode: "", // ZIP or postal code
|
||||
country: "", // Two-character country code
|
||||
primary: true // True if the primary (first) boxholder, false if an additional authorized mail recipient
|
||||
}
|
||||
```
|
||||
<a name="mailboxes.removeBoxholder"></a>
|
||||
|
||||
### mailboxes.removeBoxholder(boxNumber, uuid) ⇒ <code>Promise</code>
|
||||
Remove a boxholder by their UUID, and archive their PS Form 1583 data per USPS regulations.
|
||||
|
||||
**Kind**: static method of [<code>mailboxes</code>](#mailboxes)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| boxNumber | <code>string</code> | Mailbox number. |
|
||||
| uuid | <code>string</code> | Boxholder UUID. |
|
||||
|
||||
<a name="mailboxes.get1583"></a>
|
||||
|
||||
### mailboxes.get1583(boxNumber, uuid, archiveNumber) ⇒ <code>Promise.<FormPS1583></code>
|
||||
Get the FormPS1583 object for a boxholder by UUID.
|
||||
|
||||
**Kind**: static method of [<code>mailboxes</code>](#mailboxes)
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| boxNumber | <code>string</code> | | Mailbox number. |
|
||||
| uuid | <code>string</code> | | Boxholder UUID. |
|
||||
| archiveNumber | <code>boolean</code> | <code>false</code> | If true, returns the form for a deleted boxholder from the archive. |
|
||||
|
||||
<a name="mailboxes.set1583"></a>
|
||||
|
||||
### mailboxes.set1583(boxNumber, uuid, formps1583) ⇒ <code>Promise</code>
|
||||
Set the FormPS1583 object for a boxholder by UUID.
|
||||
|
||||
**Kind**: static method of [<code>mailboxes</code>](#mailboxes)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| boxNumber | <code>string</code> | Mailbox number. |
|
||||
| uuid | <code>string</code> | Boxholder UUID. |
|
||||
| formps1583 | <code>FormPS1583</code> | The FormPS1583 object to use. |
|
||||
|
||||
<a name="mailboxes.boxNumberValid"></a>
|
||||
|
||||
### mailboxes.boxNumberValid() ⇒ <code>boolean</code>
|
||||
Returns true if the mailbox number is an acceptable format, false if it isn't.
|
||||
Does not check if the box actually exists, merely if the number is acceptable to use as a mailbox number.
|
||||
|
||||
**Kind**: static method of [<code>mailboxes</code>](#mailboxes)
|
||||
<a name="mailboxes.getMailboxProducts"></a>
|
||||
|
||||
### mailboxes.getMailboxProducts() ⇒ <code>Promise.<Array></code>
|
||||
Get a list of merchandise items that are usable for mailbox renewals.
|
||||
|
||||
**Kind**: static method of [<code>mailboxes</code>](#mailboxes)
|
||||
**Example**
|
||||
```js
|
||||
[{
|
||||
id: "", // Unique ID for this entry in the merchandise table
|
||||
name: "", // Merch item name
|
||||
category: "", // Merch item category
|
||||
price: 0.0, // Sale price in dollars
|
||||
cost: 0.0, // Merchandise cost in dollars (likely not used for mailboxes)
|
||||
barcode: "", // Barcode/UPC (likely not used for mailboxes)
|
||||
tax: 0.0, // Sales tax rate
|
||||
rentaldays: 30, // Number of days this item adds to a mailbox (mutually exclusive with rentalmonths)
|
||||
rentalmonths: 1, // Number of months (mutually exclusive with rentaldays)
|
||||
boxsize: "1" // Mailbox size tier, 1-10
|
||||
}]
|
||||
```
|
||||
353
docs/Plugin API/pos.md
Normal file
353
docs/Plugin API/pos.md
Normal file
@ -0,0 +1,353 @@
|
||||
<a name="pos"></a>
|
||||
|
||||
## pos : <code>object</code>
|
||||
Point of Sale, transaction, and payment-related functionality.
|
||||
|
||||
**Kind**: global namespace
|
||||
|
||||
* [pos](#pos) : <code>object</code>
|
||||
* [.ReceiptItem](#pos.ReceiptItem)
|
||||
* [new ReceiptItem(id, label, text, priceEach, quantity, cost, taxrate, taxableAmount)](#new_pos.ReceiptItem_new)
|
||||
* [.ReceiptPayment](#pos.ReceiptPayment)
|
||||
* [new ReceiptPayment(amount, type, text)](#new_pos.ReceiptPayment_new)
|
||||
* [.addReceiptItem(item)](#pos.addReceiptItem)
|
||||
* [.addReceiptPayment(payment)](#pos.addReceiptPayment)
|
||||
* [.addOnscreenPaymentLog(msg)](#pos.addOnscreenPaymentLog)
|
||||
* [.getReceiptID()](#pos.getReceiptID) ⇒ <code>string</code>
|
||||
* [.onReceiptChange(f)](#pos.onReceiptChange)
|
||||
* ~~[.onTransactionFinished(f)](#pos.onTransactionFinished)~~
|
||||
* [.registerCardProcessor(f)](#pos.registerCardProcessor)
|
||||
* [.registerCryptoProcessor(f)](#pos.registerCryptoProcessor)
|
||||
* [.getShippingSalesTax()](#pos.getShippingSalesTax) ⇒ <code>Object</code>
|
||||
|
||||
<a name="pos.ReceiptItem"></a>
|
||||
|
||||
### pos.ReceiptItem
|
||||
**Kind**: static class of [<code>pos</code>](#pos)
|
||||
**Properties**
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| merch | <code>boolean</code> | <code>false</code> | True if merchandise, false if shipping. |
|
||||
| barcode | <code>string</code> | | Item barcode, or tracking number if `merch = false`. |
|
||||
| qty | <code>number</code> | <code>1</code> | Item quantity. |
|
||||
| retailPrice | <code>number</code> | | The calculated retail/markup price for a shipment, regardless of actual sale price. If unset, defaults to priceEach * qty. |
|
||||
| taxRate | <code>number</code> | <code>0</code> | Tax rate |
|
||||
| toAddress | <code>Address</code> | | Shipping destination address. |
|
||||
| fromAddress | <code>Address</code> | | Shipping return address. |
|
||||
| carrier | <code>string</code> | | Shipping carrier. |
|
||||
| service | <code>string</code> | | Shipping service. |
|
||||
| category | <code>string</code> | | Merchandise/item category. |
|
||||
| electronicReturnReceipt | <code>boolean</code> | <code>false</code> | If true, the customer's receipt will have instructions on retrieveing the return receipt from USPS. |
|
||||
| mailboxDays | <code>number</code> | <code>0</code> | Number of days this item adds to a mailbox's expiration date. |
|
||||
| mailboxMonths | <code>number</code> | <code>0</code> | Number of months this item adds to a mailbox's expiration date. |
|
||||
| mailboxNumber | <code>string</code> | | Mailbox number to apply `mailboxDays` or `mailboxMonths` to after checkout. |
|
||||
| setCertifiedInfo() | <code>function</code> | | Set Certified Mail receipt data. `setCertifiedInfo(trackingNumber, certfee, extrafees, postage, date, location, toaddress)` |
|
||||
| toJSON() | <code>function</code> | | Get the item as an object suitable for JSON encoding. |
|
||||
| fromJSON(json) | <code>static\_function</code> | | Returns a ReceiptItem created from the object returned by `item.toJSON()`. |
|
||||
|
||||
<a name="new_pos.ReceiptItem_new"></a>
|
||||
|
||||
#### new ReceiptItem(id, label, text, priceEach, quantity, cost, taxrate, taxableAmount)
|
||||
A class representing a sale item in the current transaction.
|
||||
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| id | <code>string</code> | | Unique ID number for this item (UPC code, inventory number, etc). Used to deduplicate and merge line items on the receipt. Unique items (like shipping labels) should be a unique/random ID. |
|
||||
| label | <code>string</code> | | One-line item information. |
|
||||
| text | <code>string</code> | | Extra multi-line item information. |
|
||||
| priceEach | <code>number</code> | | Sale price per unit. |
|
||||
| quantity | <code>number</code> | | Number of units. |
|
||||
| cost | <code>number</code> | | Cost per unit. Used for automatic expense tracking. |
|
||||
| taxrate | <code>number</code> | <code>0.0</code> | Examples: 0 (for 0%), 0.05 (for 5%), etc |
|
||||
| taxableAmount | <code>string</code> | | The part of the sale price that's taxable. "" for default (all), "markup" for only taxing profit. |
|
||||
|
||||
<a name="pos.ReceiptPayment"></a>
|
||||
|
||||
### pos.ReceiptPayment
|
||||
**Kind**: static class of [<code>pos</code>](#pos)
|
||||
**Properties**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| label | <code>string</code> | (readonly) The human-readable string of the payment type. |
|
||||
| id | <code>string</code> | Automatically-generated unique ID for this payment. |
|
||||
| toJSON() | <code>function</code> | Get the payment as an object suitable for JSON encoding. |
|
||||
| fromJSON(json) | <code>static\_function</code> | Returns a ReceiptPayment created from the object returned by `payment.toJSON()`. |
|
||||
|
||||
<a name="new_pos.ReceiptPayment_new"></a>
|
||||
|
||||
#### new ReceiptPayment(amount, type, text)
|
||||
A class representing a payment entry for the current transaction.
|
||||
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| amount | <code>number</code> | amount paid |
|
||||
| type | <code>string</code> | payment type |
|
||||
| text | <code>string</code> | extra data (credit card info, etc) |
|
||||
|
||||
<a name="pos.addReceiptItem"></a>
|
||||
|
||||
### pos.addReceiptItem(item)
|
||||
Add an item (shipment, merchandise, etc) to the current transaction.
|
||||
|
||||
**Kind**: static method of [<code>pos</code>](#pos)
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| item | <code>ReceiptItem</code> |
|
||||
|
||||
<a name="pos.addReceiptPayment"></a>
|
||||
|
||||
### pos.addReceiptPayment(payment)
|
||||
Add a payment to the current transaction/receipt.
|
||||
|
||||
**Kind**: static method of [<code>pos</code>](#pos)
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| payment | <code>ReceiptPayment</code> |
|
||||
|
||||
<a name="pos.addOnscreenPaymentLog"></a>
|
||||
|
||||
### pos.addOnscreenPaymentLog(msg)
|
||||
Append a line of text to the onscreen log displayed during credit card processing.
|
||||
Not shown in kiosk mode.
|
||||
|
||||
**Kind**: static method of [<code>pos</code>](#pos)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| msg | <code>string</code> | Line of text to add to the log. |
|
||||
|
||||
<a name="pos.getReceiptID"></a>
|
||||
|
||||
### pos.getReceiptID() ⇒ <code>string</code>
|
||||
Get the unique alphanumeric ID for the current transaction/receipt.
|
||||
This is the same code printed on receipts and used in digital receipt URLs.
|
||||
|
||||
**Kind**: static method of [<code>pos</code>](#pos)
|
||||
<a name="pos.onReceiptChange"></a>
|
||||
|
||||
### pos.onReceiptChange(f)
|
||||
Specify a function to be called whenever the transaction data/receipt is changed.
|
||||
It is passed a single argument, a Receipt object containing the entire transaction so far.
|
||||
|
||||
**Kind**: static method of [<code>pos</code>](#pos)
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| f | <code>function</code> |
|
||||
|
||||
<a name="pos.onTransactionFinished"></a>
|
||||
|
||||
### ~~pos.onTransactionFinished(f)~~
|
||||
***Deprecated***
|
||||
|
||||
The supplied function will be called when a transaction is finished.
|
||||
It is passed a single argument, a Receipt object containing the entire transaction.
|
||||
Recommended to listen for the `transactionFinished` event instead.
|
||||
|
||||
**Kind**: static method of [<code>pos</code>](#pos)
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| f | <code>function</code> |
|
||||
|
||||
<a name="pos.registerCardProcessor"></a>
|
||||
|
||||
### pos.registerCardProcessor(f)
|
||||
Register as a card payment processor.
|
||||
|
||||
**Kind**: static method of [<code>pos</code>](#pos)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| f | <code>Object</code> | Payment processor functions |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
global.apis.pos.registerCardProcessor({
|
||||
name: "Demo Card Processor", // Shown in PostalPoint settings menu
|
||||
init: async function () {
|
||||
// This will run after PostalPoint launches
|
||||
// and before any payments are processed.
|
||||
// In some situations it might run multiple times in a session.
|
||||
},
|
||||
checkout: async function ({amount, capture = true}) {
|
||||
// Charge a credit card using a card reader device.
|
||||
// amount is in pennies (or the equivalent base unit in the local currency).
|
||||
|
||||
// Add a payment to the receipt with the total amount paid, card details, etc.
|
||||
global.apis.pos.addReceiptPayment(
|
||||
new global.apis.pos.ReceiptPayment(
|
||||
global.apis.i18n.currencyMinorToMajor(amount),
|
||||
"card", // Payment type. Accepted values are card, ach, crypto, cash,
|
||||
// check, account, and free. Other types will be displayed as-is.
|
||||
"Demo Card\nCardholder Name, etc\nMore info for receipt" // Additional text for receipt
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
// Must return boolean false if the payment failed.
|
||||
// Otherwise it will be assumed it succeeded.
|
||||
// If an error is encountered, handle it and return false.
|
||||
// It's recommended to display a short "payment failed" error
|
||||
// message via global.apis.alert, and outputting more details
|
||||
// via global.apis.pos.addOnscreenPaymentLog.
|
||||
|
||||
// If capture is false, perform an authorization but don't capture,
|
||||
// and return a value you can use to identify the authorization later
|
||||
// and complete it. The value will be passed back to finishPayment, below.
|
||||
// This is used mainly for the self-serve kiosk mode, in case the label fails
|
||||
// to be purchased/generated by the carrier.
|
||||
},
|
||||
cancelCheckout: function () {
|
||||
// The user has requested the card transaction be canceled before it completes.
|
||||
// Reset the terminal to its resting state, clear its screen, etc.
|
||||
},
|
||||
finishPayment: async function ({checkoutResponse}) {
|
||||
// Finish a payment that was authorized but not captured
|
||||
// because checkout() was called with capture = false.
|
||||
// If payment was already captured and added
|
||||
// to the receipt, just return true.
|
||||
global.apis.pos.addReceiptPayment(
|
||||
new global.apis.pos.ReceiptPayment(
|
||||
global.apis.i18n.currencyMinorToMajor(amount),
|
||||
"card",
|
||||
"Demo Card\nCardholder Name, etc\nMore info for receipt"
|
||||
)
|
||||
);
|
||||
},
|
||||
updateCartDisplay: function (receipt) {
|
||||
// Show transaction data on the card reader display.
|
||||
// This function is called when the "cart" or total changes.
|
||||
// `receipt` is a receipt object, see docs for details.
|
||||
},
|
||||
checkoutSavedMethod: async function ({customerID, paymentMethodID, amount}) {
|
||||
// Same as checkout() except using a payment method already on file.
|
||||
// customerID and paymentMethodID are provided by getSavedPaymentMethods below.
|
||||
|
||||
// Must return true upon success.
|
||||
// If the payment is not successful, and you didn't throw an Error to show the user,
|
||||
// then `return false` instead and it'll appear that the user's action to start the payment did nothing.
|
||||
return true;
|
||||
},
|
||||
saveCardForOfflineUse: async function ({statusCallback, customerUUID, name,
|
||||
company, street1, street2, city, state, zip, country, email, phone}) {
|
||||
// Use the card reader to capture an in-person card and save it for offline use.
|
||||
// Provided details are the customer's info, which might be empty strings except for the customerUUID.
|
||||
// Saved card details must be tied to the customerUUID, as that's how saved cards are looked up.
|
||||
|
||||
// statusCallback(string, boolean) updates the progress message on the cashier's screen.
|
||||
// If the boolean is true, the progress message is replaced with a confirmation message.
|
||||
statusCallback("Saving card details...", false);
|
||||
|
||||
return true; // Card saved to customer
|
||||
// If an error occurred, you can throw it and the error
|
||||
// message will be displayed to the cashier.
|
||||
// Alternatively, return boolean false and display the error
|
||||
// yourself with global.apis.alert(message, title) or something.
|
||||
},
|
||||
cancelSaveCardForOfflineUse: function () {
|
||||
// Cancel the process running in saveCardForOfflineUse() at the user/cashier's request.
|
||||
},
|
||||
getSavedPaymentMethods: async function ({customerUUID}) {
|
||||
// Return all saved payment methods tied to the provided customer UUID.
|
||||
return [{
|
||||
customer: "<internal string referencing the customer>", // Passed to checkoutSavedMethod as customerID
|
||||
customer_uuid: customerUUID,
|
||||
id: "<card/payment method identifier>", // Passed to checkoutSavedMethod as paymentMethodID
|
||||
type: "card", // Payment type. Accepted values are card, ach, crypto, cash, check, account, and free.
|
||||
label: "Visa debit x1234 (exp. 12/29)", // Label for payment method
|
||||
label_short: "Visa debit x1234" // Abbreviated label for payment method
|
||||
}];
|
||||
},
|
||||
deleteSavedPaymentMethod: async function ({customerUUID, customerID, paymentMethodID}) {
|
||||
// Delete the payment method identified by paymentMethodID
|
||||
// and tied to the PostalPoint customerUUID and the card processor customerID.
|
||||
// If unable to delete, throw an error and the error message
|
||||
// will be displayed to the cashier.
|
||||
}
|
||||
});
|
||||
```
|
||||
<a name="pos.registerCryptoProcessor"></a>
|
||||
|
||||
### pos.registerCryptoProcessor(f)
|
||||
Register as a cryptocurrency payment processor.
|
||||
|
||||
**Kind**: static method of [<code>pos</code>](#pos)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| f | <code>Object</code> | Payment processor functions |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
global.apis.pos.registerCryptoProcessor({
|
||||
name: "Demo Crypto", // Shown in PostalPoint settings menu
|
||||
init: async function () {
|
||||
// This is run after PostalPoint starts,
|
||||
// and before any other crypto functions are called.
|
||||
},
|
||||
checkout: async function ({amount}) {
|
||||
// Run the checkout process.
|
||||
// amount is the amount of fiat currency to collect,
|
||||
// in pennies (or the local equivalent).
|
||||
|
||||
// If an error is encountered during processing,
|
||||
// display an error message in a dialog and return boolean false.
|
||||
// If this function returns anything except false or undefined,
|
||||
// and doesn't throw an error,
|
||||
// it is assumed the payment was successful.
|
||||
|
||||
// Adds a line of text visible to the cashier
|
||||
global.apis.pos.addOnscreenPaymentLog("Getting crypto payment...");
|
||||
|
||||
// Display a web page (i.e. with a payment QR code)
|
||||
// to the customer on the customer-facing display.
|
||||
global.apis.ui.setCustomerScreen("<html></html>", "html");
|
||||
global.apis.ui.setCustomerScreen("https://postalpoint.app", "raw");
|
||||
|
||||
// Poll the status of the crypto transaction
|
||||
var paymentComplete = false;
|
||||
do {
|
||||
await global.apis.util.delay(1000);
|
||||
paymentComplete = true;
|
||||
} while (paymentComplete != true);
|
||||
|
||||
global.apis.pos.addReceiptPayment(
|
||||
new global.apis.pos.ReceiptPayment(
|
||||
global.apis.i18n.currencyMinorToMajor(amount),
|
||||
"crypto", // Payment type.
|
||||
"Bitcoin\n0.00001234 BTC" // Additional text for receipt
|
||||
)
|
||||
);
|
||||
global.apis.pos.addOnscreenPaymentLog("Payment successful!");
|
||||
global.apis.ui.clearCustomerScreen();
|
||||
},
|
||||
cancelCheckout: function () {
|
||||
// The user requested to cancel the payment.
|
||||
// Reset things accordingly.
|
||||
global.apis.ui.clearCustomerScreen();
|
||||
},
|
||||
isConfigured: function () {
|
||||
// Is this plugin properly setup
|
||||
// and able to process payments?
|
||||
// If not, return false.
|
||||
// This determines if the crypto payment method button will be shown.
|
||||
return true;
|
||||
}
|
||||
});
|
||||
```
|
||||
<a name="pos.getShippingSalesTax"></a>
|
||||
|
||||
### pos.getShippingSalesTax() ⇒ <code>Object</code>
|
||||
Get the sales tax percentage to charge on a shipping service ReceiptItem.
|
||||
|
||||
**Kind**: static method of [<code>pos</code>](#pos)
|
||||
**Returns**: <code>Object</code> - `{type: "", percent: 0.15}`
|
||||
`type` is an empty string for taxing the entire price, or "markup" for only adding tax to the markup amount.
|
||||
`percent` is the tax percentage. A value of 0.15 means a 15% tax.
|
||||
55
docs/Plugin API/print.md
Normal file
55
docs/Plugin API/print.md
Normal file
@ -0,0 +1,55 @@
|
||||
<a name="print"></a>
|
||||
|
||||
## print : <code>object</code>
|
||||
Printing to connected printers
|
||||
|
||||
**Kind**: global namespace
|
||||
|
||||
* [print](#print) : <code>object</code>
|
||||
* [.printLabelImage(image)](#print.printLabelImage)
|
||||
* [.getReceiptPrinter()](#print.getReceiptPrinter) ⇒ <code>Promise.<Object></code>
|
||||
* [.printReceiptData(data)](#print.printReceiptData)
|
||||
* [.imageToBitmap(jimpImage, [dpiFrom], [dpiTo])](#print.imageToBitmap) ⇒ <code>Object</code>
|
||||
|
||||
<a name="print.printLabelImage"></a>
|
||||
|
||||
### print.printLabelImage(image)
|
||||
Print a 300 DPI image on the shipping label printer, centered on a 4x6 inch label. Image is automatically scaled to 200 DPI if required by the printer.
|
||||
|
||||
**Kind**: static method of [<code>print</code>](#print)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| image | <code>ArrayBuffer</code> \| <code>Buffer</code> \| <code>Uint8Array</code> \| <code>string</code> \| <code>Jimp</code> | image data, as a Jimp image object, raw PNG bytes, or a URL (http/https) string. 1200x1800 or 800x1200 images are scaled to 4x6 inches. Other image sizes are assumed to be 300 DPI and are centered on the shipping label. Image orientation is rotated to match the label orientation. |
|
||||
|
||||
<a name="print.getReceiptPrinter"></a>
|
||||
|
||||
### print.getReceiptPrinter() ⇒ <code>Promise.<Object></code>
|
||||
Get the receipt printer interface. See the ReceiptPrinter docs for available functions.
|
||||
|
||||
**Kind**: static method of [<code>print</code>](#print)
|
||||
<a name="print.printReceiptData"></a>
|
||||
|
||||
### print.printReceiptData(data)
|
||||
Send raw data (generated by the printer interface) to the receipt printer.
|
||||
|
||||
**Kind**: static method of [<code>print</code>](#print)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| data | <code>string</code> \| <code>Uint8Array</code> \| <code>Array.<string></code> \| <code>Array.<Uint8Array></code> | Data to send to printer. |
|
||||
|
||||
<a name="print.imageToBitmap"></a>
|
||||
|
||||
### print.imageToBitmap(jimpImage, [dpiFrom], [dpiTo]) ⇒ <code>Object</code>
|
||||
Convert a Jimp image object to 1-bit monochrome image data before sending image data to a printer interface. Optionally scales the image to a different DPI before conversion.
|
||||
|
||||
**Kind**: static method of [<code>print</code>](#print)
|
||||
**Returns**: <code>Object</code> - - Example: `{width: 300, height: 200, img: Uint8Array}`. Pass `img` to `drawImage` on a printer interface.
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| jimpImage | <code>Jimp</code> | | |
|
||||
| [dpiFrom] | <code>number</code> | <code>300</code> | Original image DPI. |
|
||||
| [dpiTo] | <code>number</code> | <code>300</code> | New image DPI. |
|
||||
|
||||
70
docs/Plugin API/reports.md
Normal file
70
docs/Plugin API/reports.md
Normal file
@ -0,0 +1,70 @@
|
||||
<a name="reports"></a>
|
||||
|
||||
## reports : <code>object</code>
|
||||
Define custom reports for the user.
|
||||
|
||||
**Kind**: global namespace
|
||||
<a name="reports.registerReport"></a>
|
||||
|
||||
### reports.registerReport(name, onload(startDate,endDate), date)
|
||||
**Kind**: static method of [<code>reports</code>](#reports)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| name | <code>string</code> | Report name |
|
||||
| onload(startDate,endDate) | <code>function</code> | Called when the report is loaded, with start and end Date objects. See example. |
|
||||
| date | <code>boolean</code> | If the report requires a date range be selected. |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
global.apis.reports.registerReport("sample", function (startDate, endDate) {
|
||||
|
||||
// Note about column datatypes:
|
||||
// Use "string" for the datatype, except for the special cases listed here.
|
||||
// Other datatypes may be added in the future, so use "string"
|
||||
// unless you like unexpected behavior!
|
||||
//
|
||||
// datetime: Column is a UNIX timestamp (in seconds).
|
||||
// It is displayed as a formatted date and time string.
|
||||
// receiptid: Column is a PostalPoint receipt ID number. Displayed as a link.
|
||||
// Clicking the ID will fetch and open the receipt in a popup.
|
||||
// userid: Column contains an employee ID number from the PostalPoint database.
|
||||
// It is queried in the database and replaced with the employee's name,
|
||||
// or with an empty string if the ID lookup has no results.
|
||||
// money: Column is a number that will be formatted as currency for display.
|
||||
// percent: Column is a percent value (as 12.3, not .123) and will be formatted
|
||||
// with a trailing % sign and rounded to two decimal places.
|
||||
|
||||
// Single-table report
|
||||
return {
|
||||
table: {
|
||||
header: ["Column 1", "Column 2"],
|
||||
datatypes: ["string", "string"],
|
||||
rows: [
|
||||
["Row 1 Col 1", "Row 1 Col 2"],
|
||||
["Row 2 Col 1", "Row 2 Col 2"]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
// Multiple-table report
|
||||
return {
|
||||
multitable: true,
|
||||
table: {
|
||||
titles: ["Report 1 Title", "Report 2 Title"],
|
||||
header: [["Report 1 Column 1", "Report 1 Column 2"], ["Report 2 Column 1", ...]],
|
||||
datatypes: [["string", "string"], ["string", "string"]],
|
||||
rows: [
|
||||
[
|
||||
["Report 1 Row 1 Col 1", "Report 1 Row 1 Col 2"],
|
||||
["Report 1 Row 2 Col 1", "Report 1 Row 2 Col 2"]
|
||||
],
|
||||
[
|
||||
["Report 2 Row 1 Col 1", "Report 2 Row 1 Col 2"],
|
||||
["Report 2 Row 2 Col 1", "Report 2 Row 2 Col 2"]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
```
|
||||
40
docs/Plugin API/settings.md
Normal file
40
docs/Plugin API/settings.md
Normal file
@ -0,0 +1,40 @@
|
||||
<a name="settings"></a>
|
||||
|
||||
## settings : <code>object</code>
|
||||
PostalPoint provides a UI for user-configurable plugin settings.
|
||||
See exports.config in examples/basic-demo/plugin.js for details.
|
||||
Settings are typically very short strings. Do not store data in settings.
|
||||
For data storage, see Storing Data. Non-string settings values are transparently converted to/from JSON objects.
|
||||
Use a unique key name prefix for your plugin to prevent key name conflicts.
|
||||
Reverse domain style is recommended (i.e. "com.example.pluginname.keyname").
|
||||
|
||||
**Kind**: global namespace
|
||||
|
||||
* [settings](#settings) : <code>object</code>
|
||||
* [.get(key, defaultValue)](#settings.get) ⇒ <code>\*</code>
|
||||
* [.set(key, value)](#settings.set)
|
||||
|
||||
<a name="settings.get"></a>
|
||||
|
||||
### settings.get(key, defaultValue) ⇒ <code>\*</code>
|
||||
Get a setting.
|
||||
|
||||
**Kind**: static method of [<code>settings</code>](#settings)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| key | <code>string</code> | Setting key/ID |
|
||||
| defaultValue | <code>\*</code> | Value to return if setting has no stored value. |
|
||||
|
||||
<a name="settings.set"></a>
|
||||
|
||||
### settings.set(key, value)
|
||||
Set a setting.
|
||||
|
||||
**Kind**: static method of [<code>settings</code>](#settings)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| key | <code>string</code> | Setting key/ID |
|
||||
| value | <code>string</code> | Value to set. |
|
||||
|
||||
243
docs/Plugin API/shipping.md
Normal file
243
docs/Plugin API/shipping.md
Normal file
@ -0,0 +1,243 @@
|
||||
<a name="shipping"></a>
|
||||
|
||||
## shipping : <code>object</code>
|
||||
Add custom carrier and rates, and adjust markup.
|
||||
|
||||
**Kind**: global namespace
|
||||
|
||||
* [shipping](#shipping) : <code>object</code>
|
||||
* [.Address](#shipping.Address)
|
||||
* [new Address()](#new_shipping.Address_new)
|
||||
* [.getZIPCode(zipcode, country)](#shipping.getZIPCode) ⇒ <code>Object</code>
|
||||
* [.getPackagingByID(id)](#shipping.getPackagingByID) ⇒ <code>Promise.<Object></code>
|
||||
* [.getRetailPriceWithMarkup(cost, retail, carrier, service, weightOz, packaging)](#shipping.getRetailPriceWithMarkup) ⇒ <code>Promise.<number></code>
|
||||
* [.getCarrierName(carrierId)](#shipping.getCarrierName) ⇒ <code>string</code>
|
||||
* [.getServiceName(serviceId, carrier)](#shipping.getServiceName) ⇒ <code>string</code>
|
||||
* [.registerRateEndpoint(getRates, purchase, idPrefix)](#shipping.registerRateEndpoint)
|
||||
* [.registerMarkupCalculator(markupFn)](#shipping.registerMarkupCalculator)
|
||||
|
||||
<a name="shipping.Address"></a>
|
||||
|
||||
### shipping.Address
|
||||
**Kind**: static class of [<code>shipping</code>](#shipping)
|
||||
<a name="new_shipping.Address_new"></a>
|
||||
|
||||
#### new Address()
|
||||
A class representing an address.
|
||||
|
||||
<a name="shipping.getZIPCode"></a>
|
||||
|
||||
### shipping.getZIPCode(zipcode, country) ⇒ <code>Object</code>
|
||||
Get data for a ZIP Code.
|
||||
|
||||
**Kind**: static method of [<code>shipping</code>](#shipping)
|
||||
**Returns**: <code>Object</code> - Data about the ZIP code. See example. Fields may be empty if not available. Type may be "STANDARD", "UNIQUE", "PO BOX", or "MILITARY".
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| zipcode | <code>string</code> | | ZIP or postal code. |
|
||||
| country | <code>string</code> | <code>"US"</code> | Currently only "US" and "CA" are supported. |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
{city: "NEW YORK", state: "NY", type: "STANDARD"}
|
||||
```
|
||||
<a name="shipping.getPackagingByID"></a>
|
||||
|
||||
### shipping.getPackagingByID(id) ⇒ <code>Promise.<Object></code>
|
||||
Get a parcel's packaging type from PostalPoint's internal ID for it.
|
||||
|
||||
**Kind**: static method of [<code>shipping</code>](#shipping)
|
||||
**Returns**: <code>Promise.<Object></code> - See examples.
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| id | <code>number</code> |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
{
|
||||
id: 100,
|
||||
type: "Parcel",
|
||||
img: "box.png",
|
||||
name: "Box",
|
||||
service: "",
|
||||
l: -1,
|
||||
w: -1,
|
||||
h: -1,
|
||||
weight: true,
|
||||
hazmat: true,
|
||||
source: "Customer"
|
||||
}
|
||||
```
|
||||
**Example**
|
||||
```js
|
||||
{
|
||||
id: 1,
|
||||
type: "FlatRateEnvelope",
|
||||
img: "pm-fres.png",
|
||||
name: "Flat Rate Envelope",
|
||||
service: "Priority",
|
||||
l: -2,
|
||||
w: -2,
|
||||
h: -2,
|
||||
weight: false,
|
||||
hazmat: true,
|
||||
usps_supplied: true,
|
||||
envelope: true,
|
||||
source: "USPS",
|
||||
skus: ["PS00001000014", "PS00001000012", "PS00001000027", "PS00001000064", "PS00001001921", "PS00001035000", "PS00001036014", "PS00001128600", "https://qr.usps.com/epsspu?p=30", "https://qr.usps.com/epsspu?p=8"]
|
||||
}
|
||||
```
|
||||
**Example**
|
||||
```js
|
||||
{
|
||||
id: 201,
|
||||
type: "UPSLetter",
|
||||
img: "ups-env.png",
|
||||
name: "Envelope",
|
||||
carrier: "UPS",
|
||||
l: -2,
|
||||
w: -2,
|
||||
h: -2,
|
||||
weight: true,
|
||||
hazmat: true,
|
||||
source: "OtherCarrier"
|
||||
}
|
||||
```
|
||||
<a name="shipping.getRetailPriceWithMarkup"></a>
|
||||
|
||||
### shipping.getRetailPriceWithMarkup(cost, retail, carrier, service, weightOz, packaging) ⇒ <code>Promise.<number></code>
|
||||
Calculate the retail price for a shipment rate based on the configured margin settings.
|
||||
|
||||
**Kind**: static method of [<code>shipping</code>](#shipping)
|
||||
**Returns**: <code>Promise.<number></code> - The amount to charge the customer
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| cost | <code>number</code> | Cost of shipment to business |
|
||||
| retail | <code>number</code> | Default retail price from label provider |
|
||||
| carrier | <code>string</code> | Shipment carrier |
|
||||
| service | <code>string</code> | Shipment service |
|
||||
| weightOz | <code>number</code> | The weight of the shipment in ounces, or null if not available. |
|
||||
| packaging | <code>string</code> | An empty string if not available, or "Letter", "FlatRateEnvelope", etc. |
|
||||
|
||||
<a name="shipping.getCarrierName"></a>
|
||||
|
||||
### shipping.getCarrierName(carrierId) ⇒ <code>string</code>
|
||||
Converts the carrier ID string into a consistent and human-readable name.
|
||||
|
||||
**Kind**: static method of [<code>shipping</code>](#shipping)
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| carrierId | <code>string</code> |
|
||||
|
||||
<a name="shipping.getServiceName"></a>
|
||||
|
||||
### shipping.getServiceName(serviceId, carrier) ⇒ <code>string</code>
|
||||
Converts the service ID string into a consistent and human-readable name. Set the carrier ID for better results.
|
||||
|
||||
**Kind**: static method of [<code>shipping</code>](#shipping)
|
||||
|
||||
| Param | Type | Default |
|
||||
| --- | --- | --- |
|
||||
| serviceId | <code>string</code> | |
|
||||
| carrier | <code>string</code> | <code>"USPS"</code> |
|
||||
|
||||
<a name="shipping.registerRateEndpoint"></a>
|
||||
|
||||
### shipping.registerRateEndpoint(getRates, purchase, idPrefix)
|
||||
Register the plugin as a shipping rate and label provider. See the Shipping example plugin.
|
||||
|
||||
**Kind**: static method of [<code>shipping</code>](#shipping)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| getRates | <code>function</code> | A function passed a Parcel object to get rates for. Returns a Promise that resolves to an array of rate objects. |
|
||||
| purchase | <code>function</code> | A function passed a rate ID to purchase. Returns a Promise that resolves to the label information. |
|
||||
| idPrefix | <code>string</code> | A unique string that will be prefixing all rate IDs from this plugin. |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
// getRates sample return value:
|
||||
[{
|
||||
rateid: `${idPrefix}_${global.apis.util.uuid.v4()}`,
|
||||
carrier: "CarrierID",
|
||||
carrierName: "Carrier Name",
|
||||
service: "CARRIER_SERVICE_ID",
|
||||
cost_rate: 10,
|
||||
retail_rate: 15,
|
||||
delivery_days: 3,
|
||||
delivery_date: null,
|
||||
guaranteed: true,
|
||||
serviceName: "Service Name",
|
||||
color: "green" // Rate card color
|
||||
}]
|
||||
```
|
||||
**Example**
|
||||
```js
|
||||
// purchase sample return value:
|
||||
{
|
||||
label: labelImageToPrint,
|
||||
labeltype: "PNG",
|
||||
receiptItem: ReceiptItem,
|
||||
tracking: "12345678901234567890",
|
||||
cost: 10.0,
|
||||
price: 15.0,
|
||||
carrier: "Carrier Name",
|
||||
service: "Service Name",
|
||||
delivery_days: 3,
|
||||
delivery_date: 1234567890, // UNIX timestamp
|
||||
to: toAddressLines // Array of strings
|
||||
}
|
||||
```
|
||||
<a name="shipping.registerMarkupCalculator"></a>
|
||||
|
||||
### shipping.registerMarkupCalculator(markupFn)
|
||||
Register the plugin to modify PostalPoint's shipping markup calculation during shipment rating.
|
||||
|
||||
**Kind**: static method of [<code>shipping</code>](#shipping)
|
||||
**Throws**:
|
||||
|
||||
- <code>Error</code> Only one plugin may register with this function;
|
||||
any subsequent attempts to register will throw an Error.
|
||||
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| markupFn | <code>function</code> | A function that must return either the retail price to charge for this rate, or `false` to opt-out of setting this particular rate. |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
global.apis.shipping.registerMarkupCalculator(
|
||||
// Parameters:
|
||||
// cost: Cost to shipper
|
||||
// retail: Carrier-suggested retail price
|
||||
// suggested: PostalPoint-suggested retail (default margin calc)
|
||||
// carrier: Shipping carrier name
|
||||
// service: Shipping service code
|
||||
// weightOz: The weight of the shipment in ounces, or null if not available.
|
||||
// packaging: An empty string if not available, or "Letter", "FlatRateEnvelope", etc. See https://docs.easypost.com/docs/parcels#predefined-package
|
||||
// parcel: The Parcel object for this shipment. May be null for some rate-only requests without a shipment, such as USPS price calculations.
|
||||
function (cost, retail, suggested, carrier, service, weightOz, packaging, parcel) {
|
||||
if (carrier == "USPS") {
|
||||
if (service == "First-Class Mail") {
|
||||
// Handle First-Class Mail differently if it's a 1oz letter (i.e. Forever stamp)
|
||||
if (weightOz <= 1 && packaging == "Letter") {
|
||||
return retail + 0.05;
|
||||
} else {
|
||||
return retail + 0.25;
|
||||
}
|
||||
}
|
||||
// Handle flat rate envelopes differently
|
||||
if (global.apis.shipping.getServiceName(service, carrier) == "Priority Mail" && packaging == "FlatRateEnvelope") {
|
||||
return retail + 1.0;
|
||||
}
|
||||
return suggested + 2.0; // Charge the PostalPoint-calculated amount plus $2
|
||||
} else {
|
||||
return cost * 2; // Charges the customer double the shipment's cost.
|
||||
}
|
||||
}
|
||||
);
|
||||
```
|
||||
87
docs/Plugin API/storage.md
Normal file
87
docs/Plugin API/storage.md
Normal file
@ -0,0 +1,87 @@
|
||||
<a name="storage"></a>
|
||||
|
||||
## storage : <code>object</code>
|
||||
Get and set data.
|
||||
|
||||
**Kind**: global namespace
|
||||
|
||||
* [storage](#storage) : <code>object</code>
|
||||
* [.getSmall(key, defaultValue)](#storage.getSmall) ⇒ <code>\*</code>
|
||||
* [.setSmall(key, value)](#storage.setSmall)
|
||||
* [.getBig(key, defaultValue)](#storage.getBig)
|
||||
* [.setBig(key, value)](#storage.setBig)
|
||||
* [.getDB(key, defaultValue)](#storage.getDB) ⇒ <code>Promise.<\*></code>
|
||||
* [.setDB(key, value)](#storage.setDB) ⇒ <code>Promise</code>
|
||||
|
||||
<a name="storage.getSmall"></a>
|
||||
|
||||
### storage.getSmall(key, defaultValue) ⇒ <code>\*</code>
|
||||
Get a value from the small data storage, using localStorage or a similar mechanism (may change in the future).
|
||||
|
||||
**Kind**: static method of [<code>storage</code>](#storage)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| key | <code>string</code> | Storage item key/ID |
|
||||
| defaultValue | <code>\*</code> | Value to return if the item key doesn't have a stored value. |
|
||||
|
||||
<a name="storage.setSmall"></a>
|
||||
|
||||
### storage.setSmall(key, value)
|
||||
Set a value in the small data storage, using localStorage or a similar mechanism (may change in the future).
|
||||
|
||||
**Kind**: static method of [<code>storage</code>](#storage)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| key | <code>string</code> | Storage item key/ID |
|
||||
| value | <code>\*</code> | Value to store. |
|
||||
|
||||
<a name="storage.getBig"></a>
|
||||
|
||||
### storage.getBig(key, defaultValue)
|
||||
Get a value in the large data storage. Unserialized from a JSON file on disk.
|
||||
|
||||
**Kind**: static method of [<code>storage</code>](#storage)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| key | <code>string</code> | Storage item key/ID |
|
||||
| defaultValue | <code>\*</code> | Value to return if the item key doesn't have a stored value. |
|
||||
|
||||
<a name="storage.setBig"></a>
|
||||
|
||||
### storage.setBig(key, value)
|
||||
Set a value in the large data storage. Serialized to JSON and stored on disk as a file.
|
||||
|
||||
**Kind**: static method of [<code>storage</code>](#storage)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| key | <code>string</code> | Storage item key/ID |
|
||||
| value | <code>\*</code> | Value to store. |
|
||||
|
||||
<a name="storage.getDB"></a>
|
||||
|
||||
### storage.getDB(key, defaultValue) ⇒ <code>Promise.<\*></code>
|
||||
Get a value from the database storage. Unlike other storage types, values in the database are available on all PostalPoint installations in a single location.
|
||||
|
||||
**Kind**: static method of [<code>storage</code>](#storage)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| key | <code>string</code> | Storage item key/ID |
|
||||
| defaultValue | <code>\*</code> | Value to return if the item key doesn't have a stored value. |
|
||||
|
||||
<a name="storage.setDB"></a>
|
||||
|
||||
### storage.setDB(key, value) ⇒ <code>Promise</code>
|
||||
Set a value in the database storage. Non-string values are serialized to JSON. Unlike other storage types, values in the database are available on all PostalPoint installations in a single location.
|
||||
|
||||
**Kind**: static method of [<code>storage</code>](#storage)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| key | <code>string</code> | Storage item key/ID |
|
||||
| value | <code>\*</code> | Value to store. |
|
||||
|
||||
291
docs/Plugin API/ui.md
Normal file
291
docs/Plugin API/ui.md
Normal file
@ -0,0 +1,291 @@
|
||||
<a name="ui"></a>
|
||||
|
||||
## ui : <code>object</code>
|
||||
Interact with and modify the PostalPoint user interface.
|
||||
|
||||
**Kind**: global namespace
|
||||
|
||||
* [ui](#ui) : <code>object</code>
|
||||
* [.addToolsPage(page, title, id, description, cardTitle, icon, type)](#ui.addToolsPage)
|
||||
* [.addHomeTab(content, title, icon, id)](#ui.addHomeTab) ⇒ <code>undefined</code>
|
||||
* [.showProgressSpinner(title, text, subtitle)](#ui.showProgressSpinner) ⇒ <code>undefined</code>
|
||||
* [.hideProgressSpinner()](#ui.hideProgressSpinner)
|
||||
* [.openSystemWebBrowser(url)](#ui.openSystemWebBrowser)
|
||||
* [.openInternalWebBrowser(url)](#ui.openInternalWebBrowser)
|
||||
* [.clearCustomerScreen()](#ui.clearCustomerScreen)
|
||||
* [.setCustomerScreen(content, type, displayInCard, cardSize, displayStatusBar)](#ui.setCustomerScreen)
|
||||
* [.collectSignatureFromCustomerScreen(title, terms, termstype)](#ui.collectSignatureFromCustomerScreen)
|
||||
* [.cancelSignatureCollection()](#ui.cancelSignatureCollection)
|
||||
* [.clearSignaturePad()](#ui.clearSignaturePad)
|
||||
* [.getCustomerDisplayInfo()](#ui.getCustomerDisplayInfo) ⇒ <code>Object</code>
|
||||
|
||||
<a name="ui.addToolsPage"></a>
|
||||
|
||||
### ui.addToolsPage(page, title, id, description, cardTitle, icon, type)
|
||||
Add a page to the Tools screen. If type is set to "function", the page argument
|
||||
will be run as a function and will not be expected to return a page component.
|
||||
[Framework7 Page Component documentation](https://framework7.io/docs/router-component#single-file-component)
|
||||
|
||||
**Kind**: static method of [<code>ui</code>](#ui)
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| page | <code>string</code> \| <code>function</code> | | Page content as a Framework7 component page. If `page` is a string ending in `.f7` it is treated as a file path and the page content will be loaded from disk. If `page` is any other string, it is treated as the page content. If `page` is a function, it will be called and must return the page content (unless `type` is set to `"function"`, see examples) |
|
||||
| title | <code>string</code> | | Page title. |
|
||||
| id | <code>string</code> | | Page ID. Make it unique, or pass an empty string to be assigned a random ID. |
|
||||
| description | <code>string</code> | | Description of this tool for its card on the Tools screen. |
|
||||
| cardTitle | <code>string</code> | | Title of the card for this page on the Tools screen. |
|
||||
| icon | <code>string</code> | | FontAwesome icon class, for example, "fa-solid fa-globe". FontAwesome Pro solid, regular, light, and duotone icons are available. |
|
||||
| type | <code>string</code> | <code>"page"</code> | What kind of data is supplied by `page`: "page" or "function". |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
// Full plugin that displays an alert when the card is clicked on the Tools page
|
||||
exports.init = function () {
|
||||
global.apis.ui.addToolsPage(
|
||||
displayMessage,
|
||||
"click me",
|
||||
"clickmecard",
|
||||
"Click here to see a message",
|
||||
"Click This Card",
|
||||
"fa-solid fa-arrow-pointer",
|
||||
"function"
|
||||
);
|
||||
}
|
||||
|
||||
function displayMessage() {
|
||||
global.apis.alert("Card clicked");
|
||||
}
|
||||
```
|
||||
**Example**
|
||||
```js
|
||||
// Open a dynamically-generated page
|
||||
function rollDice() {
|
||||
var randomNumber = Math.round(Math.random() * 6) + 1;
|
||||
return `<div class="page">
|
||||
<div class="navbar">
|
||||
<div class="navbar-bg"></div>
|
||||
<div class="navbar-inner">
|
||||
<a href="#" class="link back">
|
||||
<i class="icon icon-back"></i>
|
||||
</a>
|
||||
<div class="title">Random Number</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-content">
|
||||
<div class="block">
|
||||
<h1>You rolled ${randomNumber}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
global.apis.ui.addToolsPage(
|
||||
rollDice,
|
||||
"Random",
|
||||
"randomnumbercard",
|
||||
"Click here for a random number",
|
||||
"Random Number",
|
||||
"fa-regular fa-dice",
|
||||
"page"
|
||||
);
|
||||
```
|
||||
**Example**
|
||||
```js
|
||||
// Open a page from a file.
|
||||
// See https://framework7.io/docs/router-component#single-file-component
|
||||
global.apis.ui.addToolsPage(
|
||||
global.apis.getPluginFolder("example-plugin") + "/page.f7",
|
||||
"Page",
|
||||
"filepage",
|
||||
"Open page.f7 from the plugin installation folder",
|
||||
"Open Custom Page",
|
||||
"fa-regular fa-file",
|
||||
"page"
|
||||
);
|
||||
```
|
||||
<a name="ui.addHomeTab"></a>
|
||||
|
||||
### ui.addHomeTab(content, title, icon, id) ⇒ <code>undefined</code>
|
||||
Add a custom tab to the PostalPoint home screen. Works almost the same as addToolsPage.
|
||||
|
||||
**Kind**: static method of [<code>ui</code>](#ui)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| content | <code>string</code> \| <code>function</code> | Tab content. It is rendered/processed when the user navigates to the Home screen and clicks the tab; if the user navigates to a different screen and back to Home, it will be re-rendered. If `content` is a string ending in `.f7` it is treated as a file path and the content will be loaded from disk. If `content` is any other string, it is treated as the content. If `content` is a function, it will be called and must return the content. |
|
||||
| title | <code>string</code> | Tab title. Keep it short; depending on screen size and tab count, you have as little as 150px of space. |
|
||||
| icon | <code>string</code> | FontAwesome icon displayed above the tab title. |
|
||||
| id | <code>string</code> | Tab ID. Make it unique, or pass an empty string to be assigned a random ID. If addHomeTab is called with a tab ID that is already registered, it will be overwritten. |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
global.apis.ui.addHomeTab("<div class='block'>Hello</div>", "Hello Tab", "fa-duotone fa-hand-wave", "hellotab");
|
||||
```
|
||||
**Example**
|
||||
```js
|
||||
function renderTab() {
|
||||
return "<template><div><h1>${hellovar}</h1></div></template><script>export default (props, {$on, $update, $f7}) => {var hellovar = 'hello world'; return $render;}</script>";
|
||||
}
|
||||
global.apis.ui.addHomeTab(renderTab, "Hello Template", "fa-duotone fa-file-code", "hellotemplatetab");
|
||||
```
|
||||
<a name="ui.showProgressSpinner"></a>
|
||||
|
||||
### ui.showProgressSpinner(title, text, subtitle) ⇒ <code>undefined</code>
|
||||
Show a notification with a loading icon.
|
||||
|
||||
**Kind**: static method of [<code>ui</code>](#ui)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| title | <code>string</code> | The message to show on the spinner. |
|
||||
| text | <code>string</code> | Optional. Body text on the spinner. |
|
||||
| subtitle | <code>string</code> | Optional. Sub-heading under the `title`. |
|
||||
|
||||
<a name="ui.hideProgressSpinner"></a>
|
||||
|
||||
### ui.hideProgressSpinner()
|
||||
Close the notification opened by `showProgressSpinner`.
|
||||
|
||||
**Kind**: static method of [<code>ui</code>](#ui)
|
||||
<a name="ui.openSystemWebBrowser"></a>
|
||||
|
||||
### ui.openSystemWebBrowser(url)
|
||||
Open the native OS default browser to the URL given.
|
||||
|
||||
**Kind**: static method of [<code>ui</code>](#ui)
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| url | <code>string</code> |
|
||||
|
||||
<a name="ui.openInternalWebBrowser"></a>
|
||||
|
||||
### ui.openInternalWebBrowser(url)
|
||||
Open a web browser inside PostalPoint. The browser has forward/back/close buttons.
|
||||
|
||||
**Kind**: static method of [<code>ui</code>](#ui)
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| url | <code>string</code> |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
global.apis.ui.openInternalWebBrowser("https://postalpoint.app");
|
||||
global.apis.eventbus.on("browserNavigate", function (newUrl) {
|
||||
global.apis.alert(`New URL: ${newUrl}`, "Browser Navigated");
|
||||
if (newUrl == "https://closeme.com") {
|
||||
global.apis.eventbus.emit("browserCloseRequest");
|
||||
}
|
||||
});
|
||||
```
|
||||
<a name="ui.clearCustomerScreen"></a>
|
||||
|
||||
### ui.clearCustomerScreen()
|
||||
Clear any custom content on the customer-facing display, defaulting back to blank/receipt/shipping rates, as applicable.
|
||||
|
||||
**Kind**: static method of [<code>ui</code>](#ui)
|
||||
<a name="ui.setCustomerScreen"></a>
|
||||
|
||||
### ui.setCustomerScreen(content, type, displayInCard, cardSize, displayStatusBar)
|
||||
Render content on the customer-facing display.
|
||||
Encodes content as a data URI (example: `data:text/html;charset=utf-8,${content}`)
|
||||
and renders on the customer-facing display. If type is html, renders the string as HTML.
|
||||
If type is pdf, displays a PDF viewer. If type is raw, functions like setting an iframe's
|
||||
src to content. All other type values are rendered as text/plain.
|
||||
To display the iframe in a card centered on the screen, set displayInCard to true
|
||||
and pass the desired dimensions (w, h) of the card in px.
|
||||
If the requested size is larger than the available screen space, the card will instead
|
||||
fill the available space.
|
||||
Warning: Do not load third-party websites, this is a security risk.
|
||||
Wrap it in a <webview src="..."></webview> tag if you need to display one.
|
||||
|
||||
**Kind**: static method of [<code>ui</code>](#ui)
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| content | <code>string</code> | | Page content. |
|
||||
| type | <code>string</code> | <code>"html"</code> | Format of the `content`. One of "html", "pdf", "raw", "body", or "text". |
|
||||
| displayInCard | <code>boolean</code> | <code>false</code> | Set to true to wrap the content in a card UI. |
|
||||
| cardSize | <code>Array.<number></code> | <code>[300,300</code> | Size of the card UI if displayInCard == true. |
|
||||
| displayStatusBar | <code>boolean</code> | <code>true</code> | Whether the bar on the bottom of the screen should be visible, containing the store logo and scale weight. |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
// How content is converted by PostalPoint before display:
|
||||
if (type == "html") {
|
||||
customerScreenContent = `data:text/html;charset=utf-8,${content}`;
|
||||
} else if (type == "pdf") {
|
||||
customerScreenContent = `data:application/pdf,${content}`;
|
||||
} else if (type == "raw") {
|
||||
global.customerScreenContent = `${content}`;
|
||||
} else if (type == "body") {
|
||||
customerScreenContent = `data:text/html;charset=utf-8,<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<style>
|
||||
html, body {margin: 0; padding: 0; font-family: Roboto, Ubuntu, Arial, sans-serif;}
|
||||
h1, h2, h3 {margin: 0;}
|
||||
</style>
|
||||
<div id="maindiv">${content}</div>`;
|
||||
} else {
|
||||
customerScreenContent = `data:text/plain;charset=utf-8,${content}`;
|
||||
}
|
||||
```
|
||||
<a name="ui.collectSignatureFromCustomerScreen"></a>
|
||||
|
||||
### ui.collectSignatureFromCustomerScreen(title, terms, termstype)
|
||||
Show a signature pad on the customer-facing display.
|
||||
When the customer indicates the signature is finished,
|
||||
the customerSignatureCollected event is emitted with the data
|
||||
{"svg": "data:image/svg+xml;base64,...", "png": "data:image/png;base64,..."}
|
||||
|
||||
**Kind**: static method of [<code>ui</code>](#ui)
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| title | <code>string</code> | <code>null</code> | Display a title/header on the customer screen. Currently ignored, but may be used in the future. |
|
||||
| terms | <code>string</code> \| <code>boolean</code> | <code>false</code> | Set to a string to display terms and conditions or other text content next to the signature pad. |
|
||||
| termstype | <code>string</code> | <code>"body"</code> | "html", "pdf", "raw", or "body". See setCustomerScreen() |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
global.apis.ui.collectSignatureFromCustomerScreen("", "<p>By signing, you agree to pay us lots of money</p>", "body");
|
||||
global.apis.eventbus.on("customerSignatureCollected", function (sigdata) {
|
||||
const pngDataURL = sigdata.png;
|
||||
const svgDataURL = sigdata.svg;
|
||||
});
|
||||
```
|
||||
<a name="ui.cancelSignatureCollection"></a>
|
||||
|
||||
### ui.cancelSignatureCollection()
|
||||
Cancels customer signature collection and returns the customer-facing display to normal operation.
|
||||
|
||||
**Kind**: static method of [<code>ui</code>](#ui)
|
||||
**Example**
|
||||
```js
|
||||
global.apis.ui.cancelSignatureCollection();
|
||||
```
|
||||
<a name="ui.clearSignaturePad"></a>
|
||||
|
||||
### ui.clearSignaturePad()
|
||||
Erase the signature drawn on the customer-facing display.
|
||||
Note that the customer is also provided a button to do this.
|
||||
|
||||
**Kind**: static method of [<code>ui</code>](#ui)
|
||||
**Example**
|
||||
```js
|
||||
global.apis.ui.clearSignaturePad();
|
||||
```
|
||||
<a name="ui.getCustomerDisplayInfo"></a>
|
||||
|
||||
### ui.getCustomerDisplayInfo() ⇒ <code>Object</code>
|
||||
Describes if the customer-facing display is currently enabled,
|
||||
and if it supports customer touch interaction.
|
||||
|
||||
**Kind**: static method of [<code>ui</code>](#ui)
|
||||
**Returns**: <code>Object</code> - {"enabled": true, "touch": true}
|
||||
**Example**
|
||||
```js
|
||||
var info = global.apis.ui.getCustomerDisplayInfo();
|
||||
```
|
||||
75
docs/Plugin API/user.md
Normal file
75
docs/Plugin API/user.md
Normal file
@ -0,0 +1,75 @@
|
||||
<a name="user"></a>
|
||||
|
||||
## user : <code>object</code>
|
||||
Access data about employees.
|
||||
|
||||
**Kind**: global namespace
|
||||
|
||||
* [user](#user) : <code>object</code>
|
||||
* [.User](#user.User)
|
||||
* [new User(id, name, password, barcode, enabled)](#new_user.User_new)
|
||||
* [.getUser()](#user.getUser) ⇒ <code>User</code>
|
||||
* [.getUserID()](#user.getUserID) ⇒ <code>number</code>
|
||||
* [.getUserByID()](#user.getUserByID) ⇒ <code>Promise.<User></code>
|
||||
* [.listUsers([managerMode])](#user.listUsers) ⇒ <code>Promise.<Array.<User>></code>
|
||||
|
||||
<a name="user.User"></a>
|
||||
|
||||
### user.User
|
||||
**Kind**: static class of [<code>user</code>](#user)
|
||||
**Properties**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| id | <code>number</code> | |
|
||||
| name | <code>string</code> | |
|
||||
| pass | <code>string</code> | |
|
||||
| barcode | <code>string</code> | |
|
||||
| enabled | <code>boolean</code> | |
|
||||
| hasPassword() | <code>function</code> | Returns true if the user has a password set, else false. |
|
||||
| checkPassword(string) | <code>function</code> | Returns true if the provided password matches the user's password, or if there is no password set. |
|
||||
| icon(number) | <code>function</code> | Returns a SVG data URI with a procedurally-generated icon for the user. Size defaults to 50px if not specified. |
|
||||
|
||||
<a name="new_user.User_new"></a>
|
||||
|
||||
#### new User(id, name, password, barcode, enabled)
|
||||
A User object.
|
||||
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| id | <code>number</code> |
|
||||
| name | <code>string</code> |
|
||||
| password | <code>string</code> |
|
||||
| barcode | <code>string</code> |
|
||||
| enabled | <code>boolean</code> |
|
||||
|
||||
<a name="user.getUser"></a>
|
||||
|
||||
### user.getUser() ⇒ <code>User</code>
|
||||
Get the user currently logged in.
|
||||
|
||||
**Kind**: static method of [<code>user</code>](#user)
|
||||
<a name="user.getUserID"></a>
|
||||
|
||||
### user.getUserID() ⇒ <code>number</code>
|
||||
Get the current user's ID number.
|
||||
|
||||
**Kind**: static method of [<code>user</code>](#user)
|
||||
<a name="user.getUserByID"></a>
|
||||
|
||||
### user.getUserByID() ⇒ <code>Promise.<User></code>
|
||||
Look up the User for an ID number.
|
||||
|
||||
**Kind**: static method of [<code>user</code>](#user)
|
||||
<a name="user.listUsers"></a>
|
||||
|
||||
### user.listUsers([managerMode]) ⇒ <code>Promise.<Array.<User>></code>
|
||||
Get a list of all users in the system.
|
||||
|
||||
**Kind**: static method of [<code>user</code>](#user)
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [managerMode] | <code>boolean</code> | <code>false</code> | If false, list only active/enabled users, and if no users, return a default user account (user ID -1). If true, return all users in the database, and don't return a default account if the list is empty (return an empty list instead). |
|
||||
|
||||
399
docs/Plugin API/util.md
Normal file
399
docs/Plugin API/util.md
Normal file
@ -0,0 +1,399 @@
|
||||
<a name="util"></a>
|
||||
|
||||
## util : <code>object</code>
|
||||
Various utility functions: HTTP, time/date, barcode creation, clipboard, etc.
|
||||
|
||||
**Kind**: global namespace
|
||||
|
||||
* [util](#util) : <code>object</code>
|
||||
* [.uuid](#util.uuid) : <code>object</code>
|
||||
* [.v4()](#util.uuid.v4) ⇒ <code>string</code>
|
||||
* [.short([length])](#util.uuid.short) ⇒ <code>string</code>
|
||||
* [.http](#util.http) : <code>object</code>
|
||||
* [.webhook](#util.http.webhook) : <code>object</code>
|
||||
* [.geturl(sourcename)](#util.http.webhook.geturl) ⇒ <code>Promise.<string></code>
|
||||
* [.poll(sourcename)](#util.http.webhook.poll) ⇒ <code>Promise.<Array.<Object>></code>
|
||||
* [.ack(webhookid)](#util.http.webhook.ack)
|
||||
* [.post(url, data, [responseType], [headers], [method], [continueOnBadStatusCode], [timeoutSeconds])](#util.http.post) ⇒ <code>Promise.<(string\|Blob\|ArrayBuffer\|Object)></code>
|
||||
* [.fetch(url, [responseType], [timeoutSeconds])](#util.http.fetch) ⇒ <code>Promise.<(string\|Blob\|ArrayBuffer\|Object)></code>
|
||||
* [.string](#util.string) : <code>object</code>
|
||||
* [.split(input, separator, [limit])](#util.string.split) ⇒ <code>Array.<string></code>
|
||||
* [.chunk(input, chunksize)](#util.string.chunk) ⇒ <code>Array.<string></code>
|
||||
* [.time](#util.time) : <code>object</code>
|
||||
* [.now()](#util.time.now) ⇒ <code>number</code>
|
||||
* [.diff(compareto)](#util.time.diff) ⇒ <code>number</code>
|
||||
* [.strtotime(str)](#util.time.strtotime) ⇒ <code>number</code>
|
||||
* [.format(format, [timestamp])](#util.time.format) ⇒ <code>string</code>
|
||||
* [.toDateString(timestamp)](#util.time.toDateString) ⇒ <code>string</code>
|
||||
* [.toTimeString(timestamp)](#util.time.toTimeString) ⇒ <code>string</code>
|
||||
* [.clipboard](#util.clipboard) : <code>object</code>
|
||||
* [.copy(text, [showNotification])](#util.clipboard.copy) ⇒ <code>Promise.<boolean></code>
|
||||
* [.barcode](#util.barcode) : <code>object</code>
|
||||
* [.getBuffer(data, [type], [height], [scale], [includetext])](#util.barcode.getBuffer) ⇒ <code>Promise.<Buffer></code>
|
||||
* [.getBase64(data, [type], [height], [scale], [includetext])](#util.barcode.getBase64) ⇒ <code>Promise.<string></code>
|
||||
* [.geography](#util.geography) : <code>object</code>
|
||||
* [.isoToCountryName(iso)](#util.geography.isoToCountryName) ⇒ <code>string</code>
|
||||
* [.objectEquals(a, b)](#util.objectEquals) ⇒ <code>boolean</code>
|
||||
* [.delay([ms])](#util.delay) ⇒ <code>Promise</code>
|
||||
|
||||
<a name="util.uuid"></a>
|
||||
|
||||
### util.uuid : <code>object</code>
|
||||
Unique ID generators.
|
||||
|
||||
**Kind**: static namespace of [<code>util</code>](#util)
|
||||
|
||||
* [.uuid](#util.uuid) : <code>object</code>
|
||||
* [.v4()](#util.uuid.v4) ⇒ <code>string</code>
|
||||
* [.short([length])](#util.uuid.short) ⇒ <code>string</code>
|
||||
|
||||
<a name="util.uuid.v4"></a>
|
||||
|
||||
#### uuid.v4() ⇒ <code>string</code>
|
||||
Generate a UUID string
|
||||
|
||||
**Kind**: static method of [<code>uuid</code>](#util.uuid)
|
||||
**Returns**: <code>string</code> - UUID v4 with dashes: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
|
||||
<a name="util.uuid.short"></a>
|
||||
|
||||
#### uuid.short([length]) ⇒ <code>string</code>
|
||||
Generate a short random alphanumeric string.
|
||||
|
||||
**Kind**: static method of [<code>uuid</code>](#util.uuid)
|
||||
**Returns**: <code>string</code> - A string of length `length`, from the character set "acdefhjkmnpqrtuvwxy0123456789".
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [length] | <code>number</code> | <code>16</code> | String character count. |
|
||||
|
||||
<a name="util.http"></a>
|
||||
|
||||
### util.http : <code>object</code>
|
||||
HTTP requests and webhooks.
|
||||
|
||||
**Kind**: static namespace of [<code>util</code>](#util)
|
||||
|
||||
* [.http](#util.http) : <code>object</code>
|
||||
* [.webhook](#util.http.webhook) : <code>object</code>
|
||||
* [.geturl(sourcename)](#util.http.webhook.geturl) ⇒ <code>Promise.<string></code>
|
||||
* [.poll(sourcename)](#util.http.webhook.poll) ⇒ <code>Promise.<Array.<Object>></code>
|
||||
* [.ack(webhookid)](#util.http.webhook.ack)
|
||||
* [.post(url, data, [responseType], [headers], [method], [continueOnBadStatusCode], [timeoutSeconds])](#util.http.post) ⇒ <code>Promise.<(string\|Blob\|ArrayBuffer\|Object)></code>
|
||||
* [.fetch(url, [responseType], [timeoutSeconds])](#util.http.fetch) ⇒ <code>Promise.<(string\|Blob\|ArrayBuffer\|Object)></code>
|
||||
|
||||
<a name="util.http.webhook"></a>
|
||||
|
||||
#### http.webhook : <code>object</code>
|
||||
Use webhooks via a PostalPoint cloud relay service.
|
||||
|
||||
**Kind**: static namespace of [<code>http</code>](#util.http)
|
||||
|
||||
* [.webhook](#util.http.webhook) : <code>object</code>
|
||||
* [.geturl(sourcename)](#util.http.webhook.geturl) ⇒ <code>Promise.<string></code>
|
||||
* [.poll(sourcename)](#util.http.webhook.poll) ⇒ <code>Promise.<Array.<Object>></code>
|
||||
* [.ack(webhookid)](#util.http.webhook.ack)
|
||||
|
||||
<a name="util.http.webhook.geturl"></a>
|
||||
|
||||
##### webhook.geturl(sourcename) ⇒ <code>Promise.<string></code>
|
||||
geturl - Returns a public URL that can be used as a webhook target/endpoint for third-party integrations.
|
||||
|
||||
**Kind**: static method of [<code>webhook</code>](#util.http.webhook)
|
||||
**Returns**: <code>Promise.<string></code> - A URL for the webhook.
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| sourcename | <code>string</code> | Unique identifier for the webhook |
|
||||
|
||||
<a name="util.http.webhook.poll"></a>
|
||||
|
||||
##### webhook.poll(sourcename) ⇒ <code>Promise.<Array.<Object>></code>
|
||||
poll - Returns an array of webhook payloads received by the webhook identified by `sourcename`.
|
||||
|
||||
**Kind**: static method of [<code>webhook</code>](#util.http.webhook)
|
||||
**Returns**: <code>Promise.<Array.<Object>></code> - Payloads as received by the webhook relay service.
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| sourcename | <code>string</code> | Unique identifier for the webhook |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
[
|
||||
{
|
||||
// Unique ID. Used for ack(webhookid).
|
||||
id: 123,
|
||||
|
||||
// UNIX timestamp (in seconds) of when the data was received by the webhook URL.
|
||||
timestamp: 1234567890,
|
||||
|
||||
// Source name set in geturl()
|
||||
source: "sourcename",
|
||||
|
||||
// JSON string of all the HTTP headers sent to the webhook URL.
|
||||
headers: "{'Content-Type': 'application/json'}",
|
||||
|
||||
// Entire HTTP request body sent to the webhook URL.
|
||||
body: ""
|
||||
}
|
||||
]
|
||||
```
|
||||
<a name="util.http.webhook.ack"></a>
|
||||
|
||||
##### webhook.ack(webhookid)
|
||||
Acknowledge receipt of a webhook payload, deleting it from the relay server.
|
||||
Webhook payload is only queued for deletion, so polling may still return it for a short time.
|
||||
|
||||
**Kind**: static method of [<code>webhook</code>](#util.http.webhook)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| webhookid | <code>number</code> | Numeric unique ID received with the payload. See `poll`. |
|
||||
|
||||
<a name="util.http.post"></a>
|
||||
|
||||
#### http.post(url, data, [responseType], [headers], [method], [continueOnBadStatusCode], [timeoutSeconds]) ⇒ <code>Promise.<(string\|Blob\|ArrayBuffer\|Object)></code>
|
||||
post - Fetch a HTTP POST request.
|
||||
|
||||
**Kind**: static method of [<code>http</code>](#util.http)
|
||||
**Returns**: <code>Promise.<(string\|Blob\|ArrayBuffer\|Object)></code> - The server response body. See `responseType` parameter.
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| url | <code>string</code> | | |
|
||||
| data | <code>Object.<string, string></code> | | POST data key/value list |
|
||||
| [responseType] | <code>string</code> | <code>"text"</code> | "text", "blob", "buffer", or "json" |
|
||||
| [headers] | <code>Object.<string, string></code> | | HTTP headers to send. Defaults to `{"Content-Type": "application/json"}`. |
|
||||
| [method] | <code>string</code> | <code>"POST"</code> | |
|
||||
| [continueOnBadStatusCode] | <code>boolean</code> | <code>false</code> | If false, throws an Error when the HTTP response code is not 2XX. If true, ignores the response code and proceeds as normal. |
|
||||
| [timeoutSeconds] | <code>number</code> | <code>15</code> | Aborts the request (timeout) after this many seconds. |
|
||||
|
||||
<a name="util.http.fetch"></a>
|
||||
|
||||
#### http.fetch(url, [responseType], [timeoutSeconds]) ⇒ <code>Promise.<(string\|Blob\|ArrayBuffer\|Object)></code>
|
||||
fetch - Fetch a HTTP GET request.
|
||||
|
||||
**Kind**: static method of [<code>http</code>](#util.http)
|
||||
**Returns**: <code>Promise.<(string\|Blob\|ArrayBuffer\|Object)></code> - The server response body. See `responseType` parameter.
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| url | <code>string</code> | | |
|
||||
| [responseType] | <code>string</code> | <code>"text"</code> | "text", "blob", "buffer", or "json" |
|
||||
| [timeoutSeconds] | <code>number</code> | <code>15</code> | Aborts the request (timeout) after this many seconds. |
|
||||
|
||||
<a name="util.string"></a>
|
||||
|
||||
### util.string : <code>object</code>
|
||||
String manipulation functions.
|
||||
|
||||
**Kind**: static namespace of [<code>util</code>](#util)
|
||||
|
||||
* [.string](#util.string) : <code>object</code>
|
||||
* [.split(input, separator, [limit])](#util.string.split) ⇒ <code>Array.<string></code>
|
||||
* [.chunk(input, chunksize)](#util.string.chunk) ⇒ <code>Array.<string></code>
|
||||
|
||||
<a name="util.string.split"></a>
|
||||
|
||||
#### string.split(input, separator, [limit]) ⇒ <code>Array.<string></code>
|
||||
Split a string with a separator regex.
|
||||
|
||||
**Kind**: static method of [<code>string</code>](#util.string)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| input | <code>string</code> | Input string |
|
||||
| separator | <code>string</code> | Passed to `new RegExp(separator, 'g')` |
|
||||
| [limit] | <code>number</code> | Maximum number of splits to perform |
|
||||
|
||||
<a name="util.string.chunk"></a>
|
||||
|
||||
#### string.chunk(input, chunksize) ⇒ <code>Array.<string></code>
|
||||
Split a string into chunks of length `chunksize`.
|
||||
|
||||
**Kind**: static method of [<code>string</code>](#util.string)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| input | <code>string</code> | Input string |
|
||||
| chunksize | <code>string</code> | Number of characters per chunk |
|
||||
|
||||
<a name="util.time"></a>
|
||||
|
||||
### util.time : <code>object</code>
|
||||
Date and time functions
|
||||
|
||||
**Kind**: static namespace of [<code>util</code>](#util)
|
||||
|
||||
* [.time](#util.time) : <code>object</code>
|
||||
* [.now()](#util.time.now) ⇒ <code>number</code>
|
||||
* [.diff(compareto)](#util.time.diff) ⇒ <code>number</code>
|
||||
* [.strtotime(str)](#util.time.strtotime) ⇒ <code>number</code>
|
||||
* [.format(format, [timestamp])](#util.time.format) ⇒ <code>string</code>
|
||||
* [.toDateString(timestamp)](#util.time.toDateString) ⇒ <code>string</code>
|
||||
* [.toTimeString(timestamp)](#util.time.toTimeString) ⇒ <code>string</code>
|
||||
|
||||
<a name="util.time.now"></a>
|
||||
|
||||
#### time.now() ⇒ <code>number</code>
|
||||
Get the current UNIX timestamp in seconds.
|
||||
|
||||
**Kind**: static method of [<code>time</code>](#util.time)
|
||||
<a name="util.time.diff"></a>
|
||||
|
||||
#### time.diff(compareto) ⇒ <code>number</code>
|
||||
Get the number of seconds between now and the given Date or UNIX timestamp in seconds.
|
||||
|
||||
**Kind**: static method of [<code>time</code>](#util.time)
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| compareto | <code>number</code> \| <code>Date</code> |
|
||||
|
||||
<a name="util.time.strtotime"></a>
|
||||
|
||||
#### time.strtotime(str) ⇒ <code>number</code>
|
||||
Parse a string date and return UNIX timestamp (in seconds).
|
||||
|
||||
**Kind**: static method of [<code>time</code>](#util.time)
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| str | <code>string</code> |
|
||||
|
||||
<a name="util.time.format"></a>
|
||||
|
||||
#### time.format(format, [timestamp]) ⇒ <code>string</code>
|
||||
Take a Date or UNIX timestamp in seconds and format it to a string.
|
||||
Mostly compatible with the [PHP date format codes](https://www.php.net/manual/en/datetime.format.php).
|
||||
|
||||
**Kind**: static method of [<code>time</code>](#util.time)
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| format | <code>string</code> | | "Y-m-d H:i:s", etc |
|
||||
| [timestamp] | <code>number</code> \| <code>Date</code> | <code>now()</code> | |
|
||||
|
||||
<a name="util.time.toDateString"></a>
|
||||
|
||||
#### time.toDateString(timestamp) ⇒ <code>string</code>
|
||||
Format a UNIX timestamp (in seconds) as a localized date string.
|
||||
|
||||
**Kind**: static method of [<code>time</code>](#util.time)
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| timestamp | <code>number</code> |
|
||||
|
||||
<a name="util.time.toTimeString"></a>
|
||||
|
||||
#### time.toTimeString(timestamp) ⇒ <code>string</code>
|
||||
Format a UNIX timestamp (in seconds) as a localized time string.
|
||||
|
||||
**Kind**: static method of [<code>time</code>](#util.time)
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| timestamp | <code>number</code> |
|
||||
|
||||
<a name="util.clipboard"></a>
|
||||
|
||||
### util.clipboard : <code>object</code>
|
||||
OS clipboard
|
||||
|
||||
**Kind**: static namespace of [<code>util</code>](#util)
|
||||
<a name="util.clipboard.copy"></a>
|
||||
|
||||
#### clipboard.copy(text, [showNotification]) ⇒ <code>Promise.<boolean></code>
|
||||
Copy a string to the system clipboard.
|
||||
|
||||
**Kind**: static method of [<code>clipboard</code>](#util.clipboard)
|
||||
**Returns**: <code>Promise.<boolean></code> - True if the copy succeeded, else false.
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| text | <code>string</code> | | |
|
||||
| [showNotification] | <code>boolean</code> | <code>false</code> | If true, a "Copied" notification will appear onscreen briefly. |
|
||||
|
||||
<a name="util.barcode"></a>
|
||||
|
||||
### util.barcode : <code>object</code>
|
||||
Barcode image generation functions.
|
||||
|
||||
**Kind**: static namespace of [<code>util</code>](#util)
|
||||
|
||||
* [.barcode](#util.barcode) : <code>object</code>
|
||||
* [.getBuffer(data, [type], [height], [scale], [includetext])](#util.barcode.getBuffer) ⇒ <code>Promise.<Buffer></code>
|
||||
* [.getBase64(data, [type], [height], [scale], [includetext])](#util.barcode.getBase64) ⇒ <code>Promise.<string></code>
|
||||
|
||||
<a name="util.barcode.getBuffer"></a>
|
||||
|
||||
#### barcode.getBuffer(data, [type], [height], [scale], [includetext]) ⇒ <code>Promise.<Buffer></code>
|
||||
Get a PNG image buffer of a barcode. Uses library "bwip-js".
|
||||
|
||||
**Kind**: static method of [<code>barcode</code>](#util.barcode)
|
||||
**Returns**: <code>Promise.<Buffer></code> - PNG data for the barcode.
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| data | <code>string</code> | | |
|
||||
| [type] | <code>string</code> | <code>"\"code128\""</code> | |
|
||||
| [height] | <code>number</code> | <code>10</code> | |
|
||||
| [scale] | <code>number</code> | <code>2</code> | |
|
||||
| [includetext] | <code>boolean</code> | <code>false</code> | Set true to render the barcode's content as text below the code. |
|
||||
|
||||
<a name="util.barcode.getBase64"></a>
|
||||
|
||||
#### barcode.getBase64(data, [type], [height], [scale], [includetext]) ⇒ <code>Promise.<string></code>
|
||||
Get a PNG image of a barcode as a base64 data URI. Uses library "bwip-js".
|
||||
|
||||
**Kind**: static method of [<code>barcode</code>](#util.barcode)
|
||||
**Returns**: <code>Promise.<string></code> - "data:image/png;base64,..."
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| data | <code>string</code> | | |
|
||||
| [type] | <code>string</code> | <code>"\"code128\""</code> | |
|
||||
| [height] | <code>number</code> | <code>10</code> | |
|
||||
| [scale] | <code>number</code> | <code>2</code> | |
|
||||
| [includetext] | <code>boolean</code> | <code>false</code> | Set true to render the barcode's content as text below the code. |
|
||||
|
||||
<a name="util.geography"></a>
|
||||
|
||||
### util.geography : <code>object</code>
|
||||
**Kind**: static namespace of [<code>util</code>](#util)
|
||||
<a name="util.geography.isoToCountryName"></a>
|
||||
|
||||
#### geography.isoToCountryName(iso) ⇒ <code>string</code>
|
||||
Get a human-readable country name from an ISO country code.
|
||||
|
||||
**Kind**: static method of [<code>geography</code>](#util.geography)
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| iso | <code>string</code> \| <code>number</code> | 2 or 3 letter country code, or numeric country code. |
|
||||
|
||||
<a name="util.objectEquals"></a>
|
||||
|
||||
### util.objectEquals(a, b) ⇒ <code>boolean</code>
|
||||
Compare two objects for equality. See https://stackoverflow.com/a/16788517
|
||||
|
||||
**Kind**: static method of [<code>util</code>](#util)
|
||||
**Returns**: <code>boolean</code> - True if equal, else false.
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| a | <code>\*</code> |
|
||||
| b | <code>\*</code> |
|
||||
|
||||
<a name="util.delay"></a>
|
||||
|
||||
### util.delay([ms]) ⇒ <code>Promise</code>
|
||||
Pause execution for some amount of time in an async function, i.e., returns a Promise that resolves in some number of milliseconds.
|
||||
|
||||
**Kind**: static method of [<code>util</code>](#util)
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [ms] | <code>number</code> | <code>1000</code> | Number of milliseconds to pause. |
|
||||
|
||||
176
docs/assets/logo.svg
Normal file
176
docs/assets/logo.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 34 KiB |
4
docs/assets/styles.css
Normal file
4
docs/assets/styles.css
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
.md-header__button.md-logo img {
|
||||
height: 2rem;
|
||||
}
|
||||
68
docs/index.md
Normal file
68
docs/index.md
Normal file
@ -0,0 +1,68 @@
|
||||
# Overview
|
||||
|
||||
PostalPoint® supports JavaScript plugin extensions. Plugins can hook into PostalPoint to add features and integrations.
|
||||
|
||||
## What plugins can do
|
||||
|
||||
* Process card payments and handle saved payment methods
|
||||
* Process cryptocurrency payments
|
||||
* Add additional carriers, providing shipping rates and labels
|
||||
* Print to label and receipt printers, letting PostalPoint handle hardware support and drivers
|
||||
* Extend support for prepaid label acceptance, prepaid barcode recognition, and carrier dropoff QR codes
|
||||
* Install pages in the Tools menu, creating new interfaces and features
|
||||
* Receive transaction receipts for ingestion into third-party accounting or business software
|
||||
* Display interactive HTML5 content on the customer-facing screen
|
||||
* Run both Node.JS and browser code.
|
||||
|
||||
## Plugin Package Structure
|
||||
|
||||
A plugin is distributed as a simple ZIP file, containing a folder. The folder then has at least one file, named `plugin.js`.
|
||||
The `exports.init` function in `plugin.js` is executed when PostalPoint launches, allowing the plugin
|
||||
to request involvement with various events in PostalPoint.
|
||||
|
||||
PostalPoint installs plugin packages by unzipping their contents into a plugins folder.
|
||||
Plugins are uninstalled by deleting their folder.
|
||||
|
||||
## PostalPoint Plugin API
|
||||
|
||||
The PostalPoint plugin API is a globally-available object named `global.apis`.
|
||||
It contains many useful functions for integrating with PostalPoint.
|
||||
All the APIs listed under the Plugin API section must be prefixed with `global.apis.` in order to work.
|
||||
|
||||
## Minimal Plugin Code
|
||||
|
||||
```javascript title="plugin-name/plugin.js"
|
||||
exports.init = function () {
|
||||
global.apis.alert("This message appears when PostalPoint launches.", "Hello!");
|
||||
};
|
||||
```
|
||||
|
||||
Yes, the smallest plugin really is just two lines of code, and accessing PostalPoint features
|
||||
really is that easy.
|
||||
|
||||
## Plugin Metadata File
|
||||
|
||||
While not strictly required, a `package.json` is encouraged, and allows specifying the plugin's
|
||||
display name, PostalPoint version compatibility, and other metadata.
|
||||
|
||||
Sample:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "plugin-id-here",
|
||||
"main": "plugin.js",
|
||||
"description": "Human-readable description of the plugin",
|
||||
"version": "1.0.0",
|
||||
"author": "Your Name",
|
||||
"license": "Code license name",
|
||||
"postalpoint": {
|
||||
"pluginname": "Display Name for Plugin",
|
||||
"minVersion": "000034",
|
||||
"maxVersion": "001000"
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PostalPoint version codes are MMMnnn where MMM is the major version and nnn is the minor version, zero-padded.
|
||||
So version 0.35 is "000035", and 1.23 is "001023".
|
||||
43
jsdoc2md.js
Normal file
43
jsdoc2md.js
Normal file
@ -0,0 +1,43 @@
|
||||
import jsdoc2md from 'jsdoc-to-markdown';
|
||||
import { promises as fs } from 'node:fs';
|
||||
import path from 'path';
|
||||
|
||||
/* input and output paths */
|
||||
const inputFiles = process.argv.slice(2);
|
||||
const docroot = "./docs/Plugin API";
|
||||
|
||||
/* get template data */
|
||||
const templateData = await jsdoc2md.getTemplateData({ files: inputFiles });
|
||||
|
||||
/* reduce templateData to an array of class names */
|
||||
const namespaces = templateData.filter(i => i.kind === 'namespace');
|
||||
const globals = templateData.filter(i => (i.kind === 'constant' || i.kind === 'function') && i.scope === "global");
|
||||
|
||||
var template, output;
|
||||
|
||||
/* create a documentation file for each class */
|
||||
for (const namespace of namespaces) {
|
||||
//console.log(namespace);
|
||||
template = `{{#namespace longname="${namespace.longname}"}}{{>docs}}{{/namespace}}`;
|
||||
console.log(`rendering ${namespace.longname}`);
|
||||
output = await jsdoc2md.render({ data: templateData, template: template, "example-lang": "js" });
|
||||
const isPrimaryFile = namespace.longname == namespace.name;
|
||||
const folder = namespace.longname.split(".").slice(0, -1).join("/");
|
||||
await fs.mkdir(path.resolve(`${docroot}/${folder}`), {recursive: true});
|
||||
if (isPrimaryFile) {
|
||||
await fs.writeFile(path.resolve(`${docroot}/${namespace.name}.md`), output);
|
||||
} else {
|
||||
//const folder = namespace.longname.split(".").slice(0, -1).join("/");
|
||||
//await fs.mkdir(path.resolve(`${docroot}/${folder}`), {recursive: true});
|
||||
//await fs.writeFile(path.resolve(`${docroot}/${folder}/${namespace.name}.md`), output);
|
||||
}
|
||||
}
|
||||
|
||||
template = "";
|
||||
console.log(`rendering globals`);
|
||||
for (const glob of globals) {
|
||||
//console.log(namespace);
|
||||
template += `{{#globals longname="${glob.longname}"}}{{>docs}}{{/globals}}`;
|
||||
}
|
||||
output = await jsdoc2md.render({ data: templateData, template: template, "example-lang": "js" });
|
||||
await fs.writeFile(path.resolve(`${docroot}/global functions.md`), output);
|
||||
32
mkdocs.yml
Normal file
32
mkdocs.yml
Normal file
@ -0,0 +1,32 @@
|
||||
site_name: PostalPoint Plugin Development
|
||||
theme:
|
||||
name: material
|
||||
custom_dir: overrides
|
||||
palette:
|
||||
scheme: default
|
||||
primary: blue
|
||||
logo: assets/logo.svg
|
||||
favicon: https://postalpoint.app/images/favicon-voxel.png
|
||||
features:
|
||||
- content.code.copy
|
||||
extra:
|
||||
homepage: https://postalpoint.app
|
||||
analytics:
|
||||
provider: custom
|
||||
url: analytics.netsyms.net
|
||||
siteid: 57
|
||||
extra_css:
|
||||
- assets/styles.css
|
||||
plugins:
|
||||
- privacy
|
||||
- search
|
||||
markdown_extensions:
|
||||
- def_list
|
||||
- pymdownx.highlight:
|
||||
anchor_linenums: true
|
||||
line_spans: __span
|
||||
pygments_lang_class: true
|
||||
- pymdownx.inlinehilite
|
||||
- pymdownx.snippets:
|
||||
base_path: 'docs/'
|
||||
- pymdownx.superfences
|
||||
15
overrides/partials/integrations/analytics/custom.html
Normal file
15
overrides/partials/integrations/analytics/custom.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!-- Matomo -->
|
||||
<script>
|
||||
var _paq = window._paq = window._paq || [];
|
||||
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
||||
_paq.push(['trackPageView']);
|
||||
_paq.push(['enableLinkTracking']);
|
||||
(function() {
|
||||
var u="https://{{ config.extra.analytics.url }}/";
|
||||
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
||||
_paq.push(['setSiteId', '{{ config.extra.analytics.siteid }}']);
|
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
||||
})();
|
||||
</script>
|
||||
<!-- End Matomo Code -->
|
||||
6
publish.sh
Executable file
6
publish.sh
Executable file
@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
./build.sh
|
||||
mkdocs build
|
||||
|
||||
rsync -rv site/ webhost.netsyms.net:/var/www/dev.postalpoint.app/web
|
||||
Loading…
x
Reference in New Issue
Block a user