xero-hero
Version:
Heroic utilities to simplify and enable your progress with the [xero-node](https://www.npmjs.com/package/xero-node) SDK.
1 lines • 10 kB
Source Map (JSON)
{"version":3,"sources":["../../../../src/common/response/__tests__/selectors.test.ts","../../../../src/common/instance/operations.ts","../../../../src/common/response/selectors.ts","../../../../src/accounting/attachments/requests.ts","../../../../src/accounting/contacts/links.ts","../../../../src/accounting/invoices/lineItems.ts","../../../../src/accounting/journals/links.ts","../../../../src/projects/timeEntries.ts"],"sourcesContent":["import {\r\n BankTransaction,\r\n BankTransactions,\r\n Contact,\r\n Contacts,\r\n Invoice,\r\n Invoices,\r\n RepeatingInvoices,\r\n} from 'xero-node';\r\n\r\nimport { getListFromResponse } from '../../../';\r\n\r\ndescribe('common/response/selectors', () => {\r\n describe('getListFromResponse()', () => {\r\n it('should return the contacts list from a contacts response', () => {\r\n const contactsList = [new Contact(), new Contact(), new Contact()];\r\n const contactsBody = new Contacts();\r\n contactsBody.contacts = contactsList; // eslint-disable-line functional/immutable-data\r\n expect(getListFromResponse({ body: contactsBody })).toBe(contactsList);\r\n });\r\n\r\n it('should return the invoice list from a invoices response', () => {\r\n const invoicesList = [new Invoice(), new Invoice()];\r\n const invoicesBody = new Invoices();\r\n invoicesBody.invoices = invoicesList; // eslint-disable-line functional/immutable-data\r\n expect(getListFromResponse({ body: invoicesBody })).toBe(invoicesList);\r\n });\r\n\r\n it('should return the bankTransactions from a bankTransactions response', () => {\r\n const bankTransactionsList = [\r\n new BankTransaction(),\r\n new BankTransaction(),\r\n new BankTransaction(),\r\n new BankTransaction(),\r\n ];\r\n const bankTransactionsBody = new BankTransactions();\r\n bankTransactionsBody.bankTransactions = bankTransactionsList; // eslint-disable-line functional/immutable-data\r\n expect(getListFromResponse({ body: bankTransactionsBody })).toBe(\r\n bankTransactionsList,\r\n );\r\n });\r\n\r\n it('should play nice with an object that looks like a Xero response and has an array property on the body', () => {\r\n const itemList = [1, 2, 3, 4, 5];\r\n const itemBody = { items: itemList };\r\n expect(getListFromResponse({ body: itemBody })).toBe(itemList);\r\n });\r\n\r\n it('should return undefined for responses that lack an array property', () => {\r\n const repeatingInvoices = new RepeatingInvoices();\r\n expect(getListFromResponse({ body: repeatingInvoices })).toBe(undefined);\r\n });\r\n\r\n it('should return undefined for responses without a body', () => {\r\n // @ts-expect-error - This is an invalid type for the function.\r\n expect(getListFromResponse({})).toBe(undefined);\r\n });\r\n\r\n it('should throw an error when passed an undefined response', () => {\r\n expect(() => {\r\n // @ts-expect-error - This is an invalid type for the function.\r\n return getListFromResponse();\r\n }).toThrowError();\r\n });\r\n\r\n it('should throw an error when passed a null response', () => {\r\n expect(() => {\r\n // @ts-expect-error - This is an invalid type for the function.\r\n return getListFromResponse(null);\r\n }).toThrowError();\r\n });\r\n });\r\n});\r\n","import { isObject } from 'deep-cuts';\r\n\r\nexport const deepClone = <T extends object>(instance: T): T => {\r\n if (instance && isObject(instance)) {\r\n const { constructor } = instance;\r\n // @ts-expect-error - This is not passing for TypeScript, bit will for any Xero class.\r\n const clone = new constructor();\r\n /* eslint-disable guard-for-in, functional/immutable-data */\r\n for (const key in instance) {\r\n clone[key] = deepClone(instance[key] as object);\r\n }\r\n /* eslint-enable guard-for-in, functional/immutable-data */\r\n\r\n return clone;\r\n }\r\n\r\n return instance;\r\n};\r\n","export const getListFromResponse = <\r\n T extends Record<string, any>,\r\n U,\r\n>(response: {\r\n body: T;\r\n}): U[] | undefined => {\r\n if (response.body) {\r\n const propertyName = Object.getOwnPropertyNames(response.body);\r\n for (const name of propertyName) {\r\n if (Array.isArray(response.body[name])) {\r\n return response.body[name];\r\n }\r\n }\r\n }\r\n\r\n return undefined;\r\n};\r\n","import type { ReadStream } from 'node:fs';\r\nimport type http from 'node:http';\r\n\r\nimport { bufferToStream } from 'tranquil-stream';\r\nimport type { Attachments, XeroClient } from 'xero-node';\r\n\r\ntype ICreateInvoiceAttachmentParameters = {\r\n contents: Buffer;\r\n filename: string;\r\n invoiceId: string;\r\n};\r\n\r\nexport const createInvoiceAttachment = async (\r\n client: XeroClient,\r\n tenantId: string,\r\n { invoiceId, filename, contents }: ICreateInvoiceAttachmentParameters,\r\n): Promise<{\r\n body: Attachments;\r\n response: http.IncomingMessage;\r\n}> => {\r\n return client.accountingApi.createInvoiceAttachmentByFileName(\r\n tenantId,\r\n invoiceId,\r\n filename,\r\n bufferToStream(contents) as unknown as ReadStream,\r\n );\r\n};\r\n","import qs from 'qs';\r\nimport type { Contact } from 'xero-node';\r\n\r\nimport { hasProperty } from '../../utils/properties';\r\n\r\nexport const getContactLink = (contact: Contact | string): string => {\r\n return `https://go.xero.com/Contacts/View.aspx?${qs.stringify({\r\n contactID:\r\n (hasProperty(contact, 'contactID')\r\n ? (contact as Contact).contactID\r\n : contact) || 'null-or-empty-contact-id',\r\n })}`;\r\n};\r\n","import { isNil } from 'deep-cuts';\r\nimport type { LineItem } from 'xero-node';\r\n\r\nimport type { DecisionFunction } from '../../types';\r\n\r\nexport const filterInvoiceLineItems = (\r\n lineItems: LineItem[],\r\n minCode: DecisionFunction<LineItem> | string | number,\r\n maxCode?: string | number,\r\n): LineItem[] => {\r\n if (typeof minCode === 'function') {\r\n return (lineItems || []).filter(minCode);\r\n }\r\n\r\n const parsedMinCode = isNil(minCode)\r\n ? minCode\r\n : Number.parseInt(minCode as string, 10);\r\n const parsedMaxCode = isNil(maxCode)\r\n ? maxCode\r\n : Number.parseInt(maxCode as string, 10);\r\n if (parsedMinCode || parsedMaxCode) {\r\n return (lineItems || []).filter(({ itemCode }) => {\r\n const parsedItemCode = isNil(itemCode)\r\n ? itemCode\r\n : Number.parseInt(itemCode as string, 10);\r\n if (parsedItemCode) {\r\n const greaterThanOrEqualToMinCode = isNil(parsedMinCode)\r\n ? true\r\n : parsedItemCode >= parsedMinCode;\r\n const lessThanOrEqualToMaxCode = isNil(parsedMaxCode)\r\n ? true\r\n : parsedItemCode <= (parsedMaxCode || 0);\r\n return greaterThanOrEqualToMinCode && lessThanOrEqualToMaxCode;\r\n }\r\n\r\n return false;\r\n });\r\n }\r\n\r\n return lineItems || [];\r\n};\r\n","import qs from 'qs';\r\nimport type { ManualJournal } from 'xero-node';\r\n\r\nimport { hasProperty } from '../../utils/properties';\r\n\r\nexport const getManualJournalLink = (\r\n manualJournal: ManualJournal | string,\r\n): string => {\r\n return `https://go.xero.com/Journal/View.aspx?${qs.stringify({\r\n invoiceID:\r\n (hasProperty(manualJournal, 'manualJournalID')\r\n ? (manualJournal as ManualJournal).manualJournalID\r\n : manualJournal) || 'null-or-empty-manual-journal-id',\r\n })}`;\r\n};\r\n","import { roundToNearestFraction } from 'deep-cuts';\r\n\r\nimport type { TimeEntry } from './shimTypes';\r\n\r\nexport const hoursFromTimeEntries = (\r\n timeEntries: TimeEntry[],\r\n denominator: number = 4,\r\n maxDecimalPlaces: number = 2,\r\n): number | undefined => {\r\n const totalMinutes = timeEntries.reduce((totalMinutes, timeEntry) => {\r\n const duration = timeEntry.duration || 0;\r\n return totalMinutes + duration;\r\n }, 0);\r\n return roundToNearestFraction(\r\n totalMinutes / 60,\r\n denominator,\r\n maxDecimalPlaces,\r\n );\r\n};\r\n"],"mappings":";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACRP,SAAS,gBAAgB;;;ACAlB,IAAM,sBAAsB,CAGjC,aAEqB;AACrB,MAAI,SAAS,MAAM;AACjB,UAAM,eAAe,OAAO,oBAAoB,SAAS,IAAI;AAC7D,eAAW,QAAQ,cAAc;AAC/B,UAAI,MAAM,QAAQ,SAAS,KAAK,IAAI,CAAC,GAAG;AACtC,eAAO,SAAS,KAAK,IAAI;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;ACbA,SAAS,sBAAsB;;;ACH/B,OAAO,QAAQ;;;ACAf,SAAS,aAAa;;;ACAtB,OAAOA,SAAQ;;;ACAf,SAAS,8BAA8B;;;APYvC,SAAS,6BAA6B,MAAM;AAC1C,WAAS,yBAAyB,MAAM;AACtC,OAAG,4DAA4D,MAAM;AACnE,YAAM,eAAe,CAAC,IAAI,QAAQ,GAAG,IAAI,QAAQ,GAAG,IAAI,QAAQ,CAAC;AACjE,YAAM,eAAe,IAAI,SAAS;AAClC,mBAAa,WAAW;AACxB,aAAO,oBAAoB,EAAE,MAAM,aAAa,CAAC,CAAC,EAAE,KAAK,YAAY;AAAA,IACvE,CAAC;AAED,OAAG,2DAA2D,MAAM;AAClE,YAAM,eAAe,CAAC,IAAI,QAAQ,GAAG,IAAI,QAAQ,CAAC;AAClD,YAAM,eAAe,IAAI,SAAS;AAClC,mBAAa,WAAW;AACxB,aAAO,oBAAoB,EAAE,MAAM,aAAa,CAAC,CAAC,EAAE,KAAK,YAAY;AAAA,IACvE,CAAC;AAED,OAAG,uEAAuE,MAAM;AAC9E,YAAM,uBAAuB;AAAA,QAC3B,IAAI,gBAAgB;AAAA,QACpB,IAAI,gBAAgB;AAAA,QACpB,IAAI,gBAAgB;AAAA,QACpB,IAAI,gBAAgB;AAAA,MACtB;AACA,YAAM,uBAAuB,IAAI,iBAAiB;AAClD,2BAAqB,mBAAmB;AACxC,aAAO,oBAAoB,EAAE,MAAM,qBAAqB,CAAC,CAAC,EAAE;AAAA,QAC1D;AAAA,MACF;AAAA,IACF,CAAC;AAED,OAAG,yGAAyG,MAAM;AAChH,YAAM,WAAW,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC;AAC/B,YAAM,WAAW,EAAE,OAAO,SAAS;AACnC,aAAO,oBAAoB,EAAE,MAAM,SAAS,CAAC,CAAC,EAAE,KAAK,QAAQ;AAAA,IAC/D,CAAC;AAED,OAAG,qEAAqE,MAAM;AAC5E,YAAM,oBAAoB,IAAI,kBAAkB;AAChD,aAAO,oBAAoB,EAAE,MAAM,kBAAkB,CAAC,CAAC,EAAE,KAAK,MAAS;AAAA,IACzE,CAAC;AAED,OAAG,wDAAwD,MAAM;AAE/D,aAAO,oBAAoB,CAAC,CAAC,CAAC,EAAE,KAAK,MAAS;AAAA,IAChD,CAAC;AAED,OAAG,2DAA2D,MAAM;AAClE,aAAO,MAAM;AAEX,eAAO,oBAAoB;AAAA,MAC7B,CAAC,EAAE,aAAa;AAAA,IAClB,CAAC;AAED,OAAG,qDAAqD,MAAM;AAC5D,aAAO,MAAM;AAEX,eAAO,oBAAoB,IAAI;AAAA,MACjC,CAAC,EAAE,aAAa;AAAA,IAClB,CAAC;AAAA,EACH,CAAC;AACH,CAAC;","names":["qs"]}