@twofistedjustice/square-pie
Version:
an easy to use class based chainable interface for the Square API.
350 lines (326 loc) • 11.8 kB
JavaScript
const Invoice_RUDCnP = require("./invoice_request_abstract_RUDCP_super");
const { nanoid } = require("nanoid");
const { shazam_max_length, arrayify } = require("./utilities");
const man =
"http request to upate an invoice by modifying fields, clearing fields, or both. \n" +
"Retrieve the invoice from Square and pass it as an argument when you instantiate the class. There is no option to do it later\n" +
"If you want to change values on invoice fields, create a sparse invoice using Invoice_Object class. If the invoice\n" +
"has illegal updates this class will throw an error. To aid in debugging, the specific reason will be stashed at myVar.reason.\n" +
"You can also manually validate a sparse invoice by passing it to myVar.validate(fardel). Then check myVar.reason.\n" +
"You may not update the order_id or location_id. You may not update the primary_recipient on a published invoice.\n" +
"\nhttps://developer.squareup.com/reference/square/invoices-api/update-invoice" +
"\nhttps://developer.squareup.com/docs/invoices-api/overview#update-an-invoice";
/**
* {@link https://developer.squareup.com/reference/square/invoices-api/update-invoice | **-------> Link To Square Docs: Update endpoint<-------**}<br>
* {@link https://developer.squareup.com/docs/invoices-api/overview#update-an-invoice | **-------> Link To Square Docs: Updating an invoice<-------**}<br>
* @class Invoice_Update
* @extends Square_Request
* @param {object} invoice_document Get the invoice you want to update from Square and pass its ID as an argument.
* You MUST do this when instantiating the Invoice_Update class. There is no option do do it later.
* @classdesc
*
* http request to upate an invoice by modifying fields, clearing fields, or both.<br><br>
* Retrieve the invoice from Square and pass it as an argument when you instantiate the class. There is no option to do it later.<br><br>
* If you want to change values on invoice fields, create a sparse invoice using Invoice_Object class.<br><br>
* You can also manually validate a sparse invoice by passing it to myVar.validate(fardel). Then check myVar.reason. <br><br>
* You may not update the order_id or location_id. You may not update the primary_recipient on a published invoice. <br>
*
* **SPECIAL**<br>
* If the invoice has illegal updates this class will throw an error. To aid in debugging, the specific reason will be stashed at `myVar.reason`. <br><br>
*
* */
class Invoice_Update extends Invoice_RUDCnP {
_display_name = "Invoice_Update";
_last_verified_square_api_version = "2021-12-15";
_help = this.display_name + ": " + man;
_reason = "";
constructor(invoice_document) {
super(invoice_document.id);
this._method = "PUT";
this._square_invoice_document = invoice_document;
this._invoice_is_updatable = this.#is_updatable();
this._invoice_is_published = this.#is_invoice_published();
this._body = {
idempotency_key: nanoid(), // 128
invoice: undefined, // sparse invoice object containing only the fields you want to update - don't be like Princess Vespa
fields_to_clear: undefined,
};
this.configuration = {
maximums: {
idempotency_key: 128,
},
};
}
// GETTERS
get square_invoice_document() {
return this._square_invoice_document;
}
get idempotency_key() {
return this._body.idempotency_key;
}
get invoice() {
return this._body.invoice;
}
get fields_to_clear() {
return this._body.fields_to_clear;
}
get body() {
// don't use the !validate() syntax or it causes errors
let validated = this.validate();
if (validated === false) {
throw new Error(this.reason);
} else {
return this._body;
}
}
get reason() {
return this._reason;
}
// GETTERS for the condtionals related to the Square document passed as an argument to the constructor
get is_updatable() {
return this._invoice_is_updatable;
}
get is_published() {
return this._invoice_is_published;
}
// SETTERS
set idempotency_key(key) {
if (
shazam_max_length(
key,
this.configuration.maximums.idempotency_key,
this.display_name,
"idempotency_key"
)
) {
this._body.idempotency_key = key;
}
}
set invoice(fardel) {
this._body.invoice = fardel;
if (!this.validate()) {
throw new Error(this.reason);
}
}
set fields_to_clear(field) {
if (field === "order_id" || field === "location_id") {
let message = "It is not allowed to clear the " + field + " property.";
throw new Error(message);
}
arrayify(
this._body,
"fields_to_clear",
this._display_name,
"fields_to_clear"
);
this._body.fields_to_clear.push(field);
}
set #reason(str) {
let base = this._reason;
let final;
// if (this.reason === undefined) {
if (base === "") {
base = "Update disallowed for the following reason(s):";
}
final = base.concat("\n- " + str);
this._reason = final;
}
// PRIVATE METHODS
#is_invoice_published() {
// these are legal to update but you can't update primary_recipient
let inv = this._square_invoice_document;
let is_published =
inv.status === "UNPAID" ||
inv.status === "SCHEDULED" ||
inv.status === "PARTIALLY_PAID" ||
inv.status === "PARTIALLY_REFUNDED"
? true
: false;
return is_published;
}
#is_updatable() {
// these are always illegal to update
let inv = this._square_invoice_document;
let can_be_updated =
inv.status === "PAID" ||
inv.status === "REFUNDED" ||
inv.status === "CANCELED" ||
inv.status === "FAILED" ||
inv.status === "PAYMENT_PENDING"
? false
: true;
if (can_be_updated === false) {
this.#reason =
"Invoices cannot be updated which have a status of PAYMENT_PENDING, PAID, REFUNDED, CANCELED, or FAILED";
}
return can_be_updated;
}
#compare_version(fardel) {
let inv = this.square_invoice_document;
if (fardel.version !== inv.version) {
let message =
"Versions do not match. Expected: " +
inv.version +
" Received: " +
fardel.version;
this.#reason = message;
return false;
} else {
// if versions match return true
return true;
}
}
#validate_primary_recipient(fardel) {
if (fardel.primary_recipient !== undefined) {
if (this.is_published === true) {
this.#reason =
"It is not allowed to update primary_recipient on a published invoice.";
return false;
}
} else {
return true;
}
}
#validate_order_id(fardel) {
if (fardel.order_id !== undefined) {
this.#reason = "It is not allowed to update order_id.";
return false;
} else {
return true;
}
}
#validate_location_id(fardel) {
if (fardel.location_id !== undefined) {
this.#reason = "It is not allowed to update location_id.";
return false;
} else {
return true;
}
}
#can_clear_primary_recipient() {
if (this.is_published && Array.isArray(this._body.fields_to_clear)) {
if (this._body.fields_to_clear.includes("primary_recipient")) {
this.#reason =
"It is not allowed to clear primary_recipient on a published invoice.";
return false;
}
} else {
return true;
}
}
/**
* {@link https://developer.squareup.com/docs/invoices-api/overview#update-an-invoice | Square Docs}<br>
*
* Determines if an update is legal. If it is legal it returns true. If it is illegal,
* it returns false and the reason can be accessed at yourVar.reason. This method is run automatically when
* you call yourVar.request() and will throw an error if validation fails. If you run it manually, no error
* will be thrown. Instead errors will be recorded and visible at the `.reason` property.
*
* Determines whether the update is legal or not.<br>
* **The update is illegal if:**
* - if the versions do no match.
* - an attempt is made to update order_id or location_id.
* - the invoice is published and an attempt is made to update primary_recipient
*
* - invoice status is
* - "PAID"
* - "REFUNDED"
* - "CANCELED"
* - "FAILED"
* - "PAYMENT_PENDING
*
* @typedef {function} Invoice_Update.validate
* @memberOf Invoice_Update
* @public
* @method
* @param {invoice.fardel} fardel
* @example
* myVar.validate(invoice.fardel)
* console.log(myVar.reason) => prints any errors to console
* */
// the default is the sparse invoice object NOT the original
validate(fardel = this._body.invoice) {
let is_legal;
/**
* update_legality checks the value of is_legal
* if it is undefined - it sets it to the received value
* if it true set it to the received value
* if it is false, leave it as it is
* @function
* @param {boolean} the result of a test
* @ignore
* */
let update_legality = function (bool) {
if (is_legal === undefined || is_legal === true) {
is_legal = bool;
} else if (is_legal === false) {
return;
}
};
// disallow clearing primary_recipient if invoice is published
update_legality(this.#can_clear_primary_recipient());
// if there is not an invoice to validate, then don't!
if (fardel !== undefined) {
// disallow update if status is PAYMENT_PENDING, PAID, REFUNDED, CANCELED, or FAILED.
update_legality(this.is_updatable);
// disallow updating primary_recipient if invoice is published
update_legality(this.#validate_primary_recipient(fardel));
// disallow update of order_id or location_id
update_legality(this.#validate_order_id(fardel));
update_legality(this.#validate_location_id(fardel));
// versions must be the same
update_legality(this.#compare_version(fardel));
}
return is_legal;
}
// MAKE METHODS
/**
* @param {object} fardel
* @param {string} field
* */
/**
* make() method of Invoice_Update
* Make sure to have the Square Docs open in front of you.
* Sub-Method names are exactly the same as the property names listed
* in the Square docs. There may be additional methods and/or shortened aliases of other methods.
*
* You should read the generated docs as:
* method_name(arg) {type} description of arg
*
* @typedef {function} Invoice_Update.make
* @method
* @public
* @memberOf Invoice_Update
* @property idempotency_key(key) {string} - use only if you want to use your own key in place of the automatically generated one.
* @property invoice(fardel) {Fardel} - a sparse invoice object fardel containing only the fields to be changed.
* @property fields_to_clear(field) {string} - the key of the key:value pair to be cleared.
* @example
* You must use parentheses with every call to make and with every sub-method. If you have to make a lot
* of calls from different lines, it will reduce your tying and improve readability to set make() to a
* variable.
*
* let make = myVar.make();
* make.gizmo()
* make.gremlin()
* //is the same as
* myVar.make().gizmo().gremlin()
* */
make() {
return {
self: this,
idempotency_key: function (key) {
this.self.idempotency_key = key;
return this;
},
invoice: function (fardel) {
this.self.invoice = fardel;
return this;
},
fields_to_clear: function (field) {
this.self.fields_to_clear = field;
return this;
},
};
}
}
module.exports = Invoice_Update;