/**
* QuickBooks Migration Wizard — qb-migration-wizard.js
* Embedded in Setup Wizard Step 2.
* Manages a 9-step modal that walks the user through all QB Desktop data imports.
*/
(function () {
'use strict';
// ── Step Definitions ──────────────────────────────────────────────────────
const STEPS = [
{
id: 1, key: 'chartOfAccounts', title: 'Chart of Accounts',
icon: 'bi-diagram-3', fileAccept: '.iif',
endpoint: '/Tools/ImportChartOfAccounts',
deps: [],
intro: 'Your Chart of Accounts defines the financial structure of your books. Import this first so vendor bills and other financial data can link to the correct accounts.',
instructions: [
'In QuickBooks Desktop, go to Lists → Chart of Accounts.',
'Click the Account button at the bottom of the list.',
'Choose Import/Export → Export Chart of Accounts (or use File → Utilities → Export → Lists to IIF Files → Chart of Accounts).',
'Save the .iif file and upload it here.'
]
},
{
id: 2, key: 'customers', title: 'Customers',
icon: 'bi-people', fileAccept: '.iif',
endpoint: '/Tools/ImportCustomers',
deps: [],
intro: 'Import your customer list so that historical invoices and payments can be matched to existing customer records.',
instructions: [
'In QuickBooks Desktop, go to File → Utilities → Export → Lists to IIF Files.',
'Check Customer List and click OK.',
'Save the .iif file and upload it here.'
]
},
{
id: 3, key: 'vendors', title: 'Vendors',
icon: 'bi-truck', fileAccept: '.iif',
endpoint: '/Tools/ImportVendors',
deps: [],
intro: 'Import your vendor list. Vendors are required before importing inventory stock levels and vendor bills.',
instructions: [
'In QuickBooks Desktop, go to File → Utilities → Export → Lists to IIF Files.',
'Check Vendor List and click OK.',
'Save the .iif file and upload it here.'
]
},
{
id: 4, key: 'catalogItems', title: 'Catalog Items',
icon: 'bi-grid', fileAccept: '.iif',
endpoint: '/Tools/ImportCatalogItems',
deps: [],
intro: 'Import service items from QuickBooks to populate your catalog with pre-priced services.',
instructions: [
'In QuickBooks Desktop, go to File → Utilities → Export → Lists to IIF Files.',
'Check Item List and click OK.',
'Save the .iif file and upload it here.'
]
},
{
id: 5, key: 'inventory', title: 'Inventory',
icon: 'bi-boxes', fileAccept: '.csv',
endpoint: '/Tools/ImportQbInventoryValuation',
deps: [3],
intro: 'Import current stock levels from an Inventory Valuation Summary report. Vendors must be imported first for preferred vendor matching.',
instructions: [
'In QuickBooks Desktop, go to Reports → Inventory → Inventory Valuation Summary.',
'Click Customize Report, open the Display tab, and add the Preferred Vendor column.',
'Click Excel → Create New Worksheet → Comma Separated Values (.csv) and save.',
'Upload the CSV file here.'
]
},
{
id: 6, key: 'invoices', title: 'Invoices',
icon: 'bi-receipt', fileAccept: '.csv',
endpoint: '/Tools/ImportQbInvoices',
deps: [2, 4],
intro: 'Import historical invoices from a Customer Balance Detail report. Customers and catalog items should be imported first.',
instructions: [
'In QuickBooks Desktop, go to Reports → Customers & Receivables → Customer Balance Detail.',
'Set the date range to All to cover your full history.',
'Click Excel → Create New Worksheet → Comma Separated Values (.csv) and save.',
'Upload the CSV file here.'
]
},
{
id: 7, key: 'transactions', title: 'Customer Payments',
icon: 'bi-cash-coin', fileAccept: '.csv',
endpoint: '/Tools/ImportQbTransactions',
deps: [6],
intro: 'Import payment records to update invoice balances. Invoices must be imported first.',
instructions: [
'In QuickBooks Desktop, go to Reports → Customers & Receivables → Transaction List by Customer.',
'Set the date range to All to cover your full history.',
'Click Excel → Create New Worksheet → Comma Separated Values (.csv) and save.',
'Upload the CSV file here.'
]
},
{
id: 8, key: 'bills', title: 'Vendor Bills & Payments',
icon: 'bi-file-earmark-text', fileAccept: '.csv',
endpoint: '/Tools/ImportQbBillsAndPayments',
deps: [1, 3],
intro: 'Import vendor bills and payment history in a single step. The same Vendor Balance Detail file contains both — bills are imported first, then payments are matched against them.',
instructions: [
'In QuickBooks Desktop, go to Reports → Vendors & Payables → Vendor Balance Detail.',
'Set the date range to All to cover your full history.',
'Click Excel → Create New Worksheet → Comma Separated Values (.csv) and save.',
'Upload the CSV file here — bills and payments will both be processed automatically.'
]
}
];
// ── QuickBooks Online Step Definitions ────────────────────────────────────
const QBO_STEPS = [
{
id: 1, key: 'qbo_coa', title: 'Chart of Accounts',
icon: 'bi-diagram-3', fileAccept: '.xlsx,.xls',
endpoint: '/Tools/ImportQboChartOfAccounts',
deps: [],
intro: 'Your Chart of Accounts defines the financial structure of your books. Import this first so financial data can link to the correct accounts.',
instructions: [
'In QuickBooks Online, go to Accounting → Chart of Accounts.',
'Click the Run Report button at the top right.',
'Click the Export icon (spreadsheet icon) and choose Export to Excel.',
'Save the .xlsx file and upload it here.'
]
},
{
id: 2, key: 'qbo_customers', title: 'Customers',
icon: 'bi-people', fileAccept: '.xlsx,.xls',
endpoint: '/Tools/ImportQboCustomers',
deps: [],
intro: 'Import your customer list so invoices and payments can be matched to existing customer records.',
instructions: [
'In QuickBooks Online, go to Reports and search for Customer Contact List.',
'Click Customize if you want to include additional fields (address, terms, balance).',
'Click the Export icon and choose Export to Excel.',
'Save the .xlsx file and upload it here.'
]
},
{
id: 3, key: 'qbo_vendors', title: 'Vendors',
icon: 'bi-truck', fileAccept: '.xlsx,.xls',
endpoint: '/Tools/ImportQboVendors',
deps: [],
intro: 'Import your vendor list before importing purchase orders or inventory.',
instructions: [
'In QuickBooks Online, go to Reports and search for Vendor Contact List.',
'Click the Export icon and choose Export to Excel.',
'Save the .xlsx file and upload it here.'
]
},
{
id: 4, key: 'qbo_products', title: 'Products & Services',
icon: 'bi-grid', fileAccept: '.xlsx,.xls',
endpoint: '/Tools/ImportQboCatalogItems',
deps: [],
intro: 'Import your products and services. Inventory-type items will create inventory records; all others become catalog items.',
instructions: [
'In QuickBooks Online, go to Sales → Products and Services.',
'Click the More (⋮) button at the top right.',
'Choose Export to Excel.',
'Save the .xlsx file and upload it here.'
]
},
{
id: 5, key: 'qbo_invoices', title: 'Invoices',
icon: 'bi-receipt', fileAccept: '.xlsx,.xls',
endpoint: '/Tools/ImportQboInvoices',
deps: [2],
intro: 'Import historical invoices. Customers should be imported first for matching.',
instructions: [
'In QuickBooks Online, go to Reports and search for Invoice List.',
'Set the date range to cover all of your history.',
'Click the Export icon and choose Export to Excel.',
'Save the .xlsx file and upload it here.'
]
},
{
id: 6, key: 'qbo_transactions', title: 'Payments',
icon: 'bi-cash-stack', fileAccept: '.xlsx,.xls',
endpoint: '/Tools/ImportQboTransactions',
deps: [5],
intro: 'Import payment history to mark invoices as paid. Invoices must be imported first.',
instructions: [
'In QuickBooks Online, go to Reports and search for Transaction List by Date.',
'Set the date range to cover all of your history.',
'Click the Export icon and choose Export to Excel.',
'Save the .xlsx file and upload it here.'
]
}
];
// ── Active step set (Desktop or Online) ───────────────────────────────────
let activeSteps = STEPS; // default to Desktop
let activeSource = 'desktop';
// Statuses: pending | complete | skipped | error | blocked
let currentStepIdx = 0; // index into activeSteps
let stepState = {}; // key -> { status, result }
// ── Init ──────────────────────────────────────────────────────────────────
function init() {
// Default all steps to pending
activeSteps.forEach(s => {
stepState[s.key] = stepState[s.key] || { status: 'pending', result: null };
});
refreshBlocked();
}
function refreshBlocked() {
activeSteps.forEach(s => {
if (s.deps.length === 0) return;
const current = stepState[s.key].status;
if (current === 'complete' || current === 'skipped') return;
const anyDepUnresolved = s.deps.some(depId => {
const depStep = activeSteps.find(x => x.id === depId);
const depStatus = depStep ? stepState[depStep.key].status : 'pending';
return depStatus === 'pending' || depStatus === 'blocked' || depStatus === 'error';
});
stepState[s.key].status = anyDepUnresolved ? 'blocked' : 'pending';
});
}
// ── Open Modal ────────────────────────────────────────────────────────────
window.openQbWizard = async function (source) {
activeSteps = (source === 'online') ? QBO_STEPS : STEPS;
activeSource = source;
stepState = {}; // reset state when switching source
init();
// Load persisted state from server
try {
const resp = await fetch('/SetupWizard/GetQbMigrationState');
const data = await resp.json();
if (data.state) {
const saved = JSON.parse(data.state);
// Only restore state if same source
if (saved && saved.source === source && saved.steps) {
Object.assign(stepState, saved.steps);
}
// Resume at first incomplete step
const firstIncomplete = activeSteps.findIndex(s => {
const st = stepState[s.key]?.status;
return st !== 'complete' && st !== 'skipped';
});
currentStepIdx = firstIncomplete >= 0 ? firstIncomplete : activeSteps.length - 1;
}
} catch (e) {
// Non-fatal — start from beginning
}
refreshBlocked();
// Update modal title to reflect source
var titleEl = document.getElementById('qbMigrationWizardLabel');
if (titleEl) {
titleEl.textContent = source === 'online'
? 'QuickBooks Online Migration Wizard'
: 'QuickBooks Desktop Migration Wizard';
}
renderWizard();
const modal = new bootstrap.Modal(document.getElementById('qbMigrationWizard'));
modal.show();
};
// ── Render ────────────────────────────────────────────────────────────────
function renderWizard() {
renderStepIndicator();
renderProgressBar();
renderStepContent();
renderFooterButtons();
}
function renderStepIndicator() {
const container = document.getElementById('qbwStepIndicator');
if (!container) return;
container.innerHTML = activeSteps.map((s, idx) => {
const st = stepState[s.key]?.status || 'pending';
const isActive = idx === currentStepIdx;
let statusClass = isActive ? 'active' : st;
let icon = '';
if (st === 'complete') icon = '';
else if (st === 'skipped') icon = '';
else if (st === 'error') icon = '';
else if (st === 'blocked') icon = '';
else icon = s.id;
return `
${step.intro}