@cloudcommerce/app-tiny-erp 0.0.59 → 0.0.62

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.
Files changed (54) hide show
  1. package/.turbo/turbo-build.log +30 -4
  2. package/lib/event-to-tiny.js +117 -0
  3. package/lib/event-to-tiny.js.map +1 -0
  4. package/lib/index.js +2 -0
  5. package/lib/index.js.map +1 -0
  6. package/lib/integration/after-tiny-queue.js +79 -0
  7. package/lib/integration/after-tiny-queue.js.map +1 -0
  8. package/lib/integration/export-order-to-tiny.js +81 -0
  9. package/lib/integration/export-order-to-tiny.js.map +1 -0
  10. package/lib/integration/export-product-to-tiny.js +58 -0
  11. package/lib/integration/export-product-to-tiny.js.map +1 -0
  12. package/lib/integration/helpers/format-tiny-date.js +7 -0
  13. package/lib/integration/helpers/format-tiny-date.js.map +1 -0
  14. package/lib/integration/import-order-from-tiny.js +92 -0
  15. package/lib/integration/import-order-from-tiny.js.map +1 -0
  16. package/lib/integration/import-product-from-tiny.js +158 -0
  17. package/lib/integration/import-product-from-tiny.js.map +1 -0
  18. package/lib/integration/parsers/order-from-tiny.js +46 -0
  19. package/lib/integration/parsers/order-from-tiny.js.map +1 -0
  20. package/lib/integration/parsers/order-to-tiny.js +193 -0
  21. package/lib/integration/parsers/order-to-tiny.js.map +1 -0
  22. package/lib/integration/parsers/product-from-tiny.js +199 -0
  23. package/lib/integration/parsers/product-from-tiny.js.map +1 -0
  24. package/lib/integration/parsers/product-to-tiny.js +129 -0
  25. package/lib/integration/parsers/product-to-tiny.js.map +1 -0
  26. package/lib/integration/parsers/status-from-tiny.js +34 -0
  27. package/lib/integration/parsers/status-from-tiny.js.map +1 -0
  28. package/lib/integration/parsers/status-to-tiny.js +39 -0
  29. package/lib/integration/parsers/status-to-tiny.js.map +1 -0
  30. package/lib/integration/post-tiny-erp.js +47 -0
  31. package/lib/integration/post-tiny-erp.js.map +1 -0
  32. package/lib/tiny-erp.js +17 -0
  33. package/lib/tiny-erp.js.map +1 -0
  34. package/lib/tiny-webhook.js +92 -0
  35. package/lib/tiny-webhook.js.map +1 -0
  36. package/package.json +13 -6
  37. package/src/event-to-tiny.ts +136 -0
  38. package/src/index.ts +1 -0
  39. package/src/integration/after-tiny-queue.ts +80 -0
  40. package/src/integration/export-order-to-tiny.ts +86 -0
  41. package/src/integration/export-product-to-tiny.ts +60 -0
  42. package/src/integration/helpers/format-tiny-date.ts +6 -0
  43. package/src/integration/import-order-from-tiny.ts +102 -0
  44. package/src/integration/import-product-from-tiny.ts +162 -0
  45. package/src/integration/parsers/order-from-tiny.ts +49 -0
  46. package/src/integration/parsers/order-to-tiny.ts +205 -0
  47. package/src/integration/parsers/product-from-tiny.ts +215 -0
  48. package/src/integration/parsers/product-to-tiny.ts +138 -0
  49. package/src/integration/parsers/status-from-tiny.ts +35 -0
  50. package/src/integration/parsers/status-to-tiny.ts +42 -0
  51. package/src/integration/post-tiny-erp.ts +52 -0
  52. package/src/tiny-erp.ts +23 -0
  53. package/src/tiny-webhook.ts +100 -0
  54. package/src/firebase.ts +0 -0
