Class: CustomerService

Inherits:
Object
  • Object
show all
Includes:
AddressHelper, CustomerHierarchyManagement
Defined in:
app/services/customer_service.rb

Overview

CustomerService handles all operations related to customer management

This service provides methods for:

  • Creating and updating customers

  • Managing customer hierarchies (head office, distribution centers)

  • Creating customer-company associations

  • Handling customer shipping addresses

Example usage:

# Initialize the service
service = CustomerService.new('Admin')

# Create or update a customer
result = service.create_or_update({
  name: 'ACME Inc.',
  code: 'ACME-001',
  address_line1: '123 Main St',
  city: 'Toronto',
  province_id: 1,
  postal_code: 'M5V 2N1',
  tenant_id: 1
})

# Update customer hierarchy
service.set_initial_hierarchy(result[:customer_id])

# Create customer-company associations
service.create_customer_company_associations(result[:customer_id], [1, 2, 3])

# Create a distribution center from shipping address
shipping_address = {
  company_name: 'ACME Warehouse',
  address_line1: '456 Warehouse Ave',
  city: 'Montreal',
  province_code: 'QC',
  postal_code: 'H2Y 1C6'
}
dc = service.create_distribution_center(customer, shipping_address)

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from CustomerHierarchyManagement

#assign_customer_hierarchy, #billing_shipping_addresses_match?, #create_dc_customer_for_shipping_address, #create_member_customer_for_shipping_address, #determine_hierarchy_attributes, #find_customer_with_same_address, #shipping_address_valid?

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?

Constructor Details

#initialize(user_name = 'Admin') ⇒ CustomerService

Initializes a new CustomerService instance

Parameters:

  • user_name (String) (defaults to: 'Admin')

    The username for audit logging



51
52
53
# File 'app/services/customer_service.rb', line 51

def initialize(user_name = 'Admin')
  @user_name = user_name
end

Instance Attribute Details

#user_nameObject (readonly)

Returns the value of attribute user_name.



46
47
48
# File 'app/services/customer_service.rb', line 46

def user_name
  @user_name
end

Instance Method Details

#check_and_create_member_customer(customer, shipping_address) ⇒ Customer?

Creates a distribution center customer for a different shipping address

Parameters:

  • customer (Customer)

    The customer to create a DC for

  • shipping_address (Hash, nil)

    The shipping address details

Returns:

  • (Customer, nil)

    The created member customer or nil



278
279
280
# File 'app/services/customer_service.rb', line 278

def check_and_create_member_customer(customer, shipping_address)
  create_member_customer_for_shipping_address(customer, shipping_address)
end

#check_and_set_customer_hierarchy(customer, suffix_code) ⇒ Boolean

Checks and sets the customer hierarchy based on the suffix code

Parameters:

  • customer (Customer)

    The customer to update

  • suffix_code (String)

    The prefix/suffix code used in the customer code

Returns:

  • (Boolean)

    True if hierarchy was set or already valid, false otherwise



269
270
271
# File 'app/services/customer_service.rb', line 269

def check_and_set_customer_hierarchy(customer, suffix_code)
  assign_customer_hierarchy(customer, suffix_code)
end

#create_customer_company_associations(customer_id, company_ids, memo = nil) ⇒ Array<Hash>?

Creates customer-company associations for all specified companies

This method links a customer to multiple companies with default settings.

Parameters:

  • customer_id (Integer)

    The ID of the customer

  • company_ids (Array<Integer>)

    Array of company IDs to associate

  • memo (String, nil) (defaults to: nil)

    Optional memo to include with the association

Returns:

  • (Array<Hash>, nil)

    Array of created association results or nil



216
217
218
219
220
221
222
223
224
225
226
227
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
255
256
257
258
259
260
261
262
# File 'app/services/customer_service.rb', line 216

