stall 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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