stall 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +60 -23
  3. data/app/assets/javascripts/para/stall.coffee +1 -0
  4. data/app/assets/javascripts/para/stall/inputs/variant-select.coffee +62 -0
  5. data/app/assets/javascripts/para/stall/inputs/variants-matrix.coffee +12 -0
  6. data/app/assets/javascripts/para/stall/inputs/variants-matrix/helpers.coffee +40 -0
  7. data/app/assets/javascripts/para/stall/inputs/variants-matrix/input.coffee +133 -0
  8. data/app/assets/javascripts/para/stall/inputs/variants-matrix/nested-fields.coffee +38 -0
  9. data/app/assets/javascripts/para/stall/inputs/variants-matrix/properties_select.coffee +45 -0
  10. data/app/assets/javascripts/para/stall/inputs/variants-matrix/variant.coffee +59 -0
  11. data/app/assets/javascripts/stall.coffee +1 -0
  12. data/app/assets/javascripts/stall/add-to-cart-form.coffee +53 -28
  13. data/app/assets/javascripts/stall/cart-form.coffee +7 -2
  14. data/app/assets/stylesheets/para/stall.sass +28 -0
  15. data/app/controllers/para/stall/admin/carts_controller.rb +27 -0
  16. data/app/controllers/stall/cart_credits_controller.rb +27 -0
  17. data/app/controllers/stall/carts_controller.rb +1 -1
  18. data/app/controllers/stall/checkout/steps_controller.rb +22 -12
  19. data/app/controllers/stall/checkouts_controller.rb +1 -0
  20. data/app/controllers/stall/line_items_controller.rb +1 -0
  21. data/app/controllers/stall/payments_controller.rb +16 -1
  22. data/app/helpers/stall/credit_notes_helper.rb +25 -0
  23. data/app/helpers/stall/customers_helper.rb +3 -1
  24. data/app/models/billing_address.rb +2 -0
  25. data/app/models/cart_credit_note_adjustment.rb +3 -0
  26. data/app/models/credit_note.rb +3 -0
  27. data/app/models/credit_note_adjustment.rb +3 -0
  28. data/app/models/credit_note_usage.rb +3 -0
  29. data/app/models/product.rb +3 -0
  30. data/app/models/product_category.rb +3 -0
  31. data/app/models/product_detail.rb +3 -0
  32. data/app/models/property.rb +3 -0
  33. data/app/models/property_value.rb +3 -0
  34. data/app/models/shipping_address.rb +2 -0
  35. data/app/models/stall/models/address.rb +2 -2
  36. data/app/models/stall/models/cart.rb +2 -15
  37. data/app/models/stall/models/cart_credit_note_adjustment.rb +11 -0
  38. data/app/models/stall/models/credit_note.rb +50 -0
  39. data/app/models/stall/models/credit_note_adjustment.rb +13 -0
  40. data/app/models/stall/models/credit_note_usage.rb +16 -0
  41. data/app/models/stall/models/customer.rb +20 -8
  42. data/app/models/stall/models/line_item.rb +9 -0
  43. data/app/models/stall/models/product.rb +45 -0
  44. data/app/models/stall/models/product_category.rb +31 -0
  45. data/app/models/stall/models/product_detail.rb +17 -0
  46. data/app/models/stall/models/product_list.rb +9 -45
  47. data/app/models/stall/models/property.rb +20 -0
  48. data/app/models/stall/models/property_value.rb +23 -0
  49. data/app/models/stall/models/variant.rb +34 -0
  50. data/app/models/stall/models/variant_property_value.rb +18 -0
  51. data/app/models/variant.rb +3 -0
  52. data/app/models/variant_property_value.rb +3 -0
  53. data/app/services/stall/cart_credit_note_creation_service.rb +40 -0
  54. data/app/services/stall/cart_payment_validation_service.rb +28 -0
  55. data/app/services/stall/cart_update_service.rb +17 -6
  56. data/app/services/stall/credit_usage_service.rb +102 -0
  57. data/app/services/stall/payment_notification_service.rb +4 -8
  58. data/app/services/stall/product_list_staleness_handling_service.rb +33 -0
  59. data/app/views/admin/addresses/_fields.html.haml +9 -0
  60. data/app/views/admin/carts/_filters.html.haml +17 -0
  61. data/app/views/admin/carts/_form.html.haml +43 -0
  62. data/app/views/admin/carts/_table.html.haml +17 -0
  63. data/app/views/admin/customers/_fields.html.haml +1 -0
  64. data/app/views/admin/line_items/_fields.html.haml +15 -0
  65. data/app/views/admin/products/_table.html.haml +12 -0
  66. data/app/views/admin/properties/_form.html.haml +6 -0
  67. data/app/views/admin/properties/_table.html.haml +8 -0
  68. data/app/views/admin/property_values/_fields.html.haml +1 -0
  69. data/app/views/admin/shipments/_fields.html.haml +7 -0
  70. data/app/views/checkout/steps/_informations.html.haml +3 -1
  71. data/app/views/checkout/steps/_payment.html.haml +2 -0
  72. data/app/views/checkout/steps/_payment_return.html.haml +0 -1
  73. data/app/views/para/admin/resources/_variant_row.html.haml +24 -0
  74. data/app/views/para/stall/inputs/_variant_select.html.haml +14 -0
  75. data/app/views/para/stall/inputs/_variants_matrix.html.haml +41 -0
  76. data/app/views/stall/addresses/_fields.html.haml +6 -12
  77. data/app/views/stall/carts/_cart.html.haml +45 -37
  78. data/app/views/stall/carts/_widget.html.haml +28 -0
  79. data/app/views/stall/carts/show.html.haml +2 -0
  80. data/app/views/stall/checkout/steps/_navigation.html.haml +13 -0
  81. data/app/views/stall/credit_note_adjustments/_form.html.haml +28 -0
  82. data/app/views/stall/line_items/_added.html.haml +2 -2
  83. data/app/views/stall/line_items/_form.html.haml +1 -1
  84. data/app/views/stall/payments/manual_payment_gateway/_form.html.haml +10 -0
  85. data/app/views/stall/shared/mailers/_cart.html.haml +1 -1
  86. data/config/locales/stall.fr.yml +82 -2
  87. data/db/migrate/20161129101956_add_type_to_stall_address_ownerships.rb +52 -0
  88. data/db/migrate/20161202080218_add_reference_to_product_lists.rb +17 -0
  89. data/db/migrate/20170118103916_create_credit_notes.rb +17 -0
  90. data/db/migrate/20170118144047_create_credit_note_adjustments.rb +13 -0
  91. data/db/migrate/20170123123115_create_stall_product_categories.rb +12 -0
  92. data/db/migrate/20170123123326_create_stall_products.rb +17 -0
  93. data/db/migrate/20170123125030_create_stall_variants.rb +13 -0
  94. data/db/migrate/20170123131748_create_stall_product_category_hierarchies.rb +16 -0
  95. data/db/migrate/20170123143704_create_stall_product_details.rb +14 -0
  96. data/db/migrate/20170125152622_convert_all_money_fields_to_decimal_to_use_infinite_precision.rb +27 -0
  97. data/db/migrate/20170131162537_add_data_to_stall_adjustments.rb +5 -0
  98. data/db/migrate/20170202165514_create_stall_properties.rb +9 -0
  99. data/db/migrate/20170202165516_create_stall_property_values.rb +13 -0
  100. data/db/migrate/20170202165518_create_stall_variant_property_values.rb +13 -0
  101. data/lib/generators/stall/install/templates/initializer.rb +21 -0
  102. data/lib/generators/stall/view/view_generator.rb +41 -19
  103. data/lib/para/stall.rb +32 -0
  104. data/lib/para/stall/inputs.rb +13 -0
  105. data/lib/para/stall/inputs/variant_input_helper.rb +34 -0
  106. data/lib/para/stall/inputs/variant_select_input.rb +79 -0
  107. data/lib/para/stall/inputs/variants_matrix_input.rb +72 -0
  108. data/lib/para/stall/routes.rb +11 -0
  109. data/lib/para/stall/variants_property_config.rb +78 -0
  110. data/lib/stall.rb +10 -0
  111. data/lib/stall/addressable.rb +11 -59
  112. data/lib/stall/addresses.rb +1 -0
  113. data/lib/stall/addresses/copier_base.rb +3 -1
  114. data/lib/stall/addresses/copy.rb +10 -0
  115. data/lib/stall/addresses/copy_source_to_target.rb +10 -24
  116. data/lib/stall/addresses/prefill_target_from_source.rb +8 -16
  117. data/lib/stall/adjustable.rb +20 -0
  118. data/lib/stall/archived_paid_cart_helper.rb +36 -0
  119. data/lib/stall/cart_helper.rb +15 -7
  120. data/lib/stall/checkout/informations_checkout_step.rb +47 -50
  121. data/lib/stall/checkout/payment_return_checkout_step.rb +4 -1
  122. data/lib/stall/checkout/step.rb +24 -3
  123. data/lib/stall/checkout/step_form.rb +11 -5
  124. data/lib/stall/checkout/wizard.rb +7 -6
  125. data/lib/stall/config.rb +9 -0
  126. data/lib/stall/default_currency_manager.rb +27 -0
  127. data/lib/stall/engine.rb +14 -3
  128. data/lib/stall/payments.rb +2 -0
  129. data/lib/stall/payments/gateway_request.rb +15 -0
  130. data/lib/stall/payments/gateway_response.rb +33 -0
  131. data/lib/stall/payments/manual_payment_gateway.rb +86 -0
  132. data/lib/stall/priceable.rb +4 -0
  133. data/lib/stall/reference_manager.rb +17 -0
  134. data/lib/stall/routes.rb +1 -0
  135. data/lib/stall/shippable.rb +18 -0
  136. data/lib/stall/total_prices_manager.rb +40 -0
  137. data/lib/stall/version.rb +1 -1
  138. metadata +120 -5
  139. data/app/models/address_ownership.rb +0 -3
  140. data/app/models/stall/models/address_ownership.rb +0 -26