def create_customer_company_associations(customer_id, company_ids, memo = nil)
  return unless customer_id.present? && company_ids.present?

  company_ids.map do |company_id|
    Customer.create_or_update_customer_by_company_procedure(
      customer_key: 0,
      customer_id: customer_id,
      company_code: company_id.to_s,
      company_id: company_id,
      active: 1,
      currency_id: 1,
      ship_from_warehouse_id: 1,
      shipping_priority: 1,
      credit_checking: 1,
      use_store_province_for_taxes: 1,
      freight_terms_id: 1,
      ship_via_id: 1,
      minimum_filling_rate: 0,
      print_statements: 1,
      print_invoices: 1,
      invoice_to_store: 1,
      shipping_document: nil,
      accept_back_orders: 1,
      payment_terms_id: 1,
      discount_on_net: 1,
      credit_limit: 0,
      minimum_order_limit: 0,
      maximum_order_limit: 0,
      po_required: 1,
      hold_credit: 1,
      print_mh10_labels: 1,
      mh10_label_format_id: 1,
      print_packing_slip: 1,
      packing_slip_type: 0,
      our_code_in_client_system: 1,
      bank_id: nil,
      bank_branch_id: nil,
      bank_account_id: nil,
      box_distribution_type: 1,
      memo: memo,
      number_invoice_copies_to_print: 0,
      number_of_upcs_per_box: 0,
      consolidated_invoice: 1,
      system_user: user_name
    )
  end
end

#create_distribution_center(parent_customer, shipping_address, suffix = 'DC') ⇒ Customer?

Creates a new distribution center customer from a parent customer and shipping address

This method creates a child customer with hierarchy=2 that is linked to the parent customer as its head office.

Parameters:

  • parent_customer (Customer)

    The parent (head office) customer

  • shipping_address (Hash)

    The shipping address details

  • suffix (String) (defaults to: 'DC')

    The suffix to append to the customer code (default: ‘DC’)

Returns:

  • (Customer, nil)

    The created distribution center customer or nil



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
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'app/services/customer_service.rb', line 291

def create_distribution_center(parent_customer, shipping_address, suffix = 'DC')
  return nil unless parent_customer.present? && shipping_address_valid?(shipping_address)

  # Check if a location customer already exists with these shipping details
  existing_location = Customer.find_by(
    belongs_to_ho_id: parent_customer.customer_id,
    city: shipping_address[:city],
    postal_code: shipping_address[:postal_code]
  )
  return existing_location if existing_location.present?

  # Create a new customer location as a clone of the parent
  new_customer_attributes = extract_attributes_from_customer(parent_customer)

  # Override with shipping address details
  new_customer_attributes.merge!(
    customer_id: nil, # Ensure this is a new record
    member: true,
    hierarchy: '2',
    belongs_to_ho_id: parent_customer.customer_id,
    code: "#{parent_customer.code}-#{suffix}",
    name: shipping_address[:company_name] || parent_customer.name,
    address_line1: shipping_address[:address_line1],
    address_line2: shipping_address[:address_line2],
    city: shipping_address[:city],
    province_id: find_province_id_from_code(shipping_address[:province_code], parent_customer.province_id),
    postal_code: shipping_address[:postal_code]
  )

  # Create the customer
  result = Customer.create_or_update_procedure(**new_customer_attributes, system_user: user_name)

  # Get the customer object
  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?
    Customer.create_or_update_procedure(
      **extract_attributes_from_customer(new_customer),
      belongs_to_dc_id: new_customer.customer_id,
      system_user: user_name
    )
  end

  new_customer
end

#create_or_update(attributes) ⇒ Hash

Creates or updates a customer with the given attributes

This method preserves hierarchy information for existing customers and sets default hierarchy attributes for new customers.

Parameters:

  • attributes (Hash)

    The customer attributes to create or update

Returns:

  • (Hash)

    Result hash containing :customer_id and status



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
104
105
106
107
108
# File 'app/services/customer_service.rb', line 62