@@ -0,0 +1,215 @@
1
+ import type { Products } from '@cloudcommerce/types';
2
+ import logger from 'firebase-functions/lib/logger';
3
+ import axios from 'axios';
4
+ import FormData from 'form-data';
5
+ import ecomUtils from '@ecomplus/utils';
6
+ import getEnv from '@cloudcommerce/firebase/lib/env';
7
+
8
+ const removeAccents = (str: string) => str.replace(/áàãâÁÀÃÂ/g, 'a')
9
+ .replace(/éêÉÊ/g, 'e')
10
+ .replace(/óõôÓÕÔ/g, 'o')
11
+ .replace(/íÍ/g, 'e')
12
+ .replace(/úÚ/g, 'u')
13
+ .replace(/çÇ/g, 'c');
14
+
15
+ const tryImageUpload = (originImgUrl: string, product: Products) => new Promise((resolve) => {
16
+ const {
17
+ storeId,
18
+ apiAuth: {
19
+ authenticationId,
20
+ apiKey,
21
+ },
22
+ } = getEnv();
23
+ axios.get(originImgUrl, {
24
+ responseType: 'arraybuffer',
25
+ }).then(({ data }) => {
26
+ const form = new FormData();
27
+ form.append('file', Buffer.from(data), originImgUrl.replace(/.*\/([^/]+)$/, '$1'));
28
+
29
+ return axios.post(`https://apx-storage.e-com.plus/${storeId}/api/v1/upload.json`, form, {
30
+ headers: {
31
+ ...form.getHeaders(),
32
+ 'X-Store-ID': storeId,
33
+ 'X-My-ID': authenticationId,
34
+ 'X-API-Key': apiKey,
35
+ },
36
+ }).then(({ data, status }) => {
37
+ if (data.picture) {
38
+ Object.keys(data.picture).forEach((imgSize) => {
39
+ if (data.picture[imgSize]) {
40
+ if (!data.picture[imgSize].url) {
41
+ delete data.picture[imgSize];
42
+ return;
43
+ }
44
+ if (data.picture[imgSize].size !== undefined) {
45
+ delete data.picture[imgSize].size;
46
+ }
47
+ data.picture[imgSize].alt = `${product.name} (${imgSize})`;
48
+ }
49
+ });
50
+ if (Object.keys(data.picture).length) {
51
+ return resolve({
52
+ _id: ecomUtils.randomObjectId(),
53
+ ...data.picture,
54
+ });
55
+ }
56
+ }
57
+ const err: any = new Error('Unexpected Storage API response');
58
+ err.response = { data, status };
59
+ throw err;
60
+ });
61
+ })
62
+
63
+ .catch((err) => {
64
+ logger.error(err);
65
+ resolve({
66
+ _id: ecomUtils.randomObjectId(),
67
+ normal: {
68
+ url: originImgUrl,
69
+ alt: product.name,
70
+ },
71
+ });
72
+ });
73
+ }).then((picture) => {
74
+ if (product && product.pictures) {
75
+ // @ts-ignore
76
+ product.pictures.push(picture);
77
+ }
78
+ return picture;
79
+ });
80
+
81
+ export default (tinyProduct, isNew = true) => new Promise((resolve) => {
82
+ const sku = tinyProduct.codigo || String(tinyProduct.id);
83
+ const name = (tinyProduct.nome || sku).trim();
84
+ const product: Omit<Products, '_id'| 'store_id' | 'created_at' | 'updated_at'> = {
85
+ available: tinyProduct.situacao === 'A',
86
+ sku,
87
+ name,
88
+ cost_price: tinyProduct.preco_custo,
89
+ price: tinyProduct.preco_promocional || tinyProduct.preco,
90
+ base_price: tinyProduct.preco,
91
+ body_html: tinyProduct.descricao_complementar,
92
+ };
93
+
94
+ if (isNew) {
95
+ product.slug = removeAccents(name.toLowerCase())
96
+ .replace(/\s+/g, '-')
97
+ .replace(/[^a-z0-9-_./]/g, '');
98
+ if (!/[a-z0-9]/.test(product.slug.charAt(0))) {
99
+ product.slug = `p-${product.slug}`;
100
+ }
101
+ }
102
+ if (tinyProduct.garantia) {
103
+ product.warranty = tinyProduct.garantia;
104
+ }
105
+ if (tinyProduct.unidade_por_caixa) {
106
+ product.min_quantity = Number(tinyProduct.unidade_por_caixa);
107
+ }
108
+ if (tinyProduct.ncm) {
109
+ product.mpn = [tinyProduct.ncm];
110
+ }
111
+ const validateGtin = (gtin) => {
112
+ return typeof gtin === 'string' && /^([0-9]{8}|[0-9]{12,14})$/.test(gtin);
113
+ };
114
+ if (validateGtin(tinyProduct.gtin)) {
115
+ product.gtin = [tinyProduct.gtin];
116
+ if (validateGtin(tinyProduct.gtin_embalagem)) {
117
+ product.gtin.push(tinyProduct.gtin_embalagem);
118
+ }
119
+ }
120
+
121
+ const weight = tinyProduct.peso_bruto || tinyProduct.peso_liquido;
122
+ if (weight > 0) {
123
+ product.weight = {
124
+ unit: 'kg',
125
+ value: parseFloat(weight),
126
+ };
127
+ }
128
+ [
129
+ ['largura', 'width'],
130
+ ['altura', 'height'],
131
+ ['comprimento', 'length'],
132
+ ].forEach(([lado, side]) => {
133
+ const dimension = tinyProduct[`${lado}_embalagem`] || tinyProduct[`${lado}Embalagem`];
134
+ if (dimension > 0) {
135
+ if (!product.dimensions) {
136
+ product.dimensions = {};
137
+ }
138
+ product.dimensions[side] = {
139
+ unit: 'cm',
140
+ value: parseFloat(dimension),
141
+ };
142
+ }
143
+ });
144
+
145
+ if (isNew) {
146
+ if (Array.isArray(tinyProduct.variacoes) && tinyProduct.variacoes.length) {
147
+ product.variations = [];
148
+ tinyProduct.variacoes.forEach(({ variacao }) => {
149
+ const { codigo, preco, grade } = variacao;
150
+ if (grade && typeof grade === 'object') {
151
+ const specifications = {};
152
+ const specTexts: string[] = [];
153
+ Object.keys(grade).forEach((tipo) => {
154
+ if (grade[tipo]) {
155
+ const gridId = removeAccents(tipo.toLowerCase())
156
+ .replace(/\s+/g, '_')
157
+ .replace(/[^a-z0-9_]/g, '')
158
+ .substring(0, 30)
159
+ .padStart(2, 'i');
160
+ const spec: Record<string, string> = {
161
+ text: grade[tipo],
162
+ };
163
+ specTexts.push(spec.text);
164
+ if (gridId !== 'colors') {
165
+ spec.value = removeAccents(spec.text.toLowerCase()).substring(0, 100);
166
+ }
167
+ specifications[gridId] = [spec];
168
+ }
169
+ });
170
+
171
+ if (specTexts.length) {
172
+ product.variations?.push({
173
+ _id: ecomUtils.randomObjectId(),
174
+ name: `${name} / ${specTexts.join(' / ')}`.substring(0, 100),
175
+ sku: codigo,
176
+ specifications,
177
+ price: parseFloat(preco || 0),
178
+ });
179
+ }
180
+ }
181
+ });
182
+ }
183
+
184
+ if (Array.isArray(tinyProduct.imagens_externas)) {
185
+ product.pictures = [];
186
+ tinyProduct.imagens_externas.forEach((imagemExterna) => {
187
+ if (imagemExterna.imagem_externa) {
188
+ const { url } = imagemExterna.imagem_externa;
189
+ if (url) {
190
+ product.pictures?.push({
191
+ normal: { url },
192
+ _id: ecomUtils.randomObjectId(),
193
+ });
194
+ }
195
+ }
196
+ });
197
+ }
198
+
199
+ if (tinyProduct.anexos) {
200
+ if (!product.pictures) {
201
+ product.pictures = [];
202
+ }
203
+ const promises: Promise<any>[] = [];
204
+ tinyProduct.anexos.forEach(({ anexo }) => {
205
+ if (typeof anexo === 'string' && anexo.startsWith('http')) {
206
+ promises.push(tryImageUpload(anexo, product as Products));
207
+ }
208
+ });
209
+ Promise.all(promises).then(() => resolve(product));
210
+ return;
211
+ }
212
+ }
213
+
214
+ resolve(product);
215
+ });
@@ -0,0 +1,138 @@
1
+ /* eslint-disable no-nested-ternary */
2
+ import type { Products } from '@cloudcommerce/types';
3
+ import ecomUtils from '@ecomplus/utils';
4
+
5
+ export default async (product: Products, originalTinyProduct, appData) => {
6
+ const hasVariations = product.variations && product.variations.length;
7
+ const tinyProduct: Record<string, any> = {
8
+ sequencia: 1,
9
+ origem: 0,
10
+ situacao: product.available && product.visible ? 'A' : 'I',
11
+ tipo: 'P',
12
+ classe_produto: hasVariations ? 'V' : 'S',
13
+ codigo: product.sku,
14
+ nome: ecomUtils.name(product, 'pt_br').substring(0, 120),
15
+ unidade: originalTinyProduct && originalTinyProduct.unidade
16
+ ? originalTinyProduct.unidade
17
+ : product.measurement && product.measurement.unit !== 'oz' && product.measurement.unit !== 'ct'
18
+ ? product.measurement.unit.substring(0, 3).toUpperCase()
19
+ : 'UN',
20
+ };
21
+
22
+ if (ecomUtils.onPromotion(product)) {
23
+ tinyProduct.preco = product.base_price;
24
+ tinyProduct.preco_promocional = ecomUtils.price(product);
25
+ } else {
26
+ tinyProduct.preco = product.price;
27
+ }
28
+ if (product.cost_price) {
29
+ tinyProduct.preco_custo = product.cost_price;
30
+ }
31
+ if (product.min_quantity) {
32
+ tinyProduct.unidade_por_caixa = product.min_quantity < 1000
33
+ ? String(product.min_quantity) : '999';
34
+ }
35
+
36
+ if (product.short_description) {
37
+ tinyProduct.descricao_complementar = product.short_description;
38
+ }
39
+ if (product.warranty) {
40
+ tinyProduct.garantia = product.warranty.substring(0, 20);
41
+ }
42
+
43
+ if (product.mpn && product.mpn.length) {
44
+ [tinyProduct.ncm] = product.mpn;
45
+ }
46
+ if (product.gtin && product.gtin.length) {
47
+ [tinyProduct.gtin] = product.gtin;
48
+ if (product.gtin[1]) {
49
+ // eslint-disable-next-line prefer-destructuring
50
+ tinyProduct.gtin_embalagem = product.gtin[1];
51
+ }
52
+ }
53
+
54
+ if (product.weight && product.weight.value) {
55
+ tinyProduct.peso_bruto = product.weight.value;
56
+ if (product.weight.unit === 'mg') {
57
+ tinyProduct.peso_bruto /= 1000000;
58
+ } else if (product.weight.unit === 'g') {
59
+ tinyProduct.peso_bruto /= 1000;
60
+ }
61
+ }
62
+ const { dimensions } = product;
63
+ if (dimensions) {
64
+ Object.keys(dimensions).forEach((side) => {
65
+ if (dimensions[side] && dimensions[side].value) {
66
+ let field = side === 'width' ? 'largura'
67
+ : side === 'height' ? 'altura'
68
+ : 'comprimento';
69
+ field += '_embalagem';
70
+ tinyProduct[field] = dimensions[side].value;
71
+ if (dimensions[side].unit === 'mm') {
72
+ tinyProduct[field] *= 0.1;
73
+ } else if (dimensions[side].unit === 'm') {
74
+ tinyProduct[field] *= 100;
75
+ }
76
+ }
77
+ });
78
+ }
79
+
80
+ if (product.brands && product.brands.length) {
81
+ tinyProduct.marca = product.brands[0].name;
82
+ }
83
+ if (product.category_tree) {
84
+ tinyProduct.categoria = product.category_tree.replace(/\s?>\s?/g, ' >> ');
85
+ } else if (product.categories && product.categories.length) {
86
+ tinyProduct.categoria = product.categories.map(({ name }) => name).join(' >> ');
87
+ }
88
+
89
+ if (product.pictures && product.pictures.length) {
90
+ tinyProduct.anexos = [];
91
+ product.pictures.forEach(({ zoom, big, normal }) => {
92
+ const img = (zoom || big || normal) as { url: string };
93
+ if (img) {
94
+ tinyProduct.anexos.push({
95
+ anexo: img.url,
96
+ });
97
+ }
98
+ });
99
+ }
100
+
101
+ if (originalTinyProduct) {
102
+ tinyProduct.id = originalTinyProduct.id;
103
+ if (!appData.update_price) {
104
+ ['preco', 'preco_promocional', 'preco_custo'].forEach((field) => {
105
+ if (typeof originalTinyProduct[field] === 'number') {
106
+ tinyProduct[field] = originalTinyProduct[field];
107
+ }
108
+ });
109
+ }
110
+ } else {
111
+ tinyProduct.estoque_atual = product.quantity || 0;
112
+ }
113
+
114
+ if (hasVariations) {
115
+ tinyProduct.variacoes = [];
116
+ product.variations?.forEach((variation, i) => {
117
+ const tinyVariation: Record<string, any> = {
118
+ codigo: variation.sku || `${product.sku}-${(i + 1)}`,
119
+ grade: {},
120
+ };
121
+ if (!originalTinyProduct) {
122
+ tinyVariation.estoque_atual = variation.quantity || 0;
123
+ }
124
+ Object.keys(variation.specifications).forEach((gridId) => {
125
+ const gridOptions = variation.specifications[gridId];
126
+ if (gridOptions && gridOptions.length) {
127
+ gridOptions.forEach(({ text }, i) => {
128
+ tinyVariation.grade[i === 0 ? gridId : `${gridId}_${(i + 1)}`] = text;
129
+ });
130
+ }
131
+ });
132
+ tinyProduct.variacoes.push({
133
+ variacao: tinyVariation,
134
+ });
135
+ });
136
+ }
137
+ return tinyProduct;
138
+ };
@@ -0,0 +1,35 @@
1
+ import type { Orders } from '@cloudcommerce/types';
2
+
3
+ export default (situacao: string) => {
4
+ let financialStatus: Exclude<Orders['financial_status'], undefined>['current'] | undefined;
5
+ let fulfillmentStatus: Exclude<Orders['fulfillment_status'], undefined>['current'] | undefined;
6
+ switch (situacao) {
7
+ case 'aprovado':
8
+ financialStatus = 'paid';
9
+ break;
10
+ case 'preparando_envio':
11
+ case 'preparando envio':
12
+ fulfillmentStatus = 'in_separation';
13
+ break;
14
+ case 'faturado':
15
+ case 'faturado (atendido)':
16
+ case 'atendido':
17
+ fulfillmentStatus = 'invoice_issued';
18
+ break;
19
+ case 'pronto_envio':
20
+ case 'pronto para envio':
21
+ fulfillmentStatus = 'ready_for_shipping';
22
+ break;
23
+ case 'enviado':
24
+ fulfillmentStatus = 'shipped';
25
+ break;
26
+ case 'entregue':
27
+ fulfillmentStatus = 'delivered';
28
+ break;
29
+ case 'cancelado':
30
+ financialStatus = 'voided';
31
+ break;
32
+ default:
33
+ }
34
+ return { financialStatus, fulfillmentStatus };
35
+ };
@@ -0,0 +1,42 @@
1
+ import type { Orders } from '@cloudcommerce/types';
2
+
3
+ export default (order: Orders) => {
4
+ const financialStatus = order.financial_status && order.financial_status.current;
5
+ switch (financialStatus) {
6
+ case 'pending':
7
+ case 'under_analysis':
8
+ case 'unknown':
9
+ case 'authorized':
10
+ case 'partially_paid':
11
+ return 'aberto';
12
+ case 'voided':
13
+ case 'refunded':
14
+ case 'in_dispute':
15
+ case 'unauthorized':
16
+ return 'cancelado';
17
+ default:
18
+ }
19
+
20
+ switch (order.fulfillment_status && order.fulfillment_status.current) {
21
+ case 'in_production':
22
+ case 'in_separation':
23
+ return 'preparando_envio';
24
+ case 'invoice_issued':
25
+ return 'faturado';
26
+ case 'ready_for_shipping':
27
+ return 'pronto_envio';
28
+ case 'shipped':
29
+ case 'partially_shipped':
30
+ return 'enviado';
31
+ case 'delivered':
32
+ return 'entregue';
33
+ case 'returned':
34
+ return 'cancelado';
35
+ default:
36
+ }
37
+
38
+ if (financialStatus === 'paid') {
39
+ return 'aprovado';
40
+ }
41
+ return 'aberto';
42
+ };
@@ -0,0 +1,52 @@
1
+ import axios, { AxiosRequestConfig } from 'axios';
2
+
3
+ export default (
4
+ url: string,
5
+ body: Record<string, any>,
6
+ token = process.env.TINY_ERP_TOKEN,
7
+ options: AxiosRequestConfig = {},
8
+ ) => {
9
+ // https://www.tiny.com.br/ajuda/api/api2
10
+ let data = `token=${token}&formato=JSON`;
11
+ if (body) {
12
+ Object.keys(body).forEach((field) => {
13
+ if (body[field]) {
14
+ switch (typeof body[field]) {
15
+ case 'object':
16
+ data += `&${field}=${JSON.stringify(body[field])}`;
17
+ break;
18
+ case 'string':
19
+ case 'number':
20
+ data += `&${field}=${body[field]}`;
21
+ break;
22
+ default:
23
+ }
24
+ }
25
+ });
26
+ }
27
+
28
+ return axios.post(url, data, {
29
+ baseURL: 'https://api.tiny.com.br/api2/',
30
+ timeout: 30000,
31
+ ...options,
32
+ })
33
+ .then((response) => {
34
+ const { retorno } = response.data;
35
+ if (retorno.status === 'Erro') {
36
+ const err: any = new Error('Tiny error response');
37
+ const tinyErrorCode = parseInt(retorno.codigo_erro, 10);
38
+ if (tinyErrorCode <= 2) {
39
+ response.status = 401;
40
+ } else if (tinyErrorCode === 6) {
41
+ response.status = 503;
42
+ } else if (tinyErrorCode === 20) {
43
+ response.status = 404;
44
+ }
45
+ err.response = response;
46
+ err.config = response.config;
47
+ err.request = response.request;
48
+ throw err;
49
+ }
50
+ return retorno;
51
+ });
52
+ };
@@ -0,0 +1,23 @@
1
+ /* eslint-disable import/prefer-default-export */
2
+
3
+ import '@cloudcommerce/firebase/lib/init';
4
+ // eslint-disable-next-line import/no-unresolved
5
+ import { onRequest } from 'firebase-functions/v2/https';
6
+ import config from '@cloudcommerce/firebase/lib/config';
7
+ import {
8
+ createAppEventsFunction,
9
+ ApiEventHandler,
10
+ } from '@cloudcommerce/firebase/lib/helpers/pubsub';
11
+ import handleApiEvent from './event-to-tiny';
12
+ import handleTinyWebhook from './tiny-webhook';
13
+
14
+ const { httpsFunctionOptions } = config.get();
15
+
16
+ export const tinyErpOnApiEvent = createAppEventsFunction(
17
+ 'tinyErp',
18
+ handleApiEvent as ApiEventHandler,
19
+ );
20
+
21
+ export const tinyErpWebhook = onRequest(httpsFunctionOptions, (req, res) => {
22
+ handleTinyWebhook(req, res);
23
+ });
@@ -0,0 +1,100 @@
1
+ import type { Request, Response } from 'firebase-functions';
2
+ import type { Applications } from '@cloudcommerce/types';
3
+ import logger from 'firebase-functions/lib/logger';
4
+ import api from '@cloudcommerce/api';
5
+ import config from '@cloudcommerce/firebase/lib/config';
6
+ import importProduct from './integration/import-product-from-tiny';
7
+ import importOrder from './integration/import-order-from-tiny';
8
+
9
+ let appData: Record<string, any> = {};
10
+ let application: Applications;
11
+
12
+ export default async (req: Request, res: Response) => {
13
+ const tinyToken = req.query.token;
14
+ if (typeof tinyToken === 'string' && tinyToken && req.body) {
15
+ const { dados, tipo } = req.body;
16
+ if (dados) {
17
+ /*
18
+ TODO: Check Tiny server IPs
19
+ const clientIp = req.get('x-forwarded-for') || req.connection.remoteAddress
20
+ */
21
+ const { TINY_ERP_TOKEN } = process.env;
22
+ if (!TINY_ERP_TOKEN || TINY_ERP_TOKEN !== tinyToken) {
23
+ const { apps: { tinyErp: { appId } } } = config.get();
24
+ const applicationId = req.query._id;
25
+ const appEndpoint = applicationId && typeof applicationId === 'string'
26
+ ? `applications/${applicationId}`
27
+ : `applications/app_id:${appId}`;
28
+ application = (await api.get(appEndpoint as 'applications/id')).data;
29
+ appData = {
30
+ ...application.data,
31
+ ...application.hidden_data,
32
+ };
33
+ if (appData.tiny_api_token !== tinyToken) {
34
+ return res.sendStatus(401);
35
+ }
36
+ process.env.TINY_ERP_TOKEN = tinyToken;
37
+ }
38
+
39
+ if (dados.idVendaTiny) {
40
+ const orderNumber = `id:${dados.idVendaTiny}`;
41
+ const queueEntry = {
42
+ nextId: orderNumber,
43
+ isNotQueued: true,
44
+ };
45
+ await importOrder({}, queueEntry);
46
+ } else if (
47
+ (tipo === 'produto' || tipo === 'estoque')
48
+ && (dados.id || dados.idProduto)
49
+ && (dados.codigo || dados.sku)
50
+ ) {
51
+ const nextId = String(dados.skuMapeamento || dados.sku || dados.codigo);
52
+ const tinyStockUpdate = {
53
+ ref: `${nextId}`,
54
+ tipo,
55
+ produto: {
56
+ id: dados.idProduto,
57
+ codigo: dados.sku,
58
+ ...dados,
59
+ },
60
+ };
61
+ logger.info(`> Tiny webhook: ${nextId} => ${tinyStockUpdate.produto.saldo}`);
62
+ const queueEntry = {
63
+ nextId,
64
+ tinyStockUpdate,
65
+ isNotQueued: true,
66
+ app: application,
67
+ };
68
+ await importProduct({}, queueEntry, appData, false, true);
69
+ }
70
+
71
+ if (tipo === 'produto') {
72
+ const mapeamentos: any[] = [];
73
+ const parseTinyItem = (tinyItem) => {
74
+ if (tinyItem) {
75
+ const {
76
+ idMapeamento,
77
+ id,
78
+ codigo,
79
+ sku,
80
+ } = tinyItem;
81
+ mapeamentos.push({
82
+ idMapeamento: idMapeamento || id,
83
+ skuMapeamento: codigo || sku,
84
+ });
85
+ }
86
+ };
87
+ parseTinyItem(dados);
88
+ if (Array.isArray(dados.variacoes)) {
89
+ dados.variacoes.forEach((variacao) => {
90
+ parseTinyItem(variacao.id ? variacao : variacao.variacao);
91
+ });
92
+ }
93
+ return res.status(200).send(mapeamentos);
94
+ }
95
+ return res.sendStatus(200);
96
+ }
97
+ logger.warn('< Invalid Tiny Webhook body', req.body);
98
+ }
99
+ return res.sendStatus(403);
100
+ };
package/src/firebase.ts DELETED
File without changes