refs #4823 Supplier to organization and more

This commit is contained in:
Guillermo Bonet 2023-05-17 10:24:10 +02:00
parent f8e950034a
commit fa8eab6139
12 changed files with 95 additions and 216 deletions

View File

@ -21,8 +21,7 @@ MS_PRODUCTION_SCHEDULE = 300000
MS_TEST_SCHEDULE = 100000 MS_TEST_SCHEDULE = 100000
SECRETS = true SECRETS = true
FORCE_SYNC = true FORCE_SYNC = true
SYNC_SEQUENCE = true SYNC_ORGANIZATION = true
SYNC_SUPPLIER = true
SYNC_WAREHOUSE = true SYNC_WAREHOUSE = true
SYNC_CONN = true SYNC_CONN = true
SYNC_TRADEITEM = true SYNC_TRADEITEM = true

View File

@ -11,10 +11,9 @@ class Floriday {
try { try {
await utils.checkConfig(); await utils.checkConfig();
await utils.requestToken(); await utils.requestToken();
if (JSON.parse(env.SYNC_SEQUENCE)) await utils.syncSequence(); if (JSON.parse(env.SYNC_ORGANIZATION)) await utils.syncSuppliers();
if (JSON.parse(env.SYNC_SUPPLIER)) await utils.syncSuppliers();
if (JSON.parse(env.SYNC_WAREHOUSE)) await utils.syncWarehouses();
if (JSON.parse(env.SYNC_CONN)) await utils.syncConnections(); if (JSON.parse(env.SYNC_CONN)) await utils.syncConnections();
if (JSON.parse(env.SYNC_WAREHOUSE)) await utils.syncWarehouses();
if (JSON.parse(env.SYNC_TRADEITEM)) await utils.syncTradeItems(); if (JSON.parse(env.SYNC_TRADEITEM)) await utils.syncTradeItems();
} catch (err) { } catch (err) {
utils.criticalError(err); utils.criticalError(err);
@ -55,12 +54,15 @@ class Floriday {
async trunk() { async trunk() {
try{ try{
if (JSON.parse(env.SYNC_CONN)) await utils.syncConnections();
await utils.syncSupplyLines(); await utils.syncSupplyLines();
// Continuar con todo lo que haga falta realizar en la rutina // Continuar con todo lo que haga falta realizar en la rutina
} catch (err) { } catch (err) {
if (err.name === 'SequelizeConnectionRefusedError')
throw err; throw err;
utils.criticalError(err);
} }
} }

View File

@ -2,12 +2,11 @@ import Floriday from './floriday.js';
async function main() { async function main() {
const floriday = new Floriday(); const floriday = new Floriday();
await floriday.start();
process.on('SIGINT', async () => { process.on('SIGINT', async () => {
await floriday.stop(); await floriday.stop();
process.exit(); process.exit();
}); });
await floriday.start();
try { try {
await floriday.schedule() await floriday.schedule()

View File

@ -1,34 +0,0 @@
import { Sequelize } from 'sequelize';
const sequenceNumber = {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
sequenceNumber: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0,
},
maximumSequenceNumber: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0,
},
model: {
type: Sequelize.STRING,
},
};
export default (sequelize) => {
const sequenceNumbers = sequelize.define(
'sequenceNumber',
sequenceNumber,
{
timestamps: false,
freezeTableName: true,
}
);
return sequenceNumbers;
};

View File

@ -1,7 +1,7 @@
import { Sequelize } from 'sequelize'; import { Sequelize } from 'sequelize';
const suppliers = { const organizations = {
supplierOrganizationId: { organizationId: {
type: Sequelize.STRING, type: Sequelize.STRING,
allowNull: false, allowNull: false,
primaryKey: true, primaryKey: true,
@ -60,12 +60,12 @@ const suppliers = {
}; };
export default (sequelize) => { export default (sequelize) => {
const Suppliers = sequelize.define( const Organizations = sequelize.define(
'supplier', 'organization',
suppliers, { organizations, {
timestamps: false, timestamps: false,
freezeTableName: true, freezeTableName: true,
} }
); );
return Suppliers; return Organizations;
}; };

View File

@ -32,7 +32,7 @@ const warehouses = {
sequenceNumber: { sequenceNumber: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
}, },
supplierOrganizationId: { organizationId: {
type: Sequelize.STRING, type: Sequelize.STRING,
}, },
lastSync: { lastSync: {
@ -49,7 +49,7 @@ const warehouses = {
export default (sequelize) => { export default (sequelize) => {
const Warehouses = sequelize.define( const Warehouses = sequelize.define(
'supplier_warehouses', 'organization_warehouses',
warehouses, { warehouses, {
timestamps: false, timestamps: false,
freezeTableName: true, freezeTableName: true,

View File

@ -34,13 +34,11 @@ import supplyLine from './supplyLine/supplyLine.js';
import volumePrices from './supplyLine/volumePrices.js'; import volumePrices from './supplyLine/volumePrices.js';
// Conf Models // Conf Models
import clientConfig from './conf/clientConfig.js'; import clientConfig from './config/clientConfig.js';
import sequenceNumber from './conf/sequenceNumber.js';
// Supplier Models // Organization Models
import supplier from './supplier/supplier.js'; import organization from './organization/organization.js';
import connections from './supplier/connections.js'; import warehouses from './organization/warehouses.js';
import warehouses from './supplier/warehouses.js';
// TradeItem Models // TradeItem Models
import tradeItem from './tradeItem/tradeItem.js'; import tradeItem from './tradeItem/tradeItem.js';
@ -74,31 +72,29 @@ import characteristics from './tradeItem/characteristics.js';
*/ */
let models = { let models = {
sequelize: sequelize, sequelize: sequelize,
clientConfig: clientConfig(sequelize),
organization: organization(sequelize),
warehouses: warehouses(sequelize),
tradeItem: tradeItem(sequelize), tradeItem: tradeItem(sequelize),
packingConfiguration: packingConfigurations(sequelize), botanicalName: botanicalNames(sequelize),
photo: photos(sequelize),
characteristic: characteristics(sequelize), characteristic: characteristics(sequelize),
countryOfOriginIsoCode: countryOfOriginIsoCodes(sequelize), countryOfOriginIsoCode: countryOfOriginIsoCodes(sequelize),
package: packageModel(sequelize), packingConfiguration: packingConfigurations(sequelize),
photo: photos(sequelize),
seasonalPeriod: seasonalPeriod(sequelize), seasonalPeriod: seasonalPeriod(sequelize),
clientConfig: clientConfig(sequelize),
botanicalName: botanicalNames(sequelize),
supplyLine: supplyLine(sequelize), supplyLine: supplyLine(sequelize),
volumePrices: volumePrices(sequelize), volumePrices: volumePrices(sequelize),
supplier: supplier(sequelize), package: packageModel(sequelize),
sequenceNumber: sequenceNumber(sequelize),
connection: connections(sequelize),
warehouses: warehouses(sequelize),
}; };
try { try {
/* Remove ID atribute from models */ // Remove ID atribute from models
models.characteristic.removeAttribute('id'); models.characteristic.removeAttribute('id');
models.seasonalPeriod.removeAttribute('id'); models.seasonalPeriod.removeAttribute('id');
models.package.removeAttribute('id'); models.package.removeAttribute('id');
models.botanicalName.removeAttribute('id'); models.botanicalName.removeAttribute('id');
models.countryOfOriginIsoCode.removeAttribute('id'); models.countryOfOriginIsoCode.removeAttribute('id');
/* ------------------------------ */ // -------------------------------
models.characteristic.belongsTo(models.tradeItem, { models.characteristic.belongsTo(models.tradeItem, {
foreignKey: 'tradeItemId', foreignKey: 'tradeItemId',
@ -160,22 +156,28 @@ try {
targetKey: 'tradeItemId', targetKey: 'tradeItemId',
}); });
models.tradeItem.belongsTo(models.supplier, { models.supplyLine.belongsTo(models.warehouses, {
foreignKey: 'supplierOrganizationId', foreignKey: 'warehouseId',
as: 'supplierOrganization_Id', as: 'warehouse_Fk',
targetKey: 'supplierOrganizationId', targetKey: 'warehouseId',
}); });
models.supplyLine.belongsTo(models.supplier, { models.tradeItem.belongsTo(models.organization, {
foreignKey: 'supplierOrganizationId', foreignKey: 'organizationId',
as: 'supplierOrganization_Id', as: 'Organization_Fk',
targetKey: 'supplierOrganizationId', targetKey: 'organizationId',
}); });
models.warehouses.belongsTo(models.supplier, { models.supplyLine.belongsTo(models.organization, {
foreignKey: 'supplierOrganizationId', foreignKey: 'organizationId',
as: 'supplierOrganization_Id', as: 'Organization_Fk',
targetKey: 'supplierOrganizationId', targetKey: 'organizationId',
});
models.warehouses.belongsTo(models.organization, {
foreignKey: 'organizationId',
as: 'Organization_Fk',
targetKey: 'organizationId',
}); });
} catch (err) { } catch (err) {
criticalError(err); criticalError(err);

View File

@ -1,21 +0,0 @@
import { Sequelize } from 'sequelize';
const connections = {
supplierOrganizationId: {
type: Sequelize.STRING,
primaryKey: true,
allowNull: false,
},
};
export default (sequelize) => {
const connection = sequelize.define(
'supplier_connections',
connections,
{
timestamps: false,
freezeTableName: true,
}
);
return connection;
};

View File

@ -59,7 +59,7 @@ const supplyLine = {
tradeItemId : { tradeItemId : {
type: Sequelize.STRING, type: Sequelize.STRING,
}, },
supplierOrganizationId : { organizationId : {
type: Sequelize.STRING, type: Sequelize.STRING,
}, },
lastSync: { lastSync: {

View File

@ -32,7 +32,7 @@ const tradeItem = {
isHiddenInCatalog: { isHiddenInCatalog: {
type: Sequelize.BOOLEAN, type: Sequelize.BOOLEAN,
}, },
supplierOrganizationId: { organizationId: {
type: Sequelize.STRING type: Sequelize.STRING
}, },
lastSync: { lastSync: {

160
utils.js
View File

@ -184,74 +184,6 @@ export async function asyncQueue(fnArray, concurrency = 1) {
return results; return results;
} }
// 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
/**
* Syncs the sequence number for the given model
* if no params are given it will reset all the sequence number to 0
*
* @param {Number} current - current sequence number
* @param {String} model - model name
* @param {Number} maximumSequenceNumber - maximum sequence number
* @returns
*/
export async function syncSequence(current = 0, model = null , maximumSequenceNumber = 0){
if (model == null && current == 0){
try {
const spinner = ora(`Syncing sequence...`).start();
let mockModels = [
'supplier',
'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}`);
}
}
}
/** /**
* Sync the suppliers * Sync the suppliers
@ -278,14 +210,12 @@ export async function syncSuppliers(){
spinner.text = `Syncing suppliers, ${maxSequenceNumber - curSequenceNumber} are missing` spinner.text = `Syncing suppliers, ${maxSequenceNumber - curSequenceNumber} are missing`
if (timeFinish) if (timeFinish)
spinner.text = spinner.text + ` (${timeLeft})` spinner.text = spinner.text + ` (${timeLeft})`
await models.supplier.upsert({ await models.organization.upsert({
...supplier, ...supplier,
supplierOrganizationId: supplier.organizationId,
isConnected: JSON.parse(env.SUPPLIERS_ALWAYS_CONN), isConnected: JSON.parse(env.SUPPLIERS_ALWAYS_CONN),
lastSync: moment(), lastSync: moment(),
}); });
}; };
await syncSequence(curSequenceNumber, 'supplier', maxSequenceNumber);
timeFinish = new moment(); timeFinish = new moment();
timeToGoSec = (timeFinish.diff(timeStart, 'seconds') * (maxSequenceNumber - curSequenceNumber) / 1000) timeToGoSec = (timeFinish.diff(timeStart, 'seconds') * (maxSequenceNumber - curSequenceNumber) / 1000)
timeToGoMin = Math.trunc(timeToGoSec / 60) timeToGoMin = Math.trunc(timeToGoSec / 60)
@ -310,7 +240,10 @@ export async function syncConnections(){
await deleteConnections(); await deleteConnections();
const spinner = ora(`Creating connections...`).start(); const spinner = ora(`Creating connections...`).start();
try { try {
let connections = await models.connection.findAll(); let connections = await models.organization.findAll({
attributes: ['organizationId'],
where: { isConnected: true }
});
let headers = { let headers = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -323,21 +256,10 @@ export async function syncConnections(){
let i = 1; let i = 1;
for (let connection of connections) { for (let connection of connections) {
spinner.text = `Creating ${i++} of ${connections.length} connections...` spinner.text = `Creating ${i++} of ${connections.length} connections...`
let remoteConnection = remoteConnections.find(remoteConnection => remoteConnection == connection.supplierOrganizationId); let remoteConnection = remoteConnections.find(remoteConnection => remoteConnection == connection.organizationId);
if (!remoteConnection) if (!remoteConnection)
await vnRequest('PUT', `${env.API_URL}/connections/${connection.supplierOrganizationId}`, null, headers); await vnRequest('PUT', `${env.API_URL}/connections/${connection.organizationId}`, null, headers);
await models.connection.update({isConnected: true }, {
where: {
supplierOrganizationId: connection.supplierOrganizationId
}
});
await models.supplier.update({ isConnected: true }, {
where: {
supplierOrganizationId: connection.supplierOrganizationId
}
});
} }
spinner.succeed(); spinner.succeed();
} catch (err) { } catch (err) {
@ -347,14 +269,15 @@ export async function syncConnections(){
} }
/** /**
* Sync the connections in Floriday * Sync the trade items for organizations that are connected
*/ */
export async function syncTradeItems(){ export async function syncTradeItems(){
const spinner = ora(`Syncing trade items...`).start(); const spinner = ora(`Syncing trade items...`).start();
const suppliers = await models.supplier.findAll({ const suppliers = await models.organization.findAll({
attributes: ['organizationId'],
where: { isConnected: true } where: { isConnected: true }
}); });
let i = 0, x = 1; let i = 1, x = 1;
for (let supplier of suppliers) { for (let supplier of suppliers) {
let headers = { let headers = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -362,7 +285,10 @@ export async function syncTradeItems(){
'X-Api-Key': process.env.API_KEY 'X-Api-Key': process.env.API_KEY
}; };
try { try {
let tradeItems = (await vnRequest('GET', `${env.API_URL}/trade-items?supplierOrganizationId=${supplier.supplierOrganizationId}`, null, headers)).data const params = new URLSearchParams({
supplierOrganizationId: supplier.organizationId,
}).toString();
let tradeItems = (await vnRequest('GET', `${env.API_URL}/trade-items?${params}`, null, headers)).data
spinner.text = `Syncing ${i} trade items of [${x++}|${suppliers.length}] suppliers...` spinner.text = `Syncing ${i} trade items of [${x++}|${suppliers.length}] suppliers...`
if (!tradeItems.length) continue; if (!tradeItems.length) continue;
@ -380,10 +306,10 @@ export async function syncTradeItems(){
} }
/** /**
* Sync the supply lines for suppliers that are connected * Sync the supply lines for organizations that are connected
*/ */
export async function syncSupplyLines() { export async function syncSupplyLines() {
const spinner = ora(`(NEW) Syncing supply lines...`).start(); const spinner = ora(`Syncing supply lines...`).start();
try { try {
let headers = { let headers = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -392,24 +318,24 @@ export async function syncSupplyLines() {
}; };
let suppliersWithTradeItem = await models.tradeItem.findAll({ let suppliersWithTradeItem = await models.tradeItem.findAll({
attributes: ['supplierOrganizationId'], attributes: ['organizationId'],
group: ['supplierOrganizationId'] group: ['organizationId']
}); });
let connectedSuppliers = await models.supplier.findAll({ let connectedSuppliers = await models.organization.findAll({
attributes: ['supplierOrganizationId'], attributes: ['organizationId'],
where: { isConnected: true } where: { isConnected: true }
}); });
let suppliers = suppliersWithTradeItem.filter(supplier => { let suppliers = suppliersWithTradeItem.filter(supplier => {
return connectedSuppliers.some(connectedSupplier => { return connectedSuppliers.some(connectedSupplier => {
return connectedSupplier.supplierOrganizationId === supplier.supplierOrganizationId; return connectedSupplier.organizationId === supplier.organizationId;
}); });
}).map(supplier => supplier.supplierOrganizationId); }).map(supplier => supplier.organizationId);
let i = 0, x = 1; let i = 0, x = 1;
for (let supplier of suppliers) { for (let supplier of suppliers) {
spinner.text = `(NEW) Syncing ${i} supply lines of [${x++}|${suppliers.length}] suppliers...` spinner.text = `Syncing ${i} supply lines of [${x++}|${suppliers.length}] suppliers...`
const params = new URLSearchParams({ const params = new URLSearchParams({
supplierOrganizationId: supplier, supplierOrganizationId: supplier,
}).toString(); }).toString();
@ -427,9 +353,10 @@ export async function syncSupplyLines() {
insertItem(tradeItem[0]) insertItem(tradeItem[0])
} }
spinner.text = `(NEW) Syncing ${i++} supply lines of [${x}|${suppliers.length}] suppliers...` spinner.text = `Syncing ${i++} supply lines of [${x}|${suppliers.length}] suppliers...`
await models.supplyLine.upsert({ await models.supplyLine.upsert({
...supplyLine, ...supplyLine,
organizationId: supplyLine.supplierOrganizationId,
pricePerPiece_currency: supplyLine.pricePerPiece ? supplyLine.pricePerPiece.currency : null, pricePerPiece_currency: supplyLine.pricePerPiece ? supplyLine.pricePerPiece.currency : null,
pricePerPiece_value: supplyLine.pricePerPiece ? supplyLine.pricePerPiece.value : null, pricePerPiece_value: supplyLine.pricePerPiece ? supplyLine.pricePerPiece.value : null,
deliveryPeriod_startDateTime: supplyLine.deliveryPeriod ? supplyLine.deliveryPeriod.startDateTime : null, deliveryPeriod_startDateTime: supplyLine.deliveryPeriod ? supplyLine.deliveryPeriod.startDateTime : null,
@ -466,14 +393,10 @@ export async function insertItem(tradeItem) {
try { try {
tx = await models.sequelize.transaction(); tx = await models.sequelize.transaction();
// Upsert supplier connection
await models.connection.upsert({
supplierOrganizationId: tradeItem.supplierOrganizationId,
}, { transaction: tx });
// Upsert trade item // Upsert trade item
await models.tradeItem.upsert({ await models.tradeItem.upsert({
...tradeItem, ...tradeItem,
organizationId: tradeItem.supplierOrganizationId,
lastSync: moment(), lastSync: moment(),
}, { transaction: tx }); }, { transaction: tx });
@ -554,7 +477,7 @@ export async function insertItem(tradeItem) {
} }
/** /**
* Sync the warehouses * Sync the warehouses for organizations that are connected
**/ **/
export async function syncWarehouses(){ export async function syncWarehouses(){
let spinner = ora('Syncing warehouses...').start(); let spinner = ora('Syncing warehouses...').start();
@ -565,16 +488,18 @@ export async function syncWarehouses(){
'X-Api-Key': process.env.API_KEY, 'X-Api-Key': process.env.API_KEY,
}; };
const suppliers = await models.supplier.findAll(); const suppliers = await models.organization.findAll({
where: { isConnected: true }
});
let x = 0, i = 1; let x = 0, i = 1;
for (let supplier of suppliers) { for (let supplier of suppliers) {
spinner.text = `Syncing ${i} warehouses of [${x++}|${suppliers.length}]...` spinner.text = `Syncing ${i} warehouses of [${x++}|${suppliers.length}]...`
const warehouses = (await vnRequest('GET', `${env.API_URL}/organizations/supplier/${supplier.supplierOrganizationId}/warehouses`, null, headers)).data; const warehouses = (await vnRequest('GET', `${env.API_URL}/organizations/supplier/${supplier.organizationId}/warehouses`, null, headers)).data;
for (let warehouse of warehouses) { for (let warehouse of warehouses) {
spinner.text = `Syncing ${i++} warehouses of [${x}|${suppliers.length}]...` spinner.text = `Syncing ${i++} warehouses of [${x}|${suppliers.length}] suppliers...`
await models.warehouses.upsert({ await models.warehouses.upsert({
...warehouse, ...warehouse,
supplierOrganizationId: warehouse.organizationId,
location_gln: warehouse.location.gln, location_gln: warehouse.location.gln,
location_address_addressLine: warehouse.location.address.addressLine, location_address_addressLine: warehouse.location.address.addressLine,
location_address_city: warehouse.location.address.city, location_address_city: warehouse.location.address.city,
@ -585,6 +510,7 @@ export async function syncWarehouses(){
}); });
} }
} }
spinner.succeed();
} }
catch (err) { catch (err) {
spinner.fail(); spinner.fail();
@ -596,7 +522,7 @@ export async function syncWarehouses(){
* Removes Floriday connections that we don't have in the database * Removes Floriday connections that we don't have in the database
**/ **/
export async function deleteConnections() { export async function deleteConnections() {
const spinner = ora(`Deleting connections that aren't in the db...`).start(); let spinner;
try { try {
let i = 1; let i = 1;
const headers = { const headers = {
@ -606,12 +532,15 @@ export async function deleteConnections() {
}; };
const connectionsInFloriday = (await vnRequest('GET', `${env.API_URL}/connections`, null, headers)).data; const connectionsInFloriday = (await vnRequest('GET', `${env.API_URL}/connections`, null, headers)).data;
const connectionsInDb = await models.connection.findAll(); const connectionsInDb = await models.organization.findAll({
attributes: ['organizationId'],
where: { isConnected: true }
});
let isExists = false, ghostConnections = []; let isExists = false, ghostConnections = [];
for (let connectionInFloriday of connectionsInFloriday) { for (let connectionInFloriday of connectionsInFloriday) {
for (let connectionInDb of connectionsInDb) for (let connectionInDb of connectionsInDb)
if (connectionInFloriday == connectionInDb.supplierOrganizationId) { if (connectionInFloriday == connectionInDb.organizationId) {
isExists = true; isExists = true;
break; break;
} }
@ -619,13 +548,16 @@ export async function deleteConnections() {
isExists = false; isExists = false;
} }
if (ghostConnections.length)
spinner = ora(`Deleting connections that aren't in the db...`).start();
for (let connection of ghostConnections) { for (let connection of ghostConnections) {
await vnRequest('DELETE', `${env.API_URL}/connections/${connection}`, null, headers); await vnRequest('DELETE', `${env.API_URL}/connections/${connection}`, null, headers);
spinner.text = `Deleting ${i++} of ${ghostConnections.length} that aren't in the db...` spinner.text = `Deleting ${i++} of ${ghostConnections.length} that aren't in the db...`
} }
spinner.succeed(); if (spinner) spinner.succeed();
} catch (err) { } catch (err) {
spinner.fail(); if (spinner) spinner.fail();
criticalError(err); criticalError(err);
} }
} }