@things-factory/integration-accounting 4.0.9 → 4.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/pages/account-xero.js +8 -8
- package/dist-server/controllers/xero/apis/item/get-item.js +4 -5
- package/dist-server/controllers/xero/apis/item/get-item.js.map +1 -1
- package/dist-server/controllers/xero/apis/purchase-order/get-purchase-orders.js +28 -11
- package/dist-server/controllers/xero/apis/purchase-order/get-purchase-orders.js.map +1 -1
- package/dist-server/controllers/xero/platform-action.js +16 -2
- package/dist-server/controllers/xero/platform-action.js.map +1 -1
- package/dist-server/controllers/xero/xero.js +36 -0
- package/dist-server/controllers/xero/xero.js.map +1 -1
- package/package.json +15 -15
- package/server/controllers/xero/apis/item/get-item.ts +3 -6
- package/server/controllers/xero/apis/purchase-order/get-purchase-orders.ts +31 -12
- package/server/controllers/xero/platform-action.ts +16 -2
- package/server/controllers/xero/xero.ts +54 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { html, css } from 'lit-element'
|
|
2
1
|
import gql from 'graphql-tag'
|
|
2
|
+
import { css, html } from 'lit-element'
|
|
3
3
|
import { connect } from 'pwa-helpers/connect-mixin.js'
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
import { client, PageView, store } from '@things-factory/shell'
|
|
6
|
+
|
|
5
7
|
import LOGO from '../../assets/images/xero-logo.png'
|
|
6
8
|
|
|
7
9
|
const ACCOUNT_RESULT = `{
|
|
@@ -183,8 +185,6 @@ class AccountXero extends connect(store)(PageView) {
|
|
|
183
185
|
`
|
|
184
186
|
}
|
|
185
187
|
|
|
186
|
-
stateChanged(state) {}
|
|
187
|
-
|
|
188
188
|
updated(changed) {
|
|
189
189
|
if (changed.has('account')) {
|
|
190
190
|
var active = this.account && this.account.status == 'active'
|
|
@@ -212,7 +212,7 @@ class AccountXero extends connect(store)(PageView) {
|
|
|
212
212
|
|
|
213
213
|
const response = await client.query({
|
|
214
214
|
query: gql`
|
|
215
|
-
query($id: String!) {
|
|
215
|
+
query account($id: String!) {
|
|
216
216
|
account(id: $id) ${ACCOUNT_RESULT}
|
|
217
217
|
}
|
|
218
218
|
`,
|
|
@@ -234,7 +234,7 @@ class AccountXero extends connect(store)(PageView) {
|
|
|
234
234
|
async getXeroAuthURL() {
|
|
235
235
|
var response = await client.query({
|
|
236
236
|
query: gql`
|
|
237
|
-
query($accounting: String!) {
|
|
237
|
+
query getXeroAuthURL($accounting: String!) {
|
|
238
238
|
getXeroAuthURL(accounting: $accounting)
|
|
239
239
|
}
|
|
240
240
|
`,
|
|
@@ -266,7 +266,7 @@ class AccountXero extends connect(store)(PageView) {
|
|
|
266
266
|
|
|
267
267
|
var response = await client.mutate({
|
|
268
268
|
mutation: gql`
|
|
269
|
-
mutation($id: String!) {
|
|
269
|
+
mutation deactivateXeroAccount($id: String!) {
|
|
270
270
|
deactivateXeroAccount(id: $id) ${ACCOUNT_RESULT}
|
|
271
271
|
}
|
|
272
272
|
`,
|
|
@@ -293,7 +293,7 @@ class AccountXero extends connect(store)(PageView) {
|
|
|
293
293
|
|
|
294
294
|
var response = await client.mutate({
|
|
295
295
|
mutation: gql`
|
|
296
|
-
mutation($id: String!) {
|
|
296
|
+
mutation refreshXeroAccessToken($id: String!) {
|
|
297
297
|
refreshXeroAccessToken(id: $id) ${ACCOUNT_RESULT}
|
|
298
298
|
}
|
|
299
299
|
`,
|
|
@@ -4,13 +4,12 @@ exports.getItem = void 0;
|
|
|
4
4
|
function getItem() {
|
|
5
5
|
return {
|
|
6
6
|
method: 'get',
|
|
7
|
-
path: '/Items/{
|
|
7
|
+
path: '/Items/{Code}',
|
|
8
8
|
denormalize(req) {
|
|
9
9
|
return {
|
|
10
10
|
resource: {
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
payload: Object.assign({}, req)
|
|
11
|
+
Code: req.itemCode
|
|
12
|
+
}
|
|
14
13
|
};
|
|
15
14
|
},
|
|
16
15
|
normalize(res) {
|
|
@@ -28,7 +27,7 @@ function getItem() {
|
|
|
28
27
|
cogsAccountCode
|
|
29
28
|
};
|
|
30
29
|
});
|
|
31
|
-
return results;
|
|
30
|
+
return results[0];
|
|
32
31
|
}
|
|
33
32
|
};
|
|
34
33
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-item.js","sourceRoot":"","sources":["../../../../../server/controllers/xero/apis/item/get-item.ts"],"names":[],"mappings":";;;AAAA,SAAgB,OAAO;IACrB,OAAO;QACL,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"get-item.js","sourceRoot":"","sources":["../../../../../server/controllers/xero/apis/item/get-item.ts"],"names":[],"mappings":";;;AAAA,SAAgB,OAAO;IACrB,OAAO;QACL,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,eAAe;QACrB,WAAW,CAAC,GAAG;YACb,OAAO;gBACL,QAAQ,EAAE;oBACR,IAAI,EAAE,GAAG,CAAC,QAAQ;iBACnB;aACF,CAAA;QACH,CAAC;QACD,SAAS,CAAC,GAAG;YACX,IAAI,EAAE,KAAK,EAAE,GAAG,GAAG,CAAA;YAEnB,IAAI,OAAO,GAAU,EAAE,CAAA;YAEvB,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;gBACzB,IAAI,EACF,IAAI,EAAE,GAAG,EACT,WAAW,EAAE,WAAW,EACxB,eAAe,EACf,IAAI,EAAE,IAAI,EACV,oBAAoB,EAAE,oBAAoB,EAC1C,yBAAyB,EAAE,oBAAoB,EAChD,GAAG,IAAI,CAAA;gBACR,IAAI,EAAE,WAAW,EAAE,eAAe,EAAE,GAAG,eAAe,CAAA;gBAEtD,OAAO;oBACL,GAAG;oBACH,WAAW;oBACX,IAAI;oBACJ,oBAAoB;oBACpB,oBAAoB;oBACpB,eAAe;iBAChB,CAAA;YACH,CAAC,CAAC,CAAA;YAEF,OAAO,OAAO,CAAC,CAAC,CAAC,CAAA;QACnB,CAAC;KACF,CAAA;AACH,CAAC;AAxCD,0BAwCC"}
|
|
@@ -11,17 +11,34 @@ function getPurchaseOrders() {
|
|
|
11
11
|
};
|
|
12
12
|
},
|
|
13
13
|
normalize(res) {
|
|
14
|
-
|
|
15
|
-
let results =
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
const { PurchaseOrders } = res;
|
|
15
|
+
let results = PurchaseOrders.map(purchaseOrder => {
|
|
16
|
+
const { PurchaseOrderNumber: refNo, Reference: refNo2, DeliveryDateString, LineItems } = purchaseOrder;
|
|
17
|
+
const etaDate = DeliveryDateString !== undefined ? String(DeliveryDateString.split('T')[0]) : 'undefined';
|
|
18
|
+
let products = LineItems.map(lineItem => {
|
|
19
|
+
return {
|
|
20
|
+
product: { sku: lineItem.ItemCode },
|
|
21
|
+
batchId: refNo,
|
|
22
|
+
packQty: lineItem.Quantity,
|
|
23
|
+
unitPrice: lineItem.UnitAmount
|
|
24
|
+
};
|
|
25
|
+
}).reduce((newItem, item) => {
|
|
26
|
+
var foundItem = newItem.find(newItem => newItem.batchId === item.batchId && newItem.product.sku === item.product.sku);
|
|
27
|
+
if (!foundItem) {
|
|
28
|
+
newItem.push(Object.assign({}, item));
|
|
29
|
+
return newItem;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
return newItem.map(ni => {
|
|
33
|
+
if (ni.batchId === item.batchId && ni.product.sku === item.product.sku) {
|
|
34
|
+
return Object.assign(Object.assign({}, ni), { packQty: ni.packQty + item.packQty });
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
return ni;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}, []);
|
|
25
42
|
return {
|
|
26
43
|
refNo,
|
|
27
44
|
refNo2,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-purchase-orders.js","sourceRoot":"","sources":["../../../../../server/controllers/xero/apis/purchase-order/get-purchase-orders.ts"],"names":[],"mappings":";;;AAAA,SAAgB,iBAAiB;IAC/B,OAAO;QACL,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,iBAAiB;QACvB,WAAW,CAAC,GAAG;YACb,OAAO;gBACL,OAAO,oBACF,GAAG,CACP;aACF,CAAA;QACH,CAAC;QACD,SAAS,CAAC,GAAG;YACX,
|
|
1
|
+
{"version":3,"file":"get-purchase-orders.js","sourceRoot":"","sources":["../../../../../server/controllers/xero/apis/purchase-order/get-purchase-orders.ts"],"names":[],"mappings":";;;AAAA,SAAgB,iBAAiB;IAC/B,OAAO;QACL,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,iBAAiB;QACvB,WAAW,CAAC,GAAG;YACb,OAAO;gBACL,OAAO,oBACF,GAAG,CACP;aACF,CAAA;QACH,CAAC;QACD,SAAS,CAAC,GAAG;YACX,MAAM,EAAE,cAAc,EAAE,GAAG,GAAG,CAAA;YAE9B,IAAI,OAAO,GAAU,cAAc,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE;gBACtD,MAAM,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,kBAAkB,EAAE,SAAS,EAAE,GAAG,aAAa,CAAA;gBACtG,MAAM,OAAO,GAAQ,kBAAkB,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,kBAAkB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAA;gBAE9G,IAAI,QAAQ,GAAU,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;oBAC7C,OAAO;wBACL,OAAO,EAAE,EAAE,GAAG,EAAE,QAAQ,CAAC,QAAQ,EAAE;wBACnC,OAAO,EAAE,KAAK;wBACd,OAAO,EAAE,QAAQ,CAAC,QAAQ;wBAC1B,SAAS,EAAE,QAAQ,CAAC,UAAU;qBAC/B,CAAA;gBACH,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE;oBAC1B,IAAI,SAAS,GAAG,OAAO,CAAC,IAAI,CAC1B,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CACxF,CAAA;oBACD,IAAI,CAAC,SAAS,EAAE;wBACd,OAAO,CAAC,IAAI,mBAAM,IAAI,EAAG,CAAA;wBACzB,OAAO,OAAO,CAAA;qBACf;yBAAM;wBACL,OAAO,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;4BACtB,IAAI,EAAE,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,OAAO,CAAC,GAAG,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE;gCACtE,uCACK,EAAE,KACL,OAAO,EAAE,EAAE,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,IACnC;6BACF;iCAAM;gCACL,OAAO,EAAE,CAAA;6BACV;wBACH,CAAC,CAAC,CAAA;qBACH;gBACH,CAAC,EAAE,EAAE,CAAC,CAAA;gBAEN,OAAO;oBACL,KAAK;oBACL,MAAM;oBACN,OAAO;oBACP,YAAY,EAAE,IAAI;oBAClB,WAAW,EAAE,KAAK;oBAClB,SAAS,EAAE,KAAK;oBAChB,aAAa,EAAE,QAAQ;iBACxB,CAAA;YACH,CAAC,CAAC,CAAA;YAEF,OAAO,OAAO,CAAA;QAChB,CAAC;KACF,CAAA;AACH,CAAC;AA5DD,8CA4DC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.action = void 0;
|
|
4
|
-
const xero_1 = require("./xero");
|
|
5
4
|
const env_1 = require("@things-factory/env");
|
|
5
|
+
const xero_1 = require("./xero");
|
|
6
6
|
const xeroConfig = env_1.config.get('accountingIntegrationXero', {});
|
|
7
7
|
const { apiKey, apiSecret, callback } = xeroConfig;
|
|
8
8
|
function substitute(path, obj) {
|
|
@@ -19,7 +19,7 @@ function substitute(path, obj) {
|
|
|
19
19
|
});
|
|
20
20
|
return result;
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
async function _action({ accounting, method = 'get', path, request }) {
|
|
23
23
|
const client = new xero_1.Xero({
|
|
24
24
|
apiKey,
|
|
25
25
|
apiSecret,
|
|
@@ -30,6 +30,20 @@ const action = async ({ accounting, method = 'get', path, request }) => {
|
|
|
30
30
|
const { resource = {}, payload = {} } = request;
|
|
31
31
|
path = substitute(path, resource);
|
|
32
32
|
return await client[method](path, payload);
|
|
33
|
+
}
|
|
34
|
+
const action = async ({ accounting, method = 'get', path, request }) => {
|
|
35
|
+
try {
|
|
36
|
+
return await _action({ accounting, method, path, request });
|
|
37
|
+
}
|
|
38
|
+
catch (ex) {
|
|
39
|
+
if (ex.status === 401) {
|
|
40
|
+
var refreshedAccounting = await xero_1.Xero.refreshAccessToken(apiKey, apiSecret, accounting);
|
|
41
|
+
return await _action({ accounting: refreshedAccounting, method, path, request });
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
throw ex;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
33
47
|
};
|
|
34
48
|
exports.action = action;
|
|
35
49
|
//# sourceMappingURL=platform-action.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform-action.js","sourceRoot":"","sources":["../../../server/controllers/xero/platform-action.ts"],"names":[],"mappings":";;;AAAA,iCAA6B;AAE7B,
|
|
1
|
+
{"version":3,"file":"platform-action.js","sourceRoot":"","sources":["../../../server/controllers/xero/platform-action.ts"],"names":[],"mappings":";;;AAAA,6CAA4C;AAE5C,iCAA6B;AAE7B,MAAM,UAAU,GAAG,YAAM,CAAC,GAAG,CAAC,2BAA2B,EAAE,EAAE,CAAC,CAAA;AAC9D,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,UAAU,CAAA;AAElD,SAAS,UAAU,CAAC,IAAI,EAAE,GAAG;IAC3B,IAAI,KAAK,GAAG,EAAE,CAAA;IACd,IAAI,EAAE,GAAG,YAAY,CAAA;IACrB,IAAI,IAAI,CAAA;IAER,OAAO,CAAC,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE;QAC7B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;KACpB;IAED,IAAI,MAAM,GAAG,IAAI,CAAA;IACjB,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QACnB,IAAI,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;QAC5B,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,GAAG,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;IACxE,CAAC,CAAC,CAAA;IAEF,OAAO,MAAM,CAAA;AACf,CAAC;AAED,KAAK,UAAU,OAAO,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE;IAClE,MAAM,MAAM,GAAG,IAAI,WAAI,CAAC;QACtB,MAAM;QACN,SAAS;QACT,WAAW,EAAE,UAAU,CAAC,WAAW;QACnC,QAAQ,EAAE,UAAU,CAAC,SAAS;QAC9B,QAAQ;KACT,CAAC,CAAA;IAEF,MAAM,EAAE,QAAQ,GAAG,EAAE,EAAE,OAAO,GAAG,EAAE,EAAE,GAAG,OAAO,CAAA;IAE/C,IAAI,GAAG,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IAEjC,OAAO,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;AAC5C,CAAC;AAEM,MAAM,MAAM,GAAG,KAAK,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;IAC5E,IAAI;QACF,OAAO,MAAM,OAAO,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;KAC5D;IAAC,OAAO,EAAE,EAAE;QACX,IAAI,EAAE,CAAC,MAAM,KAAK,GAAG,EAAE;YACrB,IAAI,mBAAmB,GAAG,MAAM,WAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,CAAC,CAAA;YACtF,OAAO,MAAM,OAAO,CAAC,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;SACjF;aAAM;YACL,MAAM,EAAE,CAAA;SACT;KACF;AACH,CAAC,CAAA;AAXY,QAAA,MAAM,UAWlB"}
|
|
@@ -5,6 +5,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.Xero = void 0;
|
|
7
7
|
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
8
|
+
const typeorm_1 = require("typeorm");
|
|
9
|
+
const utils_1 = require("@things-factory/utils");
|
|
10
|
+
const account_1 = require("../../entities/account");
|
|
8
11
|
const ENDPOINT = 'https://api.xero.com/api.xro/2.0';
|
|
9
12
|
const debug = require('debug')('things-factory:integration-accounting:xero');
|
|
10
13
|
class Xero {
|
|
@@ -116,6 +119,39 @@ class Xero {
|
|
|
116
119
|
result.ok = result.Status.toLowerCase() === 'ok';
|
|
117
120
|
return result;
|
|
118
121
|
}
|
|
122
|
+
static async refreshAccessToken(apiKey, apiSecret, account) {
|
|
123
|
+
const refreshRequestData = {
|
|
124
|
+
grant_type: 'refresh_token',
|
|
125
|
+
refresh_token: account.refreshToken
|
|
126
|
+
};
|
|
127
|
+
const refreshResponse = await (0, node_fetch_1.default)(`https://identity.xero.com/connect/token`, {
|
|
128
|
+
method: 'post',
|
|
129
|
+
headers: {
|
|
130
|
+
Authorization: `Basic ${Buffer.from(apiKey + ':' + apiSecret).toString('base64')}`,
|
|
131
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
132
|
+
},
|
|
133
|
+
body: Object.entries(refreshRequestData)
|
|
134
|
+
.map(([key, value]) => encodeURIComponent(key) + '=' + encodeURIComponent(value))
|
|
135
|
+
.join('&')
|
|
136
|
+
});
|
|
137
|
+
if (!refreshResponse.ok) {
|
|
138
|
+
throw new Error(`get account information failed: ${await refreshResponse.text()}`);
|
|
139
|
+
}
|
|
140
|
+
const body = await refreshResponse.json();
|
|
141
|
+
const { access_token /* token used to call the API */, id_token /* token containing user identity details (only returned if OpenID Connect scopes are requested) */, expires_in /* amount of seconds until the access token expires */, token_type: tokenType /* must be Bearer */, refresh_token
|
|
142
|
+
/* token used to refresh the access token once it has expired (only returned if the offline_access scope is requested).
|
|
143
|
+
*/
|
|
144
|
+
} = body;
|
|
145
|
+
const { exp } = (0, utils_1.parseJwt)(access_token);
|
|
146
|
+
var patch = {
|
|
147
|
+
accessToken: access_token,
|
|
148
|
+
refreshToken: refresh_token,
|
|
149
|
+
tokenType,
|
|
150
|
+
expiresIn: new Date(exp * 1000)
|
|
151
|
+
};
|
|
152
|
+
const repository = (0, typeorm_1.getRepository)(account_1.Account);
|
|
153
|
+
return await repository.save(Object.assign(Object.assign({}, account), patch));
|
|
154
|
+
}
|
|
119
155
|
}
|
|
120
156
|
exports.Xero = Xero;
|
|
121
157
|
//# sourceMappingURL=xero.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xero.js","sourceRoot":"","sources":["../../../server/controllers/xero/xero.ts"],"names":[],"mappings":";;;;;;AAAA,4DAA8B;
|
|
1
|
+
{"version":3,"file":"xero.js","sourceRoot":"","sources":["../../../server/controllers/xero/xero.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/integration-accounting",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.13",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"browser": "client/index.js",
|
|
6
6
|
"things-factory": true,
|
|
@@ -25,19 +25,19 @@
|
|
|
25
25
|
"test": "DEBUG=things-factory:* NODE_ENV=development npx mocha -r ts-node/register ./test/**/*spec.ts"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@things-factory/apptool-ui": "^4.0.
|
|
29
|
-
"@things-factory/auth-ui": "^4.0.
|
|
30
|
-
"@things-factory/biz-base": "^4.0.
|
|
31
|
-
"@things-factory/code-ui": "^4.0.
|
|
32
|
-
"@things-factory/context-ui": "^4.0.
|
|
33
|
-
"@things-factory/grist-ui": "^4.0.
|
|
34
|
-
"@things-factory/i18n-ui": "^4.0.
|
|
35
|
-
"@things-factory/integration-ui": "^4.0.
|
|
36
|
-
"@things-factory/more-ui": "^4.0.
|
|
37
|
-
"@things-factory/product-base": "^4.0.
|
|
38
|
-
"@things-factory/resource-ui": "^4.0.
|
|
39
|
-
"@things-factory/setting-ui": "^4.0.
|
|
40
|
-
"@things-factory/system-ui": "^4.0.
|
|
28
|
+
"@things-factory/apptool-ui": "^4.0.13",
|
|
29
|
+
"@things-factory/auth-ui": "^4.0.13",
|
|
30
|
+
"@things-factory/biz-base": "^4.0.13",
|
|
31
|
+
"@things-factory/code-ui": "^4.0.13",
|
|
32
|
+
"@things-factory/context-ui": "^4.0.13",
|
|
33
|
+
"@things-factory/grist-ui": "^4.0.13",
|
|
34
|
+
"@things-factory/i18n-ui": "^4.0.13",
|
|
35
|
+
"@things-factory/integration-ui": "^4.0.13",
|
|
36
|
+
"@things-factory/more-ui": "^4.0.13",
|
|
37
|
+
"@things-factory/product-base": "^4.0.13",
|
|
38
|
+
"@things-factory/resource-ui": "^4.0.13",
|
|
39
|
+
"@things-factory/setting-ui": "^4.0.13",
|
|
40
|
+
"@things-factory/system-ui": "^4.0.13",
|
|
41
41
|
"debug": "^4.1.1",
|
|
42
42
|
"node-fetch": "^2.6.0"
|
|
43
43
|
},
|
|
@@ -52,5 +52,5 @@
|
|
|
52
52
|
"nock": "^13.0.2",
|
|
53
53
|
"should": "^13.2.3"
|
|
54
54
|
},
|
|
55
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "a542fdd4e7c447c3b571fe91c92f5919561bdc2c"
|
|
56
56
|
}
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
export function getItem() {
|
|
2
2
|
return {
|
|
3
3
|
method: 'get',
|
|
4
|
-
path: '/Items/{
|
|
4
|
+
path: '/Items/{Code}',
|
|
5
5
|
denormalize(req) {
|
|
6
6
|
return {
|
|
7
7
|
resource: {
|
|
8
|
-
|
|
9
|
-
},
|
|
10
|
-
payload: {
|
|
11
|
-
...req
|
|
8
|
+
Code: req.itemCode
|
|
12
9
|
}
|
|
13
10
|
}
|
|
14
11
|
},
|
|
@@ -38,7 +35,7 @@ export function getItem() {
|
|
|
38
35
|
}
|
|
39
36
|
})
|
|
40
37
|
|
|
41
|
-
return results
|
|
38
|
+
return results[0]
|
|
42
39
|
}
|
|
43
40
|
}
|
|
44
41
|
}
|
|
@@ -10,21 +10,39 @@ export function getPurchaseOrders() {
|
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
12
|
normalize(res) {
|
|
13
|
-
|
|
13
|
+
const { PurchaseOrders } = res
|
|
14
14
|
|
|
15
|
-
let results: any[] =
|
|
15
|
+
let results: any[] = PurchaseOrders.map(purchaseOrder => {
|
|
16
|
+
const { PurchaseOrderNumber: refNo, Reference: refNo2, DeliveryDateString, LineItems } = purchaseOrder
|
|
17
|
+
const etaDate: any = DeliveryDateString !== undefined ? String(DeliveryDateString.split('T')[0]) : 'undefined'
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
let products: any[] = LineItems.map(lineItem => {
|
|
20
|
+
return {
|
|
21
|
+
product: { sku: lineItem.ItemCode },
|
|
22
|
+
batchId: refNo,
|
|
23
|
+
packQty: lineItem.Quantity,
|
|
24
|
+
unitPrice: lineItem.UnitAmount
|
|
25
|
+
}
|
|
26
|
+
}).reduce((newItem, item) => {
|
|
27
|
+
var foundItem = newItem.find(
|
|
28
|
+
newItem => newItem.batchId === item.batchId && newItem.product.sku === item.product.sku
|
|
29
|
+
)
|
|
30
|
+
if (!foundItem) {
|
|
31
|
+
newItem.push({ ...item })
|
|
32
|
+
return newItem
|
|
33
|
+
} else {
|
|
34
|
+
return newItem.map(ni => {
|
|
35
|
+
if (ni.batchId === item.batchId && ni.product.sku === item.product.sku) {
|
|
36
|
+
return {
|
|
37
|
+
...ni,
|
|
38
|
+
packQty: ni.packQty + item.packQty
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
return ni
|
|
42
|
+
}
|
|
26
43
|
})
|
|
27
|
-
|
|
44
|
+
}
|
|
45
|
+
}, [])
|
|
28
46
|
|
|
29
47
|
return {
|
|
30
48
|
refNo,
|
|
@@ -36,6 +54,7 @@ export function getPurchaseOrders() {
|
|
|
36
54
|
orderProducts: products
|
|
37
55
|
}
|
|
38
56
|
})
|
|
57
|
+
|
|
39
58
|
return results
|
|
40
59
|
}
|
|
41
60
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { config } from '@things-factory/env'
|
|
2
|
+
|
|
1
3
|
import { Xero } from './xero'
|
|
2
4
|
|
|
3
|
-
import { config } from '@things-factory/env'
|
|
4
5
|
const xeroConfig = config.get('accountingIntegrationXero', {})
|
|
5
6
|
const { apiKey, apiSecret, callback } = xeroConfig
|
|
6
7
|
|
|
@@ -22,7 +23,7 @@ function substitute(path, obj) {
|
|
|
22
23
|
return result
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
async function _action({ accounting, method = 'get', path, request }) {
|
|
26
27
|
const client = new Xero({
|
|
27
28
|
apiKey,
|
|
28
29
|
apiSecret,
|
|
@@ -37,3 +38,16 @@ export const action = async ({ accounting, method = 'get', path, request }) => {
|
|
|
37
38
|
|
|
38
39
|
return await client[method](path, payload)
|
|
39
40
|
}
|
|
41
|
+
|
|
42
|
+
export const action = async ({ accounting, method = 'get', path, request }) => {
|
|
43
|
+
try {
|
|
44
|
+
return await _action({ accounting, method, path, request })
|
|
45
|
+
} catch (ex) {
|
|
46
|
+
if (ex.status === 401) {
|
|
47
|
+
var refreshedAccounting = await Xero.refreshAccessToken(apiKey, apiSecret, accounting)
|
|
48
|
+
return await _action({ accounting: refreshedAccounting, method, path, request })
|
|
49
|
+
} else {
|
|
50
|
+
throw ex
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import fetch from 'node-fetch'
|
|
2
|
+
import { getRepository } from 'typeorm'
|
|
3
|
+
|
|
4
|
+
import { parseJwt } from '@things-factory/utils'
|
|
5
|
+
|
|
6
|
+
import { Account } from '../../entities/account'
|
|
2
7
|
|
|
3
8
|
const ENDPOINT = 'https://api.xero.com/api.xro/2.0'
|
|
4
9
|
const debug = require('debug')('things-factory:integration-accounting:xero')
|
|
@@ -158,4 +163,53 @@ export class Xero {
|
|
|
158
163
|
result.ok = result.Status.toLowerCase() === 'ok'
|
|
159
164
|
return result
|
|
160
165
|
}
|
|
166
|
+
|
|
167
|
+
public static async refreshAccessToken(apiKey, apiSecret, account) {
|
|
168
|
+
const refreshRequestData = {
|
|
169
|
+
grant_type: 'refresh_token',
|
|
170
|
+
refresh_token: account.refreshToken
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const refreshResponse = await fetch(`https://identity.xero.com/connect/token`, {
|
|
174
|
+
method: 'post',
|
|
175
|
+
headers: {
|
|
176
|
+
Authorization: `Basic ${Buffer.from(apiKey + ':' + apiSecret).toString('base64')}`,
|
|
177
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
178
|
+
},
|
|
179
|
+
body: Object.entries(refreshRequestData)
|
|
180
|
+
.map(([key, value]) => encodeURIComponent(key) + '=' + encodeURIComponent(value))
|
|
181
|
+
.join('&')
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
if (!refreshResponse.ok) {
|
|
185
|
+
throw new Error(`get account information failed: ${await refreshResponse.text()}`)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const body = await refreshResponse.json()
|
|
189
|
+
const {
|
|
190
|
+
access_token /* token used to call the API */,
|
|
191
|
+
id_token /* token containing user identity details (only returned if OpenID Connect scopes are requested) */,
|
|
192
|
+
expires_in /* amount of seconds until the access token expires */,
|
|
193
|
+
token_type: tokenType /* must be Bearer */,
|
|
194
|
+
refresh_token
|
|
195
|
+
/* token used to refresh the access token once it has expired (only returned if the offline_access scope is requested).
|
|
196
|
+
*/
|
|
197
|
+
} = body
|
|
198
|
+
|
|
199
|
+
const { exp } = parseJwt(access_token)
|
|
200
|
+
|
|
201
|
+
var patch = {
|
|
202
|
+
accessToken: access_token,
|
|
203
|
+
refreshToken: refresh_token,
|
|
204
|
+
tokenType,
|
|
205
|
+
expiresIn: new Date(exp * 1000)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const repository = getRepository(Account)
|
|
209
|
+
|
|
210
|
+
return await repository.save({
|
|
211
|
+
...account,
|
|
212
|
+
...patch
|
|
213
|
+
})
|
|
214
|
+
}
|
|
161
215
|
}
|