@@ -0,0 +1,79 @@
1
+ module Para
2
+ module Stall
3
+ module Inputs
4
+ class VariantSelectInput < SimpleForm::Inputs::Base
5
+ include VariantInputHelper
6
+
7
+ def input(wrapper_options = nil)
8
+ ensure_target_relation_present!
9
+
10
+ template.render partial: partial_path, locals: {
11
+ form: @builder,
12
+ model: model,
13
+ attribute_name: attribute_name,
14
+ foreign_key: foreign_key,
15
+ properties: properties,
16
+ variants_data: variants_data,
17
+ price_selector: price_selector
18
+ }
19
+ end
20
+
21
+ private
22
+
23
+ def price_selector
24
+ @price_selector ||= options[:price_selector] || '[data-sellable-price]'
25
+ end
26
+
27
+ def product
28
+ @product ||= options[:product]
29
+ end
30
+
31
+ def relation
32
+ @relation ||= options.fetch(:relation, :variants)
33
+ end
34
+
35
+ # Iterate on every variant to fetch available properties and values and
36
+ # build a VariantsPropertyConfig for each available one
37
+ #
38
+ def properties
39
+ @properties ||= variants.each_with_object({}) do |variant, hash|
40
+ variant.variant_property_values.each do |variant_property_value|
41
+ property = variant_property_value.property_value.property
42
+
43
+ unless hash[property]
44
+ hash[property] = VariantsPropertyConfig.new(
45
+ product, property, variants
46
+ )
47
+ end
48
+ end
49
+ end.values
50
+ end
51
+
52
+ def variants
53
+ @variants ||= options.fetch(:variants) do
54
+ product.variants.select(&:published?)
55
+ end
56
+ end
57
+
58
+ def partial_path
59
+ options.fetch(:partial_path, 'para/stall/inputs/variant_select')
60
+ end
61
+
62
+ def foreign_key
63
+ @foreign_key ||= model.reflect_on_association(attribute_name).foreign_key
64
+ end
65
+
66
+ def variants_data
67
+ @variants_data ||= variants.map do |variant|
68
+ { id: variant.id, price: template.number_to_currency(variant.price) }.tap do |data|
69
+ properties.each do |property_config|
70
+ data[property_config.property.id] =
71
+ property_config.property_value_for(variant).id
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,72 @@
1
+ module Para
2
+ module Stall
3
+ module Inputs
4
+ class VariantsMatrixInput < SimpleForm::Inputs::Base
5
+ include VariantInputHelper
6
+
7
+ def initialize(*)
8
+ super
9
+ options[:label] = false
10
+ end
11
+
12
+ def input(wrapper_options = nil)
13
+ ensure_target_relation_present!
14
+
15
+ template.render partial: 'para/stall/inputs/variants_matrix', locals: {
16
+ form: @builder,
17
+ model: model,
18
+ attribute_name: attribute_name,
19
+ all_properties: all_properties,
20
+ properties: properties,
21
+ variants: variants,
22
+ require_all_properties: require_all_properties,
23
+ dom_identifier: dom_identifier,
24
+ variant_row_locals: variant_row_locals
25
+ }
26
+ end
27
+
28
+ private
29
+
30
+ def all_properties
31
+ @all_properties ||= Property.includes(:property_values).all.map do |property|
32
+ VariantsPropertyConfig.new(resource, property, unsorted_variants)
33
+ end
34
+ end
35
+
36
+ def properties
37
+ @properties ||= all_properties.select(&:active?)
38
+ end
39
+
40
+ def unsorted_variants
41
+ @unsorted_variants ||= resource.send(attribute_name)
42
+ end
43
+
44
+ def variants
45
+ @variants ||= unsorted_variants.sort_by(&method(:variant_sort_method))
46
+ end
47
+
48
+ def require_all_properties
49
+ options[:require_all_properties] || false
50
+ end
51
+
52
+ def dom_identifier
53
+ @dom_identifier ||= [
54
+ model.model_name.element,
55
+ resource.id || resource.object_id,
56
+ attribute_name
57
+ ].join('-')
58
+ end
59
+
60
+ def variant_row_locals
61
+ {
62
+ model: model,
63
+ attribute_name: attribute_name,
64
+ all_properties: all_properties,
65
+ properties: properties,
66
+ dom_identifier: dom_identifier
67
+ }
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,11 @@
1
+ module Para
2
+ module Stall
3
+ class Routes < Para::Plugins::Routes
4
+ def draw
5
+ plugin :stall do
6
+ crud_component controller: '/para/stall/admin/carts'
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,78 @@
1
+ module Para
2
+ module Stall
3
+ class VariantsPropertyConfig
4
+ attr_reader :resource, :property, :variants
5
+
6
+ def initialize(resource, property, variants)
7
+ @resource = resource
8
+ @property = property
9
+ @variants = variants
10
+ end
11
+
12
+ def options
13
+ property.property_values.map do |property_value|
14
+ [
15
+ property_value.value,
16
+ property_value.id,
17
+ selected: property_value_used?(property_value),
18
+ data: { name: property_value.value }
19
+ ]
20
+ end
21
+ end
22
+
23
+ def active?
24
+ variants.any? do |variant|
25
+ variant.variant_property_values.any? do |variant_property_value|
26
+ variant_property_value.property_value.try(:property) == property
27
+ end
28
+ end
29
+ end
30
+
31
+ def current_value
32
+ @current_value ||= property.property_values.find do |property_value|
33
+ property_value_used?(property_value)
34
+ end
35
+ end
36
+
37
+ def available_options
38
+ @available_options ||= property.property_values.each_with_object([]) do |property_value, ary|
39
+ if property_value_used?(property_value)
40
+ ary << [property_value.value, property_value.id]
41
+ end
42
+ end
43
+ end
44
+
45
+ def property_value_for(variant)
46
+ variant_property_value_for(variant).try(:property_value)
47
+ end
48
+
49
+ def variant_property_value_for(variant)
50
+ variant_property_value = variant.variant_property_values.find do |variant_property_value|
51
+ variant_property_value.property_value.try(:property) == property
52
+ end
53
+ end
54
+
55
+ def variant_property_value_or_build_for(variant)
56
+ variant_property_value_for(variant) || variant.variant_property_values.build
57
+ end
58
+
59
+ private
60
+
61
+ def variant_model
62
+ @variant_model ||= resource.class.reflect_on_association(relation).klass
63
+ end
64
+
65
+ def reflection
66
+ @reflection ||= variant_model.reflect_on_association(property.name)
67
+ end
68
+
69
+ def property_value_used?(property_value)
70
+ variants.any? do |variant|
71
+ variant.variant_property_values.any? do |variant_property_value|
72
+ variant_property_value.property_value == property_value
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -6,11 +6,15 @@ require 'haml-rails'
6
6
  require 'simple_form'