def create_or_update(attributes)
  customer_id = attributes[:customer_id]
  tenant_id = attributes[:tenant_id]

  # Check if customer exists for this tenant
  if customer_id.present? && customer_id.to_i != 0
    existing_customer = Customer.find_by(customer_id: customer_id, tenant_id: tenant_id)
    return { error: 'Customer not found for this tenant', status: :not_found } unless existing_customer

    # Preserve hierarchy for existing customers if not explicitly changing it
    if existing_customer.hierarchy.present? &&
       !attributes.key?(:hierarchy) &&
       !attributes.key?(:belongs_to_ho_id) &&
       !attributes.key?(:belongs_to_dc_id)

      if existing_customer.hierarchy == '1'
        Rails.logger.info "Preserving hierarchy 1 for existing customer #{customer_id}"
        attributes[:hierarchy] = '1'
        attributes[:belongs_to_ho_id] = existing_customer.belongs_to_ho_id
        attributes[:belongs_to_dc_id] = existing_customer.belongs_to_dc_id
      elsif existing_customer.hierarchy == '2' &&
            existing_customer.belongs_to_ho_id.present? &&
            existing_customer.belongs_to_ho_id != existing_customer.customer_id
        Rails.logger.info "Preserving hierarchy 2 for existing customer #{customer_id}"
        attributes[:hierarchy] = '2'
        attributes[:belongs_to_ho_id] = existing_customer.belongs_to_ho_id
        attributes[:belongs_to_dc_id] = existing_customer.belongs_to_dc_id
      end
    end

    customer_id = existing_customer.customer_id
    attributes[:customer_id] = customer_id
  end

  # Create or update the customer
  result = Customer.create_or_update_procedure(**attributes, system_user: user_name)

  # For new customers without hierarchy set, update hierarchy attributes
  if !customer_id &&
     attributes[:belongs_to_dc_id].to_i.zero? &&
     attributes[:belongs_to_ho_id].to_i.zero?

    update_customer_hierarchy(result[:customer_id], attributes)
  end

  result
end

#extract_attributes_from_customer(customer) ⇒ Hash

Extracts all attributes from a Customer object

Parameters:

  • customer (Customer)

    The customer object

Returns:

  • (Hash)

    All customer attributes as a hash



114
115
116
117
118
119
120
121
122
123
124
125
126
127
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
154
155
156
# File 'app/services/customer_service.rb', line 114

def extract_attributes_from_customer(customer)
  {
    customer_id: customer.customer_id,
    tenant_id: customer.tenant_id,
    code: customer.code,
    type: customer.type,
    name: customer.name,
    known_as: customer.known_as,
    address_line1: customer.address_line1,
    address_line2: customer.address_line2,
    city: customer.city,
    province_id: customer.province_id,
    postal_code: customer.postal_code,
    phone_1: customer.phone_1,
    phone_2: customer.phone_2,
    fax: customer.fax,
    email: customer.email,
    website: customer.website,
    attention: customer.attention,
    language: customer.language,
    member_of_a_chain: customer.member_of_a_chain,
    hierarchy: customer.hierarchy,
    belongs_to_chain_id: customer.belongs_to_chain_id,
    belongs_to_dc_id: customer.belongs_to_dc_id,
    belongs_to_ho_id: customer.belongs_to_ho_id,
    tax_exemption_code: customer.tax_exemption_code,
    pst_number: customer.pst_number,
    gst_irs_number: customer.gst_irs_number,
    edi_store_code: customer.edi_store_code,
    store_number: customer.store_number,
    active: customer.active,
    reserved_record: customer.reserved_record,
    memo: customer.memo,
    consolidate_invoice_by: customer.consolidate_invoice_by,
    consolidate_asn_by: customer.consolidate_asn_by,
    category_id: customer.category_id,
    stage: customer.stage,
    member: customer.member,
    region: customer.region,
    regional_code: customer.regional_code,
    renewal: customer.renewal
  }
end

#find_province_id_from_code(province_code, fallback_province_id) ⇒ Integer

Finds a province ID from a province code with fallback

