Module: CustomerHierarchyManagement
- Extended by:
- ActiveSupport::Concern
- Includes:
- AddressHelper
- Included in:
- CustomerService, QuickbooksCustomerHelper
- Defined in:
- app/models/concerns/customer_hierarchy_management.rb
Overview
CustomerHierarchyManagement provides methods for managing customer hierarchy relationships
This concern module is responsible for:
-
Determining if a customer should be a head office (hierarchy 1) or distribution center (hierarchy 2)
-
Creating distribution center customers for different shipping addresses
-
Finding related customers with similar addresses
Customer hierarchy in this system:
-
Hierarchy 1: Head Office - belongs_to_ho_id and belongs_to_dc_id both point to itself
-
Hierarchy 2: Distribution Center - belongs_to_ho_id points to its head office, belongs_to_dc_id points to itself
Example usage:
class CustomerImporter
include CustomerHierarchyManagement
def import_customer(customer_data)
# Create the customer
customer = Customer.create!(customer_data)
# Set appropriate hierarchy
assign_customer_hierarchy(customer, 'QB')
# Create distribution center if needed
if shipping_address_different?(customer, shipping_address)
create_member_customer_for_shipping_address(customer, shipping_address)
end
end
end
Instance Method Summary collapse
-
#assign_customer_hierarchy(customer, suffix_code) ⇒ Boolean
Checks if a customer should be a head office (hierarchy 1) or distribution center (hierarchy 2) and sets appropriate attributes.
-
#billing_shipping_addresses_match?(customer, shipping_address) ⇒ Boolean
Checks if billing and shipping addresses match.
-
#create_dc_customer_for_shipping_address(customer, shipping_address) ⇒ Customer?
Creates a distribution center customer when shipping address differs from billing.
-
#create_member_customer_for_shipping_address(customer, shipping_address = nil) ⇒ Customer?
Checks if a distribution center customer is needed based on shipping address and creates one if necessary.
-
#determine_hierarchy_attributes(customer_id, member_of_a_chain, hierarchy) ⇒ Hash
Determines the correct hierarchy attributes based on the customer type.
-
#find_customer_with_same_address(customer, suffix_code) ⇒ Customer?
Finds customers with the same suffix_code prefix and same address.
-
#shipping_address_valid?(shipping_address) ⇒ Boolean
Validates that a shipping address has the required fields.
Methods included from AddressHelper
#canadian_postal_code?, #clean_city, #normalize_postal_code, #parse_address_line, #parse_address_parts, #us_zip_code?, #valid_city?, #valid_state?
Instance Method Details
#assign_customer_hierarchy(customer, suffix_code) ⇒ Boolean
Checks if a customer should be a head office (hierarchy 1) or distribution center (hierarchy 2) and sets appropriate attributes
This method:
-
Checks if the customer already has a valid hierarchy setup
-
Looks for similar customers with the same address pattern
-
Sets hierarchy 2 if similar customers exist (this is a branch location)
-
Sets hierarchy 1 if no similar customers exist (this is a head office)
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'app/models/concerns/customer_hierarchy_management.rb', line 47 def assign_customer_hierarchy(customer, suffix_code) return false unless customer&.customer_id Rails.logger.info "Checking hierarchy for customer #{customer.customer_id} (#{customer.name}), current hierarchy: #{customer.hierarchy}, belongs_to_ho_id: #{customer.belongs_to_ho_id}, belongs_to_dc_id: #{customer.belongs_to_dc_id}" # If customer already has hierarchy 1, preserve it if customer.hierarchy == '1' && customer.belongs_to_ho_id.present? && customer.belongs_to_dc_id.present? Rails.logger.info "Customer #{customer.customer_id} already has hierarchy 1, preserving it" return true end # If customer already has a complete hierarchy 2 setup, preserve it if customer.hierarchy == '2' && customer.belongs_to_ho_id.present? && customer.belongs_to_dc_id.present? && customer.belongs_to_ho_id != customer.customer_id Rails.logger.info "Customer #{customer.customer_id} already has hierarchy 2, preserving it" return true end # Check if another customer with the same suffix_code has the same address similar_customer = find_customer_with_same_address(customer, suffix_code) if similar_customer.present? # If a similar customer exists, this is a distribution center (hierarchy 2) Rails.logger.info "Found similar customer #{similar_customer.customer_id} with same address. Setting #{customer.customer_id} as hierarchy 2." customer.assign_attributes( member: true, hierarchy: '2', belongs_to_dc_id: customer.customer_id, belongs_to_ho_id: similar_customer.customer_id ) else # If no similar customer exists, this is a head office (hierarchy 1) Rails.logger.info "No similar customer found. Setting #{customer.customer_id} as hierarchy 1." customer.assign_attributes( hierarchy: '1', belongs_to_dc_id: customer.customer_id, belongs_to_ho_id: customer.customer_id ) end # Save updated fields with error handling begin # Extract all attributes to ensure we don't lose any data if respond_to?(:extract_attributes_from_customer) attributes = extract_attributes_from_customer(customer) system_user = respond_to?(:user_name) ? user_name : 'SYSTEM' Customer.create_or_update_procedure(**attributes, system_user: system_user) else # Fallback to save! if extract_attributes_from_customer is not available customer.save! end true rescue StandardError => e Rails.logger.error "Failed to save customer with hierarchy: #{e.}" false end end |
#billing_shipping_addresses_match?(customer, shipping_address) ⇒ Boolean
Checks if billing and shipping addresses match
226 227 228 229 230 231 |
# File 'app/models/concerns/customer_hierarchy_management.rb', line 226 def billing_shipping_addresses_match?(customer, shipping_address) city_match = customer.city.to_s.strip.casecmp?(shipping_address[:city].to_s.strip) postal_match = normalize_postal_code(customer.postal_code) == normalize_postal_code(shipping_address[:postal_code]) city_match && postal_match end |
#create_dc_customer_for_shipping_address(customer, shipping_address) ⇒ Customer?
Creates a distribution center customer when shipping address differs from billing
This method:
-
Checks if billing and shipping addresses are different
-
Validates the shipping address
-
Creates a new customer as a distribution center linked to the original customer
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
# File 'app/models/concerns/customer_hierarchy_management.rb', line 243 def create_dc_customer_for_shipping_address(customer, shipping_address) # Only proceed if billing and shipping addresses differ return if billing_shipping_addresses_match?(customer, shipping_address) # Validate shipping address has required fields return unless shipping_address_valid?(shipping_address) # Find the original customer original_customer = Customer.find_by(customer_id: customer.customer_id) return unless original_customer.present? # If we have the create_distribution_center method (from CustomerService) if respond_to?(:create_distribution_center) return create_distribution_center(original_customer, shipping_address) end # Legacy implementation for backward compatibility # Check if a location customer already exists with these shipping details existing_location = Customer.find_by( belongs_to_ho_id: original_customer.customer_id, city: shipping_address[:city], postal_code: shipping_address[:postal_code] ) return if existing_location.present? # Create a new customer location new_customer = original_customer.dup # Find province_id, fallback to original customer's province if not found province_id = Customer.find_province_by_code(shipping_address[:province_code]) province_id = original_customer.province_id if province_id.nil? new_customer.assign_attributes( member: true, hierarchy: '2', belongs_to_ho_id: original_customer.customer_id, code: "#{original_customer.code}-DC", name: shipping_address[:company_name] || original_customer.name, address_line1: shipping_address[:address_line1], address_line2: shipping_address[:address_line2], city: shipping_address[:city], province_id: province_id, postal_code: shipping_address[:postal_code], created_by: respond_to?(:user_name) ? user_name : 'SYSTEM', last_modified_by: respond_to?(:user_name) ? user_name : 'SYSTEM', creation_date: Time.current, last_modification_date: Time.current ) # Save the customer begin if respond_to?(:extract_attributes_from_customer) # Use the procedure method to avoid nulling fields attributes = extract_attributes_from_customer(new_customer) system_user = respond_to?(:user_name) ? user_name : 'SYSTEM' # Create the customer with proper hierarchy attributes result = Customer.create_or_update_procedure(**attributes, system_user: system_user) new_customer = Customer.find_by(customer_id: result[:customer_id]) # Update the belongs_to_dc_id to the new customer's ID if new_customer.present? updated_attributes = extract_attributes_from_customer(new_customer).merge( belongs_to_dc_id: new_customer.customer_id ) Customer.create_or_update_procedure(**updated_attributes, system_user: system_user) end else # Fallback to save! if extract_attributes_from_customer is not available new_customer.save! new_customer.update!(belongs_to_dc_id: new_customer.customer_id) end rescue StandardError => e Rails.logger.error "Failed to create DC customer: #{e.}" Rails.logger.error "Shipping address: #{shipping_address.inspect}" Rails.logger.error "Province ID: #{province_id}" return nil end new_customer end |
#create_member_customer_for_shipping_address(customer, shipping_address = nil) ⇒ Customer?
Checks if a distribution center customer is needed based on shipping address and creates one if necessary
111 112 113 114 115 116 117 118 |
# File 'app/models/concerns/customer_hierarchy_management.rb', line 111 def create_member_customer_for_shipping_address(customer, shipping_address = nil) return unless customer.present? # If shipping address is provided, check if it differs from billing return unless shipping_address.present? create_dc_customer_for_shipping_address(customer, shipping_address) end |
#determine_hierarchy_attributes(customer_id, member_of_a_chain, hierarchy) ⇒ Hash
Determines the correct hierarchy attributes based on the customer type
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'app/models/concerns/customer_hierarchy_management.rb', line 161 def determine_hierarchy_attributes(customer_id, member_of_a_chain, hierarchy) return {} unless customer_id # First check if customer already exists and has a hierarchy customer = Customer.find_by(customer_id: customer_id) if customer.present? # If customer already has a hierarchy 1 setup, preserve it if customer.hierarchy == '1' && customer.belongs_to_ho_id.present? && customer.belongs_to_dc_id.present? && customer.belongs_to_ho_id == customer.customer_id Rails.logger.info "determine_hierarchy_attributes: preserving hierarchy 1 for customer #{customer_id}" return { belongs_to_ho_id: customer.belongs_to_ho_id, belongs_to_dc_id: customer.belongs_to_dc_id } end # If customer already has a complete hierarchy 2 setup, preserve it if customer.hierarchy == '2' && customer.belongs_to_ho_id.present? && customer.belongs_to_dc_id.present? && customer.belongs_to_ho_id != customer.customer_id Rails.logger.info "determine_hierarchy_attributes: preserving hierarchy 2 for customer #{customer_id}" return { belongs_to_ho_id: customer.belongs_to_ho_id, belongs_to_dc_id: customer.belongs_to_dc_id } end end updated_belongs_to_ho_id = nil updated_belongs_to_dc_id = nil if !member_of_a_chain updated_belongs_to_ho_id = customer_id updated_belongs_to_dc_id = customer_id elsif member_of_a_chain if hierarchy == '1' updated_belongs_to_ho_id = customer_id updated_belongs_to_dc_id = customer_id elsif hierarchy == '2' updated_belongs_to_dc_id = customer_id end end { belongs_to_ho_id: updated_belongs_to_ho_id, belongs_to_dc_id: updated_belongs_to_dc_id } end |
#find_customer_with_same_address(customer, suffix_code) ⇒ Customer?
Finds customers with the same suffix_code prefix and same address
This method helps identify if a customer is related to other customers with the same address pattern.
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'app/models/concerns/customer_hierarchy_management.rb', line 128 def find_customer_with_same_address(customer, suffix_code) # Exclude the current customer to avoid self-comparison # Look for customers with the same billing address pattern similar_customer = Customer.where( 'code LIKE ? AND city = ? AND province_id = ? AND postal_code = ? AND customer_id != ?', "#{suffix_code}-%", customer.city, customer.province_id, customer.postal_code, customer.customer_id ).where(hierarchy: '1').first if similar_customer.blank? # If no hierarchy 1 customer found, try to find any customer with the same address similar_customer = Customer.where( 'code LIKE ? AND city = ? AND province_id = ? AND postal_code = ? AND customer_id != ?', "#{suffix_code}-%", customer.city, customer.province_id, customer.postal_code, customer.customer_id ).first end similar_customer end |
#shipping_address_valid?(shipping_address) ⇒ Boolean
Validates that a shipping address has the required fields
213 214 215 216 217 218 219 |
# File 'app/models/concerns/customer_hierarchy_management.rb', line 213 def shipping_address_valid?(shipping_address) # Ensure required fields are present return false if shipping_address[:city].blank? return false if shipping_address[:postal_code].blank? true end |