You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
6.5 KiB
JavaScript
173 lines
6.5 KiB
JavaScript
const ExcelJS = require('exceljs');
|
|
const easypost = new (require('@easypost/api'))(process.env.EASYPOST_API_KEY);
|
|
|
|
module.exports = async function(users, coupons, items, filename) {
|
|
const workbook = new ExcelJS.Workbook();
|
|
workbook.creator = 'Ashe Erickson'
|
|
workbook.lastModifiedBy = 'Ashe Erickson'
|
|
workbook.created = new Date()
|
|
workbook.modified = new Date()
|
|
|
|
const usersSheet = workbook.addWorksheet('Users', {views:[{state: 'frozen', xSplit: 0, ySplit:1}]})
|
|
const ordersSheet = workbook.addWorksheet('Orders', {views:[{state: 'frozen', xSplit: 0, ySplit:1}]})
|
|
const couponsSheet = workbook.addWorksheet('Coupons', {views:[{state: 'frozen', xSplit: 0, ySplit:1}]})
|
|
|
|
const couponCache = {};
|
|
|
|
|
|
// Write coupons
|
|
couponsSheet.columns = [
|
|
{header: 'Coupon Code', width: 20},
|
|
{header: 'Expires', width: 14},
|
|
{header: 'Flat discount', key: 'flat', width: 12},
|
|
{header: '% discount', key: 'percent', width: 10},
|
|
{header: '# Socks free', width: 12},
|
|
{header: 'Per sock discount', key: 'perSock', width: 18},
|
|
{header: 'Free Shipping', width: 15},
|
|
{header: '# Allowed Uses', width: 18},
|
|
{header: '# Times Used', width: 18},
|
|
]
|
|
|
|
for (const coupon of coupons) {
|
|
const row = couponsSheet.addRow([
|
|
coupon.code,
|
|
new Date(coupon.expires * 1000),
|
|
formatMoney(coupon.flatDiscount),
|
|
coupon.percentDiscount ? coupon.percentDiscount / 100 : '',
|
|
coupon.socksFree || '',
|
|
formatMoney(coupon.perSockDiscount),
|
|
coupon.freeShipping ? 'Yes' : 'No',
|
|
coupon.numAllowedUses,
|
|
coupon.uses.length
|
|
])
|
|
|
|
row.eachCell(cell => {cell.alignment = {horizontal: 'left' }})
|
|
|
|
couponCache[coupon._id.toString()] = {
|
|
code: coupon.code,
|
|
row: row.number
|
|
}
|
|
}
|
|
|
|
// Write users + orders
|
|
usersSheet.columns = [
|
|
{header: 'Email', width: 40},
|
|
{header: 'Email Confirmed', width: 10},
|
|
{header: 'Registered', width: 20},
|
|
{header: 'Last Login', width: 20},
|
|
{header: 'Purchases', width: 20},
|
|
{header: 'Admin', width: 20},
|
|
]
|
|
|
|
ordersSheet.columns = [
|
|
{header: 'User', width: 40},
|
|
{header: 'Purchase Date', width: 14},
|
|
{header: 'Shipment Date', width: 14},
|
|
{header: 'Items', width: 60},
|
|
{header: 'Item Subtotal', key: 'itemCost', width: 20},
|
|
{header: 'Shipping Estimate', key: 'shippingEst', width: 20},
|
|
{header: 'Shipping Actual', key: 'shippingCost', width: 20},
|
|
{header: 'Total Paid', key: 'totalCost', width: 20},
|
|
{header: 'Coupon', key: 'coupon', width: 20},
|
|
{header: 'Tracking Code', width: 20},
|
|
{header: 'Address', width: 20},
|
|
]
|
|
|
|
const usersWithPurchases = users.filter(user => user.purchases?.length);
|
|
usersWithPurchases.sort((a,b) => (a.email || 'zzzz').localeCompare(b.email || 'zzzz'))
|
|
|
|
let userNum = 0;
|
|
for(const user of usersWithPurchases) {
|
|
if (user.purchases?.length < 1) continue;
|
|
userNum++
|
|
|
|
const creationDate = new Date(parseInt(user._id.toString().slice(0,8), 16)*1000)
|
|
const userID = user.email || user._id.toString();
|
|
const firstOrderRow = ordersSheet.rowCount + 1;
|
|
const userRow = usersSheet.rowCount + 1;
|
|
|
|
|
|
console.log(` (${userNum}/${usersWithPurchases.length}) Retrieving shipment + address info for orders by user ${userID}`)
|
|
|
|
for(const purchase of user.purchases) {
|
|
const shipmentPromise = easypost.Shipment.retrieve(purchase.shipment);
|
|
const addressPromise = easypost.Address.retrieve(purchase.address);
|
|
|
|
try {
|
|
purchase.shipment = await shipmentPromise;
|
|
purchase.address = await addressPromise;
|
|
} catch {
|
|
console.warn(` Unable to retrieve info for order ${purchase._id}`)
|
|
}
|
|
|
|
if (purchase.shipment?.tracker?.id) {
|
|
easypost.Tracker.retrieve(purchase.shipment.tracker.id).then(tracker => {
|
|
purchase.shipment.tracker = tracker
|
|
}).catch(() => {})
|
|
}
|
|
|
|
const coupon = couponCache[purchase.coupon]
|
|
const trackingCode = purchase.shipment?.tracking_code || purchase.trackingCode
|
|
const trackingLink = purchase.shipment?.tracker?.public_url || `https://tools.usps.com/go/TrackConfirmAction_input?qtc_tLabels1=${trackingCode}`
|
|
|
|
const row = ordersSheet.addRow([
|
|
user.email ? {formula: `=HYPERLINK("#'Users'!A${userRow}", "${userID}")`} : userID,
|
|
purchase.purchaseTime ? new Date(purchase.purchaseTime) : '',
|
|
purchase.shippedOn ? new Date(purchase.shippedOn) : '',
|
|
purchase.items.map(id => items.find(item => item._id.toString() === id.toString()).urlslug).join(', '),
|
|
formatMoney(purchase.sockPrice),
|
|
formatMoney(purchase.shippingEstimate),
|
|
formatMoney(purchase.shipment?.selected_rate?.rate),
|
|
formatMoney(purchase.totalPrice),
|
|
coupon ? {formula: `=HYPERLINK("#'Coupons'!A${coupon.row}","${coupon.code}")`} : '',
|
|
trackingCode ? {formula: `=HYPERLINK("${trackingLink}", "${trackingCode}")`} : "",
|
|
formatAddress(purchase.address)
|
|
])
|
|
|
|
row.eachCell(cell => {cell.alignment = {horizontal: 'left' }})
|
|
}
|
|
|
|
if (!user.email) continue;
|
|
|
|
const row = usersSheet.addRow([
|
|
userID,
|
|
user.emailConfirmed ? 'Yes' : 'No',
|
|
creationDate,
|
|
user.lastLogin ? new Date(user.lastLogin) : '',
|
|
{formula: `=HYPERLINK("#'Orders'!A${firstOrderRow}", "${user.purchases.length} purchase${user.purchases.length > 1 ? "s" : ""}")`},
|
|
user.isAdmin ? 'Yes' : 'No'
|
|
])
|
|
|
|
row.eachCell(cell => {cell.alignment = {horizontal: 'left' }})
|
|
}
|
|
|
|
ordersSheet.getColumn('itemCost').eachCell(cell => {cell.numFmt = '$0.00'})
|
|
ordersSheet.getColumn('shippingEst').eachCell(cell => {cell.numFmt = '$0.00'})
|
|
ordersSheet.getColumn('shippingCost').eachCell(cell => {cell.numFmt = '$0.00'})
|
|
ordersSheet.getColumn('totalCost').eachCell(cell => {cell.numFmt = '$0.00'})
|
|
|
|
couponsSheet.getColumn('flat').eachCell(cell => {cell.numFmt = '$0.00'})
|
|
couponsSheet.getColumn('percent').eachCell(cell => {cell.numFmt = '0%'})
|
|
couponsSheet.getColumn('perSock').eachCell(cell => {cell.numFmt = '$0.00'})
|
|
|
|
await workbook.xlsx.writeFile(filename);
|
|
}
|
|
|
|
function formatMoney(value) {
|
|
if(value === undefined) return ''
|
|
if(value === '') return ''
|
|
if(value === 0) return ''
|
|
|
|
if(typeof value === 'string')
|
|
return parseFloat(value);
|
|
|
|
|
|
return value
|
|
}
|
|
|
|
function formatAddress(address) {
|
|
if(typeof address !== 'object')
|
|
return address
|
|
|
|
return `${address.name || ""} | ${address.company ? address.company + "| " : ""}${address.street1} | ${address.street2 ? address.street2 + "| " : ""}${address.city} | ${address.state} | ${address.country} | ${address.zip}`
|
|
} |