Class: CloverSandboxSimulator::Services::Clover::TaxService
- Inherits:
-
BaseService
- Object
- BaseService
- CloverSandboxSimulator::Services::Clover::TaxService
- Defined in:
- lib/clover_sandbox_simulator/services/clover/tax_service.rb
Overview
Manages Clover tax rates and item-tax associations
Constant Summary collapse
- DEFAULT_TAX_RATES =
Default tax rates for restaurants
[ { name: "Sales Tax", rate: 8.25, is_default: true, description: "Standard sales tax" }, { name: "Alcohol Tax", rate: 10.0, is_default: false, description: "Additional tax on alcoholic beverages" }, { name: "Prepared Food Tax", rate: 8.25, is_default: false, description: "Tax on prepared food items" } ].freeze
- CACHE_TTL_SECONDS =
Cache configuration
300- CACHE_MAX_SIZE =
5 minutes
1000
Instance Attribute Summary
Attributes inherited from BaseService
Instance Method Summary collapse
-
#assign_taxes_by_category(items, tax_rates, category_tax_mapping = nil) ⇒ Object
Assign tax rates to items based on their category.
-
#associate_item_with_tax_rate(item_id, tax_rate_id) ⇒ Object
Associate an item with a tax rate.
-
#associate_items_with_tax_rate(item_ids, tax_rate_id) ⇒ Object
Associate multiple items with a tax rate.
-
#calculate_item_tax(item_id, amount) ⇒ Integer
Calculate tax for a specific item based on its assigned tax rates.
-
#calculate_items_tax(items) ⇒ Integer
Calculate tax for multiple items with bounded caching.
-
#calculate_tax(subtotal, tax_rate = nil) ⇒ Object
Calculate tax for an amount using a flat rate.
-
#clear_cache ⇒ Object
Clear the tax rates cache.
-
#create_tax_rate(name:, rate:, is_default: false) ⇒ Object
Create a tax rate.
-
#default_tax_rate ⇒ Object
Get default tax rate.
-
#delete_tax_rate(tax_rate_id) ⇒ Object
Delete a tax rate.
-
#get_cached_tax_rates(item_id) ⇒ Object
Get tax rates from cache or API with TTL and size limits.
-
#get_items_for_tax_rate(tax_rate_id) ⇒ Object
Get all items associated with a specific tax rate.
-
#get_tax_rates ⇒ Object
Fetch all tax rates.
-
#get_tax_rates_for_item(item_id) ⇒ Object
Get all tax rates associated with a specific item.
-
#prune_cache(keep_count) ⇒ Object
Remove oldest cache entries.
-
#remove_item_from_tax_rate(item_id, tax_rate_id) ⇒ Object
Remove an item from a tax rate.
-
#setup_default_tax_rates ⇒ Object
Set up default tax rates for a restaurant if they don’t exist.
Methods inherited from BaseService
Constructor Details
This class inherits a constructor from CloverSandboxSimulator::Services::BaseService
Instance Method Details
#assign_taxes_by_category(items, tax_rates, category_tax_mapping = nil) ⇒ Object
Assign tax rates to items based on their category
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/clover_sandbox_simulator/services/clover/tax_service.rb', line 228 def assign_taxes_by_category(items, tax_rates, category_tax_mapping = nil) category_tax_mapping ||= default_category_tax_mapping # Build a lookup for tax rates by name tax_rate_lookup = tax_rates.each_with_object({}) do |rate, hash| hash[rate["name"]&.downcase] = rate["id"] end items.each do |item| category = item.dig("categories", "elements", 0, "name") || item["category"] next unless category # Find applicable tax rates for this category applicable_taxes = category_tax_mapping[category] || category_tax_mapping["default"] || ["Sales Tax"] applicable_taxes.each do |tax_name| tax_rate_id = tax_rate_lookup[tax_name.downcase] next unless tax_rate_id begin associate_item_with_tax_rate(item["id"], tax_rate_id) rescue ApiError => e logger.debug "Could not assign tax to item #{item["id"]}: #{e.}" end end end end |
#associate_item_with_tax_rate(item_id, tax_rate_id) ⇒ Object
Associate an item with a tax rate
87 88 89 90 91 92 93 94 |
# File 'lib/clover_sandbox_simulator/services/clover/tax_service.rb', line 87 def associate_item_with_tax_rate(item_id, tax_rate_id) logger.info "Associating item #{item_id} with tax rate #{tax_rate_id}" request(:post, endpoint("tax_rate_items"), payload: { "elements" => [ { "item" => { "id" => item_id }, "taxRate" => { "id" => tax_rate_id } } ] }) end |
#associate_items_with_tax_rate(item_ids, tax_rate_id) ⇒ Object
Associate multiple items with a tax rate
103 104 105 106 107 108 109 |
# File 'lib/clover_sandbox_simulator/services/clover/tax_service.rb', line 103 def associate_items_with_tax_rate(item_ids, tax_rate_id) logger.info "Associating #{item_ids.size} items with tax rate #{tax_rate_id}" elements = item_ids.map do |item_id| { "item" => { "id" => item_id }, "taxRate" => { "id" => tax_rate_id } } end request(:post, endpoint("tax_rate_items"), payload: { "elements" => elements }) end |
#calculate_item_tax(item_id, amount) ⇒ Integer
Calculate tax for a specific item based on its assigned tax rates
117 118 119 120 121 122 123 124 125 |
# File 'lib/clover_sandbox_simulator/services/clover/tax_service.rb', line 117 def calculate_item_tax(item_id, amount) rates = get_tax_rates_for_item(item_id) return 0 if rates.empty? # Sum up all applicable tax rates (convert from basis points to percentage) total_rate_percentage = rates.sum { |r| (r["rate"] || 0) / 100_000.0 } (amount * total_rate_percentage / 100.0).round end |
#calculate_items_tax(items) ⇒ Integer
Calculate tax for multiple items with bounded caching
134 135 136 137 138 139 140 141 142 143 |
# File 'lib/clover_sandbox_simulator/services/clover/tax_service.rb', line 134 def calculate_items_tax(items) items.sum do |item| item_id = item[:item_id] amount = item[:amount] rates = get_cached_tax_rates(item_id) total_rate = rates.sum { |r| (r["rate"] || 0) / 100_000.0 } (amount * total_rate / 100.0).round end end |
#calculate_tax(subtotal, tax_rate = nil) ⇒ Object
Calculate tax for an amount using a flat rate
53 54 55 56 |
# File 'lib/clover_sandbox_simulator/services/clover/tax_service.rb', line 53 def calculate_tax(subtotal, tax_rate = nil) rate = tax_rate || config.tax_rate (subtotal * rate / 100.0).round end |
#clear_cache ⇒ Object
Clear the tax rates cache
146 147 148 149 |
# File 'lib/clover_sandbox_simulator/services/clover/tax_service.rb', line 146 def clear_cache @item_tax_rates_cache = {} @cache_timestamps = {} end |
#create_tax_rate(name:, rate:, is_default: false) ⇒ Object
Create a tax rate
32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/clover_sandbox_simulator/services/clover/tax_service.rb', line 32 def create_tax_rate(name:, rate:, is_default: false) logger.info "Creating tax rate: #{name} (#{rate}%)" # Rate is stored as basis points (8.25% = 825000) rate_basis_points = (rate * 100_000).to_i request(:post, endpoint("tax_rates"), payload: { "name" => name, "rate" => rate_basis_points, "isDefault" => is_default, "taxType" => "VAT_EXEMPT" # For US sales tax }) end |
#default_tax_rate ⇒ Object
Get default tax rate
25 26 27 28 29 |
# File 'lib/clover_sandbox_simulator/services/clover/tax_service.rb', line 25 def default_tax_rate rates = get_tax_rates # Find default rate or return first active one rates.find { |r| r["isDefault"] == true } || rates.first end |
#delete_tax_rate(tax_rate_id) ⇒ Object
Delete a tax rate
47 48 49 50 |
# File 'lib/clover_sandbox_simulator/services/clover/tax_service.rb', line 47 def delete_tax_rate(tax_rate_id) logger.info "Deleting tax rate: #{tax_rate_id}" request(:delete, endpoint("tax_rates/#{tax_rate_id}")) end |
#get_cached_tax_rates(item_id) ⇒ Object
Get tax rates from cache or API with TTL and size limits
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/clover_sandbox_simulator/services/clover/tax_service.rb', line 152 def get_cached_tax_rates(item_id) @item_tax_rates_cache ||= {} @cache_timestamps ||= {} now = Time.now.to_i # Check if cached and not expired if @item_tax_rates_cache.key?(item_id) if now - @cache_timestamps[item_id] < CACHE_TTL_SECONDS return @item_tax_rates_cache[item_id] else # Expired - remove from cache @item_tax_rates_cache.delete(item_id) @cache_timestamps.delete(item_id) end end # Prune cache if too large (LRU-style: remove oldest entries) if @item_tax_rates_cache.size >= CACHE_MAX_SIZE prune_cache(CACHE_MAX_SIZE / 2) end # Fetch and cache rates = get_tax_rates_for_item(item_id) @item_tax_rates_cache[item_id] = rates @cache_timestamps[item_id] = now rates end |
#get_items_for_tax_rate(tax_rate_id) ⇒ Object
Get all items associated with a specific tax rate
61 62 63 64 65 66 67 |
# File 'lib/clover_sandbox_simulator/services/clover/tax_service.rb', line 61 def get_items_for_tax_rate(tax_rate_id) logger.info "Fetching items for tax rate: #{tax_rate_id}" response = request(:get, endpoint("tax_rates/#{tax_rate_id}/items")) elements = response&.dig("elements") || [] logger.info "Found #{elements.size} items for tax rate" elements end |
#get_tax_rates ⇒ Object
Fetch all tax rates
16 17 18 19 20 21 22 |
# File 'lib/clover_sandbox_simulator/services/clover/tax_service.rb', line 16 def get_tax_rates logger.info "Fetching tax rates..." response = request(:get, endpoint("tax_rates")) elements = response&.dig("elements") || [] logger.info "Found #{elements.size} tax rates" elements end |
#get_tax_rates_for_item(item_id) ⇒ Object
This endpoint may not be available in sandbox environments
Get all tax rates associated with a specific item
71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/clover_sandbox_simulator/services/clover/tax_service.rb', line 71 def get_tax_rates_for_item(item_id) logger.debug "Fetching tax rates for item: #{item_id}" begin response = request(:get, endpoint("items/#{item_id}/tax_rates")) response&.dig("elements") || [] rescue ApiError => e if e..include?("405") logger.debug "Getting item tax rates not supported in this environment" [] else raise end end end |
#prune_cache(keep_count) ⇒ Object
Remove oldest cache entries
182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/clover_sandbox_simulator/services/clover/tax_service.rb', line 182 def prune_cache(keep_count) return if @cache_timestamps.empty? # Sort by timestamp and keep only the newest entries sorted_keys = @cache_timestamps.sort_by { |_, ts| ts }.map(&:first) keys_to_remove = sorted_keys.take(sorted_keys.size - keep_count) keys_to_remove.each do |key| @item_tax_rates_cache.delete(key) @cache_timestamps.delete(key) end logger.debug "Pruned #{keys_to_remove.size} expired cache entries" end |
#remove_item_from_tax_rate(item_id, tax_rate_id) ⇒ Object
Remove an item from a tax rate
97 98 99 100 |
# File 'lib/clover_sandbox_simulator/services/clover/tax_service.rb', line 97 def remove_item_from_tax_rate(item_id, tax_rate_id) logger.info "Removing item #{item_id} from tax rate #{tax_rate_id}" request(:delete, endpoint("tax_rate_items"), params: { item: item_id, taxRate: tax_rate_id }) end |
#setup_default_tax_rates ⇒ Object
Set up default tax rates for a restaurant if they don’t exist
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'lib/clover_sandbox_simulator/services/clover/tax_service.rb', line 200 def setup_default_tax_rates logger.info "Setting up default tax rates..." existing = get_tax_rates existing_names = existing.map { |r| r["name"]&.downcase } created = [] DEFAULT_TAX_RATES.each do |rate_data| if existing_names.include?(rate_data[:name].downcase) logger.debug "Tax rate '#{rate_data[:name]}' already exists, skipping" created << existing.find { |r| r["name"]&.downcase == rate_data[:name].downcase } else rate = create_tax_rate( name: rate_data[:name], rate: rate_data[:rate], is_default: rate_data[:is_default] ) created << rate if rate end end logger.info "Tax rates ready: #{created.size}" created end |