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
|
|
|
/**
|
|
|
|
* Gets the Access Token from the client config table
|
|
|
|
*
|
|
|
|
* @param {sequelize.models} models
|
|
|
|
* @returns {Date} tokenExpirationDate formated as YYYY-MM-DD HH:mm:ss
|
|
|
|
*/
|
2023-04-06 08:56:52 +00:00
|
|
|
export async function requestToken() {
|
2023-05-05 09:48:44 +00:00
|
|
|
let spinner;
|
|
|
|
try {
|
|
|
|
spinner = ora(`Requesting new token...`).start();
|
|
|
|
|
|
|
|
const clientConfigData = await models.clientConfig.findOne();
|
|
|
|
|
2023-05-11 07:43:27 +00:00
|
|
|
let tokenExpirationDate, token;
|
|
|
|
if (clientConfigData) {
|
|
|
|
token = clientConfigData.currentToken;
|
2023-05-05 09:48:44 +00:00
|
|
|
tokenExpirationDate = clientConfigData.tokenExpiration;
|
2023-05-11 07:43:27 +00:00
|
|
|
}
|
2023-05-05 09:48:44 +00:00
|
|
|
|
2023-05-11 07:43:27 +00:00
|
|
|
if (!token || !tokenExpirationDate || moment().isAfter(tokenExpirationDate)) {
|
2023-05-05 09:48:44 +00:00
|
|
|
let clientId, clientSecret
|
|
|
|
if (JSON.parse(env.USE_SECRETS_DB)) {
|
|
|
|
clientId = clientConfigData.clientId;
|
|
|
|
clientSecret = clientConfigData.clientSecret;
|
|
|
|
} else {
|
|
|
|
clientId = env.CLIENT_ID
|
|
|
|
clientSecret = env.CLIENT_SECRET
|
|
|
|
};
|
|
|
|
|
|
|
|
const data = {
|
|
|
|
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-11 06:39:02 +00:00
|
|
|
|
|
|
|
const response = await axios.post(env.API_ENDPOINT,
|
|
|
|
Object.keys(data)
|
|
|
|
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`)
|
|
|
|
.join('&'),
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
const responseData = response.data;
|
|
|
|
|
|
|
|
if (response.statusText = 'OK')
|
2023-05-05 09:48:44 +00:00
|
|
|
spinner.succeed();
|
|
|
|
else {
|
|
|
|
spinner.fail();
|
|
|
|
criticalError(new Error(`Token request failed with status: ${response.status} - ${response.statusText}`));
|
|
|
|
}
|
|
|
|
let tokenExpirationDate = moment()
|
|
|
|
.add(responseData.expires_in, 's')
|
|
|
|
.format('YYYY-MM-DD HH:mm:ss');
|
|
|
|
|
|
|
|
await updateClientConfig(
|
|
|
|
clientId,
|
|
|
|
clientSecret,
|
|
|
|
responseData.access_token,
|
|
|
|
tokenExpirationDate
|
|
|
|
);
|
|
|
|
|
|
|
|
return tokenExpirationDate;
|
2023-04-24 10:46:06 +00:00
|
|
|
} else {
|
2023-05-05 09:48:44 +00:00
|
|
|
spinner.text = 'Using stored token...'
|
|
|
|
spinner.succeed();
|
|
|
|
return tokenExpirationDate;
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
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
|
|
|
/**
|
|
|
|
* Returns the current token
|
|
|
|
*
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
2023-04-05 12:46:25 +00:00
|
|
|
export async function getCurrentToken() {
|
2023-04-24 10:46:06 +00:00
|
|
|
let data = await models.clientConfig.findOne();
|
|
|
|
return data.currentToken
|
2023-04-05 12:46:25 +00:00
|
|
|
}
|
2023-05-08 10:18:14 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Check the floriday data config
|
|
|
|
*/
|
|
|
|
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
|
|
|
/**
|
|
|
|
* Returns the expiration data of current token
|
|
|
|
*
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
2023-04-05 12:46:25 +00:00
|
|
|
export async function getCurrentTokenExpiration() {
|
2023-04-24 10:46:06 +00:00
|
|
|
let data = await models.clientConfig.findOne();
|
|
|
|
return data.tokenExpiration
|
2023-04-05 12:46:25 +00:00
|
|
|
}
|
|
|
|
|
2023-01-09 12:35:05 +00:00
|
|
|
/**
|
2023-04-06 08:56:52 +00:00
|
|
|
* Updates the Access Token in the client config table
|
2023-01-09 12:35:05 +00:00
|
|
|
*
|
|
|
|
* @param {String} clientId
|
|
|
|
* @param {String} clientSecret
|
|
|
|
* @param {String} accessToken
|
|
|
|
* @param {String} tokenExpirationDate
|
|
|
|
*/
|
2023-04-05 12:46:25 +00:00
|
|
|
export async function updateClientConfig(clientId, clientSecret, accessToken, tokenExpirationDate) {
|
2023-05-05 09:48:44 +00:00
|
|
|
try {
|
|
|
|
const spinner = ora(`Updating token...`).start();
|
|
|
|
if (!JSON.parse(process.env.USE_SECRETS_DB))
|
|
|
|
clientId = clientSecret = null
|
|
|
|
|
|
|
|
await models.clientConfig.upsert({
|
|
|
|
id: 1,
|
|
|
|
clientId: clientId,
|
|
|
|
clientSecret: clientSecret,
|
|
|
|
currentToken: accessToken,
|
|
|
|
tokenExpiration: tokenExpirationDate
|
|
|
|
});
|
|
|
|
spinner.succeed();
|
|
|
|
} catch (error) {
|
|
|
|
spinner.fail();
|
|
|
|
console.log('There was a error while updating the client config');
|
|
|
|
console.log(error);
|
|
|
|
}
|
2023-01-09 11:58:34 +00:00
|
|
|
}
|
|
|
|
|
2023-01-11 12:13:22 +00:00
|
|
|
/**
|
|
|
|
* pauses the execution of the script for the given amount of milliseconds
|
|
|
|
*
|
|
|
|
* @param {integer} ms
|
|
|
|
* @returns A promise that resolves after ms milliseconds.
|
|
|
|
*/
|
2023-04-05 12:46:25 +00:00
|
|
|
export async function sleep(ms) {
|
2023-04-24 10:46:06 +00:00
|
|
|
return new Promise((resolve) => {
|
|
|
|
setTimeout(resolve, ms);
|
|
|
|
});
|
2023-01-11 12:13:22 +00:00
|
|
|
}
|
|
|
|
|
2023-01-16 13:52:08 +00:00
|
|
|
/**
|
|
|
|
* Recieves an array of functions and executes them in a queue with the given concurrency
|
|
|
|
*
|
|
|
|
* @param {Array} fnArray
|
|
|
|
* @param {Number} concurrency
|
|
|
|
* @returns
|
|
|
|
*/
|
2023-04-05 12:46:25 +00:00
|
|
|
export async function asyncQueue(fnArray, concurrency = 1) {
|
2023-01-16 13:52:08 +00:00
|
|
|
|
2023-04-24 10:46:06 +00:00
|
|
|
const results = []; // 1
|
2023-01-16 13:52:08 +00:00
|
|
|
|
2023-04-24 10:46:06 +00:00
|
|
|
const queue = fnArray.map((fn, index) => () => fn().then((result) => results[index] = result));
|
2023-01-16 13:52:08 +00:00
|
|
|
|
2023-04-24 10:46:06 +00:00
|
|
|
const run = async () => { // 2
|
|
|
|
const fn = queue.shift();
|
|
|
|
if (fn) {
|
|
|
|
await fn();
|
|
|
|
await run();
|
|
|
|
}
|
|
|
|
};
|
2023-01-16 13:52:08 +00:00
|
|
|
|
2023-04-24 10:46:06 +00:00
|
|
|
const promises = []; // 3
|
|
|
|
while (concurrency--) { // 4
|
|
|
|
promises.push(run());
|
|
|
|
}
|
2023-01-16 13:52:08 +00:00
|
|
|
|
2023-04-24 10:46:06 +00:00
|
|
|
await Promise.all(promises); // 5
|
2023-01-24 12:51:44 +00:00
|
|
|
|
2023-04-24 10:46:06 +00:00
|
|
|
return results;
|
2023-01-16 13:52:08 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
// 1. Create an array of functions that will be executed in a queue
|
|
|
|
// 2. Create a function that will execute the functions in the queue
|
|
|
|
// 3. Create an array of promises that will execute the run function
|
|
|
|
// 4. Execute the run function while the concurrency is greater than 0
|
|
|
|
// 5. Return the results
|
|
|
|
|
2023-01-11 12:13:22 +00:00
|
|
|
/**
|
2023-02-03 12:56:34 +00:00
|
|
|
* Syncs the sequence number for the given model
|
|
|
|
* if no params are given it will reset all the sequence number to 0
|
2023-01-11 12:13:22 +00:00
|
|
|
*
|
2023-02-03 12:56:34 +00:00
|
|
|
* @param {Number} current - current sequence number
|
|
|
|
* @param {String} model - model name
|
|
|
|
* @param {Number} maximumSequenceNumber - maximum sequence number
|
|
|
|
* @returns
|
2023-01-11 12:13:22 +00:00
|
|
|
*/
|
2023-04-05 12:46:25 +00:00
|
|
|
export async function syncSequence(current = 0, model = null , maximumSequenceNumber = 0){
|
2023-04-24 10:46:06 +00:00
|
|
|
if (model == null && current == 0){
|
|
|
|
try {
|
|
|
|
const spinner = ora(`Syncing sequence...`).start();
|
|
|
|
let mockModels = [
|
2023-05-09 11:14:54 +00:00
|
|
|
'supplier',
|
2023-04-24 10:46:06 +00:00
|
|
|
'tradeItems',
|
|
|
|
'supplyLines',
|
|
|
|
];
|
|
|
|
let i = 1;
|
|
|
|
for (let mockModel in mockModels) {
|
|
|
|
spinner.text = `Syncing ${i++} sequences...`
|
|
|
|
const element = mockModels[mockModel];
|
|
|
|
await syncSequence(0, element);
|
|
|
|
}
|
|
|
|
spinner.succeed();
|
|
|
|
} catch (err) {
|
|
|
|
spinner.fail();
|
|
|
|
throw new Error(err);
|
|
|
|
}
|
|
|
|
} else if (current) {
|
|
|
|
let tx = await models.sequelize.transaction();
|
|
|
|
try {
|
|
|
|
let sequence = await models.sequenceNumber.findOrCreate({
|
|
|
|
where: {
|
|
|
|
model: model
|
|
|
|
},
|
|
|
|
defaults: {
|
|
|
|
model: model,
|
|
|
|
sequenceNumber: current,
|
|
|
|
maximumSequenceNumber: maximumSequenceNumber
|
|
|
|
},
|
|
|
|
transaction: tx
|
|
|
|
});
|
|
|
|
|
|
|
|
if (sequence[1] == false){
|
|
|
|
await models.sequenceNumber.update({
|
|
|
|
sequenceNumber: current,
|
|
|
|
maximumSequenceNumber: maximumSequenceNumber
|
|
|
|
}, {
|
|
|
|
where: {
|
|
|
|
model: model
|
|
|
|
},
|
|
|
|
transaction: tx
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
await tx.commit();
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
await tx.rollback();
|
|
|
|
console.log(`Error while syncing sequence number for: ${model}: ${error}`);
|
|
|
|
}
|
|
|
|
}
|
2023-01-10 12:24:43 +00:00
|
|
|
}
|
|
|
|
|
2023-04-05 12:46:25 +00:00
|
|
|
export async function syncSuppliers(){
|
2023-04-24 10:46:06 +00:00
|
|
|
let spinner = ora('Preparing to load suppliers...').start();
|
|
|
|
try {
|
|
|
|
let headers = {
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
'Authorization': `Bearer ${await getCurrentToken()}`,
|
2023-04-25 06:09:52 +00:00
|
|
|
'X-Api-Key': process.env.API_KEY,
|
2023-04-24 10:46:06 +00:00
|
|
|
};
|
|
|
|
|
2023-05-11 06:39:02 +00:00
|
|
|
const response = await axios.get(`${env.API_URL}/organizations/current-max-sequence`, { headers });
|
2023-04-24 10:46:06 +00:00
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
spinner.fail();
|
|
|
|
criticalError(new Error(`Max sequence request failed with status: ${response.status} - ${response.statusText}`));
|
|
|
|
}
|
2023-05-11 06:39:02 +00:00
|
|
|
const maxSequenceNumber = response.data;
|
2023-04-24 10:46:06 +00:00
|
|
|
|
|
|
|
let timeFinish, timeToGoSec, timeToGoMin, timeLeft;
|
|
|
|
for (let curSequenceNumber = 0; curSequenceNumber <= maxSequenceNumber; curSequenceNumber++) {
|
|
|
|
let timeStart = new moment();
|
2023-05-11 06:39:02 +00:00
|
|
|
let response = await axios.get(`${env.API_URL}/organizations/sync/${curSequenceNumber}?organizationType=SUPPLIER`, { headers });
|
|
|
|
let data = response.data;
|
2023-04-25 06:09:52 +00:00
|
|
|
|
2023-04-24 10:46:06 +00:00
|
|
|
let suppliers = data.results;
|
|
|
|
for (let supplier of suppliers) {
|
|
|
|
curSequenceNumber = supplier.sequenceNumber;
|
|
|
|
spinner.text = `Syncing suppliers, ${maxSequenceNumber - curSequenceNumber} are missing`
|
|
|
|
if (timeFinish)
|
|
|
|
spinner.text = spinner.text + ` (${timeLeft})`
|
|
|
|
await models.supplier.upsert({
|
2023-05-09 09:59:21 +00:00
|
|
|
supplierOrganizationId: supplier.organizationId,
|
2023-04-24 10:46:06 +00:00
|
|
|
sequenceNumber: supplier.sequenceNumber,
|
2023-05-09 09:59:21 +00:00
|
|
|
companyGln: supplier.companyGln ? supplier.companyGln : null,
|
2023-05-03 17:46:40 +00:00
|
|
|
name: supplier.name ? supplier.name : null,
|
|
|
|
commercialName: supplier.commercialName ? supplier.commercialName : null,
|
|
|
|
email: supplier.email ? supplier.email : null,
|
|
|
|
phone: supplier.phone ? supplier.phone : null,
|
|
|
|
website: supplier.website ? supplier.website : null,
|
|
|
|
rfhRelationId: supplier.rfhRelationId ? supplier.rfhRelationId : null,
|
|
|
|
paymentProviders: supplier.paymentProviders.length ? `${supplier.paymentProviders}` : null,
|
|
|
|
endDate: supplier.endDate ? supplier.endDate : null,
|
|
|
|
mailingAddress: supplier.mailingAddress ? supplier.mailingAddress : null,
|
|
|
|
physicalAddress: supplier.physicalAddress ? supplier.physicalAddress : null,
|
2023-04-24 10:46:06 +00:00
|
|
|
});
|
2023-04-25 06:09:52 +00:00
|
|
|
};
|
2023-05-09 11:14:54 +00:00
|
|
|
await syncSequence(curSequenceNumber, 'supplier', maxSequenceNumber);
|
2023-04-24 10:46:06 +00:00
|
|
|
timeFinish = new moment();
|
|
|
|
timeToGoSec = (timeFinish.diff(timeStart, 'seconds') * (maxSequenceNumber - curSequenceNumber) / 1000)
|
|
|
|
timeToGoMin = Math.trunc(timeToGoSec / 60)
|
|
|
|
if (!timeToGoMin)
|
|
|
|
timeLeft = `${Math.trunc(timeToGoSec)} sec`
|
|
|
|
else
|
|
|
|
timeLeft = `${timeToGoMin} min`
|
|
|
|
}
|
2023-04-24 12:11:25 +00:00
|
|
|
spinner.text = `Syncing suppliers...`;
|
2023-04-24 10:46:06 +00:00
|
|
|
spinner.succeed()
|
|
|
|
}
|
|
|
|
catch (err) {
|
|
|
|
spinner.fail();
|
|
|
|
throw new Error(err);
|
|
|
|
}
|
2023-01-24 12:51:44 +00:00
|
|
|
}
|
|
|
|
|
2023-04-06 08:56:52 +00:00
|
|
|
export async function syncConn(){
|
2023-04-24 10:46:06 +00:00
|
|
|
const spinner = ora(`Syncing connections...`).start();
|
|
|
|
try {
|
|
|
|
let connections = await models.connection.findAll();
|
|
|
|
|
|
|
|
let headers = {
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
'Authorization': `Bearer ${await getCurrentToken()}`,
|
|
|
|
'X-Api-Key': process.env.API_KEY
|
|
|
|
};
|
|
|
|
|
2023-05-11 07:43:27 +00:00
|
|
|
const remoteConnections = await axios.get(`${env.API_URL}/connections`, { headers });
|
2023-05-11 06:39:02 +00:00
|
|
|
|
2023-04-24 10:46:06 +00:00
|
|
|
let i = 1;
|
|
|
|
for (let connection of connections){
|
|
|
|
spinner.text = `Syncing ${i++} connections...`
|
2023-05-05 09:48:44 +00:00
|
|
|
if (connection.isConnected == false)
|
2023-04-24 10:46:06 +00:00
|
|
|
continue;
|
2023-05-11 06:39:02 +00:00
|
|
|
let remoteConnection = remoteConnections.data.find(remoteConnection => remoteConnection == connection.supplierOrganizationId);
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-05-11 07:43:27 +00:00
|
|
|
if (!remoteConnection){
|
|
|
|
console.log('Connection: ', connection.supplierOrganizationId, 'does not exist in the remote server');
|
2023-04-24 10:46:06 +00:00
|
|
|
console.log('Creating remote connection');
|
2023-05-11 07:43:27 +00:00
|
|
|
await axios.put(`${env.API_URL}/connections/${connection.supplierOrganizationId}`, null, { headers });
|
2023-04-24 10:46:06 +00:00
|
|
|
await models.connection.update({ isConnected: true }, {
|
|
|
|
where: {
|
2023-05-09 09:59:21 +00:00
|
|
|
supplierOrganizationId: connection.supplierOrganizationId
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
await models.supplier.update({ isConnected: true }, {
|
|
|
|
where: {
|
2023-05-09 09:59:21 +00:00
|
|
|
supplierOrganizationId: connection.supplierOrganizationId
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
await models.connection.update({ isConnected: true }, {
|
|
|
|
where: {
|
2023-05-09 09:59:21 +00:00
|
|
|
supplierOrganizationId: connection.supplierOrganizationId
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
await models.supplier.update({ isConnected: true }, {
|
|
|
|
where: {
|
2023-05-09 09:59:21 +00:00
|
|
|
supplierOrganizationId: connection.supplierOrganizationId
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spinner.succeed();
|
|
|
|
} catch (err) {
|
|
|
|
spinner.fail();
|
|
|
|
throw new Error(err);
|
|
|
|
}
|
2023-02-10 09:42:54 +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-09 12:13:19 +00:00
|
|
|
const suppliers = await models.supplier.findAll({
|
|
|
|
where: { isConnected: true }
|
|
|
|
});
|
2023-04-24 10:46:06 +00:00
|
|
|
let i = 1;
|
|
|
|
for (let supplier of suppliers) {
|
|
|
|
let headers = {
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
'Authorization': `Bearer ${await getCurrentToken()}`,
|
|
|
|
'X-Api-Key': process.env.API_KEY
|
|
|
|
};
|
|
|
|
try {
|
2023-05-11 06:39:02 +00:00
|
|
|
let request = await axios.get(`${env.API_URL}/trade-items?supplierOrganizationId=${supplier.supplierOrganizationId}`, { headers })
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-05-11 06:39:02 +00:00
|
|
|
let tradeItems = request.data;
|
2023-04-24 10:46:06 +00:00
|
|
|
|
|
|
|
if (!tradeItems.length)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (let tradeItem of tradeItems) {
|
2023-05-11 06:39:02 +00:00
|
|
|
await insertItem(tradeItem);
|
2023-05-08 12:55:18 +00:00
|
|
|
spinner.text = `Syncing ${i++} trade items...`
|
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-09 09:59:21 +00:00
|
|
|
* Syncs the supply lines for suppliers that are connected to do this,
|
2023-05-11 06:39:02 +00:00
|
|
|
* it searches all the supply lines for every tradeitem of the suppliers
|
2023-02-14 13:36:05 +00:00
|
|
|
*/
|
2023-04-05 12:46:25 +00:00
|
|
|
export async function syncSupplyLines(){
|
2023-05-09 11:14:54 +00:00
|
|
|
const spinner = ora(`Syncing supply lines...`).start();
|
2023-04-24 10:46:06 +00:00
|
|
|
try {
|
2023-05-09 11:14:54 +00:00
|
|
|
const currentSequenceNumber = await models.sequenceNumber.findOne({
|
|
|
|
where: { model: 'supplyLines' }
|
2023-04-24 10:46:06 +00:00
|
|
|
});
|
|
|
|
|
2023-05-09 11:14:54 +00:00
|
|
|
let connectedSuppliers = await models.supplier.findAll({
|
|
|
|
where: { isConnected: true }
|
2023-04-24 10:46:06 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
let tradeItems = await models.tradeItem.findAll({
|
2023-05-09 11:14:54 +00:00
|
|
|
where: { supplierOrganizationId: connectedSuppliers.map(supplier => supplier.supplierOrganizationId) }
|
2023-04-24 10:46:06 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
let promises = [];
|
|
|
|
|
|
|
|
let headers = {
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
'Authorization': `Bearer ${await getCurrentToken()}`,
|
|
|
|
'X-Api-Key': process.env.API_KEY
|
|
|
|
};
|
|
|
|
|
|
|
|
// Launch a promise for each supplier
|
2023-05-09 09:59:21 +00:00
|
|
|
for (let tradeItem of tradeItems) {
|
2023-05-09 11:14:54 +00:00
|
|
|
let supplier = connectedSuppliers.find(({ supplierOrganizationId }) => supplierOrganizationId === tradeItem.supplierOrganizationId);
|
|
|
|
// eslint-disable-next-line no-async-promise-executor
|
2023-04-24 10:46:06 +00:00
|
|
|
let promise = new Promise(async (resolve) => {
|
|
|
|
try {
|
2023-04-24 12:11:25 +00:00
|
|
|
let url = `${env.API_URL}/supply-lines/sync/0`
|
2023-04-24 10:46:06 +00:00
|
|
|
const params = new URLSearchParams({
|
2023-05-09 09:59:21 +00:00
|
|
|
supplierOrganizationId: supplier.supplierOrganizationId,
|
2023-04-24 10:46:06 +00:00
|
|
|
tradeItemId: tradeItem.tradeItemId,
|
|
|
|
postFilterSelectedTradeItems: false
|
|
|
|
});
|
2023-05-11 06:39:02 +00:00
|
|
|
let request = await axios.get(`${url}?${params.toString()}`, { headers });
|
2023-04-24 12:11:25 +00:00
|
|
|
|
2023-05-09 09:59:21 +00:00
|
|
|
if (request.status == 429) { // Too many request
|
|
|
|
resolve([]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-05-11 06:39:02 +00:00
|
|
|
let supplyLines = request.data;
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-05-09 09:59:21 +00:00
|
|
|
if (!supplyLines.results.length) {
|
2023-04-24 10:46:06 +00:00
|
|
|
resolve([]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
resolve(supplyLines);
|
|
|
|
|
2023-05-09 09:59:21 +00:00
|
|
|
} catch (err) {
|
2023-05-11 07:43:27 +00:00
|
|
|
if (err.message = 'Request failed with status code 429')
|
|
|
|
console.log('Request failed with status code 429 - Too many request');
|
2023-04-24 10:46:06 +00:00
|
|
|
resolve([]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
promises.push(promise);
|
|
|
|
}
|
|
|
|
|
|
|
|
let supplyLines = await Promise.all(promises);
|
|
|
|
let maximumSequenceNumber;
|
2023-05-09 09:59:21 +00:00
|
|
|
|
2023-04-24 10:46:06 +00:00
|
|
|
for (let supplyLine of supplyLines) {
|
|
|
|
maximumSequenceNumber = supplyLine.maximumSequenceNumber;
|
|
|
|
supplyLine = supplyLine.results;
|
|
|
|
try {
|
2023-05-09 09:59:21 +00:00
|
|
|
if (supplyLine) {
|
|
|
|
for (let line of supplyLine) {
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-05-09 09:59:21 +00:00
|
|
|
let tradeItem = await models.tradeItem.findOne({
|
2023-04-24 10:46:06 +00:00
|
|
|
where: {
|
|
|
|
tradeItemId: line.tradeItemId
|
|
|
|
}
|
|
|
|
});
|
2023-05-09 09:59:21 +00:00
|
|
|
|
2023-04-24 10:46:06 +00:00
|
|
|
if (!tradeItem) {
|
|
|
|
console.log('Trade item not found for supply line: ', line.supplyLineId);
|
2023-05-09 09:59:21 +00:00
|
|
|
console.log('Requesting data for trade item id: ', line.tradeItemId);
|
|
|
|
|
2023-05-11 06:39:02 +00:00
|
|
|
let queryTradeItem = await axios.get(`${env.API_URL}/trade-items?tradeItemIds=${line.tradeItemId}`, { headers });
|
2023-05-09 09:59:21 +00:00
|
|
|
|
2023-05-11 06:39:02 +00:00
|
|
|
let tradeItem = queryTradeItem.data;
|
2023-05-09 09:59:21 +00:00
|
|
|
|
|
|
|
if (tradeItem.length == 0) {
|
|
|
|
console.log('Trade item not found for supply line: ', line.supplyLineId);
|
|
|
|
console.log('Trade item id: ', line.tradeItemId);
|
|
|
|
continue;
|
|
|
|
}
|
2023-05-11 06:39:02 +00:00
|
|
|
|
|
|
|
await insertItem(tradeItem[0]);
|
2023-05-09 09:59:21 +00:00
|
|
|
|
|
|
|
tradeItem = await models.tradeItem.findOne({
|
|
|
|
where: {
|
|
|
|
tradeItemId: line.tradeItemId
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!tradeItem) {
|
|
|
|
console.log('Trade item not found for supply line: ', line.supplyLineId);
|
|
|
|
console.log('Trade item id: ', line.tradeItemId);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
2023-05-09 09:59:21 +00:00
|
|
|
|
|
|
|
await models.supplyLine.upsert({
|
|
|
|
supplyLineId: line.supplyLineId,
|
|
|
|
status: line.status,
|
|
|
|
supplierOrganizationId: line.supplierOrganizationId,
|
|
|
|
pricePerPiece_currency: line.pricePerPiece.currency,
|
|
|
|
pricePerPiece_value: line.pricePerPiece.value,
|
|
|
|
numberOfPieces: line.numberOfPieces,
|
|
|
|
deliveryPeriod_startDateTime: line.deliveryPeriod.startDateTime,
|
|
|
|
deliveryPeriod_endDateTime: line.deliveryPeriod.endDateTime,
|
|
|
|
orderPeriod_startDateTime: line.orderPeriod.startDateTime,
|
|
|
|
orderPeriod_endDateTime: line.orderPeriod.endDateTime,
|
|
|
|
warehouseId: line.warehouseId,
|
|
|
|
sequenceNumber: line.sequenceNumber,
|
|
|
|
type: line.type,
|
|
|
|
isDeleted: line.isDeleted,
|
|
|
|
salesUnit: line.salesUnit,
|
|
|
|
agreementReference_code: line.agreementReference.code ? line.agreementReference.code : null,
|
|
|
|
agreementReference_description: line.agreementReference.description ? line.agreementReference.description : null,
|
|
|
|
isLimited: line.isLimited,
|
|
|
|
isCustomerSpecific: line.isCustomerSpecific,
|
|
|
|
tradeItemId: line.tradeItemId,
|
|
|
|
});
|
2023-05-09 11:14:54 +00:00
|
|
|
|
|
|
|
for (let volumePrice of line.volumePrices)
|
|
|
|
await models.volumePrices.upsert({
|
|
|
|
unit: volumePrice.unit,
|
|
|
|
pricePerPiece: volumePrice.pricePerPiece,
|
|
|
|
supplyLineId: line.supplyLineId,
|
|
|
|
});
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (err) {
|
2023-05-09 09:59:21 +00:00
|
|
|
throw err;
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
spinner.succeed();
|
2023-05-09 11:14:54 +00:00
|
|
|
console.log('Found', connectedSuppliers.length, 'connected suppliers');
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-05-09 09:59:21 +00:00
|
|
|
await syncSequence(currentSequenceNumber, 'supplyLines' ,maximumSequenceNumber);
|
2023-04-24 10:46:06 +00:00
|
|
|
} catch (err) {
|
2023-05-09 11:14:54 +00:00
|
|
|
spinner.fail();
|
2023-05-09 09:59:21 +00:00
|
|
|
throw err;
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
2023-02-10 09:42:54 +00:00
|
|
|
}
|
|
|
|
|
2023-05-11 06:39:02 +00:00
|
|
|
/**
|
|
|
|
* Insert the trade item
|
|
|
|
*
|
|
|
|
* @param {array} tradeItem
|
|
|
|
*/
|
|
|
|
export async function insertItem(tradeItem) {
|
|
|
|
let tx;
|
2023-04-24 10:46:06 +00:00
|
|
|
try {
|
2023-05-11 06:39:02 +00:00
|
|
|
tx = await models.sequelize.transaction();
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-05-09 12:13:19 +00:00
|
|
|
// Upsert supplier connection
|
2023-04-24 10:46:06 +00:00
|
|
|
await models.connection.upsert({
|
2023-05-09 09:59:21 +00:00
|
|
|
supplierOrganizationId: tradeItem.supplierOrganizationId,
|
2023-04-24 10:46:06 +00:00
|
|
|
connect: true,
|
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 trade item
|
2023-04-24 10:46:06 +00:00
|
|
|
await models.tradeItem.upsert({
|
2023-05-09 12:13:19 +00:00
|
|
|
...tradeItem,
|
2023-04-24 10:46:06 +00:00
|
|
|
isDeleted: tradeItem.isDeleted,
|
|
|
|
isCustomerSpecific: tradeItem.isCustomerSpecific,
|
|
|
|
isHiddenInCatalog: tradeItem.isHiddenInCatalog,
|
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)
|
|
|
|
for (const characteristic of tradeItem.characteristics) {
|
|
|
|
await models.characteristic.upsert({
|
|
|
|
...characteristic,
|
|
|
|
tradeItemId: tradeItem.tradeItemId,
|
|
|
|
}, { transaction: tx });
|
|
|
|
}
|
2023-05-09 12:13:19 +00:00
|
|
|
// Upsert seasonal periods
|
2023-05-11 06:39:02 +00:00
|
|
|
if (tradeItem.seasonalPeriods)
|
|
|
|
for (const seasonalPeriod of tradeItem.seasonalPeriods) {
|
|
|
|
await models.seasonalPeriod.upsert({
|
|
|
|
...seasonalPeriod,
|
|
|
|
tradeItemId: tradeItem.tradeItemId,
|
|
|
|
}, { 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)
|
|
|
|
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)
|
|
|
|
for (const packingConfiguration of tradeItem.packingConfigurations) {
|
|
|
|
const uuid = uuidv4();
|
|
|
|
|
|
|
|
await models.packingConfiguration.upsert({
|
|
|
|
packingConfigurationId: uuid,
|
|
|
|
...packingConfiguration,
|
|
|
|
additionalPricePerPiece: JSON.stringify(packingConfiguration.additionalPricePerPiece),
|
|
|
|
tradeItemId: tradeItem.tradeItemId,
|
|
|
|
}, { transaction: tx });
|
|
|
|
|
|
|
|
await models.package.upsert({
|
|
|
|
...packingConfiguration.package,
|
|
|
|
packingConfigurationFk: uuid,
|
|
|
|
}, { 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)
|
|
|
|
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)
|
|
|
|
for (const botanicalName of tradeItem.botanicalNames) {
|
|
|
|
await models.botanicalName.upsert({
|
|
|
|
...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-04-05 12:46:25 +00:00
|
|
|
/**
|
|
|
|
* Throw critical error
|
|
|
|
*
|
|
|
|
* @param {err}
|
|
|
|
**/
|
|
|
|
export async function criticalError(err) {
|
2023-05-05 09:48:44 +00:00
|
|
|
console.log(chalk.red.bold(`[CRITICAL]`), chalk.red(`${err.message}`));
|
2023-04-24 10:46:06 +00:00
|
|
|
process.exit();
|
2023-04-05 12:46:25 +00:00
|
|
|
}
|