Parameters:

  • province_code (String)

    The province code (e.g., ‘ON’, ‘QC’)

  • fallback_province_id (Integer)

    The default province ID to use if lookup fails

Returns:

  • (Integer)

    The found province ID or the fallback ID



343
344
345
346
347
348
# File 'app/services/customer_service.rb', line 343

def find_province_id_from_code(province_code, fallback_province_id)
  return fallback_province_id unless province_code.present?

  province_id = Customer.find_province_by_code(province_code)
  province_id.presence || fallback_province_id
end

#set_initial_hierarchy(customer) ⇒ Boolean

Sets the initial hierarchy for a new customer

For new customers, this determines if they should be a head office (hierarchy 1) or a distribution center (hierarchy 2) based on their code.

Parameters:

  • customer (Customer)

    The customer to update

Returns:

  • (Boolean)

    True if hierarchy was set successfully, false otherwise



357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
# File 'app/services/customer_service.rb', line 357

def set_initial_hierarchy(customer)
  return false unless customer.present?

  # If hierarchy is already set correctly, don't override it
  if customer.hierarchy.present? && customer.belongs_to_ho_id.present? && customer.belongs_to_dc_id.present?
    if customer.hierarchy == '1' && customer.belongs_to_ho_id == customer.customer_id
      Rails.logger.info "Customer #{customer.customer_id} already has correct hierarchy 1"
      return true
    elsif customer.hierarchy == '2' && customer.belongs_to_ho_id != customer.customer_id
      Rails.logger.info "Customer #{customer.customer_id} already has correct hierarchy 2"
      return true
    end
  end

  # Extract the suffix code from the customer code if possible
  suffix_code = nil
  if customer.code.present? && customer.code.include?('-')
    suffix_code = customer.code.split('-').first
  end

  if suffix_code.present?
    assign_customer_hierarchy(customer, suffix_code)
  else
    # If no suffix code, set as head office by default
    # Get all existing attributes to avoid nulling out fields
    attributes = extract_attributes_from_customer(customer).merge(
      hierarchy: '1',
      belongs_to_dc_id: customer.customer_id,
      belongs_to_ho_id: customer.customer_id
    )

    # Update with full attributes
    Customer.create_or_update_procedure(**attributes, system_user: user_name)
  end
end

#update_customer_hierarchy(customer_id, attributes) ⇒ Boolean, Hash

Updates the hierarchy attributes of a customer

This method determines the appropriate hierarchy (head office or distribution center) and updates the customer record accordingly.

Parameters:

  • customer_id (Integer)

    The ID of the customer to update

  • attributes (Hash)

    The customer attributes to consider

Returns:

  • (Boolean, Hash)

    Result of the update operation



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
# File 'app/services/customer_service.rb', line 166

def update_customer_hierarchy(customer_id, attributes)
  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.hierarchy == '1' && customer.belongs_to_ho_id.present? && customer.belongs_to_dc_id.present?
      Rails.logger.info "Customer #{customer_id} already has hierarchy 1, preserving it"
      return true
    elsif 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_id} already has hierarchy 2, preserving it"
      return true
    end
  end

  # Determine hierarchy references
  hierarchy_attributes = determine_hierarchy_attributes(
    customer_id,
    attributes[:member_of_a_chain],
    attributes[:hierarchy]
  )

  # Only update if we have hierarchy attributes to set
  return unless hierarchy_attributes.present?

  # First get all existing attributes to avoid nulling out fields
  existing_attributes = {}
  if customer.present?
    existing_attributes = extract_attributes_from_customer(customer)
  end

  # Merge with passed attributes and hierarchy attributes
  updated_attributes = existing_attributes.merge(attributes).merge(
    customer_id: customer_id,
    belongs_to_dc_id: hierarchy_attributes[:belongs_to_dc_id],
    belongs_to_ho_id: hierarchy_attributes[:belongs_to_ho_id]
  )

  Customer.create_or_update_procedure(**updated_attributes, system_user: user_name)
end