Commercial Finance for Businesses

(function(){ "use strict"; const root=document.getElementById("mezfin-calculator"); if(!root)return; const $=id=>document.getElementById(id); const n=id=>{const el=$(id),v=el?parseFloat(el.value):0;return Number.isFinite(v)?Math.max(0,v):0}; const pct=id=>n(id)/100; const money=v=>new Intl.NumberFormat("en-GB",{style:"currency",currency:"GBP",maximumFractionDigits:0}).format(Number.isFinite(v)?v:0); const percent=v=>`${(Number.isFinite(v)?v*100:0).toFixed(1)}%`; const set=(id,val,fmt=money)=>{const el=$(id);if(el)el.textContent=fmt(val)}; const fees=["mezfin-legal","mezfin-architect","mezfin-planning","mezfin-qs","mezfin-engineer","mezfin-warranty","mezfin-insurance","mezfin-valuation","mezfin-utilities","mezfin-cil","mezfin-sales-fees","mezfin-accountancy"]; function toggleMezz(){ const enabled=$("mezfin-mezz-enabled")?$("mezfin-mezz-enabled").checked:true; const inputs=$("mezfin-mezz-fields"),results=$("mezfin-mezz-results"),status=$("mezfin-mezz-status"); if(inputs)inputs.classList.toggle("mezfin-hidden",!enabled); if(results)results.classList.toggle("mezfin-hidden",!enabled); if(status)status.textContent=enabled?"Included":"Not included"; } function calculate(){ const purchase=n("mezfin-purchase"),land=n("mezfin-land-value"),build=n("mezfin-build"),gdv=n("mezfin-gdv"),existing=n("mezfin-existing"); const contingency=build*pct("mezfin-contingency-rate"); const professional=fees.reduce((s,id)=>s+n(id),0)+n("mezfin-other-costs"); const projectCost=purchase+build+contingency+professional; const facilityTerm=Math.max(1,Math.round(n("mezfin-facility-term"))); const buildTerm=Math.max(1,Math.min(facilityTerm,Math.round(n("mezfin-build-term")))); const monthlyRate=pct("mezfin-interest-rate"); const retained=!$("mezfin-interest-treatment")||$("mezfin-interest-treatment").value==="retained"; const gdvCap=gdv*pct("mezfin-max-ltgdv"),ltcCap=projectCost*pct("mezfin-max-ltc"); const grossFacility=Math.max(0,Math.min(gdvCap,ltcCap)); // Workbook method: months 2 to build term, whole-pound gross draws, with interest based on each draw's remaining term. const netBuild=build+contingency; const drawCount=Math.max(1,buildTerm-1); const netPerDraw=netBuild/drawCount; let grossDevelopment=0,futureInterest=0; for(let month=2;month<=buildTerm;month++){ const monthsRemaining=Math.max(0,facilityTerm-month+1); const divisor=1-(monthlyRate*monthsRemaining); const calculated=retained&&divisor>0?netPerDraw/divisor:netPerDraw; const grossDraw=Math.round(calculated); grossDevelopment+=grossDraw; if(retained)futureInterest+=grossDraw*monthlyRate*monthsRemaining; } grossDevelopment=Math.min(grossFacility,grossDevelopment); const residualFacility=Math.max(0,grossFacility-grossDevelopment); const landBase=Math.min(purchase||land,land||purchase); const requested=Math.max(0,landBase*pct("mezfin-day1-rate")+existing); const grossDay1=Math.min(residualFacility,requested||residualFacility); const day1Interest=retained?grossDay1*monthlyRate*facilityTerm:0; const rolledInterest=retained?0:grossFacility*(Math.pow(1+monthlyRate,facilityTerm)-1); const totalInterest=day1Interest+futureInterest+rolledInterest; const arrangement=grossFacility*pct("mezfin-arrangement-rate"),broker=grossFacility*pct("mezfin-broker-rate"),admin=n("mezfin-admin-fee"); const retention=build*pct("mezfin-retention-rate"); const netDay1=Math.max(0,grossDay1-day1Interest-arrangement-broker-admin-existing); const totalNetLender=Math.max(0,grossFacility-day1Interest-futureInterest-arrangement-broker-admin-retention-existing); const mezzEnabled=$("mezfin-mezz-enabled")?$("mezfin-mezz-enabled").checked:true; const mezzGross=mezzEnabled?n("mezfin-mezz-gross"):0; const mezzInterest=mezzGross*pct("mezfin-mezz-interest")*n("mezfin-mezz-term"); const mezzFees=mezzGross*pct("mezfin-mezz-setup")+(mezzEnabled?n("mezfin-mezz-admin"):0)+mezzGross*pct("mezfin-mezz-broker"); const mezzNet=Math.max(0,mezzGross-mezzInterest-mezzFees); const mezzRepayment=mezzGross*(1+pct("mezfin-mezz-premium")); const totalFunds=totalNetLender+mezzNet; const equity=Math.max(0,projectCost-totalFunds); const seniorExit=grossFacility+rolledInterest+grossFacility*pct("mezfin-exit-rate"); let remaining=gdv,seniorPaid=0,mezzPaid=0; if(mezzEnabled&&$("mezfin-priority")&&$("mezfin-priority").value==="ahead"){ mezzPaid=Math.min(remaining,mezzRepayment);remaining-=mezzPaid;seniorPaid=Math.min(remaining,seniorExit);remaining-=seniorPaid; }else{ seniorPaid=Math.min(remaining,seniorExit);remaining-=seniorPaid;mezzPaid=Math.min(remaining,mezzRepayment);remaining-=mezzPaid; } const residual=Math.max(0,remaining),seniorShortfall=Math.max(0,seniorExit-seniorPaid),mezzShortfall=Math.max(0,mezzRepayment-mezzPaid); [["mezfin-out-day1",netDay1],["mezfin-out-day1-2",netDay1],["mezfin-out-equity",equity],["mezfin-out-total-funds",totalFunds],["mezfin-out-residual",residual],["mezfin-out-residual-2",residual],["mezfin-out-acquisition",purchase],["mezfin-out-build",build],["mezfin-out-contingency",contingency],["mezfin-out-professional",professional],["mezfin-out-project-cost",projectCost],["mezfin-out-gdv-cap",gdvCap],["mezfin-out-ltc-cap",ltcCap],["mezfin-out-gross-facility",grossFacility],["mezfin-out-gross-day1",grossDay1],["mezfin-out-day1-gross-2",grossDay1],["mezfin-out-development-allocation",grossDevelopment],["mezfin-out-interest",totalInterest],["mezfin-out-net-lender",totalNetLender],["mezfin-out-day1-interest",-day1Interest],["mezfin-out-arrangement",-arrangement],["mezfin-out-broker",-broker],["mezfin-out-admin",-admin],["mezfin-out-existing",-existing],["mezfin-out-mezz-gross",mezzGross],["mezfin-out-mezz-interest",mezzInterest],["mezfin-out-mezz-fees",mezzFees],["mezfin-out-mezz-net",mezzNet],["mezfin-out-mezz-repayment",mezzRepayment],["mezfin-out-senior-repayment",seniorExit],["mezfin-out-mezz-paid",mezzPaid],["mezfin-out-senior-shortfall",seniorShortfall],["mezfin-out-mezz-shortfall",mezzShortfall]].forEach(x=>set(x[0],x[1])); set("mezfin-out-ltgdv",gdv?grossFacility/gdv:0,percent);set("mezfin-out-ltc",projectCost?grossFacility/projectCost:0,percent);set("mezfin-out-total-debt-gdv",gdv?(seniorExit+mezzRepayment)/gdv:0,percent); } root.addEventListener("input",calculate); root.addEventListener("change",e=>{if(e.target.id==="mezfin-mezz-enabled")toggleMezz();calculate()}); if($("mezfin-calculate"))$("mezfin-calculate").addEventListener("click",calculate); toggleMezz();calculate(); })();