Untitled
user_0324236
plain_text
a year ago
7.5 kB
5
Indexable
const dotenv = require('dotenv');
const Shopify = require('shopify-api-node');
const Bottleneck = require('bottleneck');
const cron = require('node-cron');
const express = require('express');
const app = express();
dotenv.config();
const PORT = process.env.PORT || 8000
const INVENTORY_BUFFER = 2;
const sourceShopify = new Shopify({
shopName: process.env.SOURCE_SHOP_NAME,
accessToken: process.env.SOURCE_SHOPIFY_ACCESS_TOKEN,
accessMode: 'private',
apiVersion: '2021-07',
scopes: ['read_inventory', 'read_products']
});
const destinationShopify = new Shopify({
shopName: process.env.DESTINATION_SHOP_NAME,
accessToken: process.env.DESTINATION_SHOPIFY_ACCESS_TOKEN,
accessMode: 'private',
apiVersion: '2024-04',
scopes: ['read_inventory', 'write_inventory', 'read_products', 'write_products']
});
sourceShopify.shop.get()
.then(shop => {
console.log('Connected to Source Shopify successfully.');
})
.catch(err => {
console.error('Failed to connect to Source Shopify:', err);
});
destinationShopify.shop.get()
.then(shop => {
console.log('Connected to Destination Shopify successfully.');
})
.catch(err => {
console.error('Failed to connect to Destination Shopify:', err);
});
// LIST IS ONLY FETCHING FIRST 50 PRODUCTS, ADD RATE LIMIT FOR SHOPIFY API
// Function to fetch all source products inventory
async function getAllProducts() {
try {
let params = { limit: 250 };
let allRecords = [];
do {
const products = await sourceShopify.product.list(params);
allRecords = allRecords.concat(products)
params = products.nextPageParameters;
} while (params !== undefined);
console.log(`${allRecords.length} products found in the source store.`);
let allData = [];
for(let product of allRecords){
let allVariants = product.variants.map( i => ({
id: product.id,
title: product.title,
SKU: i.sku,
barcode: i.barcode,
inventory: i.inventory_quantity > 0 ? i.inventory_quantity - INVENTORY_BUFFER : 0
}))
allData = [...allData, ...allVariants]
}
return allData;
} catch (error) {
console.error('Failed to fetch products:', error);
return [];
}
}
async function getAllProductsFromDestination() {
try {
let params = { limit: 250 };
let allRecords = [];
do {
const products = await destinationShopify.product.list(params);
allRecords = allRecords.concat(products)
params = products.nextPageParameters;
} while (params !== undefined);
const allDestinationProducts = allRecords.filter(i => i.status === "active");
console.log(`${allDestinationProducts.length} active products found in the destination store.`);
return allDestinationProducts;
} catch (error) {
console.error('Failed to fetch products:', error);
return [];
}
}
async function updateForMultipleLocation(productFromDestination, prod, inventoryList) {
try {
inventoryList.forEach(async i => {
if(i.location_id === 73108553947){
await destinationShopify.inventoryLevel.set({ inventory_item_id: productFromDestination.inventory_item_id, location_id: i.location_id, available: prod.inventory })
console.log(`Successfully updated for location ${i.location_id}`)
}else{
console.error("Inventory quantity not updated because it is located in GreyVille")
}
})
}
catch (err) {
console.log(err);
}
}
// UPDATE INVENTORY NOT WORKING
// Function to update inventory in destination store
async function updateDestinationStore(products) {
try {
// Fetch product list from the destination store
const destinationProducts = await getAllProductsFromDestination();
for (const desProduct of destinationProducts) {
let prod = {};
let prodBarcode = '';
const productFromDestination = desProduct.variants.find(i =>
products.some(p => {
prodBarcode = p.barcode
if (p?.barcode === i?.barcode) {
prod = p;
return true
}
})
)
// console.log(prod, "EXISTS", productFromDestination)
if (productFromDestination) {
const res = await destinationShopify.inventoryItem.get(productFromDestination.inventory_item_id)
if (res.tracked) {
const inventoryList = await destinationShopify.inventoryLevel.list({ inventory_item_ids: productFromDestination.inventory_item_id })
if (inventoryList.length > 1) {
await updateForMultipleLocation(productFromDestination, prod, inventoryList);
} else {
const inventoryLocation = inventoryList[0].location_id;
if(inventoryLocation === 73108553947){
await destinationShopify.inventoryLevel.set({ inventory_item_id: productFromDestination.inventory_item_id, location_id: inventoryLocation, available: prod.inventory })
console.log(`Inventory updated for product ${productFromDestination.title} with barcode ${productFromDestination.barcode}`);
}else{
console.error("Inventory quantity not updated because it is located in GreyVille")
}
}
} else {
console.error(`Tracking not enabled for the inventory item ${productFromDestination.inventory_item_id} with variant barcode ${productFromDestination.barcode}`)
}
} else {
console.error(`No matching products found in source table ${prodBarcode ? "for barcode" + prodBarcode : "because barcode value is null"}`);
}
}
} catch (error) {
if (error.response && error.response.statusCode === 403) {
console.error('Failed to update inventory in the destination store: You do not have permission to perform this action.');
} else {
console.error(`Failed to update inventory for product with barcode in the destination store:`, error);
}
}
}
// Rate limiting configuration
const limiter = new Bottleneck({
maxConcurrent: 1, // Number of concurrent requests
minTime: 2000 // Minimum time between each request (in milliseconds)
});
//Fetch products and update inventory with rate limiting
function cronJobEveryHour(){
limiter.schedule(getAllProducts)
.then(products => {
console.table(products);
return updateDestinationStore(products);
})
.catch(error => {
console.error('Error during product fetch or inventory update:', error);
});
}
cron.schedule('0 0 */1 * * *', cronJobEveryHour);
app.listen(PORT, ()=> {
console.log(`App is running on port ${PORT}`)
})
Editor is loading...
Leave a Comment