7
7
  require 'country_select'
8
8
  require 'cocoon'
9
+ require 'deep_merge/rails_compat'
10
+ require 'para'
9
11
 
10
12
  require 'stall/rails/routing_mapper'
11
13
  require 'stall/rails/currency_helper'
12
14
  require 'stall/engine'
13
15
 
16
+ require 'para/stall'
17
+
14
18
  module Stall
15
19
  extend ActiveSupport::Autoload
16
20
 
@@ -19,6 +23,11 @@ module Stall
19
23
  autoload :Addresses
20
24
  autoload :Priceable
21
25
  autoload :Payable
26
+ autoload :Shippable
27
+ autoload :Adjustable
28
+ autoload :DefaultCurrencyManager
29
+ autoload :ReferenceManager
30
+ autoload :TotalPricesManager
22
31
 
23
32
  autoload :Checkout
24
33
  autoload :Shipping
@@ -26,6 +35,7 @@ module Stall
26
35
 
27
36
  autoload :Routes
28
37
  autoload :CartHelper
38
+ autoload :ArchivedPaidCartHelper
29
39
  autoload :Config
30
40
  autoload :Utils
31
41
 
@@ -6,67 +6,19 @@ module Stall
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
9
- has_many :address_ownerships, as: :addressable, dependent: :destroy,
10
- inverse_of: :addressable
11
-
12
- has_many :addresses, through: :address_ownerships
13
- accepts_nested_attributes_for :address_ownerships, allow_destroy: true
9
+ has_one :shipping_address, as: :addressable,
10
+ class_name: 'ShippingAddress',
11
+ dependent: :destroy,
12
+ inverse_of: :addressable
13
+ accepts_nested_attributes_for :shipping_address, allow_destroy: true
14
+
15
+ has_one :billing_address, as: :addressable,
16
+ class_name: 'BillingAddress',
17
+ dependent: :destroy,
18
+ inverse_of: :addressable
19
+ accepts_nested_attributes_for :billing_address, allow_destroy: true
14
20
 
15
21
  attr_accessor :use_another_address_for_billing
16
22
  end
17
-
18
- def address_ownership_for(type)
19
- address_ownerships.find do |ownership|
20
- ownership.public_send(type)
21
- end if [:shipping, :billing].include?(type.to_sym)
22
- end
23
-
24
- # Provide billing and shipping address fetching and assigning shortcuts by
25
- # using the address ownerships
26
- #
27
- [:shipping, :billing].each do |type|
28
- instance_variable_name = :"@#{ type }_address"
29
-
30
- define_method(:"#{ type }_address=") do |address|
31
- ownership = address_ownership_for(type) || address_ownerships.build(type => true)
32
- ownership.address = address
33
- instance_variable_set(instance_variable_name, address)
34
- end
35
-
36
- define_method(:"build_#{ type }_address") do |attributes = {}|
37
- if (ownership = address_ownership_for(type))
38
- ownership.send(:"#{ type }=", false)
39
- end
40
-
41
- ownership = address_ownerships.build(type => true)
42
- address = ownership.build_address(attributes)
43
- instance_variable_set(instance_variable_name, address)
44
- end
45
-
46
- define_method(:"#{ type }_address") do
47
- if (address = instance_variable_get(instance_variable_name))
48
- return address
49
- end
50
-
51
- if (ownership = address_ownership_for(type))
52
- instance_variable_set(instance_variable_name, ownership.address)
53
- end
54
- end
55
-
56
- define_method(:"#{ type }_address_attributes=") do |attributes|
57
- attributes.delete(:id)
58
-
59
- ownership = address_ownership_for(type) || address_ownerships.build(type => true)
60
- address = ownership.address || ownership.build_address
61
- address.assign_attributes(attributes)
62
- instance_variable_set(instance_variable_name, address)
63
- end
64
-
65
- define_method(:"mark_address_ownership_as_#{ type }") do |ownership|
66
- ownership.send(:"mark_as_#{ type }").tap do
67
- instance_variable_set(instance_variable_name, ownership.address)
68
- end
69
- end
70
- end
71
23
  end
72
24
  end
@@ -3,6 +3,7 @@ module Stall
3
3
  extend ActiveSupport::Autoload
4
4
 
5
5
  autoload :CopierBase
6
+ autoload :Copy
6
7
  autoload :CopySourceToTarget
7
8
  autoload :PrefillTargetFromSource
8
9
  end
@@ -16,7 +16,9 @@ module Stall
16
16
 
17
17
  def duplicate_attributes(model)
18
18
  model.attributes.dup.tap do |attributes|
19
- attributes.delete('id')
19
+ %w(id type addressable_id addressable_type created_at updated_at).each do |key|
20
+ attributes.delete(key)
21
+ end
20
22
  end
21
23
  end
22
24
  end
@@ -0,0 +1,10 @@
1
+ # Allows copying
2
+ module Stall
3
+ module Addresses
4
+ class Copy < Stall::Addresses::CopierBase
5
+ def copy
6
+ target.assign_attributes(duplicate_attributes(source))
7
+ end
8
+ end
9
+ end
10
+ end
@@ -8,36 +8,22 @@ module Stall
8
8
  end
9
9
 
10
10
  def copy
11
- copy_shipping_address
12
- copy_billing_address
11
+ copy_address(:shipping)
12
+ copy_address(:billing)
13
13
  end
14
14
 
15
15
  private
16
16
 
17
- def copy_shipping_address
18
- # Update or create target's shipping address with source's shipping
19
- # address attributes
20
- if target.shipping_address
21
- target.shipping_address.assign_attributes(duplicate_attributes(source.shipping_address))
22
- else
23
- target.build_shipping_address(duplicate_attributes(source.shipping_address))
24
- end
25
- end
17
+ # Update or create target address with source attributes
18
+ #
19
+ def copy_address(type)
20
+ address = target.send(:"#{ type }_address") || target.send(:"build_#{ type }_address")
26
21
 
27
- def copy_billing_address
28
- # If the source uses the same address for shipping and billing, we
29
- # reproduce this behavior ont the target
30
- if source.shipping_address == source.billing_address
31
- shipping_ownership = target.address_ownership_for(:shipping)
32
- target.mark_address_ownership_as_billing(shipping_ownership)
33
- # If the source has a separate billing address, we update or create the
34
- # target's billing address with the source's one attributes
22
+ if (source_address = source.send(:"#{ type }_address"))
23
+ attributes = duplicate_attributes(source_address)
24
+ address.assign_attributes(attributes)
35
25
  else
36
- if target.billing_address
37
- target.billing_address.assign_attributes(duplicate_attributes(source.billing_address))
38
- else
39
- target.build_billing_address(duplicate_attributes(source.billing_address))
40
- end
26
+ address.try(:mark_for_destruction)
41
27
  end
42
28
  end
43
29
  end
@@ -2,26 +2,18 @@ module Stall
2
2
  module Addresses
3
3
  class PrefillTargetFromSource < Stall::Addresses::CopierBase
4
4
  def copy
5
- prefill_shipping_address
6
- prefill_billing_address
5
+ prefill_address(:shipping)
6
+ prefill_address(:billing)
7
7
  end
8
8
 
9
9
  private
10
10
 
11
- def prefill_shipping_address
12
- if source.shipping_address && !target.shipping_address
13
- target.build_shipping_address(duplicate_attributes(source.shipping_address))
14
- elsif !target.shipping_address
15
- target.build_shipping_address
16
- end
17
- end
18
-
19
- def prefill_billing_address
20
- if source.billing_address && !target.billing_address
21
- target.build_billing_address(duplicate_attributes(source.billing_address))
22
- elsif !target.billing_address || target.billing_address == target.shipping_address
23
- target.address_ownership_for(:shipping).billing = false
24
- target.build_billing_address
11
+ def prefill_address(type)
12
+ if source.send("#{ type }_address") && !target.send("#{ type }_address")
13
+ attributes = duplicate_attributes(source.send("#{ type }_address"))
14
+ target.send("build_#{ type }_address", attributes)
15
+ elsif !target.send("#{ type }_address")
16
+ target.send("build_#{ type }_address")
25
17
  end
26
18
  end
27
19
  end