Fix tax rate lookup and add price extraction from __OO_STATE__
- Tax rate: Use Zippopotam (free, no key) to get state, then lookup from built-in state+local rate tables instead of API Ninjas - Prices: Extract prices from Toast __OO_STATE__ MenuItem objects when visible HTML prices are missing Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
76f089d1b9
commit
3cd7bbb8b7
2 changed files with 66 additions and 31 deletions
|
|
@ -285,9 +285,10 @@
|
|||
<!--- Debug: log all top-level keys in OO_STATE --->
|
||||
<cfset ooStateKeys = structKeyList(ooState)>
|
||||
<cfset arrayAppend(response.steps, "OO_STATE keys: " & left(ooStateKeys, 500))>
|
||||
<!--- Build name -> image URL map and name -> category map from OO_STATE --->
|
||||
<!--- Build name -> image URL map, name -> category map, and name -> price map from OO_STATE --->
|
||||
<cfset imageMap = structNew()>
|
||||
<cfset itemCategoryMap = structNew()>
|
||||
<cfset itemPriceMap = structNew()>
|
||||
<cfloop collection="#ooState#" item="key">
|
||||
<!--- Extract restaurant/business info --->
|
||||
<cfif left(key, 11) EQ "Restaurant:">
|
||||
|
|
@ -339,6 +340,12 @@
|
|||
<cfif len(groupName)>
|
||||
<cfset itemCategoryMap[item.name] = groupName>
|
||||
</cfif>
|
||||
<!--- Extract price --->
|
||||
<cfif structKeyExists(item, "price") AND isNumeric(item.price)>
|
||||
<cfset itemPriceMap[item.name] = val(item.price)>
|
||||
<cfelseif structKeyExists(item, "unitPrice") AND isNumeric(item.unitPrice)>
|
||||
<cfset itemPriceMap[item.name] = val(item.unitPrice)>
|
||||
</cfif>
|
||||
<!--- Extract image URLs --->
|
||||
<cfif structKeyExists(item, "imageUrls")>
|
||||
<cfset imgUrls = item.imageUrls>
|
||||
|
|
@ -355,9 +362,10 @@
|
|||
</cfif>
|
||||
</cfif>
|
||||
</cfloop>
|
||||
<!--- Apply images and categories to items --->
|
||||
<!--- Apply images, categories, and prices to items --->
|
||||
<cfset imagesMatched = 0>
|
||||
<cfset categoriesMatched = 0>
|
||||
<cfset pricesMatched = 0>
|
||||
<cfloop from="1" to="#arrayLen(toastItems)#" index="i">
|
||||
<cfif structKeyExists(imageMap, toastItems[i].name)>
|
||||
<cfset toastItems[i]["imageUrl"] = imageMap[toastItems[i].name]>
|
||||
|
|
@ -370,8 +378,13 @@
|
|||
<cfset toastItems[i]["category"] = itemCategoryMap[toastItems[i].name]>
|
||||
<cfset categoriesMatched++>
|
||||
</cfif>
|
||||
<!--- Apply price from __OO_STATE__ if not already set or is 0 --->
|
||||
<cfif structKeyExists(itemPriceMap, toastItems[i].name) AND (NOT structKeyExists(toastItems[i], "price") OR toastItems[i].price EQ 0)>
|
||||
<cfset toastItems[i]["price"] = itemPriceMap[toastItems[i].name]>
|
||||
<cfset pricesMatched++>
|
||||
</cfif>
|
||||
</cfloop>
|
||||
<cfset arrayAppend(response.steps, "Matched " & imagesMatched & " images, " & categoriesMatched & " categories from __OO_STATE__")>
|
||||
<cfset arrayAppend(response.steps, "Matched " & imagesMatched & " images, " & categoriesMatched & " categories, " & pricesMatched & " prices from __OO_STATE__")>
|
||||
<cfif structCount(toastBusiness) GT 0>
|
||||
<cfset arrayAppend(response.steps, "Extracted business info: " & structKeyList(toastBusiness))>
|
||||
</cfif>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,30 @@
|
|||
<cfcontent type="application/json; charset=utf-8" reset="true">
|
||||
<cfheader name="Cache-Control" value="no-store">
|
||||
|
||||
<cfset response = { "OK": false, "taxRate": 0, "message": "" }>
|
||||
<cfset response = { "OK": false, "taxRate": 0, "message": "", "state": "" }>
|
||||
|
||||
<!--- State base sales tax rates (2024) - local rates may add 1-5% more --->
|
||||
<cfset stateTaxRates = {
|
||||
"AL": 4, "AK": 0, "AZ": 5.6, "AR": 6.5, "CA": 7.25,
|
||||
"CO": 2.9, "CT": 6.35, "DE": 0, "FL": 6, "GA": 4,
|
||||
"HI": 4, "ID": 6, "IL": 6.25, "IN": 7, "IA": 6,
|
||||
"KS": 6.5, "KY": 6, "LA": 4.45, "ME": 5.5, "MD": 6,
|
||||
"MA": 6.25, "MI": 6, "MN": 6.875, "MS": 7, "MO": 4.225,
|
||||
"MT": 0, "NE": 5.5, "NV": 6.85, "NH": 0, "NJ": 6.625,
|
||||
"NM": 4.875, "NY": 4, "NC": 4.75, "ND": 5, "OH": 5.75,
|
||||
"OK": 4.5, "OR": 0, "PA": 6, "RI": 7, "SC": 6,
|
||||
"SD": 4.2, "TN": 7, "TX": 6.25, "UT": 6.1, "VT": 6,
|
||||
"VA": 5.3, "WA": 6.5, "WV": 6, "WI": 5, "WY": 4, "DC": 6
|
||||
}>
|
||||
|
||||
<!--- Estimated average local rate addition by state (rough estimates) --->
|
||||
<cfset stateLocalAdd = {
|
||||
"AL": 5.2, "AZ": 2.8, "AR": 3, "CA": 1.5, "CO": 4.8,
|
||||
"GA": 4, "IL": 2.5, "KS": 2.5, "LA": 5.5, "MO": 4,
|
||||
"NV": 1.4, "NM": 2.7, "NY": 4.5, "NC": 2.3, "OH": 1.5,
|
||||
"OK": 4.5, "SC": 2, "SD": 2, "TN": 2.5, "TX": 2,
|
||||
"UT": 1.3, "WA": 3, "WI": 0.6
|
||||
}>
|
||||
|
||||
<cftry>
|
||||
<!--- Get ZIP code from URL or form --->
|
||||
|
|
@ -24,44 +47,43 @@
|
|||
<!--- Use just the 5-digit portion --->
|
||||
<cfset zipCode = left(zipCode, 5)>
|
||||
|
||||
<!--- Call API Ninjas Sales Tax API --->
|
||||
<cfhttp url="https://api.api-ninjas.com/v1/salestax?zip_code=#zipCode#" method="GET" timeout="10" result="httpResult">
|
||||
<cfhttpparam type="header" name="X-Api-Key" value="free">
|
||||
<!--- Use Zippopotam.us to get state from ZIP (free, no API key) --->
|
||||
<cfhttp url="https://api.zippopotam.us/us/#zipCode#" method="GET" timeout="10" result="httpResult">
|
||||
</cfhttp>
|
||||
|
||||
<cfif httpResult.statusCode CONTAINS "200">
|
||||
<cfset apiData = deserializeJSON(httpResult.fileContent)>
|
||||
<cfset zipData = deserializeJSON(httpResult.fileContent)>
|
||||
|
||||
<!--- API returns an array, get first result --->
|
||||
<cfif isArray(apiData) AND arrayLen(apiData) GT 0>
|
||||
<cfset taxData = apiData[1]>
|
||||
<cfif structKeyExists(zipData, "places") AND isArray(zipData.places) AND arrayLen(zipData.places) GT 0>
|
||||
<cfset place = zipData.places[1]>
|
||||
<cfset stateAbbr = uCase(place["state abbreviation"])>
|
||||
<cfset response.state = stateAbbr>
|
||||
|
||||
<!--- overall_rate is decimal (e.g., 0.095 for 9.5%) --->
|
||||
<cfif structKeyExists(taxData, "overall_rate")>
|
||||
<cfset overallRate = val(taxData.overall_rate)>
|
||||
<!--- Convert to percentage --->
|
||||
<cfset response.taxRate = round(overallRate * 10000) / 100>
|
||||
<!--- Look up state base rate --->
|
||||
<cfif structKeyExists(stateTaxRates, stateAbbr)>
|
||||
<cfset baseRate = stateTaxRates[stateAbbr]>
|
||||
|
||||
<!--- Add estimated local rate if available --->
|
||||
<cfset localAdd = 0>
|
||||
<cfif structKeyExists(stateLocalAdd, stateAbbr)>
|
||||
<cfset localAdd = stateLocalAdd[stateAbbr]>
|
||||
</cfif>
|
||||
|
||||
<cfset totalRate = baseRate + localAdd>
|
||||
<!--- Round to 2 decimal places --->
|
||||
<cfset response.taxRate = round(totalRate * 100) / 100>
|
||||
<cfset response.OK = true>
|
||||
<cfset response.message = "Tax rate found">
|
||||
|
||||
<!--- Include breakdown for transparency --->
|
||||
<cfif structKeyExists(taxData, "state_rate")>
|
||||
<cfset response.stateRate = round(val(taxData.state_rate) * 10000) / 100>
|
||||
</cfif>
|
||||
<cfif structKeyExists(taxData, "county_rate")>
|
||||
<cfset response.countyRate = round(val(taxData.county_rate) * 10000) / 100>
|
||||
</cfif>
|
||||
<cfif structKeyExists(taxData, "city_rate")>
|
||||
<cfset response.cityRate = round(val(taxData.city_rate) * 10000) / 100>
|
||||
</cfif>
|
||||
<cfset response.stateRate = baseRate>
|
||||
<cfset response.localRate = localAdd>
|
||||
<cfset response.message = "Estimated rate for #place['place name']#, #stateAbbr#">
|
||||
<cfelse>
|
||||
<cfset response.message = "No rate data in response">
|
||||
<cfset response.message = "Unknown state: #stateAbbr#">
|
||||
</cfif>
|
||||
<cfelse>
|
||||
<cfset response.message = "No results for ZIP code">
|
||||
<cfset response.message = "No location data for ZIP">
|
||||
</cfif>
|
||||
<cfelse>
|
||||
<cfset response.message = "API returned status: #httpResult.statusCode#">
|
||||
<cfset response.message = "ZIP lookup failed: #httpResult.statusCode#">
|
||||
</cfif>
|
||||
|
||||
<cfcatch type="any">
|
||||
|
|
|
|||
Reference in a new issue