{"id":69986,"date":"2026-11-09T07:05:59","date_gmt":"2026-11-09T01:35:59","guid":{"rendered":"https:\/\/www.quickonlinetips.com\/archives\/tools\/budget-planner\/"},"modified":"2026-11-09T07:05:59","modified_gmt":"2026-11-09T01:35:59","slug":"budget-planner","status":"publish","type":"page","link":"https:\/\/www.quickonlinetips.com\/archives\/tools\/budget-planner\/","title":{"rendered":"Budget Planner: Free Monthly Budget Calculator You Can Print"},"content":{"rendered":"<p>This free <strong>budget planner<\/strong> turns your income and expenses into a monthly budget in a few minutes. Common categories like rent, groceries, fuel, bills and savings are ready for you, totals add up as you type, and you can add your own rows and columns. Print it, save it as a PDF or download an Excel sheet with real formulas. No sign-up.<\/p>\n<div class=\"qt-tool\" id=\"qt-bp\"><details id=\"qt-bp-paste\" style=\"background:#f8fafc;border:1px dashed #94a3b8;border-radius:12px;padding:10px 14px;margin:0 0 14px\"><summary style=\"cursor:pointer;font-weight:700;color:#174ea6\">Coming back to update a budget? Paste it here<\/summary><p class=\"qt-note\" style=\"margin:8px 0\">Paste the text you got from <strong>Copy budget<\/strong>, or all the cells copied from your Excel sheet. Your layout, categories, items and amounts come back. It is read as plain text in your browser only: nothing is uploaded and nothing in it can run.<\/p><textarea id=\"qt-bp-pastebox\" rows=\"6\" maxlength=\"40000\" placeholder=\"My budget&#10;Settings: cur=USD; sym=$; lay=one; cols=Amount&#10;&#10;INCOME&#10;Salary | 4000\"><\/textarea><div class=\"qt-row\" style=\"margin:8px 0 0\"><button type=\"button\" class=\"qt-btn qt-btn-ghost qtx-nomark\" id=\"qt-bp-load\" style=\"background:#e8f0fb;border-color:#1d5fb8;color:#174ea6\">Load my budget<\/button><\/div><p class=\"qt-status\" id=\"qt-bp-pastemsg\" aria-live=\"polite\"><\/p><\/details><div style=\"background:#eff6ff;border:1px solid #1d4ed833;border-left:5px solid #1d4ed8;border-radius:12px;padding:12px 14px;margin:0 0 12px\"><p class=\"qt-label\" style=\"font-size:1.12em;margin:0 0 10px;display:flex;align-items:center;gap:10px\"><span style=\"display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:50%;background:#1d4ed8;color:#fff;font-weight:800;font-size:.9em;flex:none\">1<\/span><span><strong>Step 1:<\/strong> Name your budget and pick your currency<\/span><\/p><p style=\"margin:0 0 10px\"><label class=\"qt-label\" for=\"qt-bp-name\" style=\"display:block;margin:0 0 4px\">Budget name<\/label><input type=\"text\" id=\"qt-bp-name\" maxlength=\"60\" style=\"max-width:420px\"><\/p><div id=\"qt-bp-cur\" style=\"display:flex;flex-wrap:wrap;gap:8px\"><\/div><div id=\"qt-bp-symwrap\" hidden style=\"margin-top:10px\"><label class=\"qt-label\" for=\"qt-bp-sym\" style=\"display:inline;margin-right:8px\">Currency symbol:<\/label><input type=\"text\" id=\"qt-bp-sym\" maxlength=\"4\" style=\"width:90px;display:inline-block\"><\/div><\/div><div style=\"background:#f0fdfa;border:1px solid #0f766e33;border-left:5px solid #0f766e;border-radius:12px;padding:12px 14px;margin:0 0 12px\"><p class=\"qt-label\" style=\"font-size:1.12em;margin:0 0 10px;display:flex;align-items:center;gap:10px\"><span style=\"display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:50%;background:#0f766e;color:#fff;font-weight:800;font-size:.9em;flex:none\">2<\/span><span><strong>Step 2:<\/strong> Choose your columns<\/span><\/p><div id=\"qt-bp-lay\" style=\"display:flex;flex-wrap:wrap;gap:8px\"><\/div><p id=\"qt-bp-layhelp\" style=\"margin:10px 0 0;color:#334155\"><\/p><\/div><div style=\"background:#f5f3ff;border:1px solid #7c3aed33;border-left:5px solid #7c3aed;border-radius:12px;padding:12px 14px;margin:0 0 12px\"><p class=\"qt-label\" style=\"font-size:1.12em;margin:0 0 10px;display:flex;align-items:center;gap:10px\"><span style=\"display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:50%;background:#7c3aed;color:#fff;font-weight:800;font-size:.9em;flex:none\">3<\/span><span><strong>Step 3:<\/strong> Tick your categories <span style=\"font-weight:400;color:#475569\">(common ones are ticked for you)<\/span><\/span><\/p><div id=\"qt-bp-grp\" style=\"display:flex;flex-wrap:wrap;gap:8px\"><\/div><\/div><div style=\"background:#eef4fc;border:1px solid #1d5fb833;border-left:5px solid #1d5fb8;border-radius:12px;padding:12px 14px;margin:0 0 12px\"><p class=\"qt-label\" style=\"font-size:1.12em;margin:0 0 10px;display:flex;align-items:center;gap:10px\"><span style=\"display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:50%;background:#1d5fb8;color:#fff;font-weight:800;font-size:.9em;flex:none\">4<\/span><span><strong>Step 4:<\/strong> Make your budget sheet<\/span><\/p><div class=\"qt-row\" style=\"margin:0\"><button type=\"button\" class=\"qt-btn qt-btn-big\" id=\"qt-bp-make\">Make my budget sheet<\/button><button type=\"button\" class=\"qt-btn qt-btn-ghost qtx-nomark\" id=\"qt-bp-reset\" style=\"background:#e8f0fb;border-color:#1d5fb8;color:#174ea6\">\u21ba Start over<\/button><\/div><p class=\"qt-status\" id=\"qt-bp-status\" aria-live=\"polite\" style=\"margin-bottom:0\"><\/p><\/div><div id=\"qt-bp-result\" hidden><div style=\"background:#fff7ed;border:1px solid #c2410c33;border-left:5px solid #c2410c;border-radius:12px;padding:12px 14px;margin:0 0 12px\"><p class=\"qt-label\" style=\"font-size:1.12em;margin:0 0 6px;display:flex;align-items:center;gap:10px\"><span style=\"display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:50%;background:#c2410c;color:#fff;font-weight:800;font-size:.9em;flex:none\">5<\/span><span><strong>Step 5:<\/strong> Type your amounts <span style=\"font-weight:400;color:#475569\">(totals add up as you type)<\/span><\/span><\/p><p style=\"margin:0 0 10px;color:#334155\">Change any item name, press <strong>+ Add row<\/strong> under a category for your own items, or <strong>+ Add column<\/strong> for another week or month. Leave blank what you don't use.<\/p><p id=\"qt-bp-title\" style=\"margin:0;font-size:1.25em;font-weight:800;color:#0f172a\"><\/p><p id=\"qt-bp-sub\" style=\"margin:2px 0 8px;color:#475569\"><\/p><div class=\"qt-row\" id=\"qt-bp-colbar\" style=\"margin:0 0 8px\"><button type=\"button\" class=\"qt-btn qt-btn-ghost qtx-nomark\" id=\"qt-bp-addcol\" style=\"background:#e8f0fb;border-color:#1d5fb8;color:#174ea6\">+ Add column<\/button><\/div><div id=\"qt-bp-wrap\" style=\"overflow-x:auto;-webkit-overflow-scrolling:touch;border:1px solid #cbd5e1;border-radius:10px;background:#fff\"><table id=\"qt-bp-table\" style=\"border-collapse:collapse;width:100%;min-width:100%;margin:0;font-size:.95em\"><\/table><\/div><div class=\"qt-row\" style=\"margin:10px 0 0\"><button type=\"button\" class=\"qt-btn qt-btn-ghost qtx-nomark\" id=\"qt-bp-addsec\" style=\"background:#e8f0fb;border-color:#1d5fb8;color:#174ea6\">+ Add a category<\/button><\/div><div id=\"qt-bp-cards\" style=\"display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:10px;margin:14px 0 6px\"><\/div><p id=\"qt-bp-basis\" style=\"margin:0 0 10px;color:#475569;font-size:.92em\"><\/p><div id=\"qt-bp-bars\"><\/div><\/div><div style=\"background:#f0fdf4;border:1px solid #15803d33;border-left:5px solid #15803d;border-radius:12px;padding:12px 14px;margin:0 0 12px\"><p class=\"qt-label\" style=\"font-size:1.12em;margin:0 0 10px;display:flex;align-items:center;gap:10px\"><span style=\"display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:50%;background:#15803d;color:#fff;font-weight:800;font-size:.9em;flex:none\">6<\/span><span><strong>Step 6:<\/strong> Print or download<\/span><\/p><div style=\"display:flex;flex-wrap:wrap;gap:8px 18px;align-items:center;margin:0 0 12px\"><strong>What to include:<\/strong><label style=\"display:inline-flex;gap:8px;align-items:center;cursor:pointer\"><input type=\"radio\" name=\"qt-bp-exp\" id=\"qt-bp-expall\" checked style=\"width:18px;height:18px;margin:0\"> With my amounts<\/label><label style=\"display:inline-flex;gap:8px;align-items:center;cursor:pointer\"><input type=\"radio\" name=\"qt-bp-exp\" id=\"qt-bp-expblank\" style=\"width:18px;height:18px;margin:0\"> Blank sheet to fill in by hand<\/label><\/div><div class=\"qt-row\" style=\"margin:0\"><button type=\"button\" class=\"qt-btn qt-btn-big\" id=\"qt-bp-print\">Print or save as PDF<\/button><button type=\"button\" class=\"qt-btn qt-btn-ghost\" id=\"qt-bp-xlsx\" style=\"background:#e8f0fb;border-color:#1d5fb8;color:#174ea6\">Download Excel sheet<\/button><button type=\"button\" class=\"qt-btn qt-btn-ghost\" id=\"qt-bp-copy\" style=\"background:#e8f0fb;border-color:#1d5fb8;color:#174ea6\">Copy budget<\/button><\/div><div style=\"margin:12px 0 4px;padding:10px 14px;background:#eff6ff;border:2px solid #1d5fb8;border-radius:10px;color:#0f2a5c\"><p style=\"margin:0 0 4px;font-weight:800;color:#174ea6;font-size:1.05em\">Want to update this budget next month? Save it with Copy budget or Excel<\/p><p style=\"margin:0\">Press <strong>Copy budget<\/strong> and keep the text in your notes app (or email it to yourself), or press <strong>Download Excel sheet<\/strong>. Next time, open <strong>Coming back to update a budget? Paste it here<\/strong> at the top of the tool and paste it in. A printed sheet or PDF cannot be loaded back.<\/p><\/div><p class=\"qt-status\" id=\"qt-bp-outmsg\" aria-live=\"polite\"><\/p><ul style=\"margin:6px 0 0;padding-left:20px;color:#334155\"><li><strong>Blank sheet<\/strong> keeps your categories and items with empty boxes and spare lines, to fill in with a pen.<\/li><li><strong>PDF:<\/strong> a printable budget sheet with all your totals.<\/li><li><strong>Excel sheet:<\/strong> real formulas, so totals and left over update when you change a number. Opens in Excel, Numbers or Google Sheets.<\/li><\/ul><\/div><\/div><p class=\"qt-note\" style=\"font-size:.95rem;margin:0 0 6px\"><strong>For planning only:<\/strong> left over = total income \u2212 total spending \u2212 total savings; savings rate = savings \u00f7 income \u00d7 100. These are simple sums of what you type, not financial advice.<\/p><p class=\"qt-note\" style=\"font-size:1rem\"><strong>Private by design:<\/strong> your budget is made in your browser. Nothing you type is sent to or stored by QuickOnlineTips. QuickOnlineTips is not affiliated with Microsoft, Google or Apple.<\/p><\/div>\n<script>(function () { function qtReady(fn) { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', fn); } else { fn(); } } qtReady(function () { var root = document.getElementById('qt-bp'); if (!root) { return; } var L = String.fromCharCode(60), A = String.fromCharCode(38); var URL_TOOL = 'https:\/\/www.quickonlinetips.com\/archives\/tools\/budget-planner\/'; function $(id) { return document.getElementById('qt-bp-' + id); } function mk(tag, css, txt) { var e = document.createElement(tag); if (css) { e.style.cssText = css; } if (txt !== undefined) { e.textContent = txt; } return e; } var TINT = 'background:#ffffff;border-color:#94a3b8;color:#1e293b', SOLID = 'background:#1d5fb8;border-color:#1d5fb8;color:#ffffff'; var CURS = [['USD', '$', '$ US dollar'], ['INR', '\u20b9', '\u20b9 Indian rupee'], ['GBP', '\u00a3', '\u00a3 British pound'], ['EUR', '\u20ac', '\u20ac Euro'], ['CAD', 'C$', 'C$ Canadian dollar'], ['AUD', 'A$', 'A$ Australian dollar'], ['other', '', 'Other']]; var MON = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; var MONL = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; var LAYS = [['one', 'One amount per item', 'One column of amounts: the simplest monthly budget.'], ['pva', 'Planned vs actual', 'Plan each amount, then type what you really spent. A Difference column shows where you went over (red) or under (green).'], ['week', 'Week by week', 'Four week columns plus a Total column on the right. Add a fifth week with + Add column.'], ['month', 'Month by month', 'Twelve month columns plus a Total column on the right: a full year budget.']]; var GROUPS = [ ['inc', 'Income', 'in', '#15803d', 1, ['Salary or wages', 'Side income or freelance', 'Business income', 'Interest and dividends', 'Rent received', 'Other income']], ['home', 'Home', 'ex', '#1d4ed8', 1, ['Rent or mortgage', 'Electricity', 'Water', 'Gas or cooking gas', 'Internet and TV', 'Mobile phone', 'Home insurance', 'Repairs and maintenance']], ['food', 'Food', 'ex', '#c2410c', 1, ['Groceries', 'Eating out', 'Food delivery', 'Snacks and coffee']], ['move', 'Transport', 'ex', '#0369a1', 1, ['Fuel', 'Public transport and cabs', 'Car or bike loan', 'Vehicle insurance', 'Servicing and repairs', 'Parking and tolls']], ['health', 'Health', 'ex', '#be123c', 1, ['Health insurance', 'Doctor visits', 'Medicines', 'Gym or fitness']], ['kids', 'Family and kids', 'ex', '#9d174d', 0, ['School or college fees', 'Childcare', 'Kids\u2019 activities', 'Pocket money', 'Support for parents']], ['me', 'Personal', 'ex', '#7c3aed', 1, ['Clothes and shoes', 'Haircut and personal care', 'Household help', 'Laundry']], ['subs', 'Subscriptions', 'ex', '#0f766e', 1, ['Streaming services', 'Apps and software', 'Cloud storage', 'Newspapers and magazines']], ['debt', 'Loans and cards', 'ex', '#b45309', 0, ['Credit card bill', 'Personal loan', 'Education loan', 'Other loan payments']], ['save', 'Savings and investing', 'sv', '#047857', 1, ['Emergency fund', 'Retirement savings', 'Mutual funds or index funds', 'Stocks', 'Fixed or recurring deposits']], ['fun', 'Fun and giving', 'ex', '#a16207', 1, ['Entertainment and outings', 'Holidays and travel', 'Gifts', 'Donations and charity', 'Hobbies']], ['misc', 'Other', 'ex', '#475569', 1, ['Pets', 'Bank charges', 'Miscellaneous']]]; var GBY = {}; GROUPS.forEach(function (g) { GBY[g[0]] = g; }); var lang = String(navigator.language || 'en-US'); var st = { cur: 'USD', sym: '$', lay: 'one', groups: {}, done: false, cols: [], secs: [], gone: {}, nc: 0 }; if (lang.indexOf('-IN') !== -1) { st.cur = 'INR'; st.sym = '\u20b9'; } else { if (lang.indexOf('-GB') !== -1) { st.cur = 'GBP'; st.sym = '\u00a3'; } else { if (lang.indexOf('-CA') !== -1) { st.cur = 'CAD'; st.sym = 'C$'; } else { if (lang.indexOf('-AU') !== -1) { st.cur = 'AUD'; st.sym = 'A$'; } else { if (['de', 'fr', 'es', 'it', 'nl', 'pt'].indexOf(lang.slice(0, 2)) !== -1) { st.cur = 'EUR'; st.sym = '\u20ac'; } } } } } GROUPS.forEach(function (g) { if (g[4]) { st.groups[g[0]] = 1; } }); var now = new Date(); function defName() { return st.lay === 'month' ? now.getFullYear() + ' budget' : MONL[now.getMonth()] + ' ' + now.getFullYear() + ' budget'; } $('name').value = defName(); var nameTouched = false; $('name').addEventListener('input', function () { nameTouched = true; if (st.done) { heads(); } }); function say(id, t, bad) { var m = $(id); m.textContent = t; m.classList.toggle('qt-error', !!bad); } function clean(s, n) { return String(s || '').replace(\/[;=|,\\t\\r\\n]\/g, ' ').replace(\/\\s+\/g, ' ').trim().slice(0, n); } function defCols(lay) { if (lay === 'pva') { return ['Planned', 'Actual']; } if (lay === 'week') { return ['Week 1', 'Week 2', 'Week 3', 'Week 4']; } if (lay === 'month') { return MON.slice(); } return ['Amount']; } function rightName() { if (st.lay === 'pva') { return 'Difference'; } if (st.cols.length > 1) { return 'Total'; } return ''; } function chip(box, label, on, fn) { var b = document.createElement('button'); b.type = 'button'; b.className = 'qt-btn qt-btn-ghost qtx-nomark'; b.textContent = label; b.setAttribute('aria-pressed', on ? 'true' : 'false'); b.style.cssText = (on ? SOLID : TINT) + ';margin:0'; b.addEventListener('click', fn); box.appendChild(b); } function drawCur() { var box = $('cur'); box.textContent = ''; CURS.forEach(function (c) { chip(box, c[2], st.cur === c[0], function () { st.cur = c[0]; if (c[0] === 'other') { $('symwrap').hidden = false; st.sym = clean($('sym').value, 4); $('sym').focus(); } else { $('symwrap').hidden = true; st.sym = c[1]; } drawCur(); if (st.done) { recalc(); heads(); } }); }); $('symwrap').hidden = st.cur !== 'other'; } $('sym').addEventListener('input', function () { st.sym = clean($('sym').value, 4); if (st.done) { recalc(); heads(); } }); function drawLay() { var box = $('lay'); box.textContent = ''; LAYS.forEach(function (l) { chip(box, l[1], st.lay === l[0], function () { if (st.lay === l[0]) { return; } var old = st.lay; st.lay = l[0]; if (!nameTouched) { $('name').value = defName(); } drawLay(); if (st.done) { relayout(old); } }); if (st.lay === l[0]) { $('layhelp').textContent = l[2]; } }); } function drawGrp() { var box = $('grp'); box.textContent = ''; GROUPS.forEach(function (g) { var on = !!st.groups[g[0]]; var locked = g[0] === 'inc'; chip(box, (on ? '\u2713 ' : '+ ') + g[1], on, function () { if (locked) { say('status', 'Income is always on the sheet: leave its rows blank if you don\u2019t need them.'); return; } if (st.groups[g[0]]) { delete st.groups[g[0]]; } else { st.groups[g[0]] = 1; } drawGrp(); if (st.done) { syncSecs(); render(); } }); }); } function blankVals() { return st.cols.map(function () { return ''; }); } function newSec(g) { return { key: g[0], name: g[1], type: g[2], col: g[3], custom: false, rows: g[5].map(function (t) { return { t: t, v: blankVals() }; }) }; } function syncSecs() { var have = {}; st.secs.forEach(function (s) { have[s.key] = s; }); var out = []; GROUPS.forEach(function (g) { if (st.groups[g[0]]) { if (have[g[0]]) { out.push(have[g[0]]); } else { if (st.gone[g[0]]) { out.push(st.gone[g[0]]); delete st.gone[g[0]]; } else { out.push(newSec(g)); } } } else { if (have[g[0]]) { st.gone[g[0]] = have[g[0]]; } } }); st.secs.forEach(function (s) { if (s.custom) { out.push(s); } }); st.secs = out; fitVals(); } function fitVals() { var n = st.cols.length; function fit(s) { s.rows.forEach(function (r) { while (n > r.v.length) { r.v.push(''); } if (r.v.length > n) { r.v = r.v.slice(0, n); } }); } st.secs.forEach(fit); Object.keys(st.gone).forEach(function (k) { fit(st.gone[k]); }); } function relayout(old) { var keep = st.cols.length; st.cols = defCols(st.lay); var moved = false; function move(s) { s.rows.forEach(function (r) { var first = r.v[0] || ''; if (old === 'pva') { if (String(r.v[1] || '').length) { first = r.v[1]; } } if (keep > 1) { if (old !== 'pva') { var tot = 0, any = false; r.v.forEach(function (x) { if (String(x).length) { any = true; tot += num(x); } }); if (any) { first = String(Math.round(tot * 100) \/ 100); } } } if (String(first).length) { moved = true; } r.v = blankVals(); r.v[0] = first; }); } st.secs.forEach(move); Object.keys(st.gone).forEach(function (k) { move(st.gone[k]); }); render(); if (moved) { say('status', 'Columns changed. Your amounts were moved into the first column (' + st.cols[0] + ').'); } } function num(x) { var s = String(x === undefined ? '' : x).replace(\/[^0-9.\\-]\/g, ''); var n = parseFloat(s); return isNaN(n) ? 0 : n; } function loc() { return st.cur === 'INR' ? 'en-IN' : 'en-US'; } function fmt(n) { var a = Math.abs(n), s; try { s = new Intl.NumberFormat(loc(), { maximumFractionDigits: 2 }).format(a); } catch (e) { s = String(a); } return (0 > n ? '\u2212' : '') + st.sym + s; } function hasA(r) { return String(r.v[1] || '').trim().length > 0; } function rowTotal(s, r) { if (st.lay === 'pva') { var p = num(r.v[0]), a = num(r.v[1]); return s.type === 'ex' ? p - a : a - p; } var t = 0; r.v.forEach(function (x) { t += num(x); }); return t; } function secTot(s) { return st.cols.map(function (c, i) { var t = 0; s.rows.forEach(function (r) { t += num(r.v[i]); }); return t; }); } function typeTot(type) { var t = st.cols.map(function () { return 0; }); st.secs.forEach(function (s) { if (s.type === type) { secTot(s).forEach(function (x, i) { t[i] += x; }); } }); return t; } function secDiff(s) { var t = 0; s.rows.forEach(function (r) { if (hasA(r)) { t += rowTotal(s, r); } }); return t; } function typeDiff(type) { if (type === 'left') { return typeDiff('in') + typeDiff('ex') - typeDiff('sv'); } var t = 0; st.secs.forEach(function (s) { if (s.type === type) { t += secDiff(s); } }); return t; } function rightOf(arr, kind, s) { if (st.lay === 'pva') { return s ? secDiff(s) : typeDiff(kind); } var t = 0; arr.forEach(function (x) { t += x; }); return t; } function anyActual() { var y = false; st.secs.forEach(function (s) { s.rows.forEach(function (r) { if (String(r.v[1] || '').trim().length) { y = true; } }); }); return y; } function expType(type) { var t = 0; st.secs.forEach(function (s) { if (s.type === type) { s.rows.forEach(function (r) { t += hasA(r) ? num(r.v[1]) : num(r.v[0]); }); } }); return t; } function mainSec(s) { if (st.lay === 'pva') { var t = 0; s.rows.forEach(function (r) { t += hasA(r) ? num(r.v[1]) : num(r.v[0]); }); return t; } return mainOf(secTot(s)); } function mainOf(arr) { var t = 0; arr.forEach(function (x) { t += x; }); return t; } function heads() { $('title').textContent = clean($('name').value, 60) || defName(); var lay = ''; LAYS.forEach(function (l) { if (l[0] === st.lay) { lay = l[1]; } }); $('sub').textContent = 'Currency: ' + (st.cur === 'other' ? 'other' : st.cur) + ' (' + (st.sym || 'no symbol') + ') \u00b7 ' + lay; } var CELL = 'padding:4px 6px;border-bottom:1px solid #e2e8f0;vertical-align:middle'; var NUMIN = 'width:96px;min-width:80px;height:34px;padding:2px 8px;margin:0;text-align:right;font-size:.95em;border:1px solid #cbd5e1;border-radius:6px;box-sizing:border-box'; function xBtn(label, fn) { var x = document.createElement('button'); x.type = 'button'; x.className = 'qtx-nomark'; x.title = label; x.setAttribute('aria-label', label); x.style.cssText = 'display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;width:26px;height:26px;min-width:0;min-height:0;border-radius:50%;border:0;background:#dc2626;padding:0;margin:0;cursor:pointer;flex:none'; var ic = document.createElementNS('http:\/\/www.w3.org\/2000\/svg', 'svg'); ic.setAttribute('viewBox', '0 0 10 10'); ic.setAttribute('width', '10'); ic.setAttribute('height', '10'); ic.setAttribute('aria-hidden', 'true'); var p = document.createElementNS('http:\/\/www.w3.org\/2000\/svg', 'path'); p.setAttribute('d', 'M1 1L9 9M9 1L1 9'); p.setAttribute('stroke', '#ffffff'); p.setAttribute('stroke-width', '2'); p.setAttribute('stroke-linecap', 'round'); ic.appendChild(p); x.appendChild(ic); x.addEventListener('click', fn); return x; } var live = []; function rowCount() { var n = 0; st.secs.forEach(function (s) { n += s.rows.length; }); return n; } function render() { var tb = $('table'); tb.textContent = ''; live = []; heads(); var nc = st.cols.length, rn = rightName(), span = nc + 2 + (rn ? 1 : 0); $('colbar').style.display = st.lay === 'pva' ? 'none' : ''; var th = mk('thead'), hr = mk('tr', 'background:#1e293b;color:#fff'); hr.appendChild(mk('th', 'text-align:left;padding:8px 6px;min-width:170px;position:sticky;left:0;background:#1e293b;z-index:1', 'Item')); st.cols.forEach(function (c, i) { var h = mk('th', 'padding:6px 4px;text-align:right;white-space:nowrap'); if (st.lay === 'pva') { h.textContent = c; } else { var w = mk('span', 'display:inline-flex;gap:4px;align-items:center'); var inp = document.createElement('input'); inp.type = 'text'; inp.value = c; inp.maxLength = 20; inp.setAttribute('aria-label', 'Column name'); inp.style.cssText = 'width:84px;height:30px;padding:2px 6px;margin:0;font-size:.9em;font-weight:700;text-align:right;border:1px solid #64748b;border-radius:6px;background:#334155;color:#fff;box-sizing:border-box'; inp.addEventListener('input', function () { st.cols[i] = clean(inp.value, 20) || ('Column ' + (i + 1)); }); w.appendChild(inp); if (nc > 1) { w.appendChild(xBtn('Remove column ' + c, function () { st.cols.splice(i, 1); st.secs.forEach(function (s) { s.rows.forEach(function (r) { r.v.splice(i, 1); }); }); Object.keys(st.gone).forEach(function (k) { st.gone[k].rows.forEach(function (r) { r.v.splice(i, 1); }); }); render(); })); } h.appendChild(w); } hr.appendChild(h); }); if (rn) { hr.appendChild(mk('th', 'padding:6px 8px;text-align:right;background:#0f172a', rn)); } hr.appendChild(mk('th', 'width:34px')); th.appendChild(hr); tb.appendChild(th); var body = mk('tbody'); st.secs.forEach(function (s) { var sh = mk('tr'), sc = mk('td', 'padding:8px 8px;background:' + s.col + ';color:#fff;font-weight:800;letter-spacing:.02em'); sc.colSpan = span; if (s.custom) { var ni = document.createElement('input'); ni.type = 'text'; ni.value = s.name; ni.maxLength = 40; ni.setAttribute('aria-label', 'Category name'); ni.style.cssText = 'height:30px;padding:2px 8px;margin:0;font-weight:800;border:1px solid #ffffff88;border-radius:6px;background:#ffffff22;color:#fff;max-width:260px;box-sizing:border-box'; ni.addEventListener('input', function () { s.name = clean(ni.value, 40) || 'My category'; tl.textContent = 'Total ' + s.name; }); var wrap = mk('span', 'display:inline-flex;gap:8px;align-items:center'); wrap.appendChild(ni); wrap.appendChild(xBtn('Remove this category', function () { st.secs.splice(st.secs.indexOf(s), 1); render(); })); sc.appendChild(wrap); } else { sc.textContent = s.name + (s.type === 'in' ? ' (money in)' : (s.type === 'sv' ? ' (money put aside)' : '')); } sh.appendChild(sc); body.appendChild(sh); s.rows.forEach(function (r) { var tr = mk('tr'), c0 = mk('td', CELL + ';position:sticky;left:0;background:#fff;z-index:1'); var ti = document.createElement('input'); ti.type = 'text'; ti.value = r.t; ti.maxLength = 60; ti.placeholder = 'Type an item'; ti.setAttribute('aria-label', 'Item name in ' + s.name); ti.style.cssText = 'width:100%;min-width:150px;height:34px;padding:2px 8px;margin:0;font-size:.95em;border:1px solid #e2e8f0;border-radius:6px;box-sizing:border-box'; ti.addEventListener('input', function () { r.t = ti.value.slice(0, 60); }); c0.appendChild(ti); tr.appendChild(c0); var rc = null; r.v.forEach(function (val, i) { var td = mk('td', CELL + ';text-align:right'); var vi = document.createElement('input'); vi.type = 'text'; vi.inputMode = 'decimal'; vi.maxLength = 14; vi.value = val; vi.placeholder = '0'; vi.setAttribute('aria-label', (r.t || 'Item') + ', ' + st.cols[i]); vi.style.cssText = NUMIN; vi.addEventListener('input', function () { r.v[i] = vi.value.slice(0, 14); recalc(); }); vi.addEventListener('keydown', function (e) { if (e.key === 'Enter') { e.preventDefault(); var all = Array.prototype.slice.call(tb.querySelectorAll('input[inputmode=decimal]')), k = all.indexOf(vi); if (all[k + nc]) { all[k + nc].focus(); } } }); td.appendChild(vi); tr.appendChild(td); }); if (rn) { rc = mk('td', CELL + ';text-align:right;font-weight:700;white-space:nowrap;background:#f8fafc'); tr.appendChild(rc); live.push({ kind: 'row', s: s, r: r, el: rc }); } var xc = mk('td', CELL + ';width:34px'); xc.appendChild(xBtn('Remove this row', function () { s.rows.splice(s.rows.indexOf(r), 1); render(); })); tr.appendChild(xc); body.appendChild(tr); }); var ar = mk('tr'), ac = mk('td', 'padding:4px 6px 8px'); ac.colSpan = span; var ab = document.createElement('button'); ab.type = 'button'; ab.className = 'qtx-nomark'; ab.textContent = '+ Add row'; ab.style.cssText = 'background:transparent;border:1px dashed ' + s.col + ';color:' + s.col + ';border-radius:8px;padding:4px 10px;font-size:.92em;font-weight:600;cursor:pointer;min-height:0;margin:0'; ab.addEventListener('click', function () { if (rowCount() > 299) { say('outmsg', 'That is a lot of rows. Remove some before adding more.', true); return; } s.rows.push({ t: '', v: blankVals() }); render(); var rows = tb.querySelectorAll('tr[data-sec=\"' + st.secs.indexOf(s) + '\"]'); var last = rows[rows.length - 1]; if (last) { var f = last.previousElementSibling; if (f) { var inp = f.querySelector('input'); if (inp) { try { inp.focus({ preventScroll: false }); } catch (e) { inp.focus(); } } } } }); ar.setAttribute('data-sec', String(st.secs.indexOf(s))); ac.appendChild(ab); ar.appendChild(ac); body.appendChild(ar); var tt = mk('tr', 'background:' + s.col + '14'), tl = mk('td', CELL + ';font-weight:800;color:' + s.col + ';position:sticky;left:0;background:#f8fafc;z-index:1', 'Total ' + s.name); tt.appendChild(tl); var cells = st.cols.map(function () { var td = mk('td', CELL + ';text-align:right;font-weight:800;white-space:nowrap;padding-right:14px'); tt.appendChild(td); return td; }); var rt = null; if (rn) { rt = mk('td', CELL + ';text-align:right;font-weight:800;white-space:nowrap;background:#f1f5f9'); tt.appendChild(rt); } tt.appendChild(mk('td', CELL)); body.appendChild(tt); live.push({ kind: 'sec', s: s, els: cells, rt: rt }); }); tb.appendChild(body); var ft = mk('tfoot'); var SUMS = [['in', 'Total income', '#15803d'], ['ex', 'Total spending', '#b91c1c'], ['sv', 'Total savings', '#047857'], ['left', 'Left over', '#0f172a']]; SUMS.forEach(function (sm, k) { if (sm[0] === 'sv') { var hasSv = false; st.secs.forEach(function (s) { if (s.type === 'sv') { hasSv = true; } }); if (!hasSv) { return; } } var tr = mk('tr', k === 0 ? 'border-top:3px solid #1e293b' : ''), l0 = mk('td', CELL + ';font-weight:800;position:sticky;left:0;background:' + (sm[0] === 'left' ? '#fef9c3' : '#fff') + ';z-index:1;color:' + sm[2], sm[1]); tr.appendChild(l0); var cells = st.cols.map(function () { var td = mk('td', CELL + ';text-align:right;font-weight:800;white-space:nowrap;padding-right:14px' + (sm[0] === 'left' ? ';background:#fef9c3;font-size:1.05em' : '')); tr.appendChild(td); return td; }); var rt = null; if (rn) { rt = mk('td', CELL + ';text-align:right;font-weight:800;white-space:nowrap;background:' + (sm[0] === 'left' ? '#fde68a' : '#f1f5f9')); tr.appendChild(rt); } tr.appendChild(mk('td', CELL + (sm[0] === 'left' ? ';background:#fef9c3' : ''))); ft.appendChild(tr); live.push({ kind: 'sum', type: sm[0], els: cells, rt: rt }); }); tb.appendChild(ft); recalc(); } function paint(el, n, signed) { el.textContent = fmt(n); el.style.color = ''; if (signed) { if (n > 0) { el.style.color = '#15803d'; } if (0 > n) { el.style.color = '#b91c1c'; } } } function leftArr() { var i = typeTot('in'), e = typeTot('ex'), v = typeTot('sv'); return i.map(function (x, k) { return x - e[k] - v[k]; }); } function recalc() { var pva = st.lay === 'pva'; live.forEach(function (o) { if (o.kind === 'row') { var has = false; o.r.v.forEach(function (x) { if (String(x).trim().length) { has = true; } }); if (pva) { has = hasA(o.r); } if (has) { paint(o.el, rowTotal(o.s, o.r), pva); } else { o.el.textContent = ''; } } if (o.kind === 'sec') { var t = secTot(o.s); t.forEach(function (x, i) { paint(o.els[i], x, false); }); if (o.rt) { paint(o.rt, rightOf(t, o.s.type, o.s), pva); } } if (o.kind === 'sum') { var a = o.type === 'left' ? leftArr() : typeTot(o.type); a.forEach(function (x, i) { paint(o.els[i], x, o.type === 'left'); }); if (o.rt) { paint(o.rt, rightOf(a, o.type), pva ? true : o.type === 'left'); } } }); cards(); } function cards() { var box = $('cards'); box.textContent = ''; var pv = st.lay === 'pva'; var inc = pv ? expType('in') : mainOf(typeTot('in')), sp = pv ? expType('ex') : mainOf(typeTot('ex')), sv = pv ? expType('sv') : mainOf(typeTot('sv')), left = inc - sp - sv; var rate = inc > 0 ? Math.round(sv \/ inc * 1000) \/ 10 : 0; var C = [['Income', fmt(inc), '#15803d', '#f0fdf4'], ['Spending', fmt(sp), '#b91c1c', '#fef2f2'], ['Savings', fmt(sv), '#047857', '#ecfdf5'], ['Left over', fmt(left), 0 > left ? '#b91c1c' : '#0f172a', '#fef9c3'], ['Savings rate', inc > 0 ? rate + '%' : '\u2013', '#1d4ed8', '#eff6ff']]; C.forEach(function (c) { var d = mk('div', 'background:' + c[3] + ';border:1px solid ' + c[2] + '33;border-radius:10px;padding:10px 12px'); d.appendChild(mk('div', 'font-size:.85em;color:#475569;font-weight:600', c[0])); d.appendChild(mk('div', 'font-size:1.25em;font-weight:800;color:' + c[2] + ';word-break:break-word', c[1])); box.appendChild(d); }); var basis = ''; if (st.lay === 'pva') { basis = anyActual() ? 'Cards use Actual where you typed it and Planned for the rest. Difference counts only rows with an Actual amount.' : 'Cards use your Planned column until you type Actual amounts.'; } else { if (st.cols.length > 1) { basis = 'Cards use the Total of all ' + st.cols.length + ' columns.'; } } if (0 > left) { basis += (basis ? ' ' : '') + 'You are spending ' + fmt(-left) + ' more than you earn.'; } $('basis').textContent = basis; var bars = $('bars'); bars.textContent = ''; var list = []; st.secs.forEach(function (s) { if (s.type !== 'in') { var m = mainSec(s); if (m > 0) { list.push([s.name, m, s.col]); } } }); if (!list.length) { return; } list.sort(function (a, b) { return b[1] - a[1]; }); var out = sp + sv, base = inc > 0 ? inc : out; bars.appendChild(mk('p', 'margin:6px 0 8px;font-weight:800;color:#0f172a', 'Where your money goes' + (inc > 0 ? ' (share of income)' : ' (share of spending and savings)'))); list.forEach(function (it) { var pct = base > 0 ? Math.round(it[1] \/ base * 1000) \/ 10 : 0; var row = mk('div', 'display:grid;grid-template-columns:minmax(110px,30%) 1fr auto;gap:8px;align-items:center;margin:0 0 6px;font-size:.92em'); row.appendChild(mk('span', 'font-weight:600;color:#334155;overflow:hidden;text-overflow:ellipsis;white-space:nowrap', it[0])); var track = mk('span', 'display:block;height:14px;background:#f1f5f9;border-radius:7px;overflow:hidden'); track.appendChild(mk('span', 'display:block;height:100%;border-radius:7px;background:' + it[2] + ';width:' + Math.min(100, pct) + '%')); row.appendChild(track); row.appendChild(mk('span', 'white-space:nowrap;color:#334155', pct + '% \u00b7 ' + fmt(it[1]))); bars.appendChild(row); }); } $('addcol').addEventListener('click', function () { if (st.cols.length > 12) { say('outmsg', 'Up to 13 columns. Remove one before adding another.', true); return; } var n = st.cols.length + 1, nm = 'Column ' + n; if (st.lay === 'week') { nm = 'Week ' + n; } if (st.lay === 'month') { nm = st.cols.length > 11 ? 'Extra' : MON[st.cols.length]; } st.cols.push(nm); fitVals(); render(); var hs = $('table').querySelectorAll('thead input'); if (hs.length) { hs[hs.length - 1].select(); } }); $('addsec').addEventListener('click', function () { if (st.secs.length > 29) { say('outmsg', 'That is a lot of categories. Remove one before adding more.', true); return; } st.nc++; st.secs.push({ key: 'c' + st.nc, name: 'My category ' + st.nc, type: 'ex', col: '#4d7c0f', custom: true, rows: [{ t: '', v: blankVals() }] }); render(); }); function gen() { if (st.cur === 'other') { st.sym = clean($('sym').value, 4); } if (!st.cols.length) { st.cols = defCols(st.lay); } syncSecs(); render(); $('result').hidden = false; } $('make').addEventListener('click', function () { if (!st.done) { st.cols = defCols(st.lay); } gen(); st.done = true; say('status', 'Your budget sheet is ready below. Step 5: type your amounts, then Step 6: print or download it.'); $('result').scrollIntoView({ behavior: 'smooth', block: 'start' }); }); function blank() { return $('expblank').checked; } function need() { if (!st.done) { say('outmsg', 'Press Step 4: Make my budget sheet first.', true); return false; } if (!st.secs.length) { say('outmsg', 'Your sheet has no categories. Tick some in Step 3.', true); return false; } return true; } function esc(s) { return String(s).split(A).join(A + 'amp;').split(L).join(A + 'lt;').split('>').join(A + 'gt;').split('\"').join(A + 'quot;'); } function T(s) { return s.split('\u2039').join(L); } function title() { return clean($('name').value, 60) || defName(); } function printHtml() { var bl = blank(), rn = rightName(), nc = st.cols.length, land = nc > 4; var css = '@page{size:' + (land ? 'landscape' : 'portrait') + ';margin:10mm}*{box-sizing:border-box;-webkit-print-color-adjust:exact;print-color-adjust:exact}body{font:' + (nc > 8 ? '9.5px' : '11.5px') + '\/1.3 system-ui,-apple-system,\"Segoe UI\",Roboto,Arial,sans-serif;color:#111;margin:0}h1{font-size:20px;margin:0 0 2px}.sub{color:#444;margin:0 0 8px}table{border-collapse:collapse;width:100%}th,td{border:1px solid #bbb;padding:' + (bl ? '5px' : '3px') + ' 5px}th{background:#1e293b;color:#fff;text-align:right}th:first-child{text-align:left}td.n{text-align:right;white-space:nowrap}tr.sec td{color:#fff;font-weight:800;text-transform:uppercase;letter-spacing:.03em}tr.tot td{font-weight:800;background:#f1f5f9}tr.left td{font-weight:800;background:#fef9c3}.pos{color:#15803d}.neg{color:#b91c1c}.foot{margin-top:8px;color:#555;font-size:10px}tr{break-inside:avoid}'; var h = '\u2039h1>' + esc(title()) + '\u2039\/h1>\u2039p class=\"sub\">' + esc($('sub').textContent) + (bl ? ' \u00b7 fill in by hand' : '') + '\u2039\/p>\u2039table>\u2039thead>\u2039tr>\u2039th>Item\u2039\/th>'; st.cols.forEach(function (c) { h += '\u2039th>' + esc(c) + '\u2039\/th>'; }); if (rn) { h += '\u2039th>' + esc(rn) + '\u2039\/th>'; } h += '\u2039\/tr>\u2039\/thead>\u2039tbody>'; var span = nc + 1 + (rn ? 1 : 0); function cellsFor(arr, signed) { var o = ''; arr.forEach(function (x) { o += '\u2039td class=\"n\">' + (bl ? '' : esc(fmt(x))) + '\u2039\/td>'; }); return o; } function rightCell(v, signed) { if (!rn) { return ''; } if (bl) { return '\u2039td class=\"n\">\u2039\/td>'; } return '\u2039td class=\"n' + (signed ? (v > 0 ? ' pos' : (0 > v ? ' neg' : '')) : '') + '\">' + esc(fmt(v)) + '\u2039\/td>'; } var pva = st.lay === 'pva'; st.secs.forEach(function (s) { h += '\u2039tr class=\"sec\">\u2039td colspan=\"' + span + '\" style=\"background:' + s.col + '\">' + esc(s.name) + '\u2039\/td>\u2039\/tr>'; s.rows.forEach(function (r) { if (!bl) { var has = false; r.v.forEach(function (x) { if (String(x).trim().length) { has = true; } }); if (!has) { if (!String(r.t).trim().length) { return; } } } h += '\u2039tr>\u2039td>' + esc(r.t) + '\u2039\/td>'; r.v.forEach(function (x) { h += '\u2039td class=\"n\">' + (bl ? '' : (String(x).trim().length ? esc(fmt(num(x))) : '')) + '\u2039\/td>'; }); var has2 = false; r.v.forEach(function (x) { if (String(x).trim().length) { has2 = true; } }); if (pva) { has2 = hasA(r); } h += rn ? (bl ? '\u2039td class=\"n\">\u2039\/td>' : (has2 ? rightCell(rowTotal(s, r), pva) : '\u2039td class=\"n\">\u2039\/td>')) : ''; h += '\u2039\/tr>'; }); if (bl) { [1, 2, 3].forEach(function () { h += '\u2039tr>\u2039td>\u00a0\u2039\/td>'; st.cols.forEach(function () { h += '\u2039td>\u2039\/td>'; }); if (rn) { h += '\u2039td>\u2039\/td>'; } h += '\u2039\/tr>'; }); } var t = secTot(s); h += '\u2039tr class=\"tot\">\u2039td>Total ' + esc(s.name) + '\u2039\/td>' + cellsFor(t) + rightCell(rightOf(t, s.type, s), pva) + '\u2039\/tr>'; }); var SUMS = [['in', 'Total income'], ['ex', 'Total spending'], ['sv', 'Total savings'], ['left', 'Left over']]; SUMS.forEach(function (sm) { if (sm[0] === 'sv') { var hs = false; st.secs.forEach(function (s) { if (s.type === 'sv') { hs = true; } }); if (!hs) { return; } } var a = sm[0] === 'left' ? leftArr() : typeTot(sm[0]); h += '\u2039tr class=\"' + (sm[0] === 'left' ? 'left' : 'tot') + '\">\u2039td>' + sm[1] + '\u2039\/td>' + cellsFor(a) + rightCell(rightOf(a, sm[0]), pva ? true : sm[0] === 'left') + '\u2039\/tr>'; }); h += '\u2039\/tbody>\u2039\/table>\u2039p class=\"foot\">Left over = total income \u2212 total spending \u2212 total savings. For planning only, not financial advice. Made with the free Budget Planner at quickonlinetips.com\/archives\/tools\/budget-planner\/\u2039\/p>'; return T('\u2039!doctype html>\u2039html lang=\"en\">\u2039head>\u2039meta charset=\"utf-8\">\u2039title>' + esc(title()) + '\u2039\/title>\u2039style>' + css + '\u2039\/style>\u2039\/head>\u2039body>' + h + '\u2039\/body>\u2039\/html>'); } $('print').addEventListener('click', function () { if (!need()) { return; } var old = document.getElementById('qt-bp-frame'); if (old) { old.remove(); } var f = document.createElement('iframe'); f.id = 'qt-bp-frame'; f.title = 'Printable budget'; f.style.cssText = 'position:fixed;right:0;bottom:0;width:0;height:0;border:0'; f.onload = function () { try { f.contentWindow.focus(); f.contentWindow.print(); say('outmsg', 'Print window opened. Choose your printer, or \"Save as PDF\" to keep a copy.'); } catch (e) { say('outmsg', 'Your browser blocked printing here. Use Download Excel sheet instead.', true); } }; document.body.appendChild(f); f.srcdoc = printHtml(); }); function saveBlob(blob, name) { var u = URL.createObjectURL(blob), a = document.createElement('a'); a.href = u; a.download = name; document.body.appendChild(a); a.click(); a.remove(); setTimeout(function () { URL.revokeObjectURL(u); }, 4000); } function loadLib(src, name) { return new Promise(function (res, rej) { if (window[name]) { res(window[name]); return; } var s = document.createElement('script'); s.src = src; s.onload = function () { if (window[name]) { res(window[name]); } else { rej(new Error('lib')); } }; s.onerror = function () { rej(new Error('lib')); }; document.head.appendChild(s); }); } function argb(hex) { return 'FF' + hex.replace('#', '').toUpperCase(); } function colL(n) { var s = ''; n = n + 1; while (n > 0) { var m = (n - 1) % 26; s = String.fromCharCode(65 + m) + s; n = Math.floor((n - 1) \/ 26); } return s; } function settingsLine() { return 'Settings: cur=' + st.cur + '; sym=' + clean(st.sym, 4) + '; lay=' + st.lay + '; cols=' + st.cols.map(function (c) { return clean(c, 20); }).join(',') + '; name=' + clean(title(), 60); } function buildXlsx(X) { var bl = blank(), rn = rightName(), nc = st.cols.length, pva = st.lay === 'pva'; var wb = new X.Workbook(), ws = wb.addWorksheet('Budget', { pageSetup: { paperSize: 9, orientation: nc > 4 ? 'landscape' : 'portrait', fitToPage: true, fitToWidth: 1, fitToHeight: 0, margins: { left: 0.4, right: 0.4, top: 0.5, bottom: 0.5, header: 0.2, footer: 0.2 } }, views: [{ showGridLines: false }] }); var lastC = nc + (rn ? 1 : 0); var cols = [{ width: 36 }]; st.cols.forEach(function () { cols.push({ width: nc > 8 ? 11 : 14 }); }); if (rn) { cols.push({ width: 14 }); } ws.columns = cols; var sym = clean(st.sym, 4).replace(\/\"\/g, ''); var NF = '\"' + sym + '\"#,##0.00;[Red]-\"' + sym + '\"#,##0.00'; var END = colL(lastC); ws.mergeCells('A1:' + END + '1'); var c1 = ws.getCell('A1'); c1.value = title(); c1.font = { size: 18, bold: true, color: { argb: 'FF0F172A' } }; ws.mergeCells('A2:' + END + '2'); var c2 = ws.getCell('A2'); c2.value = $('sub').textContent; c2.font = { size: 11, color: { argb: 'FF475569' } }; var hrow = ['Item'].concat(st.cols); if (rn) { hrow.push(rn); } var hr = ws.addRow(hrow); hr.height = 20; hr.eachCell(function (c, i) { c.font = { bold: true, color: { argb: 'FFFFFFFF' } }; c.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF1E293B' } }; c.alignment = { vertical: 'middle', horizontal: i === 1 ? 'left' : 'right' }; }); function rightF(r, type) { if (!rn) { return null; } if (pva) { return 'IF(C' + r + '=\"\",\"\",' + (type === 'ex' ? 'B' + r + '-C' + r : 'C' + r + '-B' + r) + ')'; } return 'SUM(B' + r + ':' + colL(nc) + r + ')'; } var totRows = { in: [], ex: [], sv: [] }; st.secs.forEach(function (s) { var r = ws.addRow([s.name.toUpperCase()]); ws.mergeCells('A' + r.number + ':' + END + r.number); r.height = 20; r.getCell(1).font = { bold: true, color: { argb: 'FFFFFFFF' } }; r.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: argb(s.col) } }; var first = r.number + 1, k = 0; var rows = s.rows.filter(function (x) { if (bl) { return true; } var has = false; x.v.forEach(function (y) { if (String(y).trim().length) { has = true; } }); return has ? true : String(x.t).trim().length > 0; }); rows.forEach(function (x) { var vals = [x.t]; x.v.forEach(function (y) { vals.push(bl ? null : (String(y).trim().length ? num(y) : null)); }); var row = ws.addRow(vals); k++; if (rn) { row.getCell(lastC + 1).value = { formula: rightF(row.number, s.type) }; } }); var extra = bl ? 3 : 2; while (extra > 0) { ws.addRow(['']); k++; extra--; } var last = first + k - 1; var tr = ws.addRow(['Total ' + s.name]); st.cols.forEach(function (c, i) { var L2 = colL(i + 1); tr.getCell(i + 2).value = { formula: 'SUM(' + L2 + first + ':' + L2 + last + ')' }; }); if (rn) { tr.getCell(lastC + 1).value = { formula: pva ? 'SUM(D' + first + ':D' + last + ')' : rightF(tr.number, s.type) }; } tr.eachCell({ includeEmpty: true }, function (c) { c.font = { bold: true, color: { argb: argb(s.col) } }; c.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFF1F5F9' } }; c.border = { top: { style: 'thin', color: { argb: 'FF94A3B8' } } }; }); totRows[s.type].push(tr.number); for (var q = first; last + 1 > q; q++) { var rr = ws.getRow(q); for (var z = 2; lastC + 2 > z; z++) { rr.getCell(z).numFmt = NF; } if ((q - first) % 2) { for (var z2 = 1; lastC + 2 > z2; z2++) { rr.getCell(z2).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFF8FAFC' } }; } } for (var z3 = 1; lastC + 2 > z3; z3++) { rr.getCell(z3).border = { bottom: { style: 'hair', color: { argb: 'FFCBD5E1' } } }; } } for (var z4 = 2; lastC + 2 > z4; z4++) { tr.getCell(z4).numFmt = NF; } }); ws.addRow([]); var sh = ws.addRow(['SUMMARY']); ws.mergeCells('A' + sh.number + ':' + END + sh.number); sh.getCell(1).font = { bold: true, color: { argb: 'FFFFFFFF' } }; sh.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF1E293B' } }; var refs = {}; function sumRow(label, type, colr) { var r = ws.addRow([label]); st.cols.forEach(function (c, i) { var L2 = colL(i + 1), list = totRows[type].map(function (n) { return L2 + n; }); r.getCell(i + 2).value = { formula: list.length ? list.join('+') : '0' }; }); if (rn) { r.getCell(lastC + 1).value = { formula: pva ? (totRows[type].length ? totRows[type].map(function (n) { return 'D' + n; }).join('+') : '0') : rightF(r.number, type) }; } r.eachCell({ includeEmpty: true }, function (c, i) { c.font = { bold: true, color: { argb: colr } }; if (i > 1) { c.numFmt = NF; } }); refs[type] = r.number; } sumRow('Total income', 'in', 'FF15803D'); sumRow('Total spending', 'ex', 'FFB91C1C'); if (totRows.sv.length) { sumRow('Total savings', 'sv', 'FF047857'); } var lr = ws.addRow(['Left over']); st.cols.forEach(function (c, i) { var L2 = colL(i + 1); lr.getCell(i + 2).value = { formula: L2 + refs.in + '-' + L2 + refs.ex + (refs.sv ? '-' + L2 + refs.sv : '') }; }); if (rn) { lr.getCell(lastC + 1).value = { formula: pva ? 'D' + refs.in + '+D' + refs.ex + (refs.sv ? '-D' + refs.sv : '') : rightF(lr.number, 'in') }; } lr.eachCell({ includeEmpty: true }, function (c, i) { c.font = { bold: true, size: 12, color: { argb: 'FF0F172A' } }; c.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFFEF9C3' } }; if (i > 1) { c.numFmt = NF; } }); if (refs.in) { var sr = ws.addRow(['Savings rate']); st.cols.forEach(function (c, i) { var L2 = colL(i + 1); sr.getCell(i + 2).value = { formula: refs.sv ? 'IF(' + L2 + refs.in + '>0,' + L2 + refs.sv + '\/' + L2 + refs.in + ',0)' : '0' }; sr.getCell(i + 2).numFmt = '0.0%'; }); sr.getCell(1).font = { bold: true, color: { argb: 'FF1D4ED8' } }; } ws.addRow([]); var notes = [pva ? 'Difference (only rows with an Actual amount): income and savings = actual minus planned; spending = planned minus actual. Positive = better than planned. Left over = income \u2212 spending \u2212 savings.' : 'Left over = total income \u2212 total spending \u2212 total savings. Type new items in the empty rows inside each category and the totals include them.', 'For planning only, not financial advice. Made with the free Budget Planner: ' + URL_TOOL, settingsLine(), 'To update this budget later: select all cells, copy, and paste them into the box at the top of the Budget Planner.']; notes.forEach(function (t) { var r = ws.addRow([t]); ws.mergeCells('A' + r.number + ':' + END + r.number); r.getCell(1).font = { size: 9, color: { argb: 'FF64748B' } }; r.getCell(1).alignment = { wrapText: true, vertical: 'top' }; r.height = 26; }); ws.views = [{ state: 'frozen', ySplit: 3, xSplit: 1, showGridLines: false }]; return wb.xlsx.writeBuffer(); } $('xlsx').addEventListener('click', function () { if (!need()) { return; } say('outmsg', 'Building your Excel sheet...'); loadLib('https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/exceljs\/4.4.0\/exceljs.min.js', 'ExcelJS').then(buildXlsx).then(function (buf) { saveBlob(new Blob([buf], { type: 'application\/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }), (blank() ? 'blank-budget' : 'budget') + '.xlsx'); say('outmsg', 'Downloaded ' + (blank() ? 'blank-budget' : 'budget') + '.xlsx. The totals are formulas, so they update as you type. It opens in Excel, Numbers, or Google Sheets (upload it to Google Drive and open it).'); }).catch(function () { say('outmsg', 'The Excel maker did not load. Check your connection and try again, or use Print or save as PDF.', true); }); }); function raw(x) { var s = String(x).trim(); if (!s.length) { return ''; } return String(Math.round(num(s) * 100) \/ 100); } function asText() { var rn = rightName(), out = title() + '\\r\\n' + $('sub').textContent + '\\r\\n' + settingsLine() + '\\r\\n\\r\\nItem | ' + st.cols.join(' | ') + (rn ? ' | ' + rn : '') + '\\r\\n'; st.secs.forEach(function (s) { out += '\\r\\n' + s.name.toUpperCase() + '\\r\\n'; s.rows.forEach(function (r) { var has = false; r.v.forEach(function (x) { if (String(x).trim().length) { has = true; } }); if (!String(r.t).trim().length) { if (!has) { return; } } var shw = st.lay === 'pva' ? hasA(r) : has; out += (String(r.t).replace(\/[|\\t]\/g, '\/').trim() || 'Item') + ' | ' + r.v.map(raw).join(' | ') + (rn ? ' | ' + (shw ? raw(rowTotal(s, r)) : '') : '') + '\\r\\n'; }); var t = secTot(s); out += 'Total ' + s.name + ' | ' + t.map(raw).join(' | ') + (rn ? ' | ' + raw(rightOf(t, s.type, s)) : '') + '\\r\\n'; }); out += '\\r\\nSUMMARY\\r\\n'; [['in', 'Total income'], ['ex', 'Total spending'], ['sv', 'Total savings'], ['left', 'Left over']].forEach(function (sm) { var a = sm[0] === 'left' ? leftArr() : typeTot(sm[0]); out += sm[1] + ' | ' + a.map(raw).join(' | ') + (rn ? ' | ' + raw(rightOf(a, sm[0])) : '') + '\\r\\n'; }); return out + '\\r\\nMade with the free Budget Planner: ' + URL_TOOL + '\\r\\n'; } $('copy').addEventListener('click', function () { if (!need()) { return; } var txt = asText(), done = function () { say('outmsg', 'Budget copied. Paste it into your notes app and keep it: paste it back into the box at the top of this tool next month to update it.'); }; var fb = function () { var ta = document.createElement('textarea'); ta.value = txt; document.body.appendChild(ta); ta.select(); try { document.execCommand('copy'); done(); } catch (e) { say('outmsg', 'Could not copy. Use Download Excel sheet instead.', true); } ta.remove(); }; if (navigator.clipboard) { navigator.clipboard.writeText(txt).then(done, fb); } else { fb(); } }); $('reset').addEventListener('click', function () { st.done = false; st.secs = []; st.gone = {}; st.cols = []; st.nc = 0; st.lay = 'one'; st.groups = {}; GROUPS.forEach(function (g) { if (g[4]) { st.groups[g[0]] = 1; } }); nameTouched = false; $('name').value = defName(); $('table').textContent = ''; $('cards').textContent = ''; $('bars').textContent = ''; $('basis').textContent = ''; $('result').hidden = true; $('expall').checked = true; drawCur(); drawLay(); drawGrp(); say('outmsg', ''); say('pastemsg', ''); say('status', 'Cleared. Start again from Step 1.'); }); function splitCells(ln) { var c = ln.indexOf('\\t') !== -1 ? ln.split('\\t') : ln.split(\/\\s*\\|\\s*\/); return c.map(function (x) { return x.trim(); }); } function loadPasted() { var txt = String($('pastebox').value || '').slice(0, 40000), lines = txt.split(\/\\r?\\n\/), set = null; lines.forEach(function (ln) { splitCells(ln).forEach(function (c) { if (c.indexOf('Settings:') === 0) { set = c.slice(9); } }); }); if (!set) { say('pastemsg', 'No Settings line found. Paste the whole text from Copy budget, or all the cells from your Excel sheet.', true); return; } var kv = {}; set.split(';').forEach(function (p) { var i = p.indexOf('='); if (i > 0) { kv[p.slice(0, i).trim()] = p.slice(i + 1).trim(); } }); var okLay = false; LAYS.forEach(function (l) { if (l[0] === kv.lay) { okLay = true; } }); st.lay = okLay ? kv.lay : 'one'; var cols = String(kv.cols || '').split(',').map(function (c) { return clean(c, 20); }).filter(function (c) { return c.length > 0; }).slice(0, 13); if (st.lay === 'pva') { cols = ['Planned', 'Actual']; } st.cols = cols.length ? cols : defCols(st.lay); var okCur = null; CURS.forEach(function (c) { if (c[0] === kv.cur) { okCur = c; } }); if (okCur) { st.cur = okCur[0]; st.sym = okCur[0] === 'other' ? clean(kv.sym, 4) : okCur[1]; } $('sym').value = st.cur === 'other' ? st.sym : ''; if (kv.name) { $('name').value = clean(kv.name, 60); nameTouched = true; } var byName = {}; GROUPS.forEach(function (g) { byName[g[1].toUpperCase()] = g; }); var secs = [], cur = null, n = st.cols.length, items = 0, stop = false; st.nc = 0; lines.forEach(function (ln) { if (stop) { return; } var c = splitCells(ln), ne = c.filter(function (x) { return x.length > 0; }); if (!ne.length) { return; } var first = c[0] || ''; if (first.toUpperCase() === 'SUMMARY') { stop = true; return; } if (first.indexOf('Settings:') === 0) { return; } if (kv.name) { if (first.toUpperCase() === clean(kv.name, 60).toUpperCase()) { return; } } var restEmpty = ne.length === 1 ? first.length > 0 : false; if (restEmpty) { if (first === first.toUpperCase()) { if (\/[A-Z]\/.test(first)) { if (first.length > 2) { if (61 > first.length) { var g = byName[first]; if (g) { var dup = false; secs.forEach(function (s) { if (s.key === g[0]) { dup = true; } }); if (!dup) { cur = { key: g[0], name: g[1], type: g[2], col: g[3], custom: false, rows: [] }; secs.push(cur); return; } } else { st.nc++; var nm = first.charAt(0) + first.slice(1).toLowerCase(); cur = { key: 'c' + st.nc, name: clean(nm, 40), type: 'ex', col: '#4d7c0f', custom: true, rows: [] }; secs.push(cur); return; } } } } } } if (!cur) { return; } if (!first.length) { return; } if (\/^total \/i.test(first)) { return; } if (items > 299) { return; } var v = []; for (var i = 1; n + 1 > i; i++) { var x = c[i] === undefined ? '' : c[i]; v.push(String(x).trim().length ? raw(x) : ''); } cur.rows.push({ t: first.slice(0, 60), v: v }); items++; }); if (!secs.length) { say('pastemsg', 'No categories found. Paste the whole text from Copy budget, including the category names in capitals.', true); return; } var hasInc = false; secs.forEach(function (s) { if (s.key === 'inc') { hasInc = true; } }); if (!hasInc) { var gi = GBY.inc; secs.unshift({ key: 'inc', name: gi[1], type: 'in', col: gi[3], custom: false, rows: [] }); } st.groups = {}; secs.forEach(function (s) { if (!s.custom) { st.groups[s.key] = 1; } }); var order = []; GROUPS.forEach(function (g) { secs.forEach(function (s) { if (s.key === g[0]) { order.push(s); } }); }); secs.forEach(function (s) { if (s.custom) { order.push(s); } }); st.secs = order; st.gone = {}; st.done = true; drawCur(); drawLay(); drawGrp(); render(); $('result').hidden = false; say('pastemsg', 'Your budget is back: ' + items + ' items in ' + order.length + ' categories. Change anything you like.'); $('result').scrollIntoView({ behavior: 'smooth', block: 'start' }); } $('load').addEventListener('click', loadPasted); drawCur(); drawLay(); drawGrp(); }); })();<\/script>\n<div class=\"qt-next\"><p class=\"qt-next-h\">Try another free tool<\/p><div class=\"qt-next-g\"><a href=\"https:\/\/www.quickonlinetips.com\/archives\/tools\/salary-calculator\/\">Salary Calculator<\/a><a href=\"https:\/\/www.quickonlinetips.com\/archives\/tools\/loan-calculator\/\">Loan Calculator<\/a><a href=\"https:\/\/www.quickonlinetips.com\/archives\/tools\/compound-interest-calculator\/\">Compound Interest Calculator<\/a><a href=\"https:\/\/www.quickonlinetips.com\/archives\/tools\/percentage-calculator\/\">Percentage Calculator<\/a><a href=\"https:\/\/www.quickonlinetips.com\/archives\/tools\/discount-calculator\/\">Discount Calculator<\/a><a href=\"https:\/\/www.quickonlinetips.com\/archives\/tools\/packing-list-maker\/\">Packing List Maker<\/a><\/div><\/div>\n<p><strong>Quick answer:<\/strong> Name your budget, pick your currency and columns, tick your categories and press <strong>Make my budget sheet<\/strong>. Type your amounts and the totals, left over and savings rate update straight away. Use <strong>+ Add row<\/strong> under any category for your own items, then print it, save it as a PDF or download an Excel sheet. To update it next month, save it with <strong>Copy budget<\/strong> or the Excel sheet and paste it back into the box at the top of the tool.<\/p>\n<h2>How to use the budget planner<\/h2>\n<ol>\n<li><strong>Step 1: name and currency.<\/strong> Give your budget a name (the current month is filled in) and pick US dollar, Indian rupee, British pound, euro, Canadian or Australian dollar, or type your own symbol.<\/li>\n<li><strong>Step 2: columns.<\/strong> <em>One amount per item<\/em> is the simplest monthly budget. <em>Planned vs actual<\/em> adds a Difference column that shows where you went over (red) or stayed under (green). <em>Week by week<\/em> and <em>Month by month<\/em> give you 4 weeks or 12 months side by side with a Total column on the right.<\/li>\n<li><strong>Step 3: categories.<\/strong> Income, home, food, transport, health, personal, subscriptions, savings, fun and other are ticked for you. Add family and kids or loans and cards if you need them.<\/li>\n<li><strong>Step 4: make the sheet.<\/strong> Press <strong>Make my budget sheet<\/strong>.<\/li>\n<li><strong>Step 5: type your amounts.<\/strong> Every category has its own total, and the bottom rows show total income, spending, savings and what is left over. Rename any item, press <strong>+ Add row<\/strong> for your own, <strong>+ Add column<\/strong> for another week or month, or <strong>+ Add a category<\/strong>. The red \u00d7 removes a row.<\/li>\n<li><strong>Step 6: print or download.<\/strong> Choose <em>With my amounts<\/em> or <em>Blank sheet to fill in by hand<\/em>, then print it, save it as a PDF, download an Excel sheet or copy it as text.<\/li>\n<\/ol>\n<h2>Free monthly budget template: PDF, Excel or Google Sheets<\/h2>\n<ul>\n<li><strong>Printable budget sheet (PDF):<\/strong> press <em>Print or save as PDF<\/em> and choose <em>Save as PDF<\/em>. Pick <em>Blank sheet<\/em> for a budget planner printable with your categories and spare lines to fill in with a pen.<\/li>\n<li><strong>Excel budget template:<\/strong> the downloaded sheet uses real formulas, so every category total, the left over and the savings rate update when you change a number. Each category has spare rows for new items.<\/li>\n<li><strong>Google Sheets budget template:<\/strong> upload the Excel file to Google Drive and open it with Google Sheets. The formulas keep working there and in Numbers.<\/li>\n<\/ul>\n<h2>Budget categories to include<\/h2>\n<ul>\n<li><strong>Income:<\/strong> salary or wages, side income, business income, interest and dividends, rent received.<\/li>\n<li><strong>Needs:<\/strong> rent or mortgage, electricity, water, gas, internet, phone, groceries, fuel and transport, insurance, medicines.<\/li>\n<li><strong>Wants:<\/strong> eating out, food delivery, streaming and apps, clothes, entertainment, holidays, hobbies.<\/li>\n<li><strong>Savings and debt:<\/strong> emergency fund, retirement, mutual funds or index funds, deposits, credit card bills and loan payments.<\/li>\n<\/ul>\n<p>A common starting point is the 50\/30\/20 rule: about half of your take-home pay for needs, 30% for wants and 20% for savings and paying off debt. The <em>Where your money goes<\/em> bars under the sheet show each category as a share of your income, so you can compare.<\/p>\n<h2>FAQ<\/h2>\n<h3>Is the budget planner free?<\/h3>\n<p>Yes. It is free, with no sign-up and no limit on how many budgets you make.<\/p>\n<h3>Do you see my income or spending?<\/h3>\n<p>No. The budget is made in your browser and nothing you type is sent to or stored by QuickOnlineTips. Nothing is uploaded.<\/p>\n<h3>How is left over worked out?<\/h3>\n<p>Left over = total income \u2212 total spending \u2212 total savings. The savings rate is savings \u00f7 income \u00d7 100. These are simple sums of the amounts you type: an estimate for planning, not financial advice.<\/p>\n<h3>How does planned vs actual work?<\/h3>\n<p>Type what you plan to spend in Planned, then what you really spent in Actual. Difference is shown only for rows with an Actual amount: for spending it is planned minus actual (green means under budget), for income and savings it is actual minus planned. The summary cards use Actual where you typed it and Planned for the rest.<\/p>\n<h3>How do I update my budget next month?<\/h3>\n<p>Press <strong>Copy budget<\/strong> and keep the text in your notes app, or keep the Excel sheet. Next month, open <em>Coming back to update a budget? Paste it here<\/em> at the top of the tool, paste the text or all the cells from your sheet and press <strong>Load my budget<\/strong>. Your layout, categories, items and amounts come back. The pasted text is only read as plain words in your browser.<\/p>\n<h3>Can I make a yearly or weekly budget?<\/h3>\n<p>Yes. Choose <em>Month by month<\/em> for a 12-month yearly budget or <em>Week by week<\/em> for a weekly budget. The Total column on the right adds up each row.<\/p>\n<p>Planning bigger money decisions too? Try the <a href=\"https:\/\/www.quickonlinetips.com\/archives\/tools\/salary-calculator\/\">salary calculator<\/a>, the <a href=\"https:\/\/www.quickonlinetips.com\/archives\/tools\/loan-calculator\/\">loan calculator<\/a> or the <a href=\"https:\/\/www.quickonlinetips.com\/archives\/tools\/compound-interest-calculator\/\">compound interest calculator<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This free budget planner turns your income and expenses into a monthly budget in a few minutes. Common categories like rent, groceries, fuel, bills and savings are ready for you, totals add up as you type, and you can add your own rows and columns. Print it, save it as a PDF or download an&#8230;<\/p>\n","protected":false},"author":7,"featured_media":0,"parent":65595,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","footnotes":""},"class_list":["post-69986","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.quickonlinetips.com\/archives\/wp-json\/wp\/v2\/pages\/69986","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.quickonlinetips.com\/archives\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.quickonlinetips.com\/archives\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.quickonlinetips.com\/archives\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/www.quickonlinetips.com\/archives\/wp-json\/wp\/v2\/comments?post=69986"}],"version-history":[{"count":0,"href":"https:\/\/www.quickonlinetips.com\/archives\/wp-json\/wp\/v2\/pages\/69986\/revisions"}],"up":[{"embeddable":true,"href":"https:\/\/www.quickonlinetips.com\/archives\/wp-json\/wp\/v2\/pages\/65595"}],"wp:attachment":[{"href":"https:\/\/www.quickonlinetips.com\/archives\/wp-json\/wp\/v2\/media?parent=69986"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}