2023-05-09 12:13:19 +00:00
|
|
|
import { models } from './models/sequelize.js';
|
2023-02-08 11:31:54 +00:00
|
|
|
import { v4 as uuidv4 } from 'uuid';
|
2023-05-11 06:39:02 +00:00
|
|
|
import axios from 'axios';
|
|
|
|
import moment from 'moment';
|
2023-04-05 12:46:25 +00:00
|
|
|
import chalk from 'chalk';
|
2023-05-11 06:39:02 +00:00
|
|
|
import ora from 'ora';
|
|
|
|
|
2023-04-25 06:09:52 +00:00
|
|
|
const env = process.env;
|
2023-01-11 13:37:46 +00:00
|
|
|
|
2023-01-09 12:35:05 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Gets the Access Token.
|
|
|
|
*
|
2023-05-15 12:19:43 +00:00
|
|
|
* @param {Boolean} isForce Force to request new token
|
2023-01-09 12:35:05 +00:00
|
|
|
*/
|
2023-05-15 12:19:43 +00:00
|
|
|
export async function requestToken(isForce = false) {
|
2023-05-11 12:36:55 +00:00
|
|
|
let spinner = ora(`Requesting new token...`).start();
|
2023-05-05 09:48:44 +00:00
|
|
|
try {
|
|
|
|
const clientConfigData = await models.clientConfig.findOne();
|
|
|
|
|
2023-05-19 06:47:32 +00:00
|
|
|
let tokenExpiration, token;
|
2023-05-11 07:43:27 +00:00
|
|
|
if (clientConfigData) {
|
|
|
|
token = clientConfigData.currentToken;
|
2023-05-19 06:47:32 +00:00
|
|
|
tokenExpiration = clientConfigData.tokenExpiration;
|
2023-05-11 07:43:27 +00:00
|
|
|
}
|
2023-05-15 12:19:43 +00:00
|
|
|
|
2023-05-19 06:47:32 +00:00
|
|
|
if (isForce || !token || !tokenExpiration || moment().isAfter(tokenExpiration)) {
|
|
|
|
const clientId = JSON.parse(env.USE_SECRETS_DB) ? clientConfigData.clientId || env.CLIENT_ID : env.CLIENT_ID;
|
|
|
|
const clientSecret = JSON.parse(env.USE_SECRETS_DB) ? clientConfigData.clientSecret || env.CLIENT_SECRET : env.CLIENT_SECRET;
|
2023-05-05 09:48:44 +00:00
|
|
|
|
2023-05-19 06:47:32 +00:00
|
|
|
const data = new URLSearchParams({
|
2023-05-05 09:48:44 +00:00
|
|
|
grant_type: 'client_credentials',
|
|
|
|
client_id: clientId,
|
|
|
|
client_secret: clientSecret,
|
|
|
|
scope: 'role:app catalog:read supply:read organization:read network:write network:read'
|
2023-05-19 06:47:32 +00:00
|
|
|
}).toString();
|
2023-05-15 12:19:43 +00:00
|
|
|
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
|
2023-05-22 07:47:16 +00:00
|
|
|
const response = (await vnRequest('POST', env.API_ENDPOINT, data, headers, spinner)).data;
|
2023-05-11 06:39:02 +00:00
|
|
|
|
2023-05-19 06:47:32 +00:00
|
|
|
const tokenExpiration = moment()
|
2023-05-12 07:13:03 +00:00
|
|
|
.add(response.expires_in, 's')
|
2023-05-05 09:48:44 +00:00
|
|
|
.format('YYYY-MM-DD HH:mm:ss');
|
2023-05-19 06:47:32 +00:00
|
|
|
|
|
|
|
await updateClientConfig({
|
2023-05-05 09:48:44 +00:00
|
|
|
clientId,
|
|
|
|
clientSecret,
|
2023-05-19 06:47:32 +00:00
|
|
|
currentToken: response.access_token,
|
|
|
|
tokenExpiration,
|
|
|
|
});
|
|
|
|
spinner.succeed();
|
2023-05-15 12:19:43 +00:00
|
|
|
} else
|
|
|
|
spinner.succeed('Using stored token...');
|
2023-05-05 09:48:44 +00:00
|
|
|
} catch (err) {
|
|
|
|
spinner.fail();
|
|
|
|
throw err;
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
2023-01-09 11:58:34 +00:00
|
|
|
}
|
|
|
|
|
2023-04-06 08:56:52 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Returns the current token.
|
|
|
|
*
|
2023-04-06 08:56:52 +00:00
|
|
|
* @returns {string}
|
|
|
|
*/
|
2023-04-05 12:46:25 +00:00
|
|
|
export async function getCurrentToken() {
|
2023-05-15 12:19:43 +00:00
|
|
|
return (await models.clientConfig.findOne()).currentToken;
|
2023-04-05 12:46:25 +00:00
|
|
|
}
|
2023-05-08 10:18:14 +00:00
|
|
|
|
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Check the floriday data config.
|
2023-05-08 10:18:14 +00:00
|
|
|
*/
|
|
|
|
export async function checkConfig() {
|
|
|
|
const spinner = ora(`Checking config...`).start();
|
|
|
|
|
|
|
|
const excludedEnvVars = ['VSCODE_GIT_ASKPASS_EXTRA_ARGS'];
|
|
|
|
const requiredEnvVars = Object.keys(env);
|
|
|
|
const filteredEnvVars = requiredEnvVars.filter(reqEnvVar => !excludedEnvVars.includes(reqEnvVar));
|
|
|
|
|
|
|
|
for (const reqEnvVar of filteredEnvVars) {
|
|
|
|
if (!process.env[reqEnvVar]) {
|
|
|
|
spinner.fail();
|
|
|
|
throw new Error(`You haven't provided the ${reqEnvVar} environment variable`);
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 11:02:31 +00:00
|
|
|
const clientConfigData = await models.clientConfig.findOne();
|
|
|
|
if (!clientConfigData)
|
|
|
|
await updateClientConfig(env.CLIENT_ID, env.CLIENT_SECRET);
|
|
|
|
|
2023-05-08 10:18:14 +00:00
|
|
|
spinner.succeed();
|
|
|
|
}
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-04-06 08:56:52 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Returns the expiration of current token.
|
|
|
|
*
|
2023-04-06 08:56:52 +00:00
|
|
|
* @returns {string}
|
|
|
|
*/
|
2023-04-05 12:46:25 +00:00
|
|
|
export async function getCurrentTokenExpiration() {
|
2023-05-11 12:36:55 +00:00
|
|
|
return (await models.clientConfig.findOne()).tokenExpiration;
|
2023-04-05 12:46:25 +00:00
|
|
|
}
|
|
|
|
|
2023-01-09 12:35:05 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Updates the access token in the client config table.
|
|
|
|
*
|
2023-05-19 06:47:32 +00:00
|
|
|
* @param {Array} clientConfig [clientId, clientSecret, currenToken, tokenExpiration]
|
2023-01-09 12:35:05 +00:00
|
|
|
*/
|
2023-05-19 06:47:32 +00:00
|
|
|
export async function updateClientConfig(clientConfig) {
|
|
|
|
try {
|
|
|
|
if (!JSON.parse(process.env.USE_SECRETS_DB)) clientId = clientSecret = null
|
|
|
|
await models.clientConfig.upsert({
|
|
|
|
id: 1,
|
|
|
|
...clientConfig,
|
|
|
|
});
|
|
|
|
} catch (err) {
|
|
|
|
throw(err);
|
|
|
|
}
|
2023-01-09 11:58:34 +00:00
|
|
|
}
|
|
|
|
|
2023-01-11 12:13:22 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Pauses the execution of the script for the specified number of milliseconds.
|
|
|
|
*
|
|
|
|
* @param {Integer} ms
|
2023-01-11 12:13:22 +00:00
|
|
|
*/
|
2023-04-05 12:46:25 +00:00
|
|
|
export async function sleep(ms) {
|
2023-05-19 11:47:36 +00:00
|
|
|
await new Promise(resolve => setTimeout(resolve, ms));
|
2023-01-11 12:13:22 +00:00
|
|
|
}
|
|
|
|
|
2023-05-17 05:36:58 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Sync the organizations.
|
2023-05-17 05:36:58 +00:00
|
|
|
*/
|
2023-05-19 11:47:36 +00:00
|
|
|
export async function syncOrganizations(){
|
|
|
|
let spinner = ora('Syncing organizations...').start();
|
2023-05-22 07:47:16 +00:00
|
|
|
let i = 0;
|
2023-04-24 10:46:06 +00:00
|
|
|
try {
|
2023-05-22 07:47:16 +00:00
|
|
|
const sequenceNumberDb = await models.sequenceNumber.findOne({
|
|
|
|
where: { model: 'organization' }
|
|
|
|
});
|
|
|
|
const maxSequenceNumber = (await vnRequest('GET', `${env.API_URL}/organizations/current-max-sequence`, null, null, spinner)).data;
|
|
|
|
let curSequenceNumber = (sequenceNumberDb?.maxSequenceNumber)
|
|
|
|
? sequenceNumberDb.maxSequenceNumber
|
|
|
|
: 0
|
|
|
|
for (curSequenceNumber; curSequenceNumber <= maxSequenceNumber; curSequenceNumber++) {
|
2023-05-19 11:47:36 +00:00
|
|
|
const params = new URLSearchParams({
|
|
|
|
organizationType: 'SUPPLIER'
|
|
|
|
}).toString();
|
2023-05-22 07:47:16 +00:00
|
|
|
let response = (await vnRequest('GET', `${env.API_URL}/organizations/sync/${curSequenceNumber}?${params}`, null, null, spinner)).data;
|
2023-05-19 11:47:36 +00:00
|
|
|
curSequenceNumber = response.maximumSequenceNumber;
|
|
|
|
const orgs = response.results;
|
|
|
|
for (let org of orgs) {
|
|
|
|
spinner.text = `Syncing ${i} organizations, ${maxSequenceNumber - curSequenceNumber} missing...`
|
|
|
|
if (JSON.parse(env.APPLY_ORG_FILTER) && org.companyGln && !org.endDate) { // Filtro para quitar los que parecen test
|
|
|
|
await insertOrganization(org);
|
|
|
|
spinner.text = `Syncing ${i++} organizations, ${maxSequenceNumber - curSequenceNumber} missing...`
|
|
|
|
}
|
2023-04-25 06:09:52 +00:00
|
|
|
};
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
2023-05-22 07:47:16 +00:00
|
|
|
await models.sequenceNumber.upsert({
|
|
|
|
model: 'organization',
|
|
|
|
maxSequenceNumber,
|
|
|
|
});
|
|
|
|
spinner.text = `Syncing ${i} organizations, 0 missing...`
|
2023-05-19 11:47:36 +00:00
|
|
|
spinner.succeed();
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
|
|
|
catch (err) {
|
|
|
|
spinner.fail();
|
|
|
|
throw new Error(err);
|
|
|
|
}
|
2023-01-24 12:51:44 +00:00
|
|
|
}
|
|
|
|
|
2023-05-16 12:12:34 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Create the connections in Floriday.
|
2023-05-16 12:12:34 +00:00
|
|
|
*/
|
|
|
|
export async function syncConnections(){
|
|
|
|
await deleteConnections();
|
2023-05-17 10:39:43 +00:00
|
|
|
|
|
|
|
let spinner;
|
2023-04-24 10:46:06 +00:00
|
|
|
try {
|
2023-05-18 12:06:11 +00:00
|
|
|
let connectionsInDb = await models.supplyLine.findAll({
|
|
|
|
include : {
|
|
|
|
model: models.organization,
|
|
|
|
where: {
|
|
|
|
isConnected: true,
|
|
|
|
}
|
|
|
|
},
|
2023-05-17 08:24:10 +00:00
|
|
|
attributes: ['organizationId'],
|
2023-05-18 12:06:11 +00:00
|
|
|
group: ['organizationId'],
|
2023-05-17 08:24:10 +00:00
|
|
|
});
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-05-22 07:47:16 +00:00
|
|
|
const connectionsInFloriday = (await vnRequest('GET', `${env.API_URL}/connections`, null, null, spinner)).data;
|
2023-05-11 06:39:02 +00:00
|
|
|
|
2023-05-17 10:39:43 +00:00
|
|
|
let isExists = false, connectionsToPut = [];
|
|
|
|
for (let connectionInDb of connectionsInDb) {
|
|
|
|
for (let connectionInFloriday of connectionsInFloriday)
|
|
|
|
if (connectionInFloriday == connectionInDb.organizationId) {
|
|
|
|
isExists = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!isExists) connectionsToPut.push(connectionInDb.organizationId)
|
|
|
|
isExists = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (connectionsToPut.length) spinner = ora(`Creating connections in Floriday...`).start();
|
2023-05-16 12:12:34 +00:00
|
|
|
|
2023-05-17 10:39:43 +00:00
|
|
|
let i = 1;
|
|
|
|
for (let connection of connectionsToPut) {
|
|
|
|
spinner.text = `Creating ${i++} of ${connectionsToPut.length} connections in Floriday...`
|
2023-05-22 07:47:16 +00:00
|
|
|
await vnRequest('PUT', `${env.API_URL}/connections/${connection}`, null, null, spinner);
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
2023-05-17 10:39:43 +00:00
|
|
|
if (spinner) spinner.succeed();
|
2023-04-24 10:46:06 +00:00
|
|
|
} catch (err) {
|
2023-05-17 10:39:43 +00:00
|
|
|
if (spinner) spinner.fail();
|
2023-04-24 10:46:06 +00:00
|
|
|
throw new Error(err);
|
|
|
|
}
|
2023-02-10 09:42:54 +00:00
|
|
|
}
|
|
|
|
|
2023-05-17 05:36:58 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Sync the trade items for organizations that are connected.
|
2023-05-17 05:36:58 +00:00
|
|
|
*/
|
2023-04-05 12:46:25 +00:00
|
|
|
export async function syncTradeItems(){
|
2023-04-24 10:46:06 +00:00
|
|
|
const spinner = ora(`Syncing trade items...`).start();
|
2023-05-19 11:47:36 +00:00
|
|
|
const orgs = await models.organization.findAll({
|
2023-05-17 08:24:10 +00:00
|
|
|
attributes: ['organizationId'],
|
2023-05-09 12:13:19 +00:00
|
|
|
where: { isConnected: true }
|
|
|
|
});
|
2023-05-17 10:39:43 +00:00
|
|
|
let i = 0, x = 0;
|
2023-05-19 11:47:36 +00:00
|
|
|
for (let org of orgs) {
|
2023-04-24 10:46:06 +00:00
|
|
|
try {
|
2023-05-17 08:24:10 +00:00
|
|
|
const params = new URLSearchParams({
|
2023-05-19 11:47:36 +00:00
|
|
|
supplierOrganizationId: org.organizationId,
|
2023-05-17 08:24:10 +00:00
|
|
|
}).toString();
|
2023-05-22 07:47:16 +00:00
|
|
|
let tradeItems = (await vnRequest('GET', `${env.API_URL}/trade-items?${params}`, null, null, spinner)).data
|
2023-05-12 07:13:03 +00:00
|
|
|
|
2023-05-19 11:47:36 +00:00
|
|
|
spinner.text = `Syncing ${i} trade items of [${x++}|${orgs.length}] organizations...`
|
2023-05-17 10:39:43 +00:00
|
|
|
if (!tradeItems.length) continue;
|
2023-04-24 10:46:06 +00:00
|
|
|
|
|
|
|
for (let tradeItem of tradeItems) {
|
2023-05-11 06:39:02 +00:00
|
|
|
await insertItem(tradeItem);
|
2023-05-19 11:47:36 +00:00
|
|
|
spinner.text = `Syncing ${i++} trade items of [${x}|${orgs.length}] organizations...`
|
2023-04-24 10:46:06 +00:00
|
|
|
};
|
2023-05-08 12:55:18 +00:00
|
|
|
} catch (err) {
|
2023-04-24 10:46:06 +00:00
|
|
|
spinner.fail();
|
2023-05-08 12:55:18 +00:00
|
|
|
throw err;
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
spinner.succeed()
|
2023-01-16 13:52:08 +00:00
|
|
|
}
|
|
|
|
|
2023-02-14 13:36:05 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Sync the supply lines for organizations that are connected.
|
|
|
|
*
|
|
|
|
* If necessary, create the dependences.
|
2023-02-14 13:36:05 +00:00
|
|
|
*/
|
2023-05-16 12:17:45 +00:00
|
|
|
export async function syncSupplyLines() {
|
2023-05-17 08:24:10 +00:00
|
|
|
const spinner = ora(`Syncing supply lines...`).start();
|
2023-05-11 12:36:55 +00:00
|
|
|
try {
|
2023-05-19 11:47:36 +00:00
|
|
|
let conOrgs = await models.organization.findAll({
|
2023-05-17 08:24:10 +00:00
|
|
|
attributes: ['organizationId'],
|
2023-05-11 12:36:55 +00:00
|
|
|
where: { isConnected: true }
|
|
|
|
});
|
|
|
|
|
2023-05-15 12:19:43 +00:00
|
|
|
let i = 0, x = 1;
|
2023-05-19 11:47:36 +00:00
|
|
|
for (let org of conOrgs) {
|
|
|
|
spinner.text = `Syncing ${i} supply lines of [${x++}|${conOrgs.length}] organizations...`
|
2023-05-11 12:36:55 +00:00
|
|
|
const params = new URLSearchParams({
|
2023-05-19 11:47:36 +00:00
|
|
|
supplierOrganizationId: org.organizationId,
|
2023-05-11 12:36:55 +00:00
|
|
|
}).toString();
|
2023-05-22 07:47:16 +00:00
|
|
|
let supplyLines = (await vnRequest('GET',`${env.API_URL}/supply-lines?${params}`, null, null, spinner)).data;
|
2023-05-12 07:13:03 +00:00
|
|
|
if (!supplyLines.length) continue
|
2023-05-11 12:36:55 +00:00
|
|
|
|
2023-05-15 12:19:43 +00:00
|
|
|
for (let supplyLine of supplyLines) {
|
2023-05-17 11:28:09 +00:00
|
|
|
// Check if the warehouse exists, and if it doesn't, create it
|
|
|
|
let warehouse = await models.warehouses.findOne({
|
|
|
|
where: { warehouseId: supplyLine.warehouseId }
|
|
|
|
});
|
|
|
|
if (!warehouse) {
|
2023-05-22 07:47:16 +00:00
|
|
|
let warehouse = (await vnRequest('GET', `${env.API_URL}/warehouses/${supplyLine.warehouseId}`, null, null, spinner)).data;
|
2023-05-18 12:06:11 +00:00
|
|
|
|
|
|
|
// Check if the organization exists, and if it doesn't, create it
|
|
|
|
let organization = await models.organization.findOne({
|
|
|
|
where: { organizationId: warehouse.organizationId }
|
|
|
|
});
|
|
|
|
if (!organization) {
|
2023-05-22 07:47:16 +00:00
|
|
|
let organization = (await vnRequest('GET', `${env.API_URL}/organizations/${warehouse.organizationId}`, null, null, spinner)).data;
|
2023-05-18 12:06:11 +00:00
|
|
|
await insertOrganization(organization);
|
|
|
|
}
|
|
|
|
|
2023-05-17 11:28:09 +00:00
|
|
|
await insertWarehouse(warehouse);
|
2023-05-15 12:19:43 +00:00
|
|
|
}
|
|
|
|
|
2023-05-18 12:06:11 +00:00
|
|
|
// Check if the trade item exists, and if it doesn't, create it
|
|
|
|
let tradeItem = await models.tradeItem.findOne({
|
|
|
|
where: { tradeItemId: supplyLine.tradeItemId }
|
|
|
|
});
|
|
|
|
if (!tradeItem) {
|
2023-05-22 07:47:16 +00:00
|
|
|
let tradeItem = (await vnRequest('GET', `${env.API_URL}/trade-items/${supplyLine.tradeItemId}`, null, null, spinner)).data;
|
2023-05-18 12:06:11 +00:00
|
|
|
await insertItem(tradeItem);
|
|
|
|
}
|
2023-05-17 11:28:09 +00:00
|
|
|
|
2023-05-19 11:47:36 +00:00
|
|
|
spinner.text = `Syncing ${i++} supply lines of [${x}|${conOrgs.length}] organizations...`
|
2023-05-15 12:19:43 +00:00
|
|
|
await models.supplyLine.upsert({
|
|
|
|
...supplyLine,
|
2023-05-17 08:24:10 +00:00
|
|
|
organizationId: supplyLine.supplierOrganizationId,
|
2023-05-18 12:06:11 +00:00
|
|
|
pricePerPiece_currency: supplyLine.pricePerPiece?.currency ?? null,
|
|
|
|
pricePerPiece_value: supplyLine.pricePerPiece?.value ?? null,
|
|
|
|
deliveryPeriod_startDateTime: supplyLine.deliveryPeriod?.startDateTime ?? null,
|
|
|
|
deliveryPeriod_endDateTime: supplyLine.deliveryPeriod?.endDateTime ?? null,
|
|
|
|
orderPeriod_startDateTime: supplyLine.orderPeriod?.startDateTime ?? null,
|
|
|
|
orderPeriod_endDateTime: supplyLine.orderPeriod?.endDateTime ?? null,
|
|
|
|
agreementReference_code: supplyLine.agreementReference?.code ?? null,
|
|
|
|
agreementReference_description: supplyLine.agreementReference?.description ?? null,
|
2023-05-15 12:19:43 +00:00
|
|
|
lastSync: moment(),
|
|
|
|
});
|
|
|
|
|
|
|
|
for (let volumePrice of supplyLine.volumePrices)
|
|
|
|
await models.volumePrices.upsert({
|
|
|
|
supplyLineId: supplyLine.supplyLineId,
|
2023-05-19 11:47:36 +00:00
|
|
|
...volumePrice,
|
2023-05-15 12:19:43 +00:00
|
|
|
});
|
|
|
|
}
|
2023-05-11 12:36:55 +00:00
|
|
|
}
|
2023-05-12 07:13:03 +00:00
|
|
|
spinner.succeed();
|
2023-05-11 12:36:55 +00:00
|
|
|
}
|
|
|
|
catch (err) {
|
2023-05-12 07:13:03 +00:00
|
|
|
spinner.fail();
|
2023-05-11 12:36:55 +00:00
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-11 06:39:02 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Insert trade item and dependences in the database.
|
|
|
|
*
|
2023-05-19 06:47:32 +00:00
|
|
|
* @param {Array} tradeItem
|
2023-05-11 06:39:02 +00:00
|
|
|
*/
|
|
|
|
export async function insertItem(tradeItem) {
|
2023-05-19 11:47:36 +00:00
|
|
|
const tx = await models.sequelize.transaction();
|
2023-04-24 10:46:06 +00:00
|
|
|
try {
|
2023-05-09 12:13:19 +00:00
|
|
|
// Upsert trade item
|
2023-04-24 10:46:06 +00:00
|
|
|
await models.tradeItem.upsert({
|
2023-05-09 12:13:19 +00:00
|
|
|
...tradeItem,
|
2023-05-17 08:24:10 +00:00
|
|
|
organizationId: tradeItem.supplierOrganizationId,
|
2023-05-15 12:19:43 +00:00
|
|
|
lastSync: moment(),
|
2023-05-09 12:13:19 +00:00
|
|
|
}, { transaction: tx });
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-05-09 12:13:19 +00:00
|
|
|
// Upsert characteristics
|
2023-05-11 06:39:02 +00:00
|
|
|
if (tradeItem.characteristics)
|
2023-05-15 12:19:43 +00:00
|
|
|
if (tradeItem.characteristics.length)
|
|
|
|
for (const characteristic of tradeItem.characteristics) {
|
|
|
|
await models.characteristic.upsert({
|
|
|
|
tradeItemId: tradeItem.tradeItemId,
|
2023-05-19 11:47:36 +00:00
|
|
|
...characteristic,
|
2023-05-15 12:19:43 +00:00
|
|
|
}, { transaction: tx });
|
|
|
|
}
|
2023-05-09 12:13:19 +00:00
|
|
|
// Upsert seasonal periods
|
2023-05-11 06:39:02 +00:00
|
|
|
if (tradeItem.seasonalPeriods)
|
2023-05-15 12:19:43 +00:00
|
|
|
if (tradeItem.seasonalPeriods.length)
|
|
|
|
for (const seasonalPeriod of tradeItem.seasonalPeriods) {
|
|
|
|
await models.seasonalPeriod.upsert({
|
|
|
|
tradeItemId: tradeItem.tradeItemId,
|
2023-05-19 11:47:36 +00:00
|
|
|
...seasonalPeriod,
|
2023-05-15 12:19:43 +00:00
|
|
|
}, { transaction: tx });
|
|
|
|
}
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-05-09 12:13:19 +00:00
|
|
|
// Upsert photos
|
2023-05-11 06:39:02 +00:00
|
|
|
if (tradeItem.photos)
|
2023-05-15 12:19:43 +00:00
|
|
|
if (tradeItem.photos.length)
|
|
|
|
for (const photo of tradeItem.photos) {
|
|
|
|
await models.photo.upsert({
|
|
|
|
...photo,
|
|
|
|
tradeItemId: tradeItem.tradeItemId,
|
|
|
|
}, { transaction: tx });
|
|
|
|
}
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-05-09 12:13:19 +00:00
|
|
|
// Upsert packing configurations
|
2023-05-11 06:39:02 +00:00
|
|
|
if (tradeItem.packingConfigurations)
|
2023-05-15 12:19:43 +00:00
|
|
|
if (tradeItem.packingConfigurations.length)
|
|
|
|
for (const packingConfiguration of tradeItem.packingConfigurations) {
|
|
|
|
const uuid = uuidv4();
|
|
|
|
|
|
|
|
await models.packingConfiguration.upsert({
|
|
|
|
packingConfigurationId: uuid,
|
|
|
|
...packingConfiguration,
|
|
|
|
additionalPricePerPiece_currency: packingConfiguration.additionalPricePerPiece.currency,
|
|
|
|
additionalPricePerPiece_value: packingConfiguration.additionalPricePerPiece.value,
|
|
|
|
tradeItemId: tradeItem.tradeItemId,
|
|
|
|
}, { transaction: tx });
|
|
|
|
|
|
|
|
await models.package.upsert({
|
|
|
|
...packingConfiguration.package,
|
2023-05-17 10:39:43 +00:00
|
|
|
packingConfigurationId: uuid,
|
2023-05-15 12:19:43 +00:00
|
|
|
}, { transaction: tx });
|
|
|
|
}
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-05-09 12:13:19 +00:00
|
|
|
// Upsert country of origin ISO codes
|
2023-05-11 06:39:02 +00:00
|
|
|
if (tradeItem.countryOfOriginIsoCodes)
|
2023-05-15 12:19:43 +00:00
|
|
|
if (tradeItem.countryOfOriginIsoCodes.length)
|
|
|
|
for (const isoCode of tradeItem.countryOfOriginIsoCodes || []) {
|
|
|
|
await models.countryOfOriginIsoCode.upsert({
|
|
|
|
isoCode,
|
|
|
|
tradeItemId: tradeItem.tradeItemId,
|
|
|
|
}, { transaction: tx });
|
|
|
|
}
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-05-09 12:13:19 +00:00
|
|
|
// Upsert botanical names
|
2023-05-11 06:39:02 +00:00
|
|
|
if (tradeItem.botanicalNames)
|
2023-05-15 12:19:43 +00:00
|
|
|
if (tradeItem.botanicalNames.length)
|
|
|
|
for (const botanicalName of tradeItem.botanicalNames) {
|
|
|
|
await models.botanicalName.upsert({
|
2023-05-17 10:39:43 +00:00
|
|
|
botanicalNameId: uuidv4(),
|
2023-05-15 12:19:43 +00:00
|
|
|
name: botanicalName,
|
|
|
|
tradeItemId: tradeItem.tradeItemId,
|
|
|
|
}, { transaction: tx });
|
|
|
|
}
|
2023-04-24 10:46:06 +00:00
|
|
|
|
|
|
|
await tx.commit();
|
2023-05-09 12:13:19 +00:00
|
|
|
} catch (err) {
|
2023-04-24 10:46:06 +00:00
|
|
|
await tx.rollback();
|
2023-05-11 06:39:02 +00:00
|
|
|
throw err;
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
2023-02-14 12:34:54 +00:00
|
|
|
}
|
|
|
|
|
2023-05-17 11:28:09 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Insert warehouse in the database.
|
|
|
|
*
|
2023-05-19 06:47:32 +00:00
|
|
|
* @param {Array} warehouse
|
2023-05-17 11:28:09 +00:00
|
|
|
*/
|
|
|
|
export async function insertWarehouse(warehouse) {
|
2023-05-19 11:47:36 +00:00
|
|
|
const tx = await models.sequelize.transaction();
|
2023-05-17 11:28:09 +00:00
|
|
|
try {
|
|
|
|
await models.warehouses.upsert({
|
|
|
|
...warehouse,
|
|
|
|
location_gln: warehouse.location.gln,
|
|
|
|
location_address_addressLine: warehouse.location.address.addressLine,
|
|
|
|
location_address_city: warehouse.location.address.city,
|
|
|
|
location_address_countryCode: warehouse.location.address.countryCode,
|
|
|
|
location_address_postalCode: warehouse.location.address.postalCode,
|
|
|
|
location_address_stateOrProvince: warehouse.location.address.stateOrProvince,
|
|
|
|
lastSync: moment(),
|
|
|
|
});
|
|
|
|
await tx.commit();
|
|
|
|
} catch (err) {
|
|
|
|
await tx.rollback();
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-18 12:06:11 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Insert organization in the database.
|
|
|
|
*
|
2023-05-19 06:47:32 +00:00
|
|
|
* @param {Array} organization
|
2023-05-18 12:06:11 +00:00
|
|
|
*/
|
|
|
|
export async function insertOrganization(organization) {
|
2023-05-19 11:47:36 +00:00
|
|
|
const tx = await models.sequelize.transaction();
|
2023-05-18 12:06:11 +00:00
|
|
|
try {
|
|
|
|
await models.organization.upsert({
|
|
|
|
...organization,
|
2023-05-19 11:47:36 +00:00
|
|
|
isConnected: JSON.parse(env.ORGS_ALWAYS_CONN),
|
2023-05-18 12:06:11 +00:00
|
|
|
lastSync: moment(),
|
|
|
|
});
|
|
|
|
await tx.commit();
|
|
|
|
} catch (err) {
|
|
|
|
await tx.rollback();
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-16 08:24:04 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Sync the warehouses for organizations that are connected.
|
2023-05-16 08:24:04 +00:00
|
|
|
**/
|
|
|
|
export async function syncWarehouses(){
|
|
|
|
let spinner = ora('Syncing warehouses...').start();
|
|
|
|
try {
|
2023-05-19 11:47:36 +00:00
|
|
|
const orgs = await models.organization.findAll({
|
2023-05-17 08:24:10 +00:00
|
|
|
where: { isConnected: true }
|
|
|
|
});
|
|
|
|
|
2023-05-17 10:39:43 +00:00
|
|
|
let x = 0, i = 0;
|
2023-05-19 11:47:36 +00:00
|
|
|
for (let org of orgs) {
|
|
|
|
spinner.text = `Syncing ${i} warehouses of [${x++}|${orgs.length}] organizations...`
|
2023-05-22 07:47:16 +00:00
|
|
|
const warehouses = (await vnRequest('GET', `${env.API_URL}/organizations/supplier/${org.organizationId}/warehouses`, null, null, spinner)).data;
|
2023-05-16 08:24:04 +00:00
|
|
|
for (let warehouse of warehouses) {
|
2023-05-19 11:47:36 +00:00
|
|
|
spinner.text = `Syncing ${i++} warehouses of [${x}|${orgs.length}] organizations...`
|
2023-05-17 11:28:09 +00:00
|
|
|
await insertWarehouse(warehouse);
|
2023-05-16 08:24:04 +00:00
|
|
|
}
|
|
|
|
}
|
2023-05-17 08:24:10 +00:00
|
|
|
spinner.succeed();
|
2023-05-16 08:24:04 +00:00
|
|
|
}
|
|
|
|
catch (err) {
|
|
|
|
spinner.fail();
|
|
|
|
throw new Error(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-05 12:46:25 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Removes Floriday connections that we don't have in the database.
|
2023-05-11 12:36:55 +00:00
|
|
|
**/
|
|
|
|
export async function deleteConnections() {
|
2023-05-17 08:24:10 +00:00
|
|
|
let spinner;
|
2023-05-11 12:36:55 +00:00
|
|
|
try {
|
|
|
|
let i = 1;
|
2023-05-22 07:47:16 +00:00
|
|
|
const connectionsInFloriday = (await vnRequest('GET', `${env.API_URL}/connections`, null, null, spinner)).data;
|
2023-05-17 08:24:10 +00:00
|
|
|
const connectionsInDb = await models.organization.findAll({
|
|
|
|
attributes: ['organizationId'],
|
|
|
|
where: { isConnected: true }
|
|
|
|
});
|
2023-05-16 12:12:34 +00:00
|
|
|
|
|
|
|
let isExists = false, ghostConnections = [];
|
|
|
|
for (let connectionInFloriday of connectionsInFloriday) {
|
|
|
|
for (let connectionInDb of connectionsInDb)
|
2023-05-17 08:24:10 +00:00
|
|
|
if (connectionInFloriday == connectionInDb.organizationId) {
|
2023-05-16 12:12:34 +00:00
|
|
|
isExists = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!isExists) ghostConnections.push(connectionInFloriday)
|
|
|
|
isExists = false;
|
|
|
|
}
|
|
|
|
|
2023-05-19 06:47:32 +00:00
|
|
|
if (ghostConnections.length) spinner = ora(`Deleting connections that aren't in the db...`).start();
|
2023-05-17 08:24:10 +00:00
|
|
|
|
2023-05-16 12:12:34 +00:00
|
|
|
for (let connection of ghostConnections) {
|
2023-05-22 07:47:16 +00:00
|
|
|
await vnRequest('DELETE', `${env.API_URL}/connections/${connection}`, null, null, spinner);
|
2023-05-16 12:12:34 +00:00
|
|
|
spinner.text = `Deleting ${i++} of ${ghostConnections.length} that aren't in the db...`
|
2023-05-11 12:36:55 +00:00
|
|
|
}
|
2023-05-17 08:24:10 +00:00
|
|
|
if (spinner) spinner.succeed();
|
2023-05-11 12:36:55 +00:00
|
|
|
} catch (err) {
|
2023-05-17 08:24:10 +00:00
|
|
|
if (spinner) spinner.fail();
|
2023-05-16 12:12:34 +00:00
|
|
|
criticalError(err);
|
2023-05-11 12:36:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-12 07:13:03 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Perform a REST request.
|
|
|
|
*
|
2023-05-19 06:47:32 +00:00
|
|
|
* @param {String} url
|
|
|
|
* @param {String} method
|
|
|
|
* @param {Array} body
|
|
|
|
* @param {Array} header
|
2023-05-22 07:47:16 +00:00
|
|
|
* @param {Array} spinner
|
2023-05-19 11:47:36 +00:00
|
|
|
*
|
2023-05-19 06:47:32 +00:00
|
|
|
* @return {Array}
|
2023-05-12 07:13:03 +00:00
|
|
|
**/
|
2023-05-22 07:47:16 +00:00
|
|
|
export async function vnRequest(method, url, data, headers, spinner) {
|
2023-05-17 08:59:27 +00:00
|
|
|
if (!headers)
|
|
|
|
headers = {
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
'Authorization': `Bearer ${await getCurrentToken()}`,
|
|
|
|
'X-Api-Key': process.env.API_KEY,
|
|
|
|
};
|
|
|
|
|
2023-05-19 10:20:43 +00:00
|
|
|
while(true) {
|
2023-05-22 07:47:16 +00:00
|
|
|
try {
|
2023-05-19 06:47:32 +00:00
|
|
|
return (['GET', 'DELETE'].includes(method))
|
|
|
|
? await axios({method, url, headers})
|
2023-05-19 10:20:43 +00:00
|
|
|
: await axios({method, url, data, headers});
|
2023-05-22 07:47:16 +00:00
|
|
|
} catch (err) {
|
|
|
|
switch (err.code) {
|
|
|
|
case 'ECONNRESET': // Client network socket TLS
|
|
|
|
case 'EAI_AGAIN': // getaddrinfo
|
|
|
|
warning(err);
|
|
|
|
await sleep(1000);
|
|
|
|
break;
|
|
|
|
case 'ECONNABORTED':
|
|
|
|
case 'ECONNREFUSED':
|
|
|
|
case 'ERR_BAD_REQUEST':
|
|
|
|
switch (err.response.status) {
|
|
|
|
case 504:
|
|
|
|
case 502:
|
|
|
|
if (spinner) spinner.warn();
|
|
|
|
warning(err);
|
|
|
|
await sleep(1000);
|
|
|
|
if (spinner) spinner.start();
|
|
|
|
break;
|
|
|
|
case 429: // Too Many Requests
|
|
|
|
if (spinner) spinner.warn();
|
|
|
|
warning(err);
|
|
|
|
await sleep(3400); // Stipulated by Floriday
|
|
|
|
if (spinner) spinner.start();
|
|
|
|
break;
|
|
|
|
case 401: // Unauthorized
|
|
|
|
if (spinner) spinner.warn();
|
|
|
|
warning(err);
|
|
|
|
await requestToken(true);
|
|
|
|
headers.Authorization
|
|
|
|
? headers.Authorization = `Bearer ${await getCurrentToken()}`
|
|
|
|
: criticalError(err);
|
|
|
|
if (spinner) spinner.start();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (spinner) spinner.warn();
|
|
|
|
warning(err);
|
|
|
|
await sleep(env.MS_RETRY_UNHANDLED_ERROR);
|
|
|
|
if (spinner) spinner.start();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (spinner) spinner.warn();
|
|
|
|
warning(err);
|
|
|
|
await sleep(env.MS_RETRY_UNHANDLED_ERROR);
|
|
|
|
if (spinner) spinner.start();
|
|
|
|
break;
|
|
|
|
}
|
2023-05-15 12:19:43 +00:00
|
|
|
}
|
2023-05-19 11:47:36 +00:00
|
|
|
}
|
2023-05-12 07:13:03 +00:00
|
|
|
}
|
|
|
|
|
2023-05-11 12:36:55 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Critical error.
|
|
|
|
*
|
2023-04-05 12:46:25 +00:00
|
|
|
* @param {err}
|
|
|
|
**/
|
|
|
|
export async function criticalError(err) {
|
2023-05-19 06:47:32 +00:00
|
|
|
console.log(chalk.red.bold(`[CRITICAL]`), chalk.red(err.message));
|
2023-04-24 10:46:06 +00:00
|
|
|
process.exit();
|
2023-05-11 12:36:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Warning.
|
|
|
|
*
|
2023-05-11 12:36:55 +00:00
|
|
|
* @param {err}
|
|
|
|
**/
|
|
|
|
export async function warning(err) {
|
2023-05-19 10:20:43 +00:00
|
|
|
(err.response?.status && err.response?.data?.message)
|
|
|
|
? (console.log(chalk.yellow.bold(`[WARNING]`), chalk.yellow(`${err.response.status} - ${err.response.data.message}`)))
|
|
|
|
: (console.log(chalk.yellow.bold(`[WARNING]`), chalk.yellow(err.message)));
|
2023-04-05 12:46:25 +00:00
|
|
|
}
|