UNPKG

@fye/vendor-utils

Version:

Vendor utility functions

606 lines (556 loc) 21.5 kB
/* eslint-disable no-console */ // const debug = require('debug')('aec-order-feedback'); const fs = require('fs'); const util = require('util'); const aecProductPrefix = 'aec.'; const fyeProductPrefix = 'fye.'; const aecUnitPricePercentage = 0.69; // looking at AOFT files and 69% seems to be a common value const aecUnitShippingPrice = 1.5; const aecUnitHandlingPrice = 0.5; const AecOrderAction = { Backorder: 0, Ship: 1, Cancel: 2, Return: 3, }; const unlink = util.promisify(fs.unlink); const lstat = util.promisify(fs.lstat); async function exists(path) { try { await lstat(path); return true; } catch (err) { return false; } } async function deleteFile(path) { try { if (await exists(path)) await unlink(path); } catch (err) {} } function calcAecUnitPrice(amount) { return amount * aecUnitPricePercentage; } function isAecProduct(productId) { return productId.toLowerCase().indexOf(aecProductPrefix) !== -1; } function isFyeProduct(productId) { return productId.toLowerCase().indexOf(fyeProductPrefix) !== -1; } function convertCountryName(country) { if (country.toLowerCase().indexOf('united') !== -1 && country.toLowerCase().indexOf('states') !== -1) { return 'UNITED STATES'; } return country; } function formatDate(date) { const month = date.getMonth(); const formattedMonth = `0${month}`.slice(-2); const day = date.getDate(); const formattedDay = `0${day}`.slice(-2); const year = date.getFullYear(); return `${formattedMonth}/${formattedDay}/${year}`; } function formatMoney(amount) { if (amount === 0) { return 0; } const strAmount = amount.toFixed(2); const index = strAmount.indexOf('.'); if (index !== -1) { const dollars = strAmount.slice(0, index); const cents = strAmount.slice(index + '.'.length); return dollars + cents; } return strAmount; } function trimLeadingZeros(productId) { let formattedProductId = productId; let index = -1; while (formattedProductId.startsWith('0')) { index = formattedProductId.indexOf('0'); formattedProductId = formattedProductId.slice(index + '0'.length); } return formattedProductId; } function formatProductId(productId) { if (isAecProduct(productId)) { const index = productId.toLowerCase().indexOf(aecProductPrefix); return productId.slice(index + aecProductPrefix.length).toUpperCase(); } if (isFyeProduct(productId)) { const index = productId.toLowerCase().indexOf(fyeProductPrefix); return trimLeadingZeros(productId.slice(index + fyeProductPrefix.length).toUpperCase()); } return productId; } function getAecOrderSubtotal(aecProducts) { let subtotal = 0; aecProducts.forEach(aecProduct => { subtotal += aecProduct.aecExtendedPrice; }); return subtotal; } function getConsumerOrderSubtotal(aecProducts) { let subtotal = 0; aecProducts.forEach(aecProduct => { subtotal += aecProduct.consumerExtendedPrice; }); return subtotal; } function getTotalQuantityReturned(aecProducts) { let totalQuantityReturned = 0; aecProducts.forEach(aecProduct => { totalQuantityReturned += aecProduct.quantityReturned; }); return totalQuantityReturned; } function getTotalQuantityCanceled(aecProducts) { let totalQuantityCanceled = 0; aecProducts.forEach(aecProduct => { totalQuantityCanceled += aecProduct.quantityCanceled; }); return totalQuantityCanceled; } function getTotalQuantityShipped(aecProducts) { let totalQuantityShipped = 0; aecProducts.forEach(aecProduct => { totalQuantityShipped += aecProduct.quantityShipped; }); return totalQuantityShipped; } /* function getTotalQuantityBackordered(aecProducts) { let totalQuantityBackordered = 0; aecProducts.forEach(aecProduct => { totalQuantityBackordered += aecProduct.quantityBackorded; }); return totalQuantityBackordered; } */ function getTotalQuantityOrdered(aecProducts) { let totalQuantityOrdered = 0; aecProducts.forEach(aecProduct => { totalQuantityOrdered += aecProduct.quantityOrdered; }); return totalQuantityOrdered; } function buildOrderTrailer(aecProducts, vendorShipDate) { const totQauntityOrdered = getTotalQuantityOrdered(aecProducts); const totQauntityShipped = getTotalQuantityShipped(aecProducts); const totQauntityReturned = getTotalQuantityReturned(aecProducts); const totQauntityCanceled = getTotalQuantityCanceled(aecProducts); const type = '"T"'; const totalQuantityOrdered = `"${totQauntityOrdered}"`; const totalQuantityShipped = `"${totQauntityShipped}"`; const totalQuantityReturned = `"${totQauntityReturned}"`; const totalItemLines = `"${aecProducts.length}"`; let consumerOrderSubtotal = '"0"'; let consumerOrderTotal = '"0"'; if (totQauntityShipped !== 0 || totQauntityReturned !== 0) { consumerOrderSubtotal = `"${formatMoney(getConsumerOrderSubtotal(aecProducts))}"`; consumerOrderTotal = consumerOrderSubtotal; } let workOrderOrReturn = '""'; if (totQauntityShipped !== 0) { workOrderOrReturn = '"32540063"'; // this value doesn't matter in our system } else if (totQauntityReturned !== 0) { workOrderOrReturn = '"JB04527432"'; // this value doesn't matter in our system } let invoice = '""'; if (totQauntityShipped !== 0) { invoice = '"PLS41100280"'; // this value doesn't matter in our system } else if (totQauntityReturned !== 0) { invoice = '"RJB4549680"'; // this value doesn't matter in our system } let shipViaCode = '""'; if (totQauntityReturned === 0) { shipViaCode = '"50"'; // this value doesn't matter in our system } let carrierTrackingNumber = '""'; if (totQauntityShipped !== 0) { carrierTrackingNumber = '"9274899996315500307783"'; // this value doesn't matter in our system } let shipDate = '""'; if (totQauntityShipped !== 0 || totQauntityCanceled !== 0 || totQauntityReturned !== 0) { shipDate = `"${formatDate(vendorShipDate)}"`; } let returnAuthCode = '""'; if (totQauntityReturned !== 0) { returnAuthCode = '"EBX"'; // this value doesn't matter in our system } let arOrderSubtotal = '"0"'; // AEC accounts receivable let arOrderShipping = '"0"'; let arOrderHandling = '"0"'; let arOrderTotal = '"0"'; if (totQauntityShipped !== 0) { const aecOrderSubtotal = getAecOrderSubtotal(aecProducts); const orderShipping = totQauntityShipped * aecUnitShippingPrice; const orderHandling = totQauntityShipped * aecUnitHandlingPrice; const orderTotal = aecOrderSubtotal + orderShipping + orderHandling; arOrderSubtotal = `"${formatMoney(aecOrderSubtotal)}"`; arOrderShipping = `"${formatMoney(orderShipping)}"`; arOrderHandling = `"${formatMoney(orderHandling)}"`; arOrderTotal = `"${formatMoney(orderTotal)}"`; } const part1 = `${type},${totalQuantityOrdered},${totalQuantityShipped},${totalQuantityReturned},`; const part2 = `${totalItemLines},${consumerOrderSubtotal},${'"0","0","0"'},${consumerOrderTotal},`; const part3 = `${workOrderOrReturn},${invoice},${shipViaCode},${carrierTrackingNumber},${'"0"'},`; const part4 = `${shipDate},${returnAuthCode},${'""'},${arOrderSubtotal},${'"0"'},${arOrderShipping},`; let part5 = `${arOrderHandling},${arOrderTotal}`; if (totQauntityReturned !== 0) { part5 += ',"PLS41100280"'; // this value doesn't matter in our system } return part1 + part2 + part3 + part4 + part5; } function buildOrderLineItems(aecProducts) { const items = []; aecProducts.forEach(aecProduct => { const type = '"L"'; const orderingProductId = `"${formatProductId(aecProduct.orderingProductId)}"`; const salesCode = '"01"'; // this value doesn't matter in our system const aecProductId = `"${formatProductId(aecProduct.aecProductId)}"`; const quantityOrdered = `"${aecProduct.quantityOrdered}"`; const quantityShipped = `"${aecProduct.quantityShipped}"`; const quantityReturned = `"${aecProduct.quantityReturned}"`; const quantityBackordered = `"${aecProduct.quantityBackordered}"`; const quantityCanceled = `"${aecProduct.quantityCanceled}"`; const consumerUnitPrice = `"${formatMoney(aecProduct.consumerUnitPrice)}"`; const consumerExtendedPrice = `"${formatMoney(aecProduct.consumerExtendedPrice)}"`; const aecUnitPrice = `"${formatMoney(aecProduct.aecUnitPrice)}"`; const aecExtendedPrice = `"${formatMoney(aecProduct.aecExtendedPrice)}"`; const referenceId = '""'; const wmsId = '"12345678"'; // this value doesn't matter in our system const part1 = `${type},${orderingProductId},${salesCode},${aecProductId},${quantityOrdered},`; const part2 = `${quantityShipped},${quantityReturned},${consumerUnitPrice},`; const part3 = `${consumerExtendedPrice},${aecUnitPrice},${aecExtendedPrice},`; const part4 = `${referenceId},${wmsId},${quantityBackordered},${quantityCanceled}`; const item = part1 + part2 + part3 + part4; items.push(item); }); return items; } function buildCustomerShipTo(aecShippingAddress) { const type = '"S"'; const name = `"${aecShippingAddress.name}"`; const address1 = `"${aecShippingAddress.address1}"`; const address2 = `"${aecShippingAddress.address2}"`; const city = `"${aecShippingAddress.city}"`; const state = `"${aecShippingAddress.state}"`; const postal = `"${aecShippingAddress.zip}"`; const country = `"${aecShippingAddress.country}"`; const part1 = `${type},${name},${address1},${address2},${city},${state},${postal},`; const part2 = `${'"",'}${country}${',"",""'}`; return part1 + part2; } function buildCustomerBillTo(aecBillingAddress) { const type = '"B"'; const name = `"${aecBillingAddress.name}"`; const address1 = `"${aecBillingAddress.address1}"`; const address2 = `"${aecBillingAddress.address2}"`; const city = `"${aecBillingAddress.city}"`; const state = `"${aecBillingAddress.state}"`; const postal = `"${aecBillingAddress.zip}"`; const country = `"${aecBillingAddress.country}"`; const part1 = `${type},${name},${address1},${address2},${city},${state},${postal},`; const part2 = `${'"",'}${country}${',"","","","??","","","",""'}`; return part1 + part2; } function buildOrderHeader({ vendorInvoiceDate, vendorOrderId, transactionDate, purchaseOrderInternalId, salesOrderExternalId, }) { const type = '"H"'; const orderType = '"S"'; const invoiceDate = `"${formatDate(vendorInvoiceDate)}"`; const confirmationNumber = `"${vendorOrderId}"`; const orderDate = `"${formatDate(transactionDate)}"`; const retailerPoNumber = `"${purchaseOrderInternalId}"`; const memo = '"F"'; const source = `"${salesOrderExternalId}"`; const retailerReturnAuthNumber = '""'; // created in ROMS const part1 = `${type},${orderType},${invoiceDate},${confirmationNumber},${orderDate},`; const part2 = `${retailerPoNumber},${memo},${source},${retailerReturnAuthNumber}`; return part1 + part2; } function getAecOrderFeedbackNumItems(aecOrderFeedback) { return aecOrderFeedback.items.length; } function getAecOrderFeedbackNumLineItems(aecOrderFeedback) { let totalNumberLineItems = getAecOrderFeedbackNumItems(aecOrderFeedback); totalNumberLineItems += 4; // H, B, S, T return totalNumberLineItems; } function getAecOrderFeedbacksNumItems(aecOrderFeedbacks) { let totalNumberItems = 0; aecOrderFeedbacks.forEach(aecOrderFeedback => { totalNumberItems += getAecOrderFeedbackNumItems(aecOrderFeedback); }); return totalNumberItems; } function getAecOrderFeedbacksNumLineItems(aecOrderFeedbacks) { let totalNumberLineItems = 0; aecOrderFeedbacks.forEach(aecOrderFeedback => { totalNumberLineItems += getAecOrderFeedbackNumLineItems(aecOrderFeedback); }); return totalNumberLineItems; } function buildTransmissionFileTrailer(aecOrderFeedbacks) { const type = '"E"'; const totalNumberOrders = `"${aecOrderFeedbacks.length}"`; const TotalNumberItems = `"${getAecOrderFeedbacksNumItems(aecOrderFeedbacks)}"`; const TotalNumberLineItems = `"${getAecOrderFeedbacksNumLineItems(aecOrderFeedbacks) + 2}"`; return `${type},${totalNumberOrders},${TotalNumberItems},${TotalNumberLineItems}`; } function buildTransmissionFileHeader(transmissionDate) { const type = '"F"'; const transDate = `"${formatDate(transmissionDate)}"`; const description = `"POSTED <BEGINNING> TO ${formatDate(transmissionDate)} By Batch #"`; return `${type},${transDate},${description}`; } function writeFinalLineToStream(writeStream, line) { const newLine = `${line}\n`; writeStream.end(newLine); } function writeLineToStream(writeStream, line) { const newLine = `${line}\n`; writeStream.write(newLine); } function serializeAecTransmissionFileContainer(writeStream, aecTransmissionFileContainer) { writeLineToStream(writeStream, aecTransmissionFileContainer.transmissionFileHeader); aecTransmissionFileContainer.aecOrderFeedbacks.forEach(aecOrderFeedback => { writeLineToStream(writeStream, aecOrderFeedback.orderHeader); writeLineToStream(writeStream, aecOrderFeedback.customerBillTo); writeLineToStream(writeStream, aecOrderFeedback.customerShipTo); aecOrderFeedback.items.forEach(item => { writeLineToStream(writeStream, item); }); writeLineToStream(writeStream, aecOrderFeedback.orderTrailer); }); writeFinalLineToStream(writeStream, aecTransmissionFileContainer.transmissionFileTrailer); } // *********** Public Functions *********** function convertNetsuiteBillingAddressToAecBillingAddress(netsuiteBillingAddress) { return { name: netsuiteBillingAddress.addressee, address1: netsuiteBillingAddress.address1, address2: netsuiteBillingAddress.address2, city: netsuiteBillingAddress.city, state: netsuiteBillingAddress.state, zip: netsuiteBillingAddress.zip, country: convertCountryName(netsuiteBillingAddress.country), }; } function convertNetsuiteShippingAddressToAecShippingAddress(netsuiteShippingAddress) { return { name: netsuiteShippingAddress.addressee, address1: netsuiteShippingAddress.address1, address2: netsuiteShippingAddress.address2, city: netsuiteShippingAddress.city, state: netsuiteShippingAddress.state, zip: netsuiteShippingAddress.zip, country: convertCountryName(netsuiteShippingAddress.country), }; } function convertNetsuiteProductToAecProduct(netsuiteProduct, aecOrderAction) { if (!isAecProduct(netsuiteProduct.name) && !isFyeProduct(netsuiteProduct.name)) { throw new Error('Must be an AEC or FYE product id'); } let quantityOrdered = 0; let quantityShipped = 0; let quantityReturned = 0; let quantityBackordered = 0; let quantityCanceled = 0; if (aecOrderAction === AecOrderAction.Backorder) { quantityOrdered = netsuiteProduct.quantity; quantityBackordered = netsuiteProduct.quantity; } else if (aecOrderAction === AecOrderAction.Ship) { quantityOrdered = netsuiteProduct.quantity; quantityShipped = netsuiteProduct.quantity; } else if (aecOrderAction === AecOrderAction.Cancel) { quantityOrdered = netsuiteProduct.quantity; quantityCanceled = netsuiteProduct.quantity; } else if (aecOrderAction === AecOrderAction.Return) { quantityReturned = netsuiteProduct.quantity; } let consumerExtendedPrice = 0; if (aecOrderAction === AecOrderAction.Ship || aecOrderAction === AecOrderAction.Return) { consumerExtendedPrice = netsuiteProduct.amount * netsuiteProduct.quantity; } let aecUnitPrice = 0; let aecExtendedPrice = 0; if (!isFyeProduct(netsuiteProduct.name) && aecOrderAction === AecOrderAction.Ship) { aecUnitPrice = calcAecUnitPrice(netsuiteProduct.amount); aecExtendedPrice = aecUnitPrice * netsuiteProduct.quantity; } return { orderingProductId: netsuiteProduct.name, aecProductId: netsuiteProduct.name, quantityOrdered, quantityShipped, quantityReturned, quantityBackordered, quantityCanceled, consumerUnitPrice: netsuiteProduct.amount, consumerExtendedPrice, aecUnitPrice, aecExtendedPrice, orderAction: aecOrderAction !== undefined ? aecOrderAction : AecOrderAction.Backorder, }; } function convertNetsuiteProductsToAecProducts(netsuiteProducts, aecOrderAction) { const aecProducts = []; netsuiteProducts.forEach(netsuiteProduct => { const aecProduct = convertNetsuiteProductToAecProduct(netsuiteProduct, aecOrderAction); aecProducts.push(aecProduct); }); return aecProducts; } function createAecOrderFeedback({ vendorInvoiceDate, vendorOrderId, transactionDate, vendorShipDate, purchaseOrderInternalId, salesOrderExternalId, aecBillingAddress, aecShippingAddress, aecProducts, }) { const aecOrderFeedback = { orderHeader: buildOrderHeader({ vendorInvoiceDate, vendorOrderId, transactionDate, purchaseOrderInternalId, salesOrderExternalId, }), customerBillTo: buildCustomerBillTo(aecBillingAddress), customerShipTo: buildCustomerShipTo(aecShippingAddress), items: buildOrderLineItems(aecProducts), orderTrailer: buildOrderTrailer(aecProducts, vendorShipDate), }; return aecOrderFeedback; } function dumpAecOrderFeedback(aecOrderFeedback) { console.log(aecOrderFeedback.orderHeader); console.log(aecOrderFeedback.customerBillTo); console.log(aecOrderFeedback.customerShipTo); aecOrderFeedback.items.forEach(item => { console.log(item); }); console.log(aecOrderFeedback.orderTrailer); } function aecOrderFeedbacksEqual(aecOrderFeedback1, aecOrderFeedback2) { if (aecOrderFeedback1.orderHeader !== aecOrderFeedback2.orderHeader) { return false; } if (aecOrderFeedback1.customerBillTo !== aecOrderFeedback2.customerBillTo) { return false; } if (aecOrderFeedback1.customerShipTo !== aecOrderFeedback2.customerShipTo) { return false; } if (aecOrderFeedback1.items.length !== aecOrderFeedback2.items.length) { return false; } let itemsEqual = true; aecOrderFeedback1.items.forEach((aecOrderFeedbackItem1, index) => { const aecOrderFeedbackItem2 = aecOrderFeedback2.items[index]; if (aecOrderFeedbackItem1 !== aecOrderFeedbackItem2) { itemsEqual = false; } }); if (!itemsEqual) { return false; } if (aecOrderFeedback1.orderTrailer !== aecOrderFeedback2.orderTrailer) { return false; } return true; } function createAecTransmissionFileContainer(transmissionDate, aecOrderFeedbacks) { const aecTransmissionFileContainer = { transmissionFileHeader: buildTransmissionFileHeader(transmissionDate), aecOrderFeedbacks, transmissionFileTrailer: buildTransmissionFileTrailer(aecOrderFeedbacks), }; return aecTransmissionFileContainer; } function dumpAecTransmissionFileContainer(aecTransmissionFileContainer) { console.log(aecTransmissionFileContainer.transmissionFileHeader); aecTransmissionFileContainer.aecOrderFeedbacks.forEach(aecOrderFeedback => { dumpAecOrderFeedback(aecOrderFeedback); }); console.log(aecTransmissionFileContainer.transmissionFileTrailer); } function aecTransmissionFileContainersEqual(aecTransmissionFileContainer1, aecTransmissionFileContainer2) { if (aecTransmissionFileContainer1.transmissionFileHeader !== aecTransmissionFileContainer2.transmissionFileHeader) { return false; } let aecOrderFeedbacksAreEqual = true; aecTransmissionFileContainer1.aecOrderFeedbacks.forEach((aecOrderFeedback1, index) => { if (!aecOrderFeedbacksEqual(aecOrderFeedback1, aecTransmissionFileContainer2.aecOrderFeedbacks[index])) { aecOrderFeedbacksAreEqual = false; } }); if (!aecOrderFeedbacksAreEqual) { return false; } if (aecTransmissionFileContainer1.transmissionFileTrailer !== aecTransmissionFileContainer2.transmissionFileTrailer) { return false; } return true; } function generateAecTransmissionFileName() { const date = new Date(); const aoft = 'AOFT'; const month = date.getMonth() + 1; // month is zero based const formattedMonth = `0${month}`.slice(-2); const day = date.getDate(); const formattedDay = `0${day}`.slice(-2); const hours = date.getHours(); const formattedHours = `0${hours}`.slice(-2); const minutes = date.getMinutes(); const formattedMinutes = `0${minutes}`.slice(-2); const seconds = date.getSeconds(); const formattedSeconds = `0${seconds}`.slice(-2); const milliSeconds = date.getMilliseconds(); const formattedMilliSeconds = `00${milliSeconds}`.slice(-3); return `${aoft}${formattedMonth}${formattedDay}-${formattedHours}${formattedMinutes}${formattedSeconds}${formattedMilliSeconds}`; } async function createAecTransmissionFile(aecFile, aecTransmissionFileContainer) { try { const stream = fs.createWriteStream(aecFile); return new Promise(resolve => { stream.on('finish', () => { resolve(true); }); stream.on('error', () => { resolve(false); }); serializeAecTransmissionFileContainer(stream, aecTransmissionFileContainer); }); } catch (err) { deleteFile(aecFile); // clean up just in case the file got created throw new Error(err); } } module.exports = { AecOrderAction, convertNetsuiteBillingAddressToAecBillingAddress, convertNetsuiteShippingAddressToAecShippingAddress, convertNetsuiteProductToAecProduct, convertNetsuiteProductsToAecProducts, createAecOrderFeedback, dumpAecOrderFeedback, aecOrderFeedbacksEqual, createAecTransmissionFileContainer, dumpAecTransmissionFileContainer, aecTransmissionFileContainersEqual, generateAecTransmissionFileName, createAecTransmissionFile, };