| Current Path : /home/deltalab/PMS/partner-manager-backend/rest/routes/ |
| Current File : //home/deltalab/PMS/partner-manager-backend/rest/routes/products.js |
/* eslint-disable no-return-await */
/* eslint-disable no-continue */
/* eslint-disable no-await-in-loop */
/* eslint-disable no-use-before-define */
/* eslint-disable no-restricted-syntax */
/* eslint-disable max-len */
const { ObjectId } = require('bson');
const express = require('express');
const fs = require('fs');
const formidable = require('formidable');
const AdmZip = require('adm-zip');
const { productModel } = require('../../models/mongoose/product');
const { productMediaModel } = require('../../models/mongoose/product');
const { warehouseModel } = require('../../models/mongoose/warehouse');
const ims = require('../../services/ims');
const utility = require('../../services/utility');
const importService = require('../../services/import');
const { productQuery, masterQuery } = require('../queries');
const { categoryQuery } = require('../queries');
const router = express.Router({ mergeParams: true });
router.get('/:productId', async (req, res) => {
try {
const product = await productQuery.readOne(req.params.productId);
return res.status(200).json({ success: true, data: product });
} catch (error) {
console.log(error);
return res.status(400).send({ success: false, message: 'Error reading the product' });
}
});
router.put('/updateOffersPriceList', (req, res, next) => {
if (utility.isPartnerEqualToParams(req)) {
next();
} else {
return res.status(401).send({ success: false, message: 'Not authorized' });
}
}, async (req, res) => {
try {
console.log('Aggiorno le offerte in base al listino prezzi');
const restParameters = req.body;
const { offerList } = restParameters;
const { partnerId } = restParameters;
const { channelId } = restParameters;
let productCount = 0;
for (let i = 0; i < offerList.length; i++) {
const product = await productModel.findOne({ 'offers._id': offerList[i]._id });
//ho il prodotto
for (let j = 0; j < product.offers.length; j++) {
if (product.offers[j]._id.toString() === offerList[i]._id) {
//Ho trovato l'offerta persistente
if (product.offers[j].price !== offerList[i].price) {
//se il prezzo è diverso, continuo
product.offers[j].price = offerList[i].price;
await product.save();
await ims.referenceUpdate(product, product.sku);
productCount++;
}
}
}
}
return res.status(200).json({ success: true, data: productCount });
} catch (error) {
return res.status(400).send({ success: false, message: 'Error updating the product' });
}
});
router.post('/', (req, res, next) => {
if (utility.isPartnerEqualToParams(req)) {
console.log('il partner è valido, procedo');
next();
} else {
return res.status(401).send({ success: false, message: 'Not authorized' });
}
}, async (req, res) => {
try {
const newReference = { ...req.body };
if (!newReference.masterId) {
const newMaster = {};
newMaster.title = newReference.title;
newMaster.description = newReference.description;
newMaster.msrp = newReference.msrp;
newMaster.partnerId = newReference.partnerId;
const persistentMaster = masterQuery.createOne(newMaster);
if (persistentMaster) {
newReference.masterId = persistentMaster._id;
}
}
const sku = Math.floor(100000 + Math.random() * 900000);
newReference.sku = `${newReference.customerSku.replace(/\s+/g, '')}-${sku}`.substring(0, 63);
for (let i = 0; i < newReference.offers.length; i++) {
const offerSpecificReference = JSON.parse(JSON.stringify(newReference));
const offer = newReference.offers[i];
if (offer._id.includes('TEMP')) {
const id = new ObjectId();
offer._id = id;
newReference.offers[i]._id = id;
}
offerSpecificReference.offers[i].sku = `${offer.sku.replace(/\s+/g, '')}-${sku}`.substring(0, 63);
offerSpecificReference.title = offer.title;
offerSpecificReference.customDescription = offer.description;
offerSpecificReference.price = offer.price;
offerSpecificReference.sku = offerSpecificReference.offers[i].sku;
offerSpecificReference.imsCategories = offer.imsCategories;
offerSpecificReference.deleted = offer.deleted;
offerSpecificReference.urlKey = offer.urlKey;
offerSpecificReference.attributes = offer.attributes;
// validity
offerSpecificReference.imsEnabled = newReference.imsEnabled ? offer.imsEnabled : false;
// VALID FROM
if (newReference.isValidFrom && offer.isValidFrom) {
offerSpecificReference.isValidFrom = true;
offerSpecificReference.validFrom = newReference.validFrom > offer.validFrom ? newReference.validFrom : offer.validFrom;
}
if (!newReference.isValidFrom && offer.isValidFrom) {
offerSpecificReference.isValidFrom = true;
offerSpecificReference.validFrom = offer.validFrom;
}
if (newReference.isValidFrom && !offer.isValidFrom) {
offerSpecificReference.isValidFrom = true;
offerSpecificReference.validFrom = newReference.validFrom;
}
// VALID UNTIL
if (newReference.isValidUntil && offer.isValidUntil) {
offerSpecificReference.isValidUntil = true;
offerSpecificReference.validUntil = newReference.validUntil < offer.validUntil ? newReference.validUntil : offer.validUntil;
}
if (!newReference.isValidUntil && offer.isValidUntil) {
offerSpecificReference.isValidUntil = true;
offerSpecificReference.validUntil = offer.validFrom;
}
if (newReference.isValidUntil && !offer.isValidUntil) {
offerSpecificReference.isValidUntil = true;
offerSpecificReference.validUntil = newReference.validUntil;
}
await ims.productCreate(offerSpecificReference, offer.channelId);
}
const reference = await productQuery.createOne(newReference);
return res.status(200).json({ success: true, data: reference });
} catch (error) {
console.log(error);
return res.status(400).send({ success: false, message: 'Error creating the product', error: error.message });
}
});
router.put('/updateInventory', (req, res, next) => {
if (utility.isPartnerEqualToParams(req)) {
console.log('il partner è valido, procedo');
next();
} else {
return res.status(401).send({ success: false, message: 'Not authorized' });
}
}, async (req, res) => {
try {
console.log('arrivato su inventory');
const restParameters = req.body;
const { reference } = restParameters;
const persistentReference = await productModel.findById(restParameters.reference._id);
let found = false;
if (reference.trackInventory) {
// check the inventory levels of the modified product against the persistent one.
for (let i = 0; i < reference.inventoryLevels.length; i++) {
const warehouse = await warehouseModel.findById(reference.inventoryLevels[i].warehouseId);
found = false;
for (let j = 0; j < persistentReference.inventoryLevels.length; j++) {
if (persistentReference.inventoryLevels[j].warehouseId.equals(reference.inventoryLevels[i].warehouseId)) {
found = true;
console.log(`current availability: ${persistentReference.inventoryLevels[j].amount}`);
//const startingQuantity = persistentReference.inventoryLevels[j].amount >= 0 ? persistentReference.inventoryLevels[j].amount : 0; //if starting quantity is below 0, consider it as 0 in transfer
const delta = reference.inventoryLevels[i].amount - persistentReference.inventoryLevels[j].amount;
if (delta !== 0) {
const externalWarehouse = delta > 0 && reference.partnerId !== warehouse.partnerId.toString();
if (externalWarehouse) {
console.log('diminuisco in inventory level, dovrà essere confermata');
reference.inventoryLevels[i].amount = persistentReference.inventoryLevels[j].amount;
}
console.log(`changed availability in warehouse ${reference.inventoryLevels[i].warehouseId} by ${delta}, new total ${reference.inventoryLevels[i].amount}`);
const totalQuantity = externalWarehouse ? reference.inventoryLevels[i].amount : reference.inventoryLevels[i].amount + delta;
console.log('aggiungo al journal');
ims.addEntryToWarehouseJournal(reference.inventoryLevels[i].warehouseId, reference._id, reference.partnerId, delta, totalQuantity, delta > 0 ? 'INCREASE' : 'DECREASE', reference.refrigerated, !externalWarehouse);
// await reference.save();
}
}
}
if (!found) {
// no inventory level for this product, add it
console.log(`no warehouse ${reference.inventoryLevels[i].warehouseId} in persistent product, adding it with ${reference.inventoryLevels[i].amount} starting quantity`);
// reference.inventoryLevels.push({ warehouseId: reference.inventoryLevels[i].warehouseId, amount: reference.inventoryLevels[i].amount });
const externalWarehouse = reference.inventoryLevels[i].amount > 0 && reference.partnerId !== warehouse.partnerId.toString();
ims.addEntryToWarehouseJournal(reference.inventoryLevels[i].warehouseId, reference._id, reference.partnerId, reference.inventoryLevels[i].amount, externalWarehouse ? 0 : reference.inventoryLevels[i].amount, 'INITIAL', reference.refrigerated, !externalWarehouse);
if (externalWarehouse) {
console.log('new stock in external warehouse, needs to be confirmed');
reference.inventoryLevels[i].amount = 0;
}
// await reference.save();
}
}
}
return res.status(200).json({ success: true, data: reference });
} catch (error) {
return res.status(400).send({ success: false, message: 'Error updating the product' });
}
});
router.put('/:referenceId', (req, res, next) => {
console.log('CONTROLLO');
if (utility.isPartnerEqualToParams(req)) {
console.log('il partner è valido, procedo');
next();
} else {
return res.status(401).send({ success: false, message: 'Not authorized' });
}
}, async (req, res) => {
try {
console.log('PUT riuscito');
let validOffers = 0;
const persistentReference = await productQuery.readOne(req.params.referenceId);
const toUpdate = req.body;
console.log('entro nel giro di validazione');
for (let j = 0; j < toUpdate.offers.length; j++) {
console.log('entro in update');
const isOfferPersistent = await ims.isOfferInIMS(toUpdate.offers[j]);
if (!isOfferPersistent) {
const offerSpecificReference = JSON.parse(JSON.stringify(toUpdate));
offerSpecificReference.offers[j].sku = toUpdate.offers[j].sku;
offerSpecificReference.title = toUpdate.offers[j].title;
offerSpecificReference.customDescription = toUpdate.offers[j].description;
offerSpecificReference.price = toUpdate.offers[j].price;
offerSpecificReference.sku = offerSpecificReference.offers[j].sku;
offerSpecificReference.deleted = toUpdate.offers[j].deleted;
offerSpecificReference.urlKey = toUpdate.offers[j].urlKey;
offerSpecificReference.attributes = toUpdate.offers[j].attributes;
// validity
offerSpecificReference.imsEnabled = toUpdate.imsEnabled ? toUpdate.offers[j].imsEnabled : false;
if ((toUpdate.isValidFrom && toUpdate.offers[j].isValidFrom) || (!toUpdate.isValidFrom && toUpdate.offers[j].isValidFrom)) {
offerSpecificReference.isValidFrom = toUpdate.offers[j].isValidFrom;
offerSpecificReference.validFrom = toUpdate.offers[j].validFrom;
} else {
offerSpecificReference.isValidFrom = false;
}
if ((toUpdate.isValidUntil && toUpdate.offers[j].isValidUntil) || (!toUpdate.isValidUntil && toUpdate.offers[j].isValidUntil)) {
offerSpecificReference.isValidUntil = toUpdate.offers[j].isValidUntil;
offerSpecificReference.validUntil = toUpdate.offers[j].validUntil;
} else {
offerSpecificReference.isValidUntil = false;
}
console.log('creo?');
const result = await ims.productCreate(offerSpecificReference, toUpdate.offers[j].channelId);
if (result) {
validOffers++;
}
} else {
validOffers++;
}
}
console.log('sto per entrare in update ims');
if (validOffers === toUpdate.offers.length) {
await ims.referenceUpdate(toUpdate, persistentReference.sku);
}
const updatedProduct = await productQuery.updateOne(req.params.referenceId, toUpdate);
return res.status(200).json({ success: true, data: updatedProduct });
} catch (error) {
return res.status(400).send({ success: false, message: 'Error updating the product', error: error.message });
}
});
router.put('/delete/:productId', async (req, res) => {
try {
const product = await productQuery.readOne(req.params.productId);
await ims.referenceUpdate(req.body, product.sku);
const updatedProduct = await productQuery.updateOne(req.params.productId, req.body);
return res.status(200).json({ success: true, data: updatedProduct });
} catch (error) {
console.log(error);
return res.status(400).send({ success: false, message: 'Error updating the product', error: error.message });
}
});
router.delete('/:productSku', async (req, res) => {
try {
await ims.productDelete(req.params.productSku);
await productQuery.deleteOne(req.params.productSku);
return res.status(204).send();
} catch (error) {
console.log(error);
return res.status(400).send({ success: false, message: 'Error deleting the product', error: error.message });
}
});
// TODO: verificare partner (campo nascosto)
router.post('/import', async (req, res) => {
try {
let importedCount = 0;
const form = formidable({ multiples: true });
const [fields, files] = await ns.form_parse(req, form)
.catch((e) => console.log(e));
let importedSKUs = [];
const zip = new AdmZip(files.zip.filepath);
const zipEntries = zip.getEntries(); // an array of ZipEntry records
let csvFound = false;
for (const zipEntry of zipEntries) {
if (zipEntry.entryName.includes('.csv')) {
// Look for the csv and import it
console.log('entro in import');
importedSKUs = await importCSVData(zipEntry.getData().toString('utf8'), fields);
importedCount = importedSKUs.length;
csvFound = true;
}
}
if (csvFound) {
for (const zipEntry of zipEntries) {
const name = zipEntry.entryName;
if (name.toLowerCase().includes('.png') || name.toLowerCase().includes('.jpg') || name.toLowerCase().includes('.jpeg')) {
const filename = name.substring(name.lastIndexOf('/') + 1, name.lastIndexOf('.'));
const extension = name.substring(name.lastIndexOf('.') + 1);
for (let i = 0; i < importedSKUs.length; i++) {
if (filename.includes(importedSKUs[i].sku)) {
console.log(`upload di ${filename} in corso`);
importImageToProduct(importedSKUs[i].fullSku, zipEntry, filename, extension);
}
}
}
}
}
if (fs.existsSync(files.zip.filepath)) {
fs.unlinkSync(files.zip.filepath);
}
// });
return res.status(200).json({ success: true, data: importedCount });
} catch (error) {
return res.status(400).send({ success: false, message: 'Error creating the product', error: error.message });
}
});
const ns = {
form_parse: async (req, form) => await new Promise((resolve, reject) => form.parse(req, (e, fields, files) => (e ? reject(e) : resolve([fields, files])))),
};
async function importCSVData(csvData, fields) {
console.log('importCSVData');
const importedSKUs = [];
const csvRecordsArray = csvData.split(/\r\n|\n/);
const headersRow = importService.getHeaderArray(csvRecordsArray);
// const rows = importService.getDataRecordsArrayFromCSVFile(csv, headersRow.length, partnerId);
for (let i = 1; i < csvRecordsArray.length; i++) {
const currentRecord = (csvRecordsArray[i]).split(';');
console.log(`${currentRecord.length} - ${headersRow.length}`);
if (currentRecord.length === headersRow.length) {
const [sku, title, description, customDescription, brand, stringMsrp, stringPrice, attributeSetId, stringWeight, quantity, imsCategories, imsTaxCode, requiresShipping, width, height, length] = currentRecord;
// const [title, variant, sku, stringPrice, stringCost, stringWeight, attributeSetId, shopifyTypeId, description, imsCategories, experiences, recipes, regions, creationDate, publishDate, image, taxTitle, taxRate] = currentRecord;
const product = {};
product.title = title;
product.description = description;
product.customDescription = customDescription;
product.brand = brand;
product.partnerId = fields.partnerId;
product.categoryId = fields.categoryId;
product.sku = sku;
const random = Math.floor(100000 + Math.random() * 900000);
product.sku = `${product.sku.replace(/\s+/g, '')}-${random}`.substring(0, 63);
product.msrp = parseFloat(stringMsrp); // parseFloat(stringMsrp);
product.price = parseFloat(stringPrice);
product.weight = parseFloat(stringWeight);
product.categoryId = fields.categoryId;
const attributeSet = await ims.readAttributeSetByName('all', attributeSetId);
product.attributeSetId = parseInt(attributeSet.id, 10); // attributeSetId
// product.quantity = parseFloat(currentRecord[7]);
product.imsCategories = await getCategoryData(imsCategories.split(','));
product.imsTaxCode = imsTaxCode;
product.channelId = fields.channelId;
product.inventoryLevels = [];
product.trackInventory = true; // quantity > 0;
product.refrigerated = false;
product.showMsrp = true;
product.requiresShipping = requiresShipping;
if (requiresShipping === '1') {
product.size = {};
product.size.height = height;
product.size.length = length;
product.size.width = width;
}
if (quantity && parseInt(quantity, 10) > 0) {
product.inventoryLevels.push({ warehouseId: fields.warehouseId, amount: quantity });
}
product.attributes = [];
for (let j = 16; j < headersRow.length; j++) { // change starting index based on the product schema
product.attributes.push({ attribute: { name: headersRow[j] }, value: currentRecord[j] });
}
try {
await ims.productCreate(product, product.channelId);
await productQuery.createOne(product);
importedSKUs.push({ fullSku: product.sku, sku });
} catch (error) {
console.log('import saltato');
continue;
}
}
}
return importedSKUs;
}
async function getCategoryData(categories) {
const categoryIds = [];
for (let i = 0; i < categories.length; i++) {
if (Number.isNaN(parseInt(categories[i], 10))) {
const category = await categoryQuery.readCategoryByName('all', categories[i]);
categoryIds.push(category.items[0].id);
} else {
categoryIds.push(categories[i]);
}
}
return categoryIds;
}
async function importImageToProduct(sku, zipEntry, filename, extension) {
const data = zipEntry.getData();
const buff = Buffer.from(data);
const base64data = buff.toString('base64');
const product = await productModel.findOne({ sku });
const imsImageResult = await ims.uploadImageToProduct(sku, filename, extension, base64data);
if (product) {
console.log('inserisco media');
product.media = [];
const productMedia = new productMediaModel();
productMedia.name = `${filename}.${extension}`;
productMedia.type = `image/${extension === 'jpg' ? 'jpeg' : extension}`;
productMedia.data = `${base64data}`;
productMedia.imsgid = imsImageResult;
product.media.push(productMedia);
await product.save();
}
}
module.exports = router;