@archerjessop/utilities 4.6.5 → 4.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/business.js.map +1 -1
- package/dist/config/financial.js.map +1 -1
- package/dist/config/property-types.js.map +1 -1
- package/dist/data/extractors.js.map +1 -1
- package/dist/date/utilities.js.map +1 -1
- package/dist/financial/calculations.js +1 -1
- package/dist/financial/calculations.js.map +1 -1
- package/dist/financial/formatters.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"business.js","sources":["../../src/config/business.js"],"sourcesContent":["/**\n * Business logic and fee constants\n */\n\nexport const BUSINESS_CONSTANTS = {\n // Assignment and transaction fees\n ASSIGNMENT_FEE_PERCENTAGE: 0.05, // 5% assignment fee\n NET_TO_BUYER_PERCENTAGE: 0.10, // 10% net to buyer\n CLOSING_COSTS_PERCENTAGE: 0.0125, // 1.25% closing costs\n FINANCING_FEE_PERCENTAGE: 0.03, // 3% financing fee\n REHAB_PERCENTAGE: 0.0, // 0% rehab fee\n \n // Agent commissions\n SELLER_AGENT_COMMISSION: 0.03, // 3% seller agent commission\n BUYER_AGENT_COMMISSION: 0.03, // 3% buyer agent commission\n \n // COCR15 price calculation limits\n MINIMUM_COCR15_PRICE: 10000, // $10,000 minimum\n MAX_COCR15_PRICE_MULTIPLIER: 50, // Maximum price as multiple of NOI\n CONSERVATIVE_COCR15_PRICE_MULTIPLIER: 20, // Conservative price limit\n \n // Algorithm parameters (duplicated for business logic context)\n\n \n // Calculation parameters\n MAX_ITERATIONS: 50, // Maximum iterations for iterative calculations\n CALCULATION_TOLERANCE: 0.001, // Tolerance for convergence\n ADJUSTMENT_FACTOR: 0.5, // Adjustment factor for iterations\n\n\n \n // URL constants\n EXPORT_URL_BASE: \"https://app.archerjessop.com/property-dashboard/import\",\n EXCLUDED_EXPORT_VALUES: [\"Loading...\", \"Not found\", \"\", null, undefined],\n\n DASHBOARD_URL_BASE: \"https://app.archerjessop.com/property-dashboard/\",\n \n};\n\n// Convenience exports for commonly used values\nexport const {\n ASSIGNMENT_FEE_PERCENTAGE,\n NET_TO_BUYER_PERCENTAGE,\n CLOSING_COSTS_PERCENTAGE,\n FINANCING_FEE_PERCENTAGE,\n SELLER_AGENT_COMMISSION,\n BUYER_AGENT_COMMISSION,\n MINIMUM_COCR15_PRICE,\n MAX_COCR15_PRICE_MULTIPLIER,\n CONSERVATIVE_COCR15_PRICE_MULTIPLIER\n} = BUSINESS_CONSTANTS;\n"],"names":["BUSINESS_CONSTANTS","ASSIGNMENT_FEE_PERCENTAGE","NET_TO_BUYER_PERCENTAGE","CLOSING_COSTS_PERCENTAGE","FINANCING_FEE_PERCENTAGE","REHAB_PERCENTAGE","SELLER_AGENT_COMMISSION","BUYER_AGENT_COMMISSION","MINIMUM_COCR15_PRICE","MAX_COCR15_PRICE_MULTIPLIER","CONSERVATIVE_COCR15_PRICE_MULTIPLIER","MAX_ITERATIONS","CALCULATION_TOLERANCE","ADJUSTMENT_FACTOR","EXPORT_URL_BASE","EXCLUDED_EXPORT_VALUES","undefined","DASHBOARD_URL_BASE"],"mappings":"AAIY,MAACA,EAAqB,CAEhCC,0BAA2B,IAC3BC,wBAAyB,GACzBC,yBAA0B,MAC1BC,yBAA0B,IAC1BC,iBAAkB,EAGlBC,wBAAyB,IACzBC,uBAAwB,IAGxBC,qBAAsB,IACtBC,4BAA6B,GAC7BC,qCAAsC,GAMtCC,eAAgB,GAChBC,sBAAuB,KACvBC,kBAAmB,GAKnBC,gBAAiB,yDACjBC,uBAAwB,CAAC,aAAc,YAAa,GAAI,UAAMC,GAE9DC,mBAAoB,qDAKThB,0BACXA,EAAyBC,wBACzBA,EAAuBC,yBACvBA,EAAwBC,yBACxBA,EAAwBE,wBACxBA,EAAuBC,uBACvBA,EAAsBC,qBACtBA,EAAoBC,4BACpBA,EAA2BC,qCAC3BA,GACEV"}
|
|
1
|
+
{"version":3,"file":"business.js","sources":["../../src/config/business.js"],"sourcesContent":["/**\r\n * Business logic and fee constants\r\n */\r\n\r\nexport const BUSINESS_CONSTANTS = {\r\n // Assignment and transaction fees\r\n ASSIGNMENT_FEE_PERCENTAGE: 0.05, // 5% assignment fee\r\n NET_TO_BUYER_PERCENTAGE: 0.10, // 10% net to buyer\r\n CLOSING_COSTS_PERCENTAGE: 0.0125, // 1.25% closing costs\r\n FINANCING_FEE_PERCENTAGE: 0.03, // 3% financing fee\r\n REHAB_PERCENTAGE: 0.0, // 0% rehab fee\r\n \r\n // Agent commissions\r\n SELLER_AGENT_COMMISSION: 0.03, // 3% seller agent commission\r\n BUYER_AGENT_COMMISSION: 0.03, // 3% buyer agent commission\r\n \r\n // COCR15 price calculation limits\r\n MINIMUM_COCR15_PRICE: 10000, // $10,000 minimum\r\n MAX_COCR15_PRICE_MULTIPLIER: 50, // Maximum price as multiple of NOI\r\n CONSERVATIVE_COCR15_PRICE_MULTIPLIER: 20, // Conservative price limit\r\n \r\n // Algorithm parameters (duplicated for business logic context)\r\n\r\n \r\n // Calculation parameters\r\n MAX_ITERATIONS: 50, // Maximum iterations for iterative calculations\r\n CALCULATION_TOLERANCE: 0.001, // Tolerance for convergence\r\n ADJUSTMENT_FACTOR: 0.5, // Adjustment factor for iterations\r\n\r\n\r\n \r\n // URL constants\r\n EXPORT_URL_BASE: \"https://app.archerjessop.com/property-dashboard/import\",\r\n EXCLUDED_EXPORT_VALUES: [\"Loading...\", \"Not found\", \"\", null, undefined],\r\n\r\n DASHBOARD_URL_BASE: \"https://app.archerjessop.com/property-dashboard/\",\r\n \r\n};\r\n\r\n// Convenience exports for commonly used values\r\nexport const {\r\n ASSIGNMENT_FEE_PERCENTAGE,\r\n NET_TO_BUYER_PERCENTAGE,\r\n CLOSING_COSTS_PERCENTAGE,\r\n FINANCING_FEE_PERCENTAGE,\r\n SELLER_AGENT_COMMISSION,\r\n BUYER_AGENT_COMMISSION,\r\n MINIMUM_COCR15_PRICE,\r\n MAX_COCR15_PRICE_MULTIPLIER,\r\n CONSERVATIVE_COCR15_PRICE_MULTIPLIER\r\n} = BUSINESS_CONSTANTS;\r\n"],"names":["BUSINESS_CONSTANTS","ASSIGNMENT_FEE_PERCENTAGE","NET_TO_BUYER_PERCENTAGE","CLOSING_COSTS_PERCENTAGE","FINANCING_FEE_PERCENTAGE","REHAB_PERCENTAGE","SELLER_AGENT_COMMISSION","BUYER_AGENT_COMMISSION","MINIMUM_COCR15_PRICE","MAX_COCR15_PRICE_MULTIPLIER","CONSERVATIVE_COCR15_PRICE_MULTIPLIER","MAX_ITERATIONS","CALCULATION_TOLERANCE","ADJUSTMENT_FACTOR","EXPORT_URL_BASE","EXCLUDED_EXPORT_VALUES","undefined","DASHBOARD_URL_BASE"],"mappings":"AAIY,MAACA,EAAqB,CAEhCC,0BAA2B,IAC3BC,wBAAyB,GACzBC,yBAA0B,MAC1BC,yBAA0B,IAC1BC,iBAAkB,EAGlBC,wBAAyB,IACzBC,uBAAwB,IAGxBC,qBAAsB,IACtBC,4BAA6B,GAC7BC,qCAAsC,GAMtCC,eAAgB,GAChBC,sBAAuB,KACvBC,kBAAmB,GAKnBC,gBAAiB,yDACjBC,uBAAwB,CAAC,aAAc,YAAa,GAAI,UAAMC,GAE9DC,mBAAoB,qDAKThB,0BACXA,EAAyBC,wBACzBA,EAAuBC,yBACvBA,EAAwBC,yBACxBA,EAAwBE,wBACxBA,EAAuBC,uBACvBA,EAAsBC,qBACtBA,EAAoBC,4BACpBA,EAA2BC,qCAC3BA,GACEV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"financial.js","sources":["../../src/config/financial.js"],"sourcesContent":["/**\n * Financial calculation constants\n */\n\nexport const FINANCIAL_CONSTANTS = {\n // Interest rates\n DSCR_INTEREST_RATE: 0.075, // 7.5%\n SELLER_FI_INTEREST_RATE: 0.0, // 0% for seller financing\n \n // Loan terms and amortization\n DSCR_AMORTIZATION: 30, // 30 years\n SELLER_FI_AMORTIZATION: 30, // 30 years\n DEFAULT_BALLOON_PERIOD_YEARS: 7, // 7 years\n \n // Down payments and financing structure\n DEFAULT_DOWN_PAYMENT: 0.30, // 30%\n SELLER_FI_DOWN_PAYMENT: 0.60, // 60%\n SELLER_FI_CARRY: 0.40, // 40% seller financing\n MIN_DOWN_PAYMENT_PERCENT: 0, // 0% minimum down\n \n // DSCR loan percentages\n DEFAULT_DSCR_PERCENTAGE: 0.70, // 70%\n MAX_DSCR_PERCENTAGE: 0.70, // Maximum DSCR loan\n \n // Cap rates\n DEFAULT_CAP_RATE: 0.05, // 5% default cap rate\n MAX_ESTIMATED_CAP_RATE: 20, // 20% maximum cap rate\n \n // Market assumptions\n APPRECIATION_RATE: 0.045, // 4.5% annual appreciation\n};\n\n// Convenience exports for common calculations\nexport const {\n DSCR_INTEREST_RATE,\n SELLER_FI_INTEREST_RATE,\n DSCR_AMORTIZATION,\n SELLER_FI_AMORTIZATION,\n DEFAULT_DOWN_PAYMENT,\n SELLER_FI_DOWN_PAYMENT,\n SELLER_FI_CARRY,\n DEFAULT_DSCR_PERCENTAGE,\n DEFAULT_CAP_RATE,\n MAX_ITERATIONS,\n CALCULATION_TOLERANCE\n} = FINANCIAL_CONSTANTS;\n"],"names":["FINANCIAL_CONSTANTS","DSCR_INTEREST_RATE","SELLER_FI_INTEREST_RATE","DSCR_AMORTIZATION","SELLER_FI_AMORTIZATION","DEFAULT_BALLOON_PERIOD_YEARS","DEFAULT_DOWN_PAYMENT","SELLER_FI_DOWN_PAYMENT","SELLER_FI_CARRY","MIN_DOWN_PAYMENT_PERCENT","DEFAULT_DSCR_PERCENTAGE","MAX_DSCR_PERCENTAGE","DEFAULT_CAP_RATE","MAX_ESTIMATED_CAP_RATE","APPRECIATION_RATE","MAX_ITERATIONS","CALCULATION_TOLERANCE"],"mappings":"AAIY,MAACA,EAAsB,CAEjCC,mBAAoB,KACpBC,wBAAyB,EAGzBC,kBAAmB,GACnBC,uBAAwB,GACxBC,6BAA8B,EAG9BC,qBAAsB,GACtBC,uBAAwB,GACxBC,gBAAiB,GACjBC,yBAA0B,EAG1BC,wBAAyB,GACzBC,oBAAqB,GAGrBC,iBAAkB,IAClBC,uBAAwB,GAGxBC,kBAAmB,OAIRb,mBACXA,EAAkBC,wBAClBA,EAAuBC,kBACvBA,EAAiBC,uBACjBA,EAAsBE,qBACtBA,EAAoBC,uBACpBA,EAAsBC,gBACtBA,EAAeE,wBACfA,EAAuBE,iBACvBA,EAAgBG,eAChBA,EAAcC,sBACdA,GACEhB"}
|
|
1
|
+
{"version":3,"file":"financial.js","sources":["../../src/config/financial.js"],"sourcesContent":["/**\r\n * Financial calculation constants\r\n */\r\n\r\nexport const FINANCIAL_CONSTANTS = {\r\n // Interest rates\r\n DSCR_INTEREST_RATE: 0.075, // 7.5%\r\n SELLER_FI_INTEREST_RATE: 0.0, // 0% for seller financing\r\n \r\n // Loan terms and amortization\r\n DSCR_AMORTIZATION: 30, // 30 years\r\n SELLER_FI_AMORTIZATION: 30, // 30 years\r\n DEFAULT_BALLOON_PERIOD_YEARS: 7, // 7 years\r\n \r\n // Down payments and financing structure\r\n DEFAULT_DOWN_PAYMENT: 0.30, // 30%\r\n SELLER_FI_DOWN_PAYMENT: 0.60, // 60%\r\n SELLER_FI_CARRY: 0.40, // 40% seller financing\r\n MIN_DOWN_PAYMENT_PERCENT: 0, // 0% minimum down\r\n \r\n // DSCR loan percentages\r\n DEFAULT_DSCR_PERCENTAGE: 0.70, // 70%\r\n MAX_DSCR_PERCENTAGE: 0.70, // Maximum DSCR loan\r\n \r\n // Cap rates\r\n DEFAULT_CAP_RATE: 0.05, // 5% default cap rate\r\n MAX_ESTIMATED_CAP_RATE: 20, // 20% maximum cap rate\r\n \r\n // Market assumptions\r\n APPRECIATION_RATE: 0.045, // 4.5% annual appreciation\r\n};\r\n\r\n// Convenience exports for common calculations\r\nexport const {\r\n DSCR_INTEREST_RATE,\r\n SELLER_FI_INTEREST_RATE,\r\n DSCR_AMORTIZATION,\r\n SELLER_FI_AMORTIZATION,\r\n DEFAULT_DOWN_PAYMENT,\r\n SELLER_FI_DOWN_PAYMENT,\r\n SELLER_FI_CARRY,\r\n DEFAULT_DSCR_PERCENTAGE,\r\n DEFAULT_CAP_RATE,\r\n MAX_ITERATIONS,\r\n CALCULATION_TOLERANCE\r\n} = FINANCIAL_CONSTANTS;\r\n"],"names":["FINANCIAL_CONSTANTS","DSCR_INTEREST_RATE","SELLER_FI_INTEREST_RATE","DSCR_AMORTIZATION","SELLER_FI_AMORTIZATION","DEFAULT_BALLOON_PERIOD_YEARS","DEFAULT_DOWN_PAYMENT","SELLER_FI_DOWN_PAYMENT","SELLER_FI_CARRY","MIN_DOWN_PAYMENT_PERCENT","DEFAULT_DSCR_PERCENTAGE","MAX_DSCR_PERCENTAGE","DEFAULT_CAP_RATE","MAX_ESTIMATED_CAP_RATE","APPRECIATION_RATE","MAX_ITERATIONS","CALCULATION_TOLERANCE"],"mappings":"AAIY,MAACA,EAAsB,CAEjCC,mBAAoB,KACpBC,wBAAyB,EAGzBC,kBAAmB,GACnBC,uBAAwB,GACxBC,6BAA8B,EAG9BC,qBAAsB,GACtBC,uBAAwB,GACxBC,gBAAiB,GACjBC,yBAA0B,EAG1BC,wBAAyB,GACzBC,oBAAqB,GAGrBC,iBAAkB,IAClBC,uBAAwB,GAGxBC,kBAAmB,OAIRb,mBACXA,EAAkBC,wBAClBA,EAAuBC,kBACvBA,EAAiBC,uBACjBA,EAAsBE,qBACtBA,EAAoBC,uBACpBA,EAAsBC,gBACtBA,EAAeE,wBACfA,EAAuBE,iBACvBA,EAAgBG,eAChBA,EAAcC,sBACdA,GACEhB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"property-types.js","sources":["../../src/config/property-types.js"],"sourcesContent":["/**\n * Property type specific constants\n */\n\nexport const PROPERTY_TYPE_CONSTANTS = {\n // Short-term rental (STR) calculations\n STR: {\n ESTIMATED_GROSS_RATE: 0.10, // 10% of price\n NOI_PERCENTAGE: 0.55, // 55% of gross income\n DEFAULT_CAP_RATE: 0.05, // 5% default cap rate\n },\n \n // Assisted living calculations\n ASSISTED_LIVING: {\n INCOME_PER_BEDROOM_MONTHLY: 1500, // $1,500 per bedroom per month\n DEFAULT_BEDROOM_COUNT: 10, // Default assumption\n DEFAULT_CAP_RATE: 0.05, // 5% default cap rate\n },\n \n // Multifamily calculations\n MULTIFAMILY: {\n DEFAULT_CAP_RATE: 0.05, // 5% default cap rate\n },\n};\n\n// Property type enum for consistency\nexport const PROPERTY_TYPES = {\n MULTIFAMILY: 'multifamily',\n STR: 'str',\n ASSISTED_LIVING: 'assisted',\n};\n\n// Convenience exports\nexport const {\n STR,\n ASSISTED_LIVING,\n MULTIFAMILY\n} = PROPERTY_TYPE_CONSTANTS;\n"],"names":["PROPERTY_TYPE_CONSTANTS","STR","ESTIMATED_GROSS_RATE","NOI_PERCENTAGE","DEFAULT_CAP_RATE","ASSISTED_LIVING","INCOME_PER_BEDROOM_MONTHLY","DEFAULT_BEDROOM_COUNT","MULTIFAMILY","PROPERTY_TYPES"],"mappings":"AAIY,MAACA,EAA0B,CAErCC,IAAK,CACHC,qBAAsB,GACtBC,eAAgB,IAChBC,iBAAkB,KAIpBC,gBAAiB,CACfC,2BAA4B,KAC5BC,sBAAuB,GACvBH,iBAAkB,KAIpBI,YAAa,CACXJ,iBAAkB,MAKTK,EAAiB,CAC5BD,YAAa,cACbP,IAAK,MACLI,gBAAiB,aAINJ,IACXA,EAAGI,gBACHA,EAAeG,YACfA,GACER"}
|
|
1
|
+
{"version":3,"file":"property-types.js","sources":["../../src/config/property-types.js"],"sourcesContent":["/**\r\n * Property type specific constants\r\n */\r\n\r\nexport const PROPERTY_TYPE_CONSTANTS = {\r\n // Short-term rental (STR) calculations\r\n STR: {\r\n ESTIMATED_GROSS_RATE: 0.10, // 10% of price\r\n NOI_PERCENTAGE: 0.55, // 55% of gross income\r\n DEFAULT_CAP_RATE: 0.05, // 5% default cap rate\r\n },\r\n \r\n // Assisted living calculations\r\n ASSISTED_LIVING: {\r\n INCOME_PER_BEDROOM_MONTHLY: 1500, // $1,500 per bedroom per month\r\n DEFAULT_BEDROOM_COUNT: 10, // Default assumption\r\n DEFAULT_CAP_RATE: 0.05, // 5% default cap rate\r\n },\r\n \r\n // Multifamily calculations\r\n MULTIFAMILY: {\r\n DEFAULT_CAP_RATE: 0.05, // 5% default cap rate\r\n },\r\n};\r\n\r\n// Property type enum for consistency\r\nexport const PROPERTY_TYPES = {\r\n MULTIFAMILY: 'multifamily',\r\n STR: 'str',\r\n ASSISTED_LIVING: 'assisted',\r\n};\r\n\r\n// Convenience exports\r\nexport const {\r\n STR,\r\n ASSISTED_LIVING,\r\n MULTIFAMILY\r\n} = PROPERTY_TYPE_CONSTANTS;\r\n"],"names":["PROPERTY_TYPE_CONSTANTS","STR","ESTIMATED_GROSS_RATE","NOI_PERCENTAGE","DEFAULT_CAP_RATE","ASSISTED_LIVING","INCOME_PER_BEDROOM_MONTHLY","DEFAULT_BEDROOM_COUNT","MULTIFAMILY","PROPERTY_TYPES"],"mappings":"AAIY,MAACA,EAA0B,CAErCC,IAAK,CACHC,qBAAsB,GACtBC,eAAgB,IAChBC,iBAAkB,KAIpBC,gBAAiB,CACfC,2BAA4B,KAC5BC,sBAAuB,GACvBH,iBAAkB,KAIpBI,YAAa,CACXJ,iBAAkB,MAKTK,EAAiB,CAC5BD,YAAa,cACbP,IAAK,MACLI,gBAAiB,aAINJ,IACXA,EAAGI,gBACHA,EAAeG,YACfA,GACER"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractors.js","sources":["../../src/data/extractors.js"],"sourcesContent":["export function extractPhoneNumber() {\n const phoneElement = document.querySelector(\".phone-number\") ||\n document.querySelector(\"a[href^='tel:']\") ||\n document.querySelector(\".number\") ||\n document.querySelector(\"[class*='phone']\");\n \n if (phoneElement) {\n if (phoneElement.textContent && phoneElement.textContent.trim() !== \"Call\") {\n return phoneElement.textContent.trim();\n } else if (phoneElement.href) {\n // Extract from tel: link\n const telMatch = phoneElement.href.match(/tel:(.+)/);\n if (telMatch) {\n return telMatch[1];\n }\n }\n }\n \n // Fallback to text search with multiple patterns\n const pageText = document.body ? document.body.textContent || \"\" : \"\";\n const phoneMatch = pageText.match(/(\\+?1?\\s*\\(?[0-9]{3}\\)?[\\s.-]*[0-9]{3}[\\s.-]*[0-9]{4})/);\n if (phoneMatch) {\n return phoneMatch[1].trim();\n }\n \n return \"Not found\";\n}\n\nexport function extractBedrooms() {\n try {\n // Look for bedroom information in various places\n const bodyText = document.body?.textContent || \"\";\n \n // Common patterns for bedroom information\n const bedroomPatterns = [\n /(\\d+)\\s*bed/i,\n /(\\d+)\\s*bedroom/i,\n /beds?\\s*:\\s*(\\d+)/i,\n /bedrooms?\\s*:\\s*(\\d+)/i,\n /(\\d+)\\s*BR/i,\n /(\\d+)br/i\n ];\n \n for (const pattern of bedroomPatterns) {\n const match = bodyText.match(pattern);\n if (match) {\n const bedrooms = parseInt(match[1]);\n if (bedrooms > 0 && bedrooms < 100) { // Sanity check\n return bedrooms;\n }\n }\n }\n \n // Look in property details section specifically\n const propertyDetails = document.querySelector(\".property-details\") || \n document.querySelector(\"#PropertyDetails\") ||\n document.querySelector(\".details\");\n \n if (propertyDetails) {\n const detailsText = propertyDetails.textContent || \"\";\n for (const pattern of bedroomPatterns) {\n const match = detailsText.match(pattern);\n if (match) {\n const bedrooms = parseInt(match[1]);\n if (bedrooms > 0 && bedrooms < 100) {\n return bedrooms;\n }\n }\n }\n }\n \n // Default fallback\n return 10; // Default assumption for assisted living\n } catch (error) {\n return 10; // Default fallback\n }\n}\n\n"],"names":["extractPhoneNumber","phoneElement","document","querySelector","textContent","trim","href","telMatch","match","phoneMatch","body","extractBedrooms","bodyText","bedroomPatterns","pattern","bedrooms","parseInt","propertyDetails","detailsText","error"],"mappings":"AAAO,SAASA,qBACd,MAAMC,EAAeC,SAASC,cAAc,kBACxBD,SAASC,cAAc,oBACvBD,SAASC,cAAc,YACvBD,SAASC,cAAc,oBAE3C,GAAIF,EAAc,CAChB,GAAIA,EAAaG,aAAmD,SAApCH,EAAaG,YAAYC,OACvD,OAAOJ,EAAaG,YAAYC,OAC3B,GAAIJ,EAAaK,KAAM,CAE5B,MAAMC,EAAWN,EAAaK,KAAKE,MAAM,YACzC,GAAID,EACF,OAAOA,EAAS,EAEpB,CACF,CAGA,MACME,GADWP,SAASQ,MAAOR,SAASQ,KAAKN,aAAoB,IACvCI,MAAM,0DAClC,OAAIC,EACKA,EAAW,GAAGJ,OAGhB,WACT,CAEO,SAASM,kBACd,IAEE,MAAMC,EAAWV,SAASQ,MAAMN,aAAe,GAGzCS,EAAkB,CACtB,eACA,mBACA,qBACA,yBACA,cACA,YAGF,IAAK,MAAMC,KAAWD,EAAiB,CACrC,MAAML,EAAQI,EAASJ,MAAMM,GAC7B,GAAIN,EAAO,CACT,MAAMO,EAAWC,SAASR,EAAM,IAChC,GAAIO,EAAW,GAAKA,EAAW,IAC7B,OAAOA,CAEX,CACF,CAGA,MAAME,EAAkBf,SAASC,cAAc,sBACxBD,SAASC,cAAc,qBACvBD,SAASC,cAAc,YAE9C,GAAIc,EAAiB,CACnB,MAAMC,EAAcD,EAAgBb,aAAe,GACnD,IAAK,MAAMU,KAAWD,EAAiB,CACrC,MAAML,EAAQU,EAAYV,MAAMM,GAChC,GAAIN,EAAO,CACT,MAAMO,EAAWC,SAASR,EAAM,IAChC,GAAIO,EAAW,GAAKA,EAAW,IAC7B,OAAOA,CAEX,CACF,CACF,CAGA,OAAO,EACT,CAAE,MAAOI,GACP,OAAO,EACT,CACF"}
|
|
1
|
+
{"version":3,"file":"extractors.js","sources":["../../src/data/extractors.js"],"sourcesContent":["export function extractPhoneNumber() {\r\n const phoneElement = document.querySelector(\".phone-number\") ||\r\n document.querySelector(\"a[href^='tel:']\") ||\r\n document.querySelector(\".number\") ||\r\n document.querySelector(\"[class*='phone']\");\r\n \r\n if (phoneElement) {\r\n if (phoneElement.textContent && phoneElement.textContent.trim() !== \"Call\") {\r\n return phoneElement.textContent.trim();\r\n } else if (phoneElement.href) {\r\n // Extract from tel: link\r\n const telMatch = phoneElement.href.match(/tel:(.+)/);\r\n if (telMatch) {\r\n return telMatch[1];\r\n }\r\n }\r\n }\r\n \r\n // Fallback to text search with multiple patterns\r\n const pageText = document.body ? document.body.textContent || \"\" : \"\";\r\n const phoneMatch = pageText.match(/(\\+?1?\\s*\\(?[0-9]{3}\\)?[\\s.-]*[0-9]{3}[\\s.-]*[0-9]{4})/);\r\n if (phoneMatch) {\r\n return phoneMatch[1].trim();\r\n }\r\n \r\n return \"Not found\";\r\n}\r\n\r\nexport function extractBedrooms() {\r\n try {\r\n // Look for bedroom information in various places\r\n const bodyText = document.body?.textContent || \"\";\r\n \r\n // Common patterns for bedroom information\r\n const bedroomPatterns = [\r\n /(\\d+)\\s*bed/i,\r\n /(\\d+)\\s*bedroom/i,\r\n /beds?\\s*:\\s*(\\d+)/i,\r\n /bedrooms?\\s*:\\s*(\\d+)/i,\r\n /(\\d+)\\s*BR/i,\r\n /(\\d+)br/i\r\n ];\r\n \r\n for (const pattern of bedroomPatterns) {\r\n const match = bodyText.match(pattern);\r\n if (match) {\r\n const bedrooms = parseInt(match[1]);\r\n if (bedrooms > 0 && bedrooms < 100) { // Sanity check\r\n return bedrooms;\r\n }\r\n }\r\n }\r\n \r\n // Look in property details section specifically\r\n const propertyDetails = document.querySelector(\".property-details\") || \r\n document.querySelector(\"#PropertyDetails\") ||\r\n document.querySelector(\".details\");\r\n \r\n if (propertyDetails) {\r\n const detailsText = propertyDetails.textContent || \"\";\r\n for (const pattern of bedroomPatterns) {\r\n const match = detailsText.match(pattern);\r\n if (match) {\r\n const bedrooms = parseInt(match[1]);\r\n if (bedrooms > 0 && bedrooms < 100) {\r\n return bedrooms;\r\n }\r\n }\r\n }\r\n }\r\n \r\n // Default fallback\r\n return 10; // Default assumption for assisted living\r\n } catch (error) {\r\n return 10; // Default fallback\r\n }\r\n}\r\n\r\n"],"names":["extractPhoneNumber","phoneElement","document","querySelector","textContent","trim","href","telMatch","match","phoneMatch","body","extractBedrooms","bodyText","bedroomPatterns","pattern","bedrooms","parseInt","propertyDetails","detailsText","error"],"mappings":"AAAO,SAASA,qBACd,MAAMC,EAAeC,SAASC,cAAc,kBACxBD,SAASC,cAAc,oBACvBD,SAASC,cAAc,YACvBD,SAASC,cAAc,oBAE3C,GAAIF,EAAc,CAChB,GAAIA,EAAaG,aAAmD,SAApCH,EAAaG,YAAYC,OACvD,OAAOJ,EAAaG,YAAYC,OAC3B,GAAIJ,EAAaK,KAAM,CAE5B,MAAMC,EAAWN,EAAaK,KAAKE,MAAM,YACzC,GAAID,EACF,OAAOA,EAAS,EAEpB,CACF,CAGA,MACME,GADWP,SAASQ,MAAOR,SAASQ,KAAKN,aAAoB,IACvCI,MAAM,0DAClC,OAAIC,EACKA,EAAW,GAAGJ,OAGhB,WACT,CAEO,SAASM,kBACd,IAEE,MAAMC,EAAWV,SAASQ,MAAMN,aAAe,GAGzCS,EAAkB,CACtB,eACA,mBACA,qBACA,yBACA,cACA,YAGF,IAAK,MAAMC,KAAWD,EAAiB,CACrC,MAAML,EAAQI,EAASJ,MAAMM,GAC7B,GAAIN,EAAO,CACT,MAAMO,EAAWC,SAASR,EAAM,IAChC,GAAIO,EAAW,GAAKA,EAAW,IAC7B,OAAOA,CAEX,CACF,CAGA,MAAME,EAAkBf,SAASC,cAAc,sBACxBD,SAASC,cAAc,qBACvBD,SAASC,cAAc,YAE9C,GAAIc,EAAiB,CACnB,MAAMC,EAAcD,EAAgBb,aAAe,GACnD,IAAK,MAAMU,KAAWD,EAAiB,CACrC,MAAML,EAAQU,EAAYV,MAAMM,GAChC,GAAIN,EAAO,CACT,MAAMO,EAAWC,SAASR,EAAM,IAChC,GAAIO,EAAW,GAAKA,EAAW,IAC7B,OAAOA,CAEX,CACF,CACF,CAGA,OAAO,EACT,CAAE,MAAOI,GACP,OAAO,EACT,CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utilities.js","sources":["../../src/date/utilities.js"],"sourcesContent":["/**\n * DATE utilities\n */\n\nexport function calculateDOM(listingDateText) {\n if (!listingDateText || listingDateText === \"Not found\") {\n return \"Not found\";\n }\n\n try {\n // Parse various date formats\n let listingDate;\n \n // Try parsing MM/DD/YYYY format\n const mmddyyyyMatch = listingDateText.match(/(\\d{1,2})\\/(\\d{1,2})\\/(\\d{4})/);\n if (mmddyyyyMatch) {\n const [, month, day, year] = mmddyyyyMatch;\n listingDate = new Date(year, month - 1, day);\n } else {\n // Try parsing other common date formats\n listingDate = new Date(listingDateText);\n }\n\n if (isNaN(listingDate.getTime())) {\n return \"Invalid date\";\n }\n\n // Calculate days difference\n const today = new Date();\n const diffTime = today - listingDate;\n const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));\n\n // Format the original date as MM/DD/YYYY\n const formatDate = (date) => {\n const month = (date.getMonth() + 1).toString().padStart(2, \"0\");\n const day = date.getDate().toString().padStart(2, \"0\");\n const year = date.getFullYear();\n return `${month}/${day}/${year}`;\n };\n\n return `${diffDays} (${formatDate(listingDate)})`;\n } catch (error) {\n return \"Calculation error\";\n }\n}\n\n"],"names":["calculateDOM","listingDateText","listingDate","mmddyyyyMatch","match","month","day","year","Date","isNaN","getTime","diffTime","diffDays","Math","floor","date","getMonth","toString","padStart","getDate","getFullYear","formatDate","error"],"mappings":"AAIO,SAASA,aAAaC,GAC3B,IAAKA,GAAuC,cAApBA,EACtB,MAAO,YAGT,IAEE,IAAIC,EAGJ,MAAMC,EAAgBF,EAAgBG,MAAM,iCAC5C,GAAID,EAAe,CACjB,
|
|
1
|
+
{"version":3,"file":"utilities.js","sources":["../../src/date/utilities.js"],"sourcesContent":["/**\r\n * DATE utilities\r\n */\r\n\r\nexport function calculateDOM(listingDateText) {\r\n if (!listingDateText || listingDateText === \"Not found\") {\r\n return \"Not found\";\r\n }\r\n\r\n try {\r\n // Parse various date formats\r\n let listingDate;\r\n \r\n // Try parsing MM/DD/YYYY format\r\n const mmddyyyyMatch = listingDateText.match(/(\\d{1,2})\\/(\\d{1,2})\\/(\\d{4})/);\r\n if (mmddyyyyMatch) {\r\n const [, month, day, year] = mmddyyyyMatch;\r\n listingDate = new Date(year, month - 1, day);\r\n } else {\r\n // Try parsing other common date formats\r\n listingDate = new Date(listingDateText);\r\n }\r\n\r\n if (isNaN(listingDate.getTime())) {\r\n return \"Invalid date\";\r\n }\r\n\r\n // Calculate days difference\r\n const today = new Date();\r\n const diffTime = today - listingDate;\r\n const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));\r\n\r\n // Format the original date as MM/DD/YYYY\r\n const formatDate = (date) => {\r\n const month = (date.getMonth() + 1).toString().padStart(2, \"0\");\r\n const day = date.getDate().toString().padStart(2, \"0\");\r\n const year = date.getFullYear();\r\n return `${month}/${day}/${year}`;\r\n };\r\n\r\n return `${diffDays} (${formatDate(listingDate)})`;\r\n } catch (error) {\r\n return \"Calculation error\";\r\n }\r\n}\r\n\r\n"],"names":["calculateDOM","listingDateText","listingDate","mmddyyyyMatch","match","month","day","year","Date","isNaN","getTime","diffTime","diffDays","Math","floor","date","getMonth","toString","padStart","getDate","getFullYear","formatDate","error"],"mappings":"AAIO,SAASA,aAAaC,GAC3B,IAAKA,GAAuC,cAApBA,EACtB,MAAO,YAGT,IAEE,IAAIC,EAGJ,MAAMC,EAAgBF,EAAgBG,MAAM,iCAC5C,GAAID,EAAe,CACjB,MAAM,CAAGE,EAAOC,EAAKC,GAAQJ,EAC7BD,EAAc,IAAIM,KAAKD,EAAMF,EAAQ,EAAGC,EAC1C,MAEEJ,EAAc,IAAIM,KAAKP,GAGzB,GAAIQ,MAAMP,EAAYQ,WACpB,MAAO,eAIT,MACMC,EADQ,IAAIH,KACON,EACnBU,EAAWC,KAAKC,MAAMH,EAAQ,OAUpC,MAAO,GAAGC,MAPS,CAACG,GAIX,IAHQA,EAAKC,WAAa,GAAGC,WAAWC,SAAS,EAAG,QAC/CH,EAAKI,UAAUF,WAAWC,SAAS,EAAG,QACrCH,EAAKK,gBAIGC,CAAWnB,KACpC,CAAE,MAAOoB,GACP,MAAO,mBACT,CACF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{FINANCIAL_CONSTANTS as t}from"../config/financial.js";import{BUSINESS_CONSTANTS as e}from"../config/business.js";import{PROPERTY_TYPES as c,PROPERTY_TYPE_CONSTANTS as a}from"../config/property-types.js";function calculatePMT(t,e,c){if(0===e)return t/(12*c);const a=e/12,r=12*c;return t*(a*Math.pow(1+a,r))/(Math.pow(1+a,r)-1)}function calculateCOCR30(t,e){try{const c=.3*t,a=12*calculatePMT(.7*t,.075,30);return(e-a)/c*100}catch(t){return 0}}function calculateCashFlowYield(t,e){if(!e||e<=0)return 0;return 12*t/e*100}function calculatePriceForCOCR(c,a=.15,r={}){const{downPercent:n=100*t.DEFAULT_DOWN_PAYMENT,dscrLtvPercent:l=100*t.DEFAULT_DSCR_PERCENTAGE,dscrRate:u=t.DSCR_INTEREST_RATE,dscrTerm:
|
|
1
|
+
import{FINANCIAL_CONSTANTS as t}from"../config/financial.js";import{BUSINESS_CONSTANTS as e}from"../config/business.js";import{PROPERTY_TYPES as c,PROPERTY_TYPE_CONSTANTS as a}from"../config/property-types.js";function calculatePMT(t,e,c){if(0===e)return t/(12*c);const a=e/12,r=12*c;return t*(a*Math.pow(1+a,r))/(Math.pow(1+a,r)-1)}function calculateCOCR30(t,e){try{const c=.3*t,a=12*calculatePMT(.7*t,.075,30);return(e-a)/c*100}catch(t){return 0}}function calculateCashFlowYield(t,e){if(!e||e<=0)return 0;return 12*t/e*100}function calculatePriceForCOCR(c,a=.15,r={}){const{downPercent:n=100*t.DEFAULT_DOWN_PAYMENT,dscrLtvPercent:l=100*t.DEFAULT_DSCR_PERCENTAGE,dscrRate:u=t.DSCR_INTEREST_RATE,dscrTerm:o=t.DSCR_AMORTIZATION,maxIterations:E=e.MAX_ITERATIONS,tolerance:T=e.CALCULATION_TOLERANCE}=r;try{let t=c/.08,r=0;for(;r<E;){const E=t*(n/100),R=12*calculatePMT(t*(l/100),u,o),s=(c-R)/E;if(Math.abs(s-a)<T)break;const A=s-a,i=A*e.ADJUSTMENT_FACTOR;t*=A>0?1+Math.abs(i):1-Math.abs(i),t<1e3&&(t=1e3),t>c*e.MAX_COCR15_PRICE_MULTIPLIER&&(t=c*e.CONSERVATIVE_COCR15_PRICE_MULTIPLIER),r++}return t<e.MINIMUM_COCR15_PRICE&&(t=e.MINIMUM_COCR15_PRICE),t}catch(t){return 0}}function calculateCOCRAtPercent(e,c,a,r={}){const{dscrRate:n=t.DSCR_INTEREST_RATE,dscrTerm:l=t.DSCR_AMORTIZATION}=r;try{const t=e*(a/100),r=12*calculatePMT(e-t,n,l);return(c-r)/t*100}catch(t){return 0}}function calculateNOIByType(t,e,r=c.MULTIFAMILY,n={}){const{strGrossIncomeMultiplier:l=a.STR.ESTIMATED_GROSS_RATE,strNoiPercentage:u=a.STR.NOI_PERCENTAGE,assistedIncomePerBedroom:o=a.ASSISTED_LIVING.INCOME_PER_BEDROOM_MONTHLY,bedroomCount:E=a.ASSISTED_LIVING.DEFAULT_BEDROOM_COUNT}=n;try{switch(r.toLowerCase()){case c.STR:return t*l*u;case c.ASSISTED_LIVING:return E*o*12;case c.MULTIFAMILY:default:return t*e}}catch(t){return 0}}function calculateAssignmentFee(t,c=100*e.ASSIGNMENT_FEE_PERCENTAGE){try{return t*(c/100)}catch(t){return 0}}function calculateNetToBuyer(c,a={}){const{buyerCostPercent:r=100*e.NET_TO_BUYER_PERCENTAGE,sellerCostAssignment:n=100*e.ASSIGNMENT_FEE_PERCENTAGE,sellerCostClosing:l=100*e.CLOSING_COSTS_PERCENTAGE,additionalCostRehab:u=100*e.REHAB_PERCENTAGE,additionalCostFinancing:o=100*e.FINANCING_FEE_PERCENTAGE,dscrLtvPercent:E=100*t.DEFAULT_DSCR_PERCENTAGE}=a;try{return c*(r/100)-c*((n+l)/100)-c*(u/100)-o/100*(c-c*(E/100))}catch(t){return 0}}function calculateBalloonBalance(e,c,a,r=t.DEFAULT_BALLOON_PERIOD_YEARS){try{if(e<=0||c<0||a<=0||r<=0)return 0;if(r>=a)return 0;if(0===c){const t=12*a;return e*(t-12*r)/t}const t=c/12,n=12*a,l=12*r,u=Math.pow(1+t,n),o=e*(u-Math.pow(1+t,l))/(u-1);return Math.max(0,o)}catch(t){return 0}}function calculateAppreciatedValue(e,c=t.APPRECIATION_RATE,a=t.DEFAULT_BALLOON_PERIOD_YEARS){try{return e<=0||c<0||a<0?e:e*Math.pow(1+c,a)}catch(t){return e}}function calculateCashOutAfterRefi(e,c,a,r={}){const{appreciationRate:n=t.APPRECIATION_RATE,balloonYears:l=t.DEFAULT_BALLOON_PERIOD_YEARS,dscrRate:u=t.DSCR_INTEREST_RATE,dscrTerm:o=t.DSCR_AMORTIZATION,sellerFiTerm:E=t.SELLER_FI_AMORTIZATION,refiLtvPercent:T=70}=r;try{const r=calculateAppreciatedValue(e,n,l),R=calculateBalloonBalance(c,u,o,l),s=calculateBalloonBalance(a,t.SELLER_FI_INTEREST_RATE,E,l);return r*(T/100)-(R+s)}catch(t){return 0}}function calculateDscrPayment(t,e,c,a){return calculatePMT(t*(e/100),c,a)}function calculateSfPayment(t,e,c,a){return calculatePMT(t*(e/100),c,a)}function calculateCashFlow(t,e,c){return t-(e+c)}function calculateDiscountFromPrice(t,e){return!t||t<=0?0:(t-e)/t}function calculatePriceFromDiscount(t,e){return!t||t<=0?0:t*(1-e)}function safePercentage(t,e=100){return null==t||isNaN(t)?e:100*t}export{calculateAppreciatedValue,calculateAssignmentFee,calculateBalloonBalance,calculateCOCR30,calculateCOCRAtPercent,calculateCashFlow,calculateCashFlowYield,calculateCashOutAfterRefi,calculateDiscountFromPrice,calculateDscrPayment,calculateNOIByType,calculateNetToBuyer,calculatePMT,calculatePriceForCOCR,calculatePriceFromDiscount,calculateSfPayment,safePercentage};
|
|
2
2
|
//# sourceMappingURL=calculations.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calculations.js","sources":["../../src/financial/calculations.js"],"sourcesContent":["// src/financial/calculations.js\r\n\r\nimport { FINANCIAL_CONSTANTS } from '../config/financial.js';\r\nimport { BUSINESS_CONSTANTS } from '../config/business.js';\r\nimport { PROPERTY_TYPE_CONSTANTS, PROPERTY_TYPES } from '../config/property-types.js';\r\n\r\n\r\n/**\r\n * PMT function for loan payment calculation\r\n * @param {number} principal - Loan principal amount \r\n * @param {number} annualRate - Annual interest rate (as decimal, e.g., 0.075 for 7.5%)\r\n * @param {number} years - Loan term in years\r\n * @returns {number} Monthly payment amount\r\n */\r\nexport function calculatePMT(principal, annualRate, years) {\r\n if (annualRate === 0) {\r\n return principal / (years * 12);\r\n }\r\n \r\n const monthlyRate = annualRate / 12;\r\n const numPayments = years * 12;\r\n const pmt = principal * (monthlyRate * Math.pow(1 + monthlyRate, numPayments)) / \r\n (Math.pow(1 + monthlyRate, numPayments) - 1);\r\n return pmt;\r\n}\r\n\r\nexport function calculateCOCR30(askingPrice, noi) {\r\n try {\r\n const cashInvested = askingPrice * 0.30; // 30% down payment\r\n const dscrLoanAmount = askingPrice * 0.70; // Fixed 70% DSCR loan\r\n const dscrPayment = calculatePMT(dscrLoanAmount, 0.075, 30) * 12; // Annual DSCR payment\r\n const annualCashFlow = noi - dscrPayment;\r\n const cocr = (annualCashFlow / cashInvested) * 100;\r\n \r\n return cocr;\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\nexport function calculateCashFlowYield(monthlyCashFlow, purchasePrice) {\r\n if (!purchasePrice || purchasePrice <= 0) return 0;\r\n const annualCashFlow = monthlyCashFlow * 12;\r\n return (annualCashFlow / purchasePrice) * 100;\r\n}\r\n\r\n\r\n/**\r\n * Calculate the property price that yields a target COCR percentage\r\n * @param {number} noi - Net Operating Income (annual)\r\n * @param {number} targetCOCR - Target COCR as decimal (default: 0.15 for 15%)\r\n * @param {Object} options - Configuration options (uses config constants as defaults)\r\n * @returns {number} Calculated property price\r\n */\r\nexport function calculatePriceForCOCR(noi, targetCOCR = 0.15, options = {}) {\r\n const {\r\n downPercent = FINANCIAL_CONSTANTS.DEFAULT_DOWN_PAYMENT * 100,\r\n dscrLtvPercent = FINANCIAL_CONSTANTS.DEFAULT_DSCR_PERCENTAGE * 100,\r\n dscrRate = FINANCIAL_CONSTANTS.DSCR_INTEREST_RATE,\r\n dscrTerm = FINANCIAL_CONSTANTS.DSCR_AMORTIZATION,\r\n maxIterations = BUSINESS_CONSTANTS.MAX_ITERATIONS,\r\n tolerance = BUSINESS_CONSTANTS.CALCULATION_TOLERANCE\r\n } = options;\r\n\r\n try {\r\n let targetPrice = noi / 0.08; // Initial estimate: NOI / 8% cap rate\r\n let iterations = 0;\r\n \r\n while (iterations < maxIterations) {\r\n const cashInvested = targetPrice * (downPercent / 100);\r\n const dscrLoanAmount = targetPrice * (dscrLtvPercent / 100);\r\n const dscrPayment = calculatePMT(dscrLoanAmount, dscrRate, dscrTerm) * 12;\r\n const annualCashFlow = noi - dscrPayment;\r\n const currentCOCR = annualCashFlow / cashInvested;\r\n \r\n if (Math.abs(currentCOCR - targetCOCR) < tolerance) {\r\n break;\r\n }\r\n \r\n const error = currentCOCR - targetCOCR;\r\n const adjustment = error * BUSINESS_CONSTANTS.ADJUSTMENT_FACTOR;\r\n \r\n if (error > 0) {\r\n targetPrice = targetPrice * (1 + Math.abs(adjustment));\r\n } else {\r\n targetPrice = targetPrice * (1 - Math.abs(adjustment));\r\n }\r\n \r\n // Reasonable bounds during iteration (prevent extreme values)\r\n if (targetPrice < 1000) targetPrice = 1000;\r\n if (targetPrice > noi * BUSINESS_CONSTANTS.MAX_COCR15_PRICE_MULTIPLIER) {\r\n targetPrice = noi * BUSINESS_CONSTANTS.CONSERVATIVE_COCR15_PRICE_MULTIPLIER;\r\n }\r\n \r\n iterations++;\r\n }\r\n \r\n // Apply final bounds check AFTER iteration\r\n if (targetPrice < BUSINESS_CONSTANTS.MINIMUM_COCR15_PRICE) {\r\n targetPrice = BUSINESS_CONSTANTS.MINIMUM_COCR15_PRICE;\r\n }\r\n \r\n return targetPrice;\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate COCR at a specific down payment percentage\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} noi - Net Operating Income (annual)\r\n * @param {number} downPercent - Down payment percentage\r\n * @param {Object} options - Configuration options (uses config constants as defaults)\r\n * @returns {number} COCR percentage\r\n */\r\nexport function calculateCOCRAtPercent(askingPrice, noi, downPercent, options = {}) {\r\n const {\r\n dscrLtvPercent = FINANCIAL_CONSTANTS.DEFAULT_DSCR_PERCENTAGE * 100,\r\n sellerFiPercent = 0,\r\n dscrRate = FINANCIAL_CONSTANTS.DSCR_INTEREST_RATE,\r\n dscrTerm = FINANCIAL_CONSTANTS.DSCR_AMORTIZATION,\r\n sellerFiTerm = FINANCIAL_CONSTANTS.SELLER_FI_AMORTIZATION\r\n } = options;\r\n\r\n try {\r\n const downDecimal = downPercent / 100;\r\n const cashInvested = askingPrice * downDecimal;\r\n \r\n // Fix financing structure: seller financing reduces available DSCR loan\r\n const sellerFiAmount = askingPrice * (sellerFiPercent / 100);\r\n const availableDscrPercent = Math.max(0, dscrLtvPercent - sellerFiPercent);\r\n const dscrLoanAmount = askingPrice * (availableDscrPercent / 100);\r\n \r\n const dscrPayment = calculatePMT(dscrLoanAmount, dscrRate, dscrTerm) * 12;\r\n const sfPayment = calculatePMT(sellerFiAmount, FINANCIAL_CONSTANTS.SELLER_FI_INTEREST_RATE, sellerFiTerm) * 12;\r\n \r\n const annualCashFlow = noi - dscrPayment - sfPayment;\r\n const cocr = (annualCashFlow / cashInvested) * 100;\r\n \r\n return cocr;\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate NOI based on property type\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} capRate - Cap rate as decimal (e.g., 0.08 for 8%)\r\n * @param {string} propertyType - Property type from PROPERTY_TYPES\r\n * @param {Object} options - Configuration options (uses config constants as defaults)\r\n * @returns {number} Calculated NOI\r\n */\r\nexport function calculateNOIByType(askingPrice, capRate, propertyType = PROPERTY_TYPES.MULTIFAMILY, options = {}) {\r\n const {\r\n strGrossIncomeMultiplier = PROPERTY_TYPE_CONSTANTS.STR.ESTIMATED_GROSS_RATE,\r\n strNoiPercentage = PROPERTY_TYPE_CONSTANTS.STR.NOI_PERCENTAGE,\r\n assistedIncomePerBedroom = PROPERTY_TYPE_CONSTANTS.ASSISTED_LIVING.INCOME_PER_BEDROOM_MONTHLY,\r\n bedroomCount = PROPERTY_TYPE_CONSTANTS.ASSISTED_LIVING.DEFAULT_BEDROOM_COUNT\r\n } = options;\r\n\r\n try {\r\n switch (propertyType.toLowerCase()) {\r\n case PROPERTY_TYPES.STR:\r\n const estimatedGrossIncome = askingPrice * strGrossIncomeMultiplier;\r\n return estimatedGrossIncome * strNoiPercentage;\r\n \r\n case PROPERTY_TYPES.ASSISTED_LIVING:\r\n return bedroomCount * assistedIncomePerBedroom * 12;\r\n \r\n case PROPERTY_TYPES.MULTIFAMILY:\r\n default:\r\n return askingPrice * capRate;\r\n }\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate assignment fee\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} assignmentPercent - Assignment fee percentage (uses config default)\r\n * @returns {number} Assignment fee amount\r\n */\r\nexport function calculateAssignmentFee(askingPrice, assignmentPercent = BUSINESS_CONSTANTS.ASSIGNMENT_FEE_PERCENTAGE * 100) {\r\n try {\r\n return askingPrice * (assignmentPercent / 100);\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate net to buyer\r\n * @param {number} askingPrice - Property asking price\r\n * @param {Object} options - Configuration options (uses config constants as defaults)\r\n * @returns {number} Net to buyer amount\r\n */\r\nexport function calculateNetToBuyer(askingPrice, options = {}) {\r\n const {\r\n buyerCostPercent = BUSINESS_CONSTANTS.NET_TO_BUYER_PERCENTAGE * 100,\r\n sellerCostAssignment = BUSINESS_CONSTANTS.ASSIGNMENT_FEE_PERCENTAGE * 100,\r\n sellerCostClosing = BUSINESS_CONSTANTS.CLOSING_COSTS_PERCENTAGE * 100,\r\n additionalCostRehab = BUSINESS_CONSTANTS.REHAB_PERCENTAGE * 100,\r\n additionalCostFinancing = BUSINESS_CONSTANTS.FINANCING_FEE_PERCENTAGE * 100,\r\n dscrLtvPercent = FINANCIAL_CONSTANTS.DEFAULT_DSCR_PERCENTAGE * 100\r\n } = options;\r\n\r\n try {\r\n const dscrLoanAmount = askingPrice * (dscrLtvPercent / 100);\r\n \r\n return askingPrice * (buyerCostPercent / 100) - \r\n askingPrice * ((sellerCostAssignment + sellerCostClosing) / 100) - \r\n askingPrice * (additionalCostRehab / 100) - \r\n (additionalCostFinancing / 100) * (askingPrice - dscrLoanAmount);\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate remaining loan balance at end of balloon period\r\n * @param {number} loanAmount - Initial loan amount\r\n * @param {number} interestRate - Annual interest rate as decimal (e.g., 0.075 for 7.5%)\r\n * @param {number} amortizationYears - Full amortization period in years\r\n * @param {number} balloonYears - Balloon period in years\r\n * @returns {number} Remaining balance at end of balloon period\r\n */\r\nexport function calculateBalloonBalance(loanAmount, interestRate, amortizationYears, balloonYears = FINANCIAL_CONSTANTS.DEFAULT_BALLOON_PERIOD_YEARS) {\r\n try {\r\n if (loanAmount <= 0 || interestRate < 0 || amortizationYears <= 0 || balloonYears <= 0) {\r\n return 0;\r\n }\r\n\r\n // If balloon period equals or exceeds amortization, loan is fully paid\r\n if (balloonYears >= amortizationYears) {\r\n return 0;\r\n }\r\n\r\n // Special handling for zero interest rate (simple linear paydown)\r\n if (interestRate === 0) {\r\n const totalPayments = amortizationYears * 12;\r\n const paymentsMade = balloonYears * 12;\r\n return loanAmount * (totalPayments - paymentsMade) / totalPayments;\r\n }\r\n\r\n const monthlyRate = interestRate / 12;\r\n const totalPayments = amortizationYears * 12;\r\n const balloonPayments = balloonYears * 12;\r\n\r\n // Calculate remaining balance using loan balance formula\r\n // Balance = P * [(1 + r)^n - (1 + r)^p] / [(1 + r)^n - 1]\r\n // Where P = principal, r = monthly rate, n = total payments, p = payments made\r\n \r\n const factor1 = Math.pow(1 + monthlyRate, totalPayments);\r\n const factor2 = Math.pow(1 + monthlyRate, balloonPayments);\r\n \r\n const remainingBalance = loanAmount * (factor1 - factor2) / (factor1 - 1);\r\n \r\n return Math.max(0, remainingBalance); // Ensure non-negative\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate property value after appreciation period\r\n * @param {number} currentValue - Current property value\r\n * @param {number} appreciationRate - Annual appreciation rate as decimal\r\n * @param {number} years - Number of years\r\n * @returns {number} Appreciated property value\r\n */\r\nexport function calculateAppreciatedValue(currentValue, appreciationRate = FINANCIAL_CONSTANTS.APPRECIATION_RATE, years = FINANCIAL_CONSTANTS.DEFAULT_BALLOON_PERIOD_YEARS) {\r\n try {\r\n if (currentValue <= 0 || appreciationRate < 0 || years < 0) {\r\n return currentValue;\r\n }\r\n \r\n return currentValue * Math.pow(1 + appreciationRate, years);\r\n } catch (error) {\r\n return currentValue;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate cash out amount after appreciation refinance\r\n * @param {number} originalPrice - Original purchase price\r\n * @param {number} dscrLoanAmount - Original DSCR loan amount \r\n * @param {number} sellerFiAmount - Original seller financing amount\r\n * @param {Object} options - Configuration options\r\n * @returns {number} Cash out amount (positive = cash out, negative = cash in)\r\n */\r\nexport function calculateCashOutAfterRefi(originalPrice, dscrLoanAmount, sellerFiAmount, options = {}) {\r\n const {\r\n appreciationRate = FINANCIAL_CONSTANTS.APPRECIATION_RATE,\r\n balloonYears = FINANCIAL_CONSTANTS.DEFAULT_BALLOON_PERIOD_YEARS,\r\n dscrRate = FINANCIAL_CONSTANTS.DSCR_INTEREST_RATE,\r\n dscrTerm = FINANCIAL_CONSTANTS.DSCR_AMORTIZATION,\r\n sellerFiTerm = FINANCIAL_CONSTANTS.SELLER_FI_AMORTIZATION,\r\n refiLtvPercent = 70 // 70% LTV on refi\r\n } = options;\r\n\r\n try {\r\n // Calculate appreciated property value\r\n const appreciatedValue = calculateAppreciatedValue(originalPrice, appreciationRate, balloonYears);\r\n \r\n // Calculate remaining balance on DSCR loan\r\n const dscrRemainingBalance = calculateBalloonBalance(dscrLoanAmount, dscrRate, dscrTerm, balloonYears);\r\n \r\n // Calculate remaining balance on seller financing (0% interest)\r\n const sellerFiRemainingBalance = calculateBalloonBalance(sellerFiAmount, FINANCIAL_CONSTANTS.SELLER_FI_INTEREST_RATE, sellerFiTerm, balloonYears);\r\n \r\n // Total remaining debt\r\n const totalRemainingDebt = dscrRemainingBalance + sellerFiRemainingBalance;\r\n \r\n // Calculate new loan amount at 70% LTV of appreciated value\r\n const newLoanAmount = appreciatedValue * (refiLtvPercent / 100);\r\n \r\n // Cash out = new loan - total remaining debt\r\n const cashOut = newLoanAmount - totalRemainingDebt;\r\n \r\n return cashOut;\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n// DSCR Payment calculation using existing calculatePMT\r\nexport function calculateDscrPayment(askingPrice, dscrPercent, dscrRate, dscrAmortization) {\r\n const dscrLoanAmount = askingPrice * (dscrPercent / 100);\r\n return calculatePMT(dscrLoanAmount, dscrRate, dscrAmortization);\r\n}\r\n\r\n// SF Payment calculation using existing calculatePMT \r\nexport function calculateSfPayment(askingPrice, sellerFiPercent, sellerFiRate, sellerFiAmortization) {\r\n const sellerFiAmount = askingPrice * (sellerFiPercent / 100);\r\n return calculatePMT(sellerFiAmount, sellerFiRate, sellerFiAmortization);\r\n}\r\n\r\n// Cash Flow calculation (matching loopnet-analyzer exactly)\r\nexport function calculateCashFlow(monthlyNOI, dscrPayment, sfPayment) {\r\n return monthlyNOI - (dscrPayment + sfPayment);\r\n}\r\n\r\n/**\r\n * Calculate discount percentage from asking price and offered price\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} priceOffered - Offered price\r\n * @returns {number} Discount as decimal (positive = discount, negative = premium)\r\n */\r\nexport function calculateDiscountFromPrice(askingPrice, priceOffered) {\r\n if (!askingPrice || askingPrice <= 0) return 0;\r\n \r\n return (askingPrice - priceOffered) / askingPrice;\r\n}\r\n\r\n/**\r\n * Calculate price from asking price and discount percentage\r\n * @param {number} askingPrice - Property asking price \r\n * @param {number} discountPercent - Discount as decimal (positive = discount, negative = premium)\r\n * @returns {number} Calculated price\r\n */\r\nexport function calculatePriceFromDiscount(askingPrice, discountPercent) {\r\n if (!askingPrice || askingPrice <= 0) return 0;\r\n \r\n return askingPrice * (1 - discountPercent);\r\n}"],"names":["calculatePMT","principal","annualRate","years","monthlyRate","numPayments","Math","pow","calculateCOCR30","askingPrice","noi","cashInvested","dscrPayment","error","calculateCashFlowYield","monthlyCashFlow","purchasePrice","calculatePriceForCOCR","targetCOCR","options","downPercent","FINANCIAL_CONSTANTS","DEFAULT_DOWN_PAYMENT","dscrLtvPercent","DEFAULT_DSCR_PERCENTAGE","dscrRate","DSCR_INTEREST_RATE","dscrTerm","DSCR_AMORTIZATION","maxIterations","BUSINESS_CONSTANTS","MAX_ITERATIONS","tolerance","CALCULATION_TOLERANCE","targetPrice","iterations","currentCOCR","abs","adjustment","ADJUSTMENT_FACTOR","MAX_COCR15_PRICE_MULTIPLIER","CONSERVATIVE_COCR15_PRICE_MULTIPLIER","MINIMUM_COCR15_PRICE","calculateCOCRAtPercent","sellerFiPercent","sellerFiTerm","SELLER_FI_AMORTIZATION","sellerFiAmount","availableDscrPercent","max","sfPayment","SELLER_FI_INTEREST_RATE","calculateNOIByType","capRate","propertyType","PROPERTY_TYPES","MULTIFAMILY","strGrossIncomeMultiplier","PROPERTY_TYPE_CONSTANTS","STR","ESTIMATED_GROSS_RATE","strNoiPercentage","NOI_PERCENTAGE","assistedIncomePerBedroom","ASSISTED_LIVING","INCOME_PER_BEDROOM_MONTHLY","bedroomCount","DEFAULT_BEDROOM_COUNT","toLowerCase","calculateAssignmentFee","assignmentPercent","ASSIGNMENT_FEE_PERCENTAGE","calculateNetToBuyer","buyerCostPercent","NET_TO_BUYER_PERCENTAGE","sellerCostAssignment","sellerCostClosing","CLOSING_COSTS_PERCENTAGE","additionalCostRehab","REHAB_PERCENTAGE","additionalCostFinancing","FINANCING_FEE_PERCENTAGE","calculateBalloonBalance","loanAmount","interestRate","amortizationYears","balloonYears","DEFAULT_BALLOON_PERIOD_YEARS","totalPayments","balloonPayments","factor1","remainingBalance","calculateAppreciatedValue","currentValue","appreciationRate","APPRECIATION_RATE","calculateCashOutAfterRefi","originalPrice","dscrLoanAmount","refiLtvPercent","appreciatedValue","dscrRemainingBalance","sellerFiRemainingBalance","calculateDscrPayment","dscrPercent","dscrAmortization","calculateSfPayment","sellerFiRate","sellerFiAmortization","calculateCashFlow","monthlyNOI","calculateDiscountFromPrice","priceOffered","calculatePriceFromDiscount","discountPercent"],"mappings":"kNAcO,SAASA,aAAaC,EAAWC,EAAYC,GAClD,GAAmB,IAAfD,EACF,OAAOD,GAAqB,GAARE,GAGtB,MAAMC,EAAcF,EAAa,GAC3BG,EAAsB,GAARF,EAGpB,OAFYF,GAAaG,EAAcE,KAAKC,IAAI,EAAIH,EAAaC,KACpDC,KAAKC,IAAI,EAAIH,EAAaC,GAAe,EAExD,CAEO,SAASG,gBAAgBC,EAAaC,GAC3C,IACE,MAAMC,EAA6B,GAAdF,EAEfG,EAAwD,GAA1CZ,aADiB,GAAdS,EAC0B,KAAO,IAIxD,OAHuBC,EAAME,GACED,EAAgB,GAGjD,CAAE,MAAOE,GACP,OAAO,CACT,CACF,CAEO,SAASC,uBAAuBC,EAAiBC,GACtD,IAAKA,GAAiBA,GAAiB,EAAG,OAAO,EAEjD,OADyC,GAAlBD,EACEC,EAAiB,GAC5C,CAUO,SAASC,sBAAsBP,EAAKQ,EAAa,IAAMC,EAAU,CAAA,GACtE,MAAMC,YACJA,EAAyD,IAA3CC,EAAoBC,qBAA0BC,eAC5DA,EAA+D,IAA9CF,EAAoBG,wBAA6BC,SAClEA,EAAWJ,EAAoBK,mBAAkBC,SACjDA,EAAWN,EAAoBO,kBAAiBC,cAChDA,EAAgBC,EAAmBC,eAAcC,UACjDA,EAAYF,EAAmBG,uBAC7Bd,EAEJ,IACE,IAAIe,EAAcxB,EAAM,IACpByB,EAAa,EAEjB,KAAOA,EAAaN,GAAe,CACjC,MAAMlB,EAAeuB,GAAed,EAAc,KAE5CR,EAAiE,GAAnDZ,aADGkC,GAAeX,EAAiB,KACNE,EAAUE,GAErDS,GADiB1B,EAAME,GACQD,EAErC,GAAIL,KAAK+B,IAAID,EAAclB,GAAcc,EACvC,MAGF,MAAMnB,EAAQuB,EAAclB,EACtBoB,EAAazB,EAAQiB,EAAmBS,kBAG5CL,GADErB,EAAQ,EACmB,EAAIP,KAAK+B,IAAIC,GAEb,EAAIhC,KAAK+B,IAAIC,GAIxCJ,EAAc,MAAMA,EAAc,KAClCA,EAAcxB,EAAMoB,EAAmBU,8BACzCN,EAAcxB,EAAMoB,EAAmBW,sCAGzCN,GACF,CAOA,OAJID,EAAcJ,EAAmBY,uBACnCR,EAAcJ,EAAmBY,sBAG5BR,CACT,CAAE,MAAOrB,GACP,OAAO,CACT,CACF,CAUO,SAAS8B,uBAAuBlC,EAAaC,EAAKU,EAAaD,EAAU,CAAA,GAC9E,MAAMI,eACJA,EAA+D,IAA9CF,EAAoBG,wBAA6BoB,gBAClEA,EAAkB,EAACnB,SACnBA,EAAWJ,EAAoBK,mBAAkBC,SACjDA,EAAWN,EAAoBO,kBAAiBiB,aAChDA,EAAexB,EAAoByB,wBACjC3B,EAEJ,IACE,MACMR,EAAeF,GADDW,EAAc,KAI5B2B,EAAiBtC,GAAemC,EAAkB,KAClDI,EAAuB1C,KAAK2C,IAAI,EAAG1B,EAAiBqB,GAGpDhC,EAAiE,GAAnDZ,aAFGS,GAAeuC,EAAuB,KAEZvB,EAAUE,GACrDuB,EAAsG,GAA1FlD,aAAa+C,EAAgB1B,EAAoB8B,wBAAyBN,GAK5F,OAHuBnC,EAAME,EAAcsC,GACZvC,EAAgB,GAGjD,CAAE,MAAOE,GACP,OAAO,CACT,CACF,CAUO,SAASuC,mBAAmB3C,EAAa4C,EAASC,EAAeC,EAAeC,YAAarC,EAAU,IAC5G,MAAMsC,yBACJA,EAA2BC,EAAwBC,IAAIC,qBAAoBC,iBAC3EA,EAAmBH,EAAwBC,IAAIG,eAAcC,yBAC7DA,EAA2BL,EAAwBM,gBAAgBC,2BAA0BC,aAC7FA,EAAeR,EAAwBM,gBAAgBG,uBACrDhD,EAEJ,IACE,OAAQmC,EAAac,eACnB,KAAKb,EAAeI,IAElB,OAD6BlD,EAAcgD,EACbI,EAEhC,KAAKN,EAAeS,gBAClB,OAAOE,EAAeH,EAA2B,GAEnD,KAAKR,EAAeC,YACpB,QACE,OAAO/C,EAAc4C,EAE3B,CAAE,MAAOxC,GACP,OAAO,CACT,CACF,CAQO,SAASwD,uBAAuB5D,EAAa6D,EAAmE,IAA/CxC,EAAmByC,2BACzF,IACE,OAAO9D,GAAe6D,EAAoB,IAC5C,CAAE,MAAOzD,GACP,OAAO,CACT,CACF,CAQO,SAAS2D,oBAAoB/D,EAAaU,EAAU,IACzD,MAAMsD,iBACJA,EAAgE,IAA7C3C,EAAmB4C,wBAA6BC,qBACnEA,EAAsE,IAA/C7C,EAAmByC,0BAA+BK,kBACzEA,EAAkE,IAA9C9C,EAAmB+C,yBAA8BC,oBACrEA,EAA4D,IAAtChD,EAAmBiD,iBAAsBC,wBAC/DA,EAAwE,IAA9ClD,EAAmBmD,yBAA8B1D,eAC3EA,EAA+D,IAA9CF,EAAoBG,yBACnCL,EAEJ,IAGE,OAAOV,GAAegE,EAAmB,KAClChE,IAAgBkE,EAAuBC,GAAqB,KAC5DnE,GAAeqE,EAAsB,KACpCE,EAA0B,KAAQvE,EALnBA,GAAec,EAAiB,KAMzD,CAAE,MAAOV,GACP,OAAO,CACT,CACF,CAUO,SAASqE,wBAAwBC,EAAYC,EAAcC,EAAmBC,EAAejE,EAAoBkE,8BACtH,IACE,GAAIJ,GAAc,GAAKC,EAAe,GAAKC,GAAqB,GAAKC,GAAgB,EACnF,OAAO,EAIT,GAAIA,GAAgBD,EAClB,OAAO,EAIT,GAAqB,IAAjBD,EAAoB,CACtB,MAAMI,EAAoC,GAApBH,EAEtB,OAAOF,GAAcK,EADe,GAAfF,GACgCE,CACvD,CAEA,MAAMpF,EAAcgF,EAAe,GAC7BI,EAAoC,GAApBH,EAChBI,EAAiC,GAAfH,EAMlBI,EAAUpF,KAAKC,IAAI,EAAIH,EAAaoF,GAGpCG,EAAmBR,GAAcO,EAFvBpF,KAAKC,IAAI,EAAIH,EAAaqF,KAEmBC,EAAU,GAEvE,OAAOpF,KAAK2C,IAAI,EAAG0C,EACrB,CAAE,MAAO9E,GACP,OAAO,CACT,CACF,CASO,SAAS+E,0BAA0BC,EAAcC,EAAmBzE,EAAoB0E,kBAAmB5F,EAAQkB,EAAoBkE,8BAC5I,IACE,OAAIM,GAAgB,GAAKC,EAAmB,GAAK3F,EAAQ,EAChD0F,EAGFA,EAAevF,KAAKC,IAAI,EAAIuF,EAAkB3F,EACvD,CAAE,MAAOU,GACP,OAAOgF,CACT,CACF,CAUO,SAASG,0BAA0BC,EAAeC,EAAgBnD,EAAgB5B,EAAU,CAAA,GACjG,MAAM2E,iBACJA,EAAmBzE,EAAoB0E,kBAAiBT,aACxDA,EAAejE,EAAoBkE,6BAA4B9D,SAC/DA,EAAWJ,EAAoBK,mBAAkBC,SACjDA,EAAWN,EAAoBO,kBAAiBiB,aAChDA,EAAexB,EAAoByB,uBAAsBqD,eACzDA,EAAiB,IACfhF,EAEJ,IAEE,MAAMiF,EAAmBR,0BAA0BK,EAAeH,EAAkBR,GAG9Ee,EAAuBnB,wBAAwBgB,EAAgBzE,EAAUE,EAAU2D,GAGnFgB,EAA2BpB,wBAAwBnC,EAAgB1B,EAAoB8B,wBAAyBN,EAAcyC,GAWpI,OALsBc,GAAoBD,EAAiB,MAHhCE,EAAuBC,EASpD,CAAE,MAAOzF,GACP,OAAO,CACT,CACF,CAGO,SAAS0F,qBAAqB9F,EAAa+F,EAAa/E,EAAUgF,GAEvE,OAAOzG,aADgBS,GAAe+F,EAAc,KAChB/E,EAAUgF,EAChD,CAGO,SAASC,mBAAmBjG,EAAamC,EAAiB+D,EAAcC,GAE7E,OAAO5G,aADgBS,GAAemC,EAAkB,KACpB+D,EAAcC,EACpD,CAGO,SAASC,kBAAkBC,EAAYlG,EAAasC,GACzD,OAAO4D,GAAclG,EAAcsC,EACrC,CAQO,SAAS6D,2BAA2BtG,EAAauG,GACtD,OAAKvG,GAAeA,GAAe,EAAU,GAErCA,EAAcuG,GAAgBvG,CACxC,CAQO,SAASwG,2BAA2BxG,EAAayG,GACtD,OAAKzG,GAAeA,GAAe,EAAU,EAEtCA,GAAe,EAAIyG,EAC5B"}
|
|
1
|
+
{"version":3,"file":"calculations.js","sources":["../../src/financial/calculations.js"],"sourcesContent":["// src/financial/calculations.js\r\n\r\nimport { FINANCIAL_CONSTANTS } from '../config/financial.js';\r\nimport { BUSINESS_CONSTANTS } from '../config/business.js';\r\nimport { PROPERTY_TYPE_CONSTANTS, PROPERTY_TYPES } from '../config/property-types.js';\r\n\r\n\r\n/**\r\n * PMT function for loan payment calculation\r\n * @param {number} principal - Loan principal amount \r\n * @param {number} annualRate - Annual interest rate (as decimal, e.g., 0.075 for 7.5%)\r\n * @param {number} years - Loan term in years\r\n * @returns {number} Monthly payment amount\r\n */\r\nexport function calculatePMT(principal, annualRate, years) {\r\n if (annualRate === 0) {\r\n return principal / (years * 12);\r\n }\r\n \r\n const monthlyRate = annualRate / 12;\r\n const numPayments = years * 12;\r\n const pmt = principal * (monthlyRate * Math.pow(1 + monthlyRate, numPayments)) / \r\n (Math.pow(1 + monthlyRate, numPayments) - 1);\r\n return pmt;\r\n}\r\n\r\nexport function calculateCOCR30(askingPrice, noi) {\r\n try {\r\n const cashInvested = askingPrice * 0.30; // 30% down payment\r\n const dscrLoanAmount = askingPrice * 0.70; // Fixed 70% DSCR loan\r\n const dscrPayment = calculatePMT(dscrLoanAmount, 0.075, 30) * 12; // Annual DSCR payment\r\n const annualCashFlow = noi - dscrPayment;\r\n const cocr = (annualCashFlow / cashInvested) * 100;\r\n \r\n return cocr;\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\nexport function calculateCashFlowYield(monthlyCashFlow, purchasePrice) {\r\n if (!purchasePrice || purchasePrice <= 0) return 0;\r\n const annualCashFlow = monthlyCashFlow * 12;\r\n return (annualCashFlow / purchasePrice) * 100;\r\n}\r\n\r\n\r\n/**\r\n * Calculate the property price that yields a target COCR percentage\r\n * @param {number} noi - Net Operating Income (annual)\r\n * @param {number} targetCOCR - Target COCR as decimal (default: 0.15 for 15%)\r\n * @param {Object} options - Configuration options (uses config constants as defaults)\r\n * @returns {number} Calculated property price\r\n */\r\nexport function calculatePriceForCOCR(noi, targetCOCR = 0.15, options = {}) {\r\n const {\r\n downPercent = FINANCIAL_CONSTANTS.DEFAULT_DOWN_PAYMENT * 100,\r\n dscrLtvPercent = FINANCIAL_CONSTANTS.DEFAULT_DSCR_PERCENTAGE * 100,\r\n dscrRate = FINANCIAL_CONSTANTS.DSCR_INTEREST_RATE,\r\n dscrTerm = FINANCIAL_CONSTANTS.DSCR_AMORTIZATION,\r\n maxIterations = BUSINESS_CONSTANTS.MAX_ITERATIONS,\r\n tolerance = BUSINESS_CONSTANTS.CALCULATION_TOLERANCE\r\n } = options;\r\n\r\n try {\r\n let targetPrice = noi / 0.08; // Initial estimate: NOI / 8% cap rate\r\n let iterations = 0;\r\n \r\n while (iterations < maxIterations) {\r\n const cashInvested = targetPrice * (downPercent / 100);\r\n const dscrLoanAmount = targetPrice * (dscrLtvPercent / 100);\r\n const dscrPayment = calculatePMT(dscrLoanAmount, dscrRate, dscrTerm) * 12;\r\n const annualCashFlow = noi - dscrPayment;\r\n const currentCOCR = annualCashFlow / cashInvested;\r\n \r\n if (Math.abs(currentCOCR - targetCOCR) < tolerance) {\r\n break;\r\n }\r\n \r\n const error = currentCOCR - targetCOCR;\r\n const adjustment = error * BUSINESS_CONSTANTS.ADJUSTMENT_FACTOR;\r\n \r\n if (error > 0) {\r\n targetPrice = targetPrice * (1 + Math.abs(adjustment));\r\n } else {\r\n targetPrice = targetPrice * (1 - Math.abs(adjustment));\r\n }\r\n \r\n // Reasonable bounds during iteration (prevent extreme values)\r\n if (targetPrice < 1000) targetPrice = 1000;\r\n if (targetPrice > noi * BUSINESS_CONSTANTS.MAX_COCR15_PRICE_MULTIPLIER) {\r\n targetPrice = noi * BUSINESS_CONSTANTS.CONSERVATIVE_COCR15_PRICE_MULTIPLIER;\r\n }\r\n \r\n iterations++;\r\n }\r\n \r\n // Apply final bounds check AFTER iteration\r\n if (targetPrice < BUSINESS_CONSTANTS.MINIMUM_COCR15_PRICE) {\r\n targetPrice = BUSINESS_CONSTANTS.MINIMUM_COCR15_PRICE;\r\n }\r\n \r\n return targetPrice;\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate COCR at a specific down payment percentage\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} noi - Net Operating Income (annual)\r\n * @param {number} downPercent - Down payment percentage\r\n * @param {Object} options - Configuration options (uses config constants as defaults)\r\n * @returns {number} COCR percentage\r\n */\r\nexport function calculateCOCRAtPercent(askingPrice, noi, downPercent, options = {}) {\r\n const {\r\n dscrRate = FINANCIAL_CONSTANTS.DSCR_INTEREST_RATE,\r\n dscrTerm = FINANCIAL_CONSTANTS.DSCR_AMORTIZATION,\r\n } = options;\r\n\r\n try {\r\n const downDecimal = downPercent / 100;\r\n const cashInvested = askingPrice * downDecimal;\r\n \r\n // Fix financing structure: seller financing reduces available DSCR loan\r\n const dscrLoanAmount = askingPrice - cashInvested;\r\n \r\n const dscrPayment = calculatePMT(dscrLoanAmount, dscrRate, dscrTerm) * 12;\r\n \r\n const annualCashFlow = noi - dscrPayment;\r\n const cocr = (annualCashFlow / cashInvested) * 100;\r\n \r\n return cocr;\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate NOI based on property type\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} capRate - Cap rate as decimal (e.g., 0.08 for 8%)\r\n * @param {string} propertyType - Property type from PROPERTY_TYPES\r\n * @param {Object} options - Configuration options (uses config constants as defaults)\r\n * @returns {number} Calculated NOI\r\n */\r\nexport function calculateNOIByType(askingPrice, capRate, propertyType = PROPERTY_TYPES.MULTIFAMILY, options = {}) {\r\n const {\r\n strGrossIncomeMultiplier = PROPERTY_TYPE_CONSTANTS.STR.ESTIMATED_GROSS_RATE,\r\n strNoiPercentage = PROPERTY_TYPE_CONSTANTS.STR.NOI_PERCENTAGE,\r\n assistedIncomePerBedroom = PROPERTY_TYPE_CONSTANTS.ASSISTED_LIVING.INCOME_PER_BEDROOM_MONTHLY,\r\n bedroomCount = PROPERTY_TYPE_CONSTANTS.ASSISTED_LIVING.DEFAULT_BEDROOM_COUNT\r\n } = options;\r\n\r\n try {\r\n switch (propertyType.toLowerCase()) {\r\n case PROPERTY_TYPES.STR:\r\n const estimatedGrossIncome = askingPrice * strGrossIncomeMultiplier;\r\n return estimatedGrossIncome * strNoiPercentage;\r\n \r\n case PROPERTY_TYPES.ASSISTED_LIVING:\r\n return bedroomCount * assistedIncomePerBedroom * 12;\r\n \r\n case PROPERTY_TYPES.MULTIFAMILY:\r\n default:\r\n return askingPrice * capRate;\r\n }\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate assignment fee\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} assignmentPercent - Assignment fee percentage (uses config default)\r\n * @returns {number} Assignment fee amount\r\n */\r\nexport function calculateAssignmentFee(askingPrice, assignmentPercent = BUSINESS_CONSTANTS.ASSIGNMENT_FEE_PERCENTAGE * 100) {\r\n try {\r\n return askingPrice * (assignmentPercent / 100);\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate net to buyer\r\n * @param {number} askingPrice - Property asking price\r\n * @param {Object} options - Configuration options (uses config constants as defaults)\r\n * @returns {number} Net to buyer amount\r\n */\r\nexport function calculateNetToBuyer(askingPrice, options = {}) {\r\n const {\r\n buyerCostPercent = BUSINESS_CONSTANTS.NET_TO_BUYER_PERCENTAGE * 100,\r\n sellerCostAssignment = BUSINESS_CONSTANTS.ASSIGNMENT_FEE_PERCENTAGE * 100,\r\n sellerCostClosing = BUSINESS_CONSTANTS.CLOSING_COSTS_PERCENTAGE * 100,\r\n additionalCostRehab = BUSINESS_CONSTANTS.REHAB_PERCENTAGE * 100,\r\n additionalCostFinancing = BUSINESS_CONSTANTS.FINANCING_FEE_PERCENTAGE * 100,\r\n dscrLtvPercent = FINANCIAL_CONSTANTS.DEFAULT_DSCR_PERCENTAGE * 100\r\n } = options;\r\n\r\n try {\r\n const dscrLoanAmount = askingPrice * (dscrLtvPercent / 100);\r\n \r\n return askingPrice * (buyerCostPercent / 100) - \r\n askingPrice * ((sellerCostAssignment + sellerCostClosing) / 100) - \r\n askingPrice * (additionalCostRehab / 100) - \r\n (additionalCostFinancing / 100) * (askingPrice - dscrLoanAmount);\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate remaining loan balance at end of balloon period\r\n * @param {number} loanAmount - Initial loan amount\r\n * @param {number} interestRate - Annual interest rate as decimal (e.g., 0.075 for 7.5%)\r\n * @param {number} amortizationYears - Full amortization period in years\r\n * @param {number} balloonYears - Balloon period in years\r\n * @returns {number} Remaining balance at end of balloon period\r\n */\r\nexport function calculateBalloonBalance(loanAmount, interestRate, amortizationYears, balloonYears = FINANCIAL_CONSTANTS.DEFAULT_BALLOON_PERIOD_YEARS) {\r\n try {\r\n if (loanAmount <= 0 || interestRate < 0 || amortizationYears <= 0 || balloonYears <= 0) {\r\n return 0;\r\n }\r\n\r\n // If balloon period equals or exceeds amortization, loan is fully paid\r\n if (balloonYears >= amortizationYears) {\r\n return 0;\r\n }\r\n\r\n // Special handling for zero interest rate (simple linear paydown)\r\n if (interestRate === 0) {\r\n const totalPayments = amortizationYears * 12;\r\n const paymentsMade = balloonYears * 12;\r\n return loanAmount * (totalPayments - paymentsMade) / totalPayments;\r\n }\r\n\r\n const monthlyRate = interestRate / 12;\r\n const totalPayments = amortizationYears * 12;\r\n const balloonPayments = balloonYears * 12;\r\n\r\n // Calculate remaining balance using loan balance formula\r\n // Balance = P * [(1 + r)^n - (1 + r)^p] / [(1 + r)^n - 1]\r\n // Where P = principal, r = monthly rate, n = total payments, p = payments made\r\n \r\n const factor1 = Math.pow(1 + monthlyRate, totalPayments);\r\n const factor2 = Math.pow(1 + monthlyRate, balloonPayments);\r\n \r\n const remainingBalance = loanAmount * (factor1 - factor2) / (factor1 - 1);\r\n \r\n return Math.max(0, remainingBalance); // Ensure non-negative\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate property value after appreciation period\r\n * @param {number} currentValue - Current property value\r\n * @param {number} appreciationRate - Annual appreciation rate as decimal\r\n * @param {number} years - Number of years\r\n * @returns {number} Appreciated property value\r\n */\r\nexport function calculateAppreciatedValue(currentValue, appreciationRate = FINANCIAL_CONSTANTS.APPRECIATION_RATE, years = FINANCIAL_CONSTANTS.DEFAULT_BALLOON_PERIOD_YEARS) {\r\n try {\r\n if (currentValue <= 0 || appreciationRate < 0 || years < 0) {\r\n return currentValue;\r\n }\r\n \r\n return currentValue * Math.pow(1 + appreciationRate, years);\r\n } catch (error) {\r\n return currentValue;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate cash out amount after appreciation refinance\r\n * @param {number} originalPrice - Original purchase price\r\n * @param {number} dscrLoanAmount - Original DSCR loan amount \r\n * @param {number} sellerFiAmount - Original seller financing amount\r\n * @param {Object} options - Configuration options\r\n * @returns {number} Cash out amount (positive = cash out, negative = cash in)\r\n */\r\nexport function calculateCashOutAfterRefi(originalPrice, dscrLoanAmount, sellerFiAmount, options = {}) {\r\n const {\r\n appreciationRate = FINANCIAL_CONSTANTS.APPRECIATION_RATE,\r\n balloonYears = FINANCIAL_CONSTANTS.DEFAULT_BALLOON_PERIOD_YEARS,\r\n dscrRate = FINANCIAL_CONSTANTS.DSCR_INTEREST_RATE,\r\n dscrTerm = FINANCIAL_CONSTANTS.DSCR_AMORTIZATION,\r\n sellerFiTerm = FINANCIAL_CONSTANTS.SELLER_FI_AMORTIZATION,\r\n refiLtvPercent = 70 // 70% LTV on refi\r\n } = options;\r\n\r\n try {\r\n // Calculate appreciated property value\r\n const appreciatedValue = calculateAppreciatedValue(originalPrice, appreciationRate, balloonYears);\r\n \r\n // Calculate remaining balance on DSCR loan\r\n const dscrRemainingBalance = calculateBalloonBalance(dscrLoanAmount, dscrRate, dscrTerm, balloonYears);\r\n \r\n // Calculate remaining balance on seller financing (0% interest)\r\n const sellerFiRemainingBalance = calculateBalloonBalance(sellerFiAmount, FINANCIAL_CONSTANTS.SELLER_FI_INTEREST_RATE, sellerFiTerm, balloonYears);\r\n \r\n // Total remaining debt\r\n const totalRemainingDebt = dscrRemainingBalance + sellerFiRemainingBalance;\r\n \r\n // Calculate new loan amount at 70% LTV of appreciated value\r\n const newLoanAmount = appreciatedValue * (refiLtvPercent / 100);\r\n \r\n // Cash out = new loan - total remaining debt\r\n const cashOut = newLoanAmount - totalRemainingDebt;\r\n \r\n return cashOut;\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n// DSCR Payment calculation using existing calculatePMT\r\nexport function calculateDscrPayment(askingPrice, dscrPercent, dscrRate, dscrAmortization) {\r\n const dscrLoanAmount = askingPrice * (dscrPercent / 100);\r\n return calculatePMT(dscrLoanAmount, dscrRate, dscrAmortization);\r\n}\r\n\r\n// SF Payment calculation using existing calculatePMT \r\nexport function calculateSfPayment(askingPrice, sellerFiPercent, sellerFiRate, sellerFiAmortization) {\r\n const sellerFiAmount = askingPrice * (sellerFiPercent / 100);\r\n return calculatePMT(sellerFiAmount, sellerFiRate, sellerFiAmortization);\r\n}\r\n\r\n// Cash Flow calculation (matching loopnet-analyzer exactly)\r\nexport function calculateCashFlow(monthlyNOI, dscrPayment, sfPayment) {\r\n return monthlyNOI - (dscrPayment + sfPayment);\r\n}\r\n\r\n/**\r\n * Calculate discount percentage from asking price and offered price\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} priceOffered - Offered price\r\n * @returns {number} Discount as decimal (positive = discount, negative = premium)\r\n */\r\nexport function calculateDiscountFromPrice(askingPrice, priceOffered) {\r\n if (!askingPrice || askingPrice <= 0) return 0;\r\n \r\n return (askingPrice - priceOffered) / askingPrice;\r\n}\r\n\r\n/**\r\n * Calculate price from asking price and discount percentage\r\n * @param {number} askingPrice - Property asking price \r\n * @param {number} discountPercent - Discount as decimal (positive = discount, negative = premium)\r\n * @returns {number} Calculated price\r\n */\r\nexport function calculatePriceFromDiscount(askingPrice, discountPercent) {\r\n if (!askingPrice || askingPrice <= 0) return 0;\r\n \r\n return askingPrice * (1 - discountPercent);\r\n}\r\n\r\nexport function safePercentage(value, fallback = 100) {\r\n return (value != null && !isNaN(value)) ? (value * 100) : fallback;\r\n}"],"names":["calculatePMT","principal","annualRate","years","monthlyRate","numPayments","Math","pow","calculateCOCR30","askingPrice","noi","cashInvested","dscrPayment","error","calculateCashFlowYield","monthlyCashFlow","purchasePrice","calculatePriceForCOCR","targetCOCR","options","downPercent","FINANCIAL_CONSTANTS","DEFAULT_DOWN_PAYMENT","dscrLtvPercent","DEFAULT_DSCR_PERCENTAGE","dscrRate","DSCR_INTEREST_RATE","dscrTerm","DSCR_AMORTIZATION","maxIterations","BUSINESS_CONSTANTS","MAX_ITERATIONS","tolerance","CALCULATION_TOLERANCE","targetPrice","iterations","currentCOCR","abs","adjustment","ADJUSTMENT_FACTOR","MAX_COCR15_PRICE_MULTIPLIER","CONSERVATIVE_COCR15_PRICE_MULTIPLIER","MINIMUM_COCR15_PRICE","calculateCOCRAtPercent","calculateNOIByType","capRate","propertyType","PROPERTY_TYPES","MULTIFAMILY","strGrossIncomeMultiplier","PROPERTY_TYPE_CONSTANTS","STR","ESTIMATED_GROSS_RATE","strNoiPercentage","NOI_PERCENTAGE","assistedIncomePerBedroom","ASSISTED_LIVING","INCOME_PER_BEDROOM_MONTHLY","bedroomCount","DEFAULT_BEDROOM_COUNT","toLowerCase","calculateAssignmentFee","assignmentPercent","ASSIGNMENT_FEE_PERCENTAGE","calculateNetToBuyer","buyerCostPercent","NET_TO_BUYER_PERCENTAGE","sellerCostAssignment","sellerCostClosing","CLOSING_COSTS_PERCENTAGE","additionalCostRehab","REHAB_PERCENTAGE","additionalCostFinancing","FINANCING_FEE_PERCENTAGE","calculateBalloonBalance","loanAmount","interestRate","amortizationYears","balloonYears","DEFAULT_BALLOON_PERIOD_YEARS","totalPayments","balloonPayments","factor1","remainingBalance","max","calculateAppreciatedValue","currentValue","appreciationRate","APPRECIATION_RATE","calculateCashOutAfterRefi","originalPrice","dscrLoanAmount","sellerFiAmount","sellerFiTerm","SELLER_FI_AMORTIZATION","refiLtvPercent","appreciatedValue","dscrRemainingBalance","sellerFiRemainingBalance","SELLER_FI_INTEREST_RATE","calculateDscrPayment","dscrPercent","dscrAmortization","calculateSfPayment","sellerFiPercent","sellerFiRate","sellerFiAmortization","calculateCashFlow","monthlyNOI","sfPayment","calculateDiscountFromPrice","priceOffered","calculatePriceFromDiscount","discountPercent","safePercentage","value","fallback","isNaN"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatters.js","sources":["../../src/financial/formatters.js"],"sourcesContent":["export function formatCurrency(amount, isMonthly = false) {\n if (isNaN(amount) || !isFinite(amount)) return \"N/A\";\n \n const absAmount = Math.abs(amount);\n const isNegative = amount < 0;\n const prefix = isNegative ? \"-$\" : \"$\";\n \n if (isMonthly) {\n // For monthly payments, show full amount with commas\n return prefix + absAmount.toLocaleString(\"en-US\", { maximumFractionDigits: 0 });\n } else {\n // For larger amounts, use K/M notation with 2-3 decimal places as needed\n if (absAmount >= 1000000) {\n const millions = absAmount / 1000000;\n // Use 2-3 decimal places but remove unnecessary trailing zeros\n const formatted = millions.toFixed(3).replace(/\\.?0+$/, \"\");\n return prefix + formatted + \"M\";\n } else if (absAmount >= 1000) {\n const thousands = absAmount / 1000;\n // Use 2-3 decimal places but remove unnecessary trailing zeros \n const formatted = thousands.toFixed(3).replace(/\\.?0+$/, \"\");\n return prefix + formatted + \"K\";\n } else {\n return prefix + absAmount.toLocaleString(\"en-US\", { maximumFractionDigits: 0 });\n }\n }\n}\n\nexport function formatPriceValue(amount) {\n if (isNaN(amount) || !isFinite(amount)) return \"N/A\";\n \n const absAmount = Math.abs(amount);\n const isNegative = amount < 0;\n const prefix = isNegative ? \"-$\" : \"$\";\n \n if (absAmount >= 1000000) {\n return prefix + (absAmount / 1000000).toFixed(1) + \"M\";\n } else if (absAmount >= 1000) {\n return prefix + (absAmount / 1000).toFixed(0) + \"K\";\n } else {\n return prefix + absAmount.toLocaleString(\"en-US\", { maximumFractionDigits: 0 });\n }\n}\n\nexport function formatPercentage(percentage) {\n if (isNaN(percentage) || !isFinite(percentage)) return \"N/A\";\n return percentage.toFixed(1) + \"%\";\n}\n\n"],"names":["formatCurrency","amount","isMonthly","isNaN","isFinite","absAmount","Math","abs","prefix","toLocaleString","maximumFractionDigits","toFixed","replace","formatPriceValue","formatPercentage","percentage"],"mappings":"AAAO,SAASA,eAAeC,EAAQC,GAAY,GACjD,GAAIC,MAAMF,KAAYG,SAASH,GAAS,MAAO,MAE/C,MAAMI,EAAYC,KAAKC,IAAIN,GAErBO,EADaP,EAAS,EACA,KAAO,IAEnC,GAAIC,EAEF,OAAOM,EAASH,EAAUI,eAAe,QAAS,CAAEC,sBAAuB,IAG3E,GAAIL,GAAa,IAAS,CAIxB,OAAOG,GAHUH,EAAY,KAEFM,QAAQ,GAAGC,QAAQ,SAAU,IAC5B,GAC9B,CAAO,GAAIP,GAAa,IAAM,CAI5B,OAAOG,GAHWH,EAAY,KAEFM,QAAQ,GAAGC,QAAQ,SAAU,IAC7B,GAC9B,CACE,OAAOJ,EAASH,EAAUI,eAAe,QAAS,CAAEC,sBAAuB,GAGjF,CAEO,SAASG,iBAAiBZ,GAC/B,GAAIE,MAAMF,KAAYG,SAASH,GAAS,MAAO,MAE/C,MAAMI,EAAYC,KAAKC,IAAIN,GAErBO,EADaP,EAAS,EACA,KAAO,IAEnC,OAAII,GAAa,IACRG,GAAUH,EAAY,KAASM,QAAQ,GAAK,IAC1CN,GAAa,IACfG,GAAUH,EAAY,KAAMM,QAAQ,GAAK,IAEzCH,EAASH,EAAUI,eAAe,QAAS,CAAEC,sBAAuB,GAE/E,CAEO,SAASI,iBAAiBC,GAC/B,OAAIZ,MAAMY,KAAgBX,SAASW,GAAoB,MAChDA,EAAWJ,QAAQ,GAAK,GACjC"}
|
|
1
|
+
{"version":3,"file":"formatters.js","sources":["../../src/financial/formatters.js"],"sourcesContent":["export function formatCurrency(amount, isMonthly = false) {\r\n if (isNaN(amount) || !isFinite(amount)) return \"N/A\";\r\n \r\n const absAmount = Math.abs(amount);\r\n const isNegative = amount < 0;\r\n const prefix = isNegative ? \"-$\" : \"$\";\r\n \r\n if (isMonthly) {\r\n // For monthly payments, show full amount with commas\r\n return prefix + absAmount.toLocaleString(\"en-US\", { maximumFractionDigits: 0 });\r\n } else {\r\n // For larger amounts, use K/M notation with 2-3 decimal places as needed\r\n if (absAmount >= 1000000) {\r\n const millions = absAmount / 1000000;\r\n // Use 2-3 decimal places but remove unnecessary trailing zeros\r\n const formatted = millions.toFixed(3).replace(/\\.?0+$/, \"\");\r\n return prefix + formatted + \"M\";\r\n } else if (absAmount >= 1000) {\r\n const thousands = absAmount / 1000;\r\n // Use 2-3 decimal places but remove unnecessary trailing zeros \r\n const formatted = thousands.toFixed(3).replace(/\\.?0+$/, \"\");\r\n return prefix + formatted + \"K\";\r\n } else {\r\n return prefix + absAmount.toLocaleString(\"en-US\", { maximumFractionDigits: 0 });\r\n }\r\n }\r\n}\r\n\r\nexport function formatPriceValue(amount) {\r\n if (isNaN(amount) || !isFinite(amount)) return \"N/A\";\r\n \r\n const absAmount = Math.abs(amount);\r\n const isNegative = amount < 0;\r\n const prefix = isNegative ? \"-$\" : \"$\";\r\n \r\n if (absAmount >= 1000000) {\r\n return prefix + (absAmount / 1000000).toFixed(1) + \"M\";\r\n } else if (absAmount >= 1000) {\r\n return prefix + (absAmount / 1000).toFixed(0) + \"K\";\r\n } else {\r\n return prefix + absAmount.toLocaleString(\"en-US\", { maximumFractionDigits: 0 });\r\n }\r\n}\r\n\r\nexport function formatPercentage(percentage) {\r\n if (isNaN(percentage) || !isFinite(percentage)) return \"N/A\";\r\n return percentage.toFixed(1) + \"%\";\r\n}\r\n\r\n"],"names":["formatCurrency","amount","isMonthly","isNaN","isFinite","absAmount","Math","abs","prefix","toLocaleString","maximumFractionDigits","toFixed","replace","formatPriceValue","formatPercentage","percentage"],"mappings":"AAAO,SAASA,eAAeC,EAAQC,GAAY,GACjD,GAAIC,MAAMF,KAAYG,SAASH,GAAS,MAAO,MAE/C,MAAMI,EAAYC,KAAKC,IAAIN,GAErBO,EADaP,EAAS,EACA,KAAO,IAEnC,GAAIC,EAEF,OAAOM,EAASH,EAAUI,eAAe,QAAS,CAAEC,sBAAuB,IAG3E,GAAIL,GAAa,IAAS,CAIxB,OAAOG,GAHUH,EAAY,KAEFM,QAAQ,GAAGC,QAAQ,SAAU,IAC5B,GAC9B,CAAO,GAAIP,GAAa,IAAM,CAI5B,OAAOG,GAHWH,EAAY,KAEFM,QAAQ,GAAGC,QAAQ,SAAU,IAC7B,GAC9B,CACE,OAAOJ,EAASH,EAAUI,eAAe,QAAS,CAAEC,sBAAuB,GAGjF,CAEO,SAASG,iBAAiBZ,GAC/B,GAAIE,MAAMF,KAAYG,SAASH,GAAS,MAAO,MAE/C,MAAMI,EAAYC,KAAKC,IAAIN,GAErBO,EADaP,EAAS,EACA,KAAO,IAEnC,OAAII,GAAa,IACRG,GAAUH,EAAY,KAASM,QAAQ,GAAK,IAC1CN,GAAa,IACfG,GAAUH,EAAY,KAAMM,QAAQ,GAAK,IAEzCH,EAASH,EAAUI,eAAe,QAAS,CAAEC,sBAAuB,GAE/E,CAEO,SAASI,iBAAiBC,GAC/B,OAAIZ,MAAMY,KAAgBX,SAASW,GAAoB,MAChDA,EAAWJ,QAAQ,GAAK,GACjC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export{calculateAppreciatedValue,calculateAssignmentFee,calculateBalloonBalance,calculateCOCR30,calculateCOCRAtPercent,calculateCashFlow,calculateCashFlowYield,calculateCashOutAfterRefi,calculateDiscountFromPrice,calculateDscrPayment,calculateNOIByType,calculateNetToBuyer,calculatePMT,calculatePriceForCOCR,calculatePriceFromDiscount,calculateSfPayment}from"./financial/calculations.js";export{formatCurrency,formatPercentage,formatPriceValue}from"./financial/formatters.js";export{extractBedrooms,extractPhoneNumber}from"./data/extractors.js";export{calculateDOM}from"./date/utilities.js";export{calculateCursorPosition,extractNumericValue,filterNumericInput,formatInputDisplay,formatLiveInput,formatLiveNumber,parseNumericInput}from"./formatting/financial-formatting.js";export{normalizeWhitespace}from"./formatting/text.js";export{CALCULATION_TOLERANCE,DEFAULT_CAP_RATE,DEFAULT_DOWN_PAYMENT,DEFAULT_DSCR_PERCENTAGE,DSCR_AMORTIZATION,DSCR_INTEREST_RATE,FINANCIAL_CONSTANTS,MAX_ITERATIONS,SELLER_FI_AMORTIZATION,SELLER_FI_CARRY,SELLER_FI_DOWN_PAYMENT,SELLER_FI_INTEREST_RATE}from"./config/financial.js";export{ASSISTED_LIVING,MULTIFAMILY,PROPERTY_TYPES,PROPERTY_TYPE_CONSTANTS,STR}from"./config/property-types.js";export{ASSIGNMENT_FEE_PERCENTAGE,BUSINESS_CONSTANTS,BUYER_AGENT_COMMISSION,CLOSING_COSTS_PERCENTAGE,CONSERVATIVE_COCR15_PRICE_MULTIPLIER,FINANCING_FEE_PERCENTAGE,MAX_COCR15_PRICE_MULTIPLIER,MINIMUM_COCR15_PRICE,NET_TO_BUYER_PERCENTAGE,SELLER_AGENT_COMMISSION}from"./config/business.js";const e="./dist/styles/base.css";export{e as STYLES_PATH};
|
|
1
|
+
export{calculateAppreciatedValue,calculateAssignmentFee,calculateBalloonBalance,calculateCOCR30,calculateCOCRAtPercent,calculateCashFlow,calculateCashFlowYield,calculateCashOutAfterRefi,calculateDiscountFromPrice,calculateDscrPayment,calculateNOIByType,calculateNetToBuyer,calculatePMT,calculatePriceForCOCR,calculatePriceFromDiscount,calculateSfPayment,safePercentage}from"./financial/calculations.js";export{formatCurrency,formatPercentage,formatPriceValue}from"./financial/formatters.js";export{extractBedrooms,extractPhoneNumber}from"./data/extractors.js";export{calculateDOM}from"./date/utilities.js";export{calculateCursorPosition,extractNumericValue,filterNumericInput,formatInputDisplay,formatLiveInput,formatLiveNumber,parseNumericInput}from"./formatting/financial-formatting.js";export{normalizeWhitespace}from"./formatting/text.js";export{CALCULATION_TOLERANCE,DEFAULT_CAP_RATE,DEFAULT_DOWN_PAYMENT,DEFAULT_DSCR_PERCENTAGE,DSCR_AMORTIZATION,DSCR_INTEREST_RATE,FINANCIAL_CONSTANTS,MAX_ITERATIONS,SELLER_FI_AMORTIZATION,SELLER_FI_CARRY,SELLER_FI_DOWN_PAYMENT,SELLER_FI_INTEREST_RATE}from"./config/financial.js";export{ASSISTED_LIVING,MULTIFAMILY,PROPERTY_TYPES,PROPERTY_TYPE_CONSTANTS,STR}from"./config/property-types.js";export{ASSIGNMENT_FEE_PERCENTAGE,BUSINESS_CONSTANTS,BUYER_AGENT_COMMISSION,CLOSING_COSTS_PERCENTAGE,CONSERVATIVE_COCR15_PRICE_MULTIPLIER,FINANCING_FEE_PERCENTAGE,MAX_COCR15_PRICE_MULTIPLIER,MINIMUM_COCR15_PRICE,NET_TO_BUYER_PERCENTAGE,SELLER_AGENT_COMMISSION}from"./config/business.js";const e="./dist/styles/base.css";export{e as STYLES_PATH};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/index.js"],"sourcesContent":["/**\r\n * @archerjessop/utilities\r\n * Shared utilities for ArcherJessop property analysis tools\r\n */\r\n\r\n// Financial calculations\r\nexport { \r\n calculateAppreciatedValue,\r\n calculateAssignmentFee,\r\n calculateBalloonBalance,\r\n calculateCashFlow,\r\n calculateCashFlowYield,\r\n calculateCashOutAfterRefi,\r\n calculateCOCR30, \r\n calculateCOCRAtPercent,\r\n calculateDiscountFromPrice,\r\n calculateDscrPayment,\r\n calculateNetToBuyer,\r\n calculateNOIByType,\r\n calculatePMT, \r\n calculatePriceForCOCR,\r\n calculatePriceFromDiscount,\r\n calculateSfPayment,\r\n} from \"./financial/calculations.js\";\r\n\r\n// Financial formatters\r\nexport { formatCurrency, formatPriceValue, formatPercentage } from \"./financial/formatters.js\";\r\n\r\n// Data extractors\r\nexport { extractPhoneNumber, extractBedrooms } from \"./data/extractors.js\";\r\n\r\n// Date utilities\r\nexport { calculateDOM } from \"./date/utilities.js\";\r\n\r\n// Formatting utilities\r\nexport { \r\n calculateCursorPosition,\r\n extractNumericValue,\r\n filterNumericInput,\r\n formatInputDisplay,\r\n formatLiveInput,\r\n formatLiveNumber,\r\n parseNumericInput\r\n} from \"./formatting/financial-formatting.js\";\r\n\r\n// Text formatting utilities\r\nexport { normalizeWhitespace } from \"./formatting/text.js\";\r\n\r\n// Configuration constants\r\nexport * from './config/financial.js';\r\nexport * from './config/property-types.js';\r\nexport * from './config/business.js';\r\n\r\nexport const STYLES_PATH = './dist/styles/base.css';\r\n"],"names":["STYLES_PATH"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.js"],"sourcesContent":["/**\r\n * @archerjessop/utilities\r\n * Shared utilities for ArcherJessop property analysis tools\r\n */\r\n\r\n// Financial calculations\r\nexport { \r\n calculateAppreciatedValue,\r\n calculateAssignmentFee,\r\n calculateBalloonBalance,\r\n calculateCashFlow,\r\n calculateCashFlowYield,\r\n calculateCashOutAfterRefi,\r\n calculateCOCR30, \r\n calculateCOCRAtPercent,\r\n calculateDiscountFromPrice,\r\n calculateDscrPayment,\r\n calculateNetToBuyer,\r\n calculateNOIByType,\r\n calculatePMT, \r\n calculatePriceForCOCR,\r\n calculatePriceFromDiscount,\r\n calculateSfPayment,\r\n safePercentage,\r\n} from \"./financial/calculations.js\";\r\n\r\n// Financial formatters\r\nexport { formatCurrency, formatPriceValue, formatPercentage } from \"./financial/formatters.js\";\r\n\r\n// Data extractors\r\nexport { extractPhoneNumber, extractBedrooms } from \"./data/extractors.js\";\r\n\r\n// Date utilities\r\nexport { calculateDOM } from \"./date/utilities.js\";\r\n\r\n// Formatting utilities\r\nexport { \r\n calculateCursorPosition,\r\n extractNumericValue,\r\n filterNumericInput,\r\n formatInputDisplay,\r\n formatLiveInput,\r\n formatLiveNumber,\r\n parseNumericInput\r\n} from \"./formatting/financial-formatting.js\";\r\n\r\n// Text formatting utilities\r\nexport { normalizeWhitespace } from \"./formatting/text.js\";\r\n\r\n// Configuration constants\r\nexport * from './config/financial.js';\r\nexport * from './config/property-types.js';\r\nexport * from './config/business.js';\r\n\r\nexport const STYLES_PATH = './dist/styles/base.css';\r\n"],"names":["STYLES_PATH"],"mappings":"6+CAsDY,MAACA,EAAc"}
|