spree_core 1.1.3 → 1.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/app/assets/images/noimage/large.png +0 -0
- data/app/assets/javascripts/admin/admin.js.erb +14 -14
- data/app/assets/javascripts/admin/checkouts/edit.js +1 -1
- data/app/assets/javascripts/admin/spree_core.js +0 -2
- data/app/assets/javascripts/store/checkout.js +2 -2
- data/app/assets/stylesheets/store/screen.css.scss +1 -1
- data/app/controllers/spree/admin/images_controller.rb +5 -13
- data/app/controllers/spree/admin/mail_methods_controller.rb +7 -0
- data/app/controllers/spree/admin/orders/customer_details_controller.rb +1 -1
- data/app/controllers/spree/admin/payment_methods_controller.rb +7 -1
- data/app/controllers/spree/admin/products_controller.rb +5 -13
- data/app/controllers/spree/admin/prototypes_controller.rb +2 -2
- data/app/controllers/spree/checkout_controller.rb +10 -0
- data/app/controllers/spree/orders_controller.rb +9 -5
- data/app/helpers/spree/base_helper.rb +17 -4
- data/app/helpers/spree/checkout_helper.rb +1 -9
- data/app/helpers/spree/products_helper.rb +0 -4
- data/app/models/spree/address.rb +1 -1
- data/app/models/spree/app_configuration.rb +1 -0
- data/app/models/spree/calculator/price_sack.rb +5 -3
- data/app/models/spree/image.rb +3 -1
- data/app/models/spree/inventory_unit.rb +8 -6
- data/app/models/spree/line_item.rb +2 -1
- data/app/models/spree/order.rb +23 -2
- data/app/models/spree/payment/processing.rb +22 -20
- data/app/models/spree/preferences/preferable_class_methods.rb +2 -0
- data/app/models/spree/preferences/store.rb +20 -2
- data/app/models/spree/product.rb +35 -29
- data/app/models/spree/product/scopes.rb +21 -6
- data/app/models/spree/return_authorization.rb +1 -0
- data/app/models/spree/shipping_method.rb +3 -0
- data/app/models/spree/variant.rb +19 -9
- data/app/views/spree/admin/images/_form.html.erb +4 -8
- data/app/views/spree/admin/return_authorizations/_form.html.erb +17 -33
- data/app/views/spree/admin/shared/_head.html.erb +2 -15
- data/app/views/spree/admin/shared/_order_details.html.erb +1 -1
- data/app/views/spree/admin/shared/_routes.html.erb +8 -0
- data/app/views/spree/admin/shared/_translations.html.erb +17 -0
- data/app/views/spree/admin/tax_rates/index.html.erb +2 -2
- data/app/views/spree/admin/variants/_form.html.erb +2 -2
- data/app/views/spree/checkout/payment/_gateway.html.erb +1 -1
- data/app/views/spree/order_mailer/cancel_email.text.erb +5 -5
- data/app/views/spree/order_mailer/confirm_email.text.erb +7 -6
- data/app/views/spree/products/_thumbnails.html.erb +1 -1
- data/app/views/spree/shared/_google_analytics.html.erb +17 -16
- data/app/views/spree/shared/_head.html.erb +0 -3
- data/app/views/spree/shared/_order_details.html.erb +2 -3
- data/app/views/spree/shipment_mailer/shipped_email.text.erb +7 -7
- data/config/initializers/check_for_orphaned_preferences.rb +1 -1
- data/config/initializers/rails_5868.rb +8 -0
- data/config/locales/en.yml +17 -0
- data/config/routes.rb +7 -0
- data/db/migrate/20121017010007_remove_not_null_constraint_from_products_on_hand.rb +11 -0
- data/lib/generators/spree/dummy/dummy_generator.rb +1 -1
- data/lib/generators/spree/dummy/templates/rails/database.yml +1 -1
- data/lib/generators/spree/install/templates/app/assets/javascripts/admin/all.js +2 -0
- data/lib/spree/core/mail_settings.rb +2 -1
- data/lib/spree/core/search/base.rb +5 -2
- data/lib/spree/core/testing_support/factories/tax_category_factory.rb +5 -2
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/product_filters.rb +1 -1
- metadata +41 -42
- data/app/helpers/spree/account_helper.rb +0 -4
- data/app/helpers/spree/orders_helper.rb +0 -5
- data/app/helpers/spree/trackers_helper.rb +0 -4
- data/config/initializers/workarounds_for_ruby19.rb +0 -72
- data/lib/spree/core/relation_serialization.rb +0 -9
- data/lib/spree/core/testing_support/env.rb +0 -2
Binary file
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
//<%#encoding: UTF-8%>
|
2
2
|
//= require_self
|
3
3
|
|
4
4
|
/**
|
@@ -61,9 +61,9 @@ format_product_autocomplete = function(item){
|
|
61
61
|
|
62
62
|
html += "<div><h4>" + product['name'] + "</h4>";
|
63
63
|
if (product['master']) {
|
64
|
-
html += "<span><strong
|
64
|
+
html += "<span><strong>" + Spree.translations.sku + ":</strong>" + product['master']['sku'] + "</span>";
|
65
65
|
}
|
66
|
-
html += "<span><strong
|
66
|
+
html += "<span><strong>" + Spree.translations.on_hand + ":</strong>" + product['count_on_hand'] + "</span></div>";
|
67
67
|
}else{
|
68
68
|
// variant
|
69
69
|
var variant = item.data['variant'];
|
@@ -82,8 +82,8 @@ format_product_autocomplete = function(item){
|
|
82
82
|
}).join(", ")
|
83
83
|
|
84
84
|
html += "<div><h4>" + name + "</h4>";
|
85
|
-
html += "<span><strong
|
86
|
-
html += "<span><strong
|
85
|
+
html += "<span><strong>" + Spree.translations.sku + ":</strong>" + variant['sku'] + "</span>";
|
86
|
+
html += "<span><strong>" + Spree.translations.on_hand + ":</strong>" + variant['count_on_hand'] + "</span></div>";
|
87
87
|
}
|
88
88
|
|
89
89
|
return html
|
@@ -164,9 +164,9 @@ $.fn.product_autocomplete = function(){
|
|
164
164
|
$.fn.objectPicker = function(url){
|
165
165
|
$(this).tokenInput(url + "&authenticity_token=" + escape(AUTH_TOKEN), {
|
166
166
|
searchDelay : 600,
|
167
|
-
hintText : Spree.
|
168
|
-
noResultsText : Spree.
|
169
|
-
searchingText : Spree.
|
167
|
+
hintText : Spree.translations.type_to_search,
|
168
|
+
noResultsText : Spree.translations.no_results,
|
169
|
+
searchingText : Spree.translations.searching,
|
170
170
|
prePopulateFromInput : true
|
171
171
|
});
|
172
172
|
};
|
@@ -181,12 +181,12 @@ $.fn.userPicker = function(){
|
|
181
181
|
|
182
182
|
handle_date_picker_fields = function(){
|
183
183
|
$('.datepicker').datepicker({
|
184
|
-
dateFormat:
|
185
|
-
dayNames:
|
186
|
-
dayNamesMin:
|
187
|
-
monthNames:
|
188
|
-
prevText:
|
189
|
-
nextText:
|
184
|
+
dateFormat: Spree.translations.date_picker,
|
185
|
+
dayNames: Spree.translations.abbr_day_names,
|
186
|
+
dayNamesMin: Spree.translations.abbr_day_names,
|
187
|
+
monthNames: Spree.translations.month_names,
|
188
|
+
prevText: Spree.translations.previous,
|
189
|
+
nextText: Spree.translations.next,
|
190
190
|
showOn: "button",
|
191
191
|
buttonImage: "<%= asset_path 'datepicker/cal.gif' %>",
|
192
192
|
buttonImageOnly: true
|
@@ -80,7 +80,7 @@ $(document).ready(function(){
|
|
80
80
|
$('#user_id').val(ui.item.data['id']);
|
81
81
|
$('#guest_checkout_true').prop("checked", false);
|
82
82
|
$('#guest_checkout_false').prop("checked", true);
|
83
|
-
$('#guest_checkout_false').
|
83
|
+
$('#guest_checkout_false').prop("disabled", false);
|
84
84
|
return true;
|
85
85
|
}
|
86
86
|
}).data("autocomplete")._renderItem = function(ul, item) {
|
@@ -16,7 +16,7 @@
|
|
16
16
|
state_input = $('p#' + region + 'state input');
|
17
17
|
|
18
18
|
if(states) {
|
19
|
-
selected = state_select.val();
|
19
|
+
selected = parseInt(state_select.val());
|
20
20
|
state_select.html('');
|
21
21
|
states_with_blank = [["",""]].concat(states);
|
22
22
|
$.each(states_with_blank, function(pos,id_nm) {
|
@@ -72,7 +72,7 @@
|
|
72
72
|
})(jQuery);
|
73
73
|
|
74
74
|
function disableSaveOnClick() {
|
75
|
-
$('form.
|
75
|
+
$('form.edit_order').submit(function() {
|
76
76
|
$(this).find(':submit, :image').attr('disabled', true).removeClass('primary').addClass('disabled');
|
77
77
|
});
|
78
78
|
}
|
@@ -19,30 +19,22 @@ module Spree
|
|
19
19
|
|
20
20
|
|
21
21
|
private
|
22
|
-
|
22
|
+
|
23
23
|
def location_after_save
|
24
24
|
admin_product_images_url(@product)
|
25
25
|
end
|
26
26
|
|
27
27
|
def load_data
|
28
|
-
@product = Product.
|
28
|
+
@product = Product.where(:permalink => params[:product_id]).first
|
29
29
|
@variants = @product.variants.collect do |variant|
|
30
30
|
[variant.options_text, variant.id]
|
31
31
|
end
|
32
|
-
@variants.insert(0, [I18n.t(:all),
|
32
|
+
@variants.insert(0, [I18n.t(:all), @product.master.id])
|
33
33
|
end
|
34
34
|
|
35
35
|
def set_viewable
|
36
|
-
|
37
|
-
|
38
|
-
@image.viewable = @product.master
|
39
|
-
else
|
40
|
-
@image.viewable_type = 'Spree::Variant'
|
41
|
-
@image.viewable_id = params[:image][:viewable_id]
|
42
|
-
end
|
43
|
-
else
|
44
|
-
@image.viewable = @product.master
|
45
|
-
end
|
36
|
+
@image.viewable_type = 'Spree::Variant'
|
37
|
+
@image.viewable_id = params[:image][:viewable_id]
|
46
38
|
end
|
47
39
|
|
48
40
|
def destroy_before
|
@@ -3,6 +3,13 @@ module Spree
|
|
3
3
|
class MailMethodsController < ResourceController
|
4
4
|
after_filter :initialize_mail_settings
|
5
5
|
|
6
|
+
def update
|
7
|
+
if params[:mail_method][:preferred_smtp_password].blank?
|
8
|
+
params[:mail_method].delete(:preferred_smtp_password)
|
9
|
+
end
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
6
13
|
def testmail
|
7
14
|
@mail_method = Spree::MailMethod.find(params[:id])
|
8
15
|
if TestMailer.test_email(@mail_method, respond_to?(:current_user) ? current_user : nil).deliver
|
@@ -10,7 +10,7 @@ module Spree
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def edit
|
13
|
-
country_id = Address.default.country
|
13
|
+
country_id = Address.default.country.id
|
14
14
|
@order.build_bill_address(:country_id => country_id) if @order.bill_address.nil?
|
15
15
|
@order.build_ship_address(:country_id => country_id) if @order.ship_address.nil?
|
16
16
|
end
|
@@ -27,7 +27,13 @@ module Spree
|
|
27
27
|
end
|
28
28
|
|
29
29
|
payment_method_params = params[ActiveModel::Naming.param_key(@payment_method)] || {}
|
30
|
-
|
30
|
+
attributes = params[:payment_method].merge(payment_method_params)
|
31
|
+
attributes.each do |k,v|
|
32
|
+
if k.include?("password") && attributes[k].blank?
|
33
|
+
attributes.delete(k)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
if @payment_method.update_attributes(attributes)
|
31
37
|
invoke_callbacks(:update, :after)
|
32
38
|
flash.notice = I18n.t(:successfully_updated, :resource => I18n.t(:payment_method))
|
33
39
|
respond_with(@payment_method, :location => edit_admin_payment_method_path(@payment_method))
|
@@ -19,13 +19,9 @@ module Spree
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
# override the destory method to set deleted_at value
|
23
|
-
# instead of actually deleting the product.
|
24
22
|
def destroy
|
25
|
-
@product = Product.
|
26
|
-
@product.
|
27
|
-
|
28
|
-
@product.variants_including_master.update_all(:deleted_at => Time.now)
|
23
|
+
@product = Product.where(:permalink => params[:id]).first!
|
24
|
+
@product.delete
|
29
25
|
|
30
26
|
flash.notice = I18n.t('notice_messages.product_deleted')
|
31
27
|
|
@@ -90,13 +86,9 @@ module Spree
|
|
90
86
|
includes([:master, {:variants => [:images, :option_values]}]).
|
91
87
|
page(params[:page]).
|
92
88
|
per(Spree::Config[:admin_products_per_page])
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
# It seems to only work when the price is actually being sorted in the query
|
97
|
-
# To be investigated later.
|
98
|
-
@collection = @collection.group("spree_variants.price")
|
99
|
-
end
|
89
|
+
if params[:q][:s].include?("master_price")
|
90
|
+
@collection = @collection.group("spree_variants.price")
|
91
|
+
end
|
100
92
|
else
|
101
93
|
includes = [{:variants => [:images, {:option_values => :option_type}]}, {:master => :images}]
|
102
94
|
|
@@ -29,8 +29,8 @@ module Spree
|
|
29
29
|
private
|
30
30
|
|
31
31
|
def set_habtm_associations
|
32
|
-
@prototype.property_ids = params[:
|
33
|
-
@prototype.option_type_ids = params[:option_type][
|
32
|
+
@prototype.property_ids = params[:option_type].blank? ? [] : params[:property][:id]
|
33
|
+
@prototype.option_type_ids = params[:option_type].blank? ? [] : params[:option_type][:id]
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
@@ -6,6 +6,8 @@ module Spree
|
|
6
6
|
ssl_required
|
7
7
|
|
8
8
|
before_filter :load_order
|
9
|
+
before_filter :ensure_valid_state
|
10
|
+
|
9
11
|
rescue_from Spree::Core::GatewayError, :with => :rescue_from_spree_gateway_error
|
10
12
|
|
11
13
|
respond_to :html
|
@@ -36,6 +38,14 @@ module Spree
|
|
36
38
|
end
|
37
39
|
|
38
40
|
private
|
41
|
+
def ensure_valid_state
|
42
|
+
if params[:state] && params[:state] != 'cart' &&
|
43
|
+
!@order.checkout_steps.include?(params[:state])
|
44
|
+
@order.state = 'cart'
|
45
|
+
redirect_to checkout_path
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
39
49
|
# Provides a route to redirect after order completion
|
40
50
|
def completion_route
|
41
51
|
order_path(@order)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module Spree
|
2
2
|
class OrdersController < BaseController
|
3
|
+
ssl_required :show
|
4
|
+
|
3
5
|
rescue_from ActiveRecord::RecordNotFound, :with => :render_404
|
4
6
|
helper 'spree/products'
|
5
7
|
|
@@ -13,7 +15,7 @@ module Spree
|
|
13
15
|
def update
|
14
16
|
@order = current_order
|
15
17
|
if @order.update_attributes(params[:order])
|
16
|
-
@order.line_items = @order.line_items.select {|li| li.quantity > 0 }
|
18
|
+
@order.line_items = @order.line_items.select { |li| li.quantity > 0 }
|
17
19
|
fire_event('spree.order.contents_changed')
|
18
20
|
respond_with(@order) { |format| format.html { redirect_to cart_path } }
|
19
21
|
else
|
@@ -57,14 +59,16 @@ module Spree
|
|
57
59
|
|
58
60
|
def empty
|
59
61
|
if @order = current_order
|
60
|
-
@order.
|
62
|
+
@order.empty!
|
61
63
|
end
|
62
64
|
|
63
65
|
redirect_to spree.cart_path
|
64
66
|
end
|
65
67
|
|
66
|
-
|
67
|
-
|
68
|
-
|
68
|
+
private
|
69
|
+
|
70
|
+
def accurate_title
|
71
|
+
@order && @order.completed? ? "#{Order.model_name.human} #{@order.number}" : t(:shopping_cart)
|
72
|
+
end
|
69
73
|
end
|
70
74
|
end
|
@@ -65,13 +65,22 @@ module Spree
|
|
65
65
|
|
66
66
|
def meta_data_tags
|
67
67
|
object = instance_variable_get('@'+controller_name.singularize)
|
68
|
-
meta = {
|
68
|
+
meta = {}
|
69
69
|
|
70
|
-
if object.kind_of?
|
70
|
+
if object.kind_of? ActiveRecord::Base
|
71
71
|
meta[:keywords] = object.meta_keywords if object[:meta_keywords].present?
|
72
72
|
meta[:description] = object.meta_description if object[:meta_description].present?
|
73
73
|
end
|
74
74
|
|
75
|
+
if meta[:description].blank? && object.kind_of?(Spree::Product)
|
76
|
+
meta[:description] = strip_tags(object.description)
|
77
|
+
end
|
78
|
+
|
79
|
+
meta.reverse_merge!({
|
80
|
+
:keywords => Spree::Config[:default_meta_keywords],
|
81
|
+
:description => Spree::Config[:default_meta_description]
|
82
|
+
})
|
83
|
+
|
75
84
|
meta.map do |name, content|
|
76
85
|
tag('meta', :name => name, :content => content)
|
77
86
|
end.join("\n")
|
@@ -86,9 +95,13 @@ module Spree
|
|
86
95
|
link_to image_tag(image_path), root_path
|
87
96
|
end
|
88
97
|
|
89
|
-
def flash_messages
|
98
|
+
def flash_messages(opts = {})
|
99
|
+
opts[:ignore_types] = [:commerce_tracking].concat(opts[:ignore_types] || [])
|
100
|
+
|
90
101
|
flash.each do |msg_type, text|
|
91
|
-
|
102
|
+
unless opts[:ignore_types].include?(msg_type)
|
103
|
+
concat(content_tag :div, text, :class => "flash #{msg_type}")
|
104
|
+
end
|
92
105
|
end
|
93
106
|
nil
|
94
107
|
end
|
@@ -1,15 +1,7 @@
|
|
1
1
|
module Spree
|
2
2
|
module CheckoutHelper
|
3
|
-
def checkout_states
|
4
|
-
if @order.payment and @order.payment.payment_method.payment_profiles_supported?
|
5
|
-
%w(address delivery payment confirm complete)
|
6
|
-
else
|
7
|
-
%w(address delivery payment complete)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
3
|
def checkout_progress
|
12
|
-
states =
|
4
|
+
states = @order.checkout_steps
|
13
5
|
items = states.map do |state|
|
14
6
|
text = t("order_state.#{state}").titleize
|
15
7
|
|
@@ -17,10 +17,6 @@ module Spree
|
|
17
17
|
raw(product.description.gsub(/(.*?)\r?\n\r?\n/m, '<p>\1</p>'))
|
18
18
|
end
|
19
19
|
|
20
|
-
def variant_images_hash(product)
|
21
|
-
product.variant_images.inject({}) { |h, img| (h[img.viewable_id] ||= []) << img; h }
|
22
|
-
end
|
23
|
-
|
24
20
|
def line_item_description(variant)
|
25
21
|
description = variant.product.description
|
26
22
|
if description.present?
|
data/app/models/spree/address.rb
CHANGED
@@ -77,6 +77,7 @@ module Spree
|
|
77
77
|
preference :s3_headers, :string, :default => "{\"Cache-Control\":\"max-age=31557600\"}"
|
78
78
|
preference :use_s3, :boolean, :default => false # Use S3 for images rather than the file system
|
79
79
|
preference :s3_protocol, :string
|
80
|
+
preference :s3_host_alias, :string
|
80
81
|
|
81
82
|
# searcher_class allows spree extension writers to provide their own Search class
|
82
83
|
def searcher_class
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require_dependency 'spree/calculator'
|
2
|
+
# For #to_d method on Ruby 1.8
|
3
|
+
require 'bigdecimal/util'
|
2
4
|
|
3
5
|
module Spree
|
4
6
|
class Calculator::PriceSack < Calculator
|
@@ -17,12 +19,12 @@ module Spree
|
|
17
19
|
# as object we always get line items, as calculable we have Coupon, ShippingMethod
|
18
20
|
def compute(object)
|
19
21
|
if object.is_a?(Array)
|
20
|
-
base = object.map { |o| o.respond_to?(:amount) ? o.amount : o.
|
22
|
+
base = object.map { |o| o.respond_to?(:amount) ? o.amount : BigDecimal(o.to_s) }.sum
|
21
23
|
else
|
22
|
-
base = object.respond_to?(:amount) ? object.amount : object.
|
24
|
+
base = object.respond_to?(:amount) ? object.amount : BigDecimal(object.to_s)
|
23
25
|
end
|
24
26
|
|
25
|
-
if base
|
27
|
+
if base < self.preferred_minimal_amount
|
26
28
|
self.preferred_normal_amount
|
27
29
|
else
|
28
30
|
self.preferred_discount_amount
|
data/app/models/spree/image.rb
CHANGED
@@ -9,7 +9,8 @@ module Spree
|
|
9
9
|
:styles => { :mini => '48x48>', :small => '100x100>', :product => '240x240>', :large => '600x600>' },
|
10
10
|
:default_style => :product,
|
11
11
|
:url => '/spree/products/:id/:style/:basename.:extension',
|
12
|
-
:path => ':rails_root/public/spree/products/:id/:style/:basename.:extension'
|
12
|
+
:path => ':rails_root/public/spree/products/:id/:style/:basename.:extension',
|
13
|
+
:convert_options => { :all => '-strip' }
|
13
14
|
# save the w,h of the original image (from which others can be calculated)
|
14
15
|
# we need to look at the write-queue for images which have not been saved yet
|
15
16
|
after_post_process :find_dimensions
|
@@ -22,6 +23,7 @@ module Spree
|
|
22
23
|
Spree::Image.attachment_definitions[:attachment][:s3_headers] = ActiveSupport::JSON.decode(Spree::Config[:s3_headers])
|
23
24
|
Spree::Image.attachment_definitions[:attachment][:bucket] = Spree::Config[:s3_bucket]
|
24
25
|
Spree::Image.attachment_definitions[:attachment][:s3_protocol] = Spree::Config[:s3_protocol] unless Spree::Config[:s3_protocol].blank?
|
26
|
+
Spree::Image.attachment_definitions[:attachment][:s3_host_alias] = Spree::Config[:s3_host_alias] unless Spree::Config[:s3_host_alias].blank?
|
25
27
|
end
|
26
28
|
|
27
29
|
Spree::Image.attachment_definitions[:attachment][:styles] = ActiveSupport::JSON.decode(Spree::Config[:attachment_styles])
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module Spree
|
2
2
|
class InventoryUnit < ActiveRecord::Base
|
3
|
-
belongs_to :variant
|
4
|
-
belongs_to :order
|
5
|
-
belongs_to :shipment
|
6
|
-
belongs_to :return_authorization
|
3
|
+
belongs_to :variant
|
4
|
+
belongs_to :order
|
5
|
+
belongs_to :shipment
|
6
|
+
belongs_to :return_authorization
|
7
7
|
|
8
8
|
scope :backorder, where(:state => 'backordered')
|
9
9
|
|
@@ -107,8 +107,10 @@ module Spree
|
|
107
107
|
end
|
108
108
|
|
109
109
|
def restock_variant
|
110
|
-
|
111
|
-
|
110
|
+
if Spree::Config[:track_inventory_levels]
|
111
|
+
variant.on_hand += 1
|
112
|
+
variant.save
|
113
|
+
end
|
112
114
|
end
|
113
115
|
end
|
114
116
|
end
|