ba_spree 1.0.0

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 (160) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +673 -0
  3. data/README.md +150 -0
  4. data/app/assets/config/manifest.js +1 -0
  5. data/app/assets/javascripts/ba_spree/admin_overrides/query_builder_controller.js +505 -0
  6. data/app/assets/javascripts/ba_spree/storefront/ba_spree_local_time.js +9 -0
  7. data/app/assets/stylesheets/spree/admin/custom.scss +40 -0
  8. data/app/controllers/ba_spree/checkout_controller_decorator.rb +111 -0
  9. data/app/controllers/ba_spree/store_controller_decorator.rb +55 -0
  10. data/app/controllers/concerns/ba_spree/theme_concern_decorator.rb +16 -0
  11. data/app/controllers/spree/admin/assets_controller_decorator.rb +48 -0
  12. data/app/controllers/spree/admin/products_controller_decorator.rb +25 -0
  13. data/app/controllers/spree/admin/promotion_rules_controller_decorator.rb +30 -0
  14. data/app/controllers/spree/admin/users_controller_decorator.rb +34 -0
  15. data/app/controllers/spree/contacts_controller_decorator.rb +23 -0
  16. data/app/controllers/spree/digital_links_controller.rb +61 -0
  17. data/app/controllers/spree/orders_controller_decorator.rb +24 -0
  18. data/app/decorators/ba_spree/product_pricing_decorator.rb +65 -0
  19. data/app/decorators/spree/current_decorator.rb +17 -0
  20. data/app/decorators/spree/money_decorator.rb +51 -0
  21. data/app/decorators/spree/order_email_subscriber_decorator.rb +64 -0
  22. data/app/decorators/spree/page_sections/featured_posts_decorator.rb +17 -0
  23. data/app/helpers/currency_rounding_helper.rb +10 -0
  24. data/app/helpers/spree/admin/filter_params_preservation.rb +130 -0
  25. data/app/helpers/spree/admin/navigation_helper_decorator.rb +23 -0
  26. data/app/helpers/spree/admin/promotion_rules_helper_decorator.rb +33 -0
  27. data/app/helpers/spree/admin/table_helper_decorator.rb +40 -0
  28. data/app/helpers/spree/base_helper_decorator.rb +63 -0
  29. data/app/helpers/spree/checkout_analytics_helper_decorator.rb +32 -0
  30. data/app/javascript/ba_spree/controllers/disable_when_hidden_controller.js +70 -0
  31. data/app/javascript/ba_spree/index.js +11 -0
  32. data/app/javascript/spree/admin/controllers/calendar_range_controller.js +61 -0
  33. data/app/javascript/spree/admin/controllers/slug_form_controller.js +17 -0
  34. data/app/models/ba_spree/address_decorator.rb +43 -0
  35. data/app/models/ba_spree/adjustment_decorator.rb +13 -0
  36. data/app/models/ba_spree/order_decorator.rb +95 -0
  37. data/app/models/ba_spree/order_updater_decorator.rb +33 -0
  38. data/app/models/ba_spree/payment_method_decorator.rb +10 -0
  39. data/app/models/ba_spree/product_decorator.rb +44 -0
  40. data/app/models/ba_spree/store_decorator.rb +23 -0
  41. data/app/models/ba_spree/variant_decorator.rb +49 -0
  42. data/app/models/concerns/spree/adjustment_source_decorator.rb +66 -0
  43. data/app/models/concerns/spree/product_property_metafields.rb +52 -0
  44. data/app/models/spree/adjustable/adjuster/promotion_decorator.rb +16 -0
  45. data/app/models/spree/admin/table/filter_decorator.rb +52 -0
  46. data/app/models/spree/admin/table/filter_group_decorator.rb +64 -0
  47. data/app/models/spree/asset_decorator.rb +102 -0
  48. data/app/models/spree/contact_decorator.rb +22 -0
  49. data/app/models/spree/digital_link_decorator.rb +9 -0
  50. data/app/models/spree/export_decorator.rb +34 -0
  51. data/app/models/spree/line_item_destroy_callback_decorator.rb +30 -0
  52. data/app/models/spree/metafield_definition_decorator.rb +34 -0
  53. data/app/models/spree/option_type_decorator.rb +22 -0
  54. data/app/models/spree/option_value_decorator.rb +19 -0
  55. data/app/models/spree/option_value_variant_decorator.rb +30 -0
  56. data/app/models/spree/order/address_book_decorator.rb +14 -0
  57. data/app/models/spree/order_merger_decorator.rb +29 -0
  58. data/app/models/spree/price_rules/customer_group_rule_decorator.rb +18 -0
  59. data/app/models/spree/price_rules/user_rule_decorator.rb +25 -0
  60. data/app/models/spree/price_rules/volume_rule_decorator.rb +25 -0
  61. data/app/models/spree/price_rules/zone_rule_decorator.rb +25 -0
  62. data/app/models/spree/pricing/batch_resolver.rb +96 -0
  63. data/app/models/spree/promotion_handler/cart_decorator.rb +51 -0
  64. data/app/models/spree/stock/packer_decorator.rb +61 -0
  65. data/app/models/spree/stock/prioritizer_decorator.rb +27 -0
  66. data/app/models/spree/tax_rate_decorator.rb +93 -0
  67. data/app/models/spree/taxon_decorator.rb +42 -0
  68. data/app/models/spree/user_bill_address_sync_decorator.rb +33 -0
  69. data/app/overrides/add_ba_spree_storefront_javascript_importmap_tags.rb +8 -0
  70. data/app/overrides/add_cache_to_product_listing_page.rb +41 -0
  71. data/app/overrides/add_class_to_login_link.rb +6 -0
  72. data/app/overrides/add_confirm_state_to_checkout_content_block.rb.rb +30 -0
  73. data/app/overrides/add_promotions_nav.rb +8 -0
  74. data/app/overrides/add_turbo_frame_to_admin_order_line_item_edit_link.rb +23 -0
  75. data/app/overrides/add_turbo_frame_to_promotion_action_edit_link.rb +20 -0
  76. data/app/overrides/add_turbo_stream_format_to_cart_path_in_cart_pane.rb +8 -0
  77. data/app/overrides/ba_spree_local_time_add_custom_javascript_import_map_tags.rb +8 -0
  78. data/app/overrides/disable_hidden_bill_address_fields_on_payment.rb +15 -0
  79. data/app/overrides/disable_turbo_on_user_registration_form.rb +18 -0
  80. data/app/overrides/fix_coupon_apply_button_layout.rb +7 -0
  81. data/app/overrides/hide_gift_cards_and_store_credits_from_account_nav.rb +11 -0
  82. data/app/overrides/hide_shipping_message_for_digital_only_orders.rb +15 -0
  83. data/app/overrides/localize_apply_button_text.rb +6 -0
  84. data/app/overrides/localize_newsletter_button_text.rb +6 -0
  85. data/app/overrides/replace_announcement_bar_text.rb +6 -0
  86. data/app/overrides/replace_button_edit_checkout.rb +10 -0
  87. data/app/overrides/replace_checkout_delivery_info.rb +6 -0
  88. data/app/overrides/replace_checkout_pay_button_with_dynamic_label.rb +14 -0
  89. data/app/overrides/replace_variant_not_available_button.rb +19 -0
  90. data/app/overrides/set_logo_width_height_in_admin_store_basic_form.rb +12 -0
  91. data/app/overrides/show_breadcrumbs_on_taxon_page.rb +30 -0
  92. data/app/overrides/show_individual_adjustments_in_order_details.rb +18 -0
  93. data/app/overrides/show_only_selected_payment_method_on_confirm.rb +29 -0
  94. data/app/overrides/spree/admin/preserve_filter_params_across_table_forms.rb +64 -0
  95. data/app/overrides/spree/admin/promotion_rules/_promotion_rule/set_turbo_frame_on_edit_link.html.erb.deface +11 -0
  96. data/app/overrides/spree/admin/stores/add_special_instruction_message_settings.rb +17 -0
  97. data/app/overrides/use_absolute_time_for_order_header_completed_at.rb +15 -0
  98. data/app/presenters/spree/mega_nav_presenter_decorator.rb +26 -0
  99. data/app/services/ba_spree/setup_service.rb +144 -0
  100. data/app/services/spree/assets/position_normalizer.rb +67 -0
  101. data/app/services/spree/products/duplicator_decorator.rb +39 -0
  102. data/app/services/spree/products/prepare_nested_attributes_decorator.rb +58 -0
  103. data/app/views/spree/addresses/edit.html.erb +64 -0
  104. data/app/views/spree/admin/orders/_order.html.erb +32 -0
  105. data/app/views/spree/admin/orders/_summary.html.erb +122 -0
  106. data/app/views/spree/admin/shared/_custom_head.html.erb +56 -0
  107. data/app/views/spree/admin/shared/sidebar/_promotions_nav.html.erb +55 -0
  108. data/app/views/spree/admin/stores/_special_instruction_message_settings.html.erb +14 -0
  109. data/app/views/spree/checkout/_accept_marketing_checkbox.html.erb +40 -0
  110. data/app/views/spree/checkout/_address.html.erb +135 -0
  111. data/app/views/spree/checkout/_confirm.html.erb +25 -0
  112. data/app/views/spree/checkout/_line_item.html.erb +26 -0
  113. data/app/views/spree/checkout/_payment_method_confirm.html.erb +8 -0
  114. data/app/views/spree/checkout/_payment_methods.html.erb +88 -0
  115. data/app/views/spree/checkout/_summary.html.erb +146 -0
  116. data/app/views/spree/shared/_purchased_items_table.html.erb +60 -0
  117. data/app/views/spree/shared/_purchased_items_table.text.erb +24 -0
  118. data/app/views/themes/default/spree/orders/_empty.html.erb +14 -0
  119. data/app/views/themes/default/spree/orders/_summary.html.erb +87 -0
  120. data/app/views/themes/default/spree/orders/edit.turbo_stream.erb +10 -0
  121. data/app/views/themes/default/spree/page_sections/_featured_taxons.html.erb +38 -0
  122. data/app/views/themes/default/spree/page_sections/_header.html.erb +124 -0
  123. data/app/views/themes/default/spree/page_sections/_newsletter.html.erb +4 -0
  124. data/app/views/themes/default/spree/page_sections/_post_details.html.erb +22 -0
  125. data/app/views/themes/default/spree/page_sections/_product_details.html.erb +88 -0
  126. data/app/views/themes/default/spree/page_sections/_product_grid.html.erb +58 -0
  127. data/app/views/themes/default/spree/page_sections/nav/_desktop.html.erb +94 -0
  128. data/app/views/themes/default/spree/page_sections/nav/_mobile.html.erb +148 -0
  129. data/app/views/themes/default/spree/posts/_post.html.erb +16 -0
  130. data/app/views/themes/default/spree/products/_description.html.erb +7 -0
  131. data/app/views/themes/default/spree/products/_details.html.erb +65 -0
  132. data/app/views/themes/default/spree/products/_json_ld_variant.html.erb +15 -0
  133. data/app/views/themes/default/spree/products/_price.html.erb +76 -0
  134. data/app/views/themes/default/spree/products/_swiper.html.erb +90 -0
  135. data/app/views/themes/default/spree/shared/_account_pane.html.erb +18 -0
  136. data/app/views/themes/default/spree/shared/_cart_icon.html.erb +19 -0
  137. data/app/views/themes/default/spree/shared/_line_item_options.html.erb +9 -0
  138. data/app/views/themes/default/spree/shared/_logo.html.erb +39 -0
  139. data/app/views/themes/default/spree/shared/_nav_taxon_square_image.html.erb +8 -0
  140. data/app/views/themes/default/spree/shared/_order_shipment.html.erb +79 -0
  141. data/app/views/themes/default/spree/shared/_search.html.erb +33 -0
  142. data/app/views/themes/default/spree/shared/icons/_cart.html.erb +6 -0
  143. data/app/views/themes/default/spree/shared/icons/_cart_48.html.erb +8 -0
  144. data/app/views/themes/default/spree/shared/icons/_heart.html.erb +15 -0
  145. data/config/importmap.rb +12 -0
  146. data/config/initializers/admin_navigation.rb +43 -0
  147. data/config/initializers/spree.rb +6 -0
  148. data/config/initializers/spree_admin_label_i18n_alias.rb +52 -0
  149. data/config/initializers/spree_admin_tables.rb +57 -0
  150. data/config/initializers/spree_permitted_attributes.rb +1 -0
  151. data/config/locales/en.yml +15 -0
  152. data/config/locales/ja.yml +18 -0
  153. data/config/locales/spree.en.yml +4 -0
  154. data/config/locales/spree.ja.yml +3493 -0
  155. data/lib/ba_spree/engine.rb +41 -0
  156. data/lib/ba_spree/spree_events_dedup_patch.rb +44 -0
  157. data/lib/ba_spree/version.rb +3 -0
  158. data/lib/ba_spree.rb +20 -0
  159. data/lib/generators/ba_spree/install/install_generator.rb +85 -0
  160. metadata +594 -0
@@ -0,0 +1,34 @@
1
+ module Spree
2
+ module ExportDecorator
3
+ # @gem-override spree_core-5.3.6/app/models/spree/export.rb#generate_csv
4
+ # @see https://github.com/be-agile/giga-repeat/issues/1271
5
+ # 標準CSVエクスポートは UTF-8(BOMなし)で出力するため、Windows/Excel や
6
+ # 日本語エディタが Shift_JIS と誤判定して文字化けする。生成後のファイル先頭に
7
+ # UTF-8 BOM を付与して、UTF-8 として正しく認識されるようにする。
8
+ #
9
+ # Spree 5.3 追従: メソッド本体(FormulaSanitizer 等)は super 経由で取り込むため、
10
+ # 本 override はファイルへの BOM 付与のみを追加する設計(upstream 追従)。
11
+ def generate_csv
12
+ super
13
+ prepend_utf8_bom(export_tmp_file_path)
14
+ end
15
+
16
+ private
17
+
18
+ def prepend_utf8_bom(path)
19
+ return unless ::File.exist?(path)
20
+
21
+ bom = "\xEF\xBB\xBF".b
22
+ return if ::File.binread(path, bom.bytesize) == bom # 既に BOM 付きなら何もしない
23
+
24
+ tmp_path = "#{path}.bom"
25
+ ::File.open(tmp_path, 'wb') do |out|
26
+ out.write(bom)
27
+ ::File.open(path, 'rb') { |input| ::IO.copy_stream(input, out) }
28
+ end
29
+ ::File.rename(tmp_path, path)
30
+ end
31
+ end
32
+ end
33
+
34
+ Spree::Export.prepend(Spree::ExportDecorator)
@@ -0,0 +1,30 @@
1
+ module Spree
2
+ # 明細(line_item)削除時に、在庫を戻してから inventory_unit を消す順序を保証する。
3
+ #
4
+ # Spree 5.3.6 で `has_many :inventory_units` に `dependent: :destroy` が付いた結果、
5
+ # アソシエーション宣言の時点で登録される before_destroy が
6
+ # `verify_order_inventory_before_destroy` より先に走るようになった。
7
+ # そのため Spree::OrderInventory#verify に到達した時点で inventory_units が 0 件になっており、
8
+ # 「削除」ではなく「追加」に分岐して次の 2 つの不具合が起きる。
9
+ #
10
+ # 1. 削除しようとしている line_item を指す inventory_unit を作り直してしまう。
11
+ # line_item が消えたあと orphan となり、Spree::Stock::ContentItem#amount が
12
+ # nil * quantity で NoMethodError を投げる (管理画面の明細削除が 500)
13
+ # 2. remove_from_shipment を通らないため restock されず、逆に unstock される (在庫が戻らない)
14
+ #
15
+ # ActiveSupport::Callbacks は同じシンボルのコールバックを重複排除するため、
16
+ # prepend: true で登録し直すだけで元の登録は取り除かれる (skip_callback は不要)。
17
+ # これにより 5.1.x と同じ順序に戻る。
18
+ #
19
+ # @gem-override spree_core-5.3.6/app/models/spree/line_item.rb
20
+ # @see https://github.com/be-agile/giga-repeat/issues/1298
21
+ module LineItemDestroyCallbackDecorator
22
+ def self.prepended(base)
23
+ base.before_destroy :verify_order_inventory_before_destroy,
24
+ prepend: true,
25
+ if: -> { order.has_checkout_step?('delivery') }
26
+ end
27
+ end
28
+ end
29
+
30
+ Spree::LineItem.prepend Spree::LineItemDestroyCallbackDecorator
@@ -0,0 +1,34 @@
1
+ module Spree
2
+ # @gem-override spree_core-5.3.6/app/models/spree/metafield_definition.rb
3
+ # reason: 本家 MetafieldDefinition は key/namespace に
4
+ # `normalizes :key, with: ->(v){ v.parameterize.underscore.strip }` を掛けており、
5
+ # parameterize が非ASCII(日本語)を除去するため、日本語のプロパティ名(「地図形式」「提供形態」等)を
6
+ # key にすると空文字になり、key 必須・name 必須(set_name_from_key も空)でバリデーションに失敗する。
7
+ # ba_spree は既に Property / OptionType / OptionValue / FilterParam で
8
+ # 「parameterize を使わず日本語を保持する」方針を採用済み(ba_spree の各 *_decorator 参照)。
9
+ # MetafieldDefinition も同方針に揃え、Spree 5.2 の product properties → metafields 移行で
10
+ # 日本語のプロパティ名をそのまま key/name として保持できるようにする。
11
+ #
12
+ # 実装メモ:
13
+ # 本家の normalizes は decorate_attributes で属性の型を NormalizedValueType でラップし、
14
+ # before_validation(:normalize_changed_in_place_attributes) でも再正規化する。
15
+ # 後から normalizes を再宣言しても元の parameterize が累積して残るため、
16
+ # いったん `attribute` で型をリセットしてラップを外し、normalized_attributes からも除外したうえで、
17
+ # parameterize を使わない自前の正規化(前後空白の除去のみ)を宣言し直す。
18
+ module MetafieldDefinitionDecorator
19
+ extend ActiveSupport::Concern
20
+
21
+ included do
22
+ # 本家 normalizes(parameterize)のラップを外す
23
+ attribute :key, :string
24
+ attribute :namespace, :string
25
+ self.normalized_attributes -= [ :key, :namespace ]
26
+
27
+ # parameterize せず、前後空白の除去のみ(日本語を保持)
28
+ normalizes :key, with: ->(value) { value.to_s.strip }
29
+ normalizes :namespace, with: ->(value) { value.to_s.strip }
30
+ end
31
+ end
32
+ end
33
+
34
+ Spree::MetafieldDefinition.include(Spree::MetafieldDefinitionDecorator)
@@ -0,0 +1,22 @@
1
+ module Spree
2
+ module OptionTypeDecorator
3
+ # @gem-override spree_core-5.3.6/app/models/spree/option_type.rb#filter_param
4
+ # @see https://github.com/be-agile/giga-repeat/commit/108316dd2cab88a61933080614e15663517c7291
5
+ # Spree 5.3 追従: 本家変更は deprecation 文言のみ。本実装は日本語保持のため独自(name.parameterize を使わない)。
6
+ def filter_param
7
+ # parameterizeを使わず、空白を_に置換し小文字化
8
+ name.to_s.gsub(/\s+/, '_').downcase
9
+ end
10
+
11
+ # @gem-override spree_core-5.3.6/app/models/concerns/spree/parameterizable_name.rb#normalize_name
12
+ # @see https://github.com/be-agile/giga-repeat/commit/108316dd2cab88a61933080614e15663517c7291
13
+ def normalize_name
14
+ # 日本語などの非ASCII文字を保持するための修正
15
+ # parameterizeを使わず、空白の除去と小文字化のみを行う
16
+ self.name = name.to_s.strip.downcase
17
+ end
18
+ end
19
+ end
20
+
21
+ # Decoratorを適用
22
+ Spree::OptionType.prepend(Spree::OptionTypeDecorator)
@@ -0,0 +1,19 @@
1
+ module Spree
2
+ module OptionValueDecorator
3
+ def filter_param
4
+ # parameterizeを使わず、空白を_に置換し小文字化
5
+ name.to_s.gsub(/\s+/, '_').downcase
6
+ end
7
+
8
+ # @gem-override spree_core-5.3.6/app/models/concerns/spree/parameterizable_name.rb#normalize_name
9
+ # @see https://github.com/be-agile/giga-repeat/commit/108316dd2cab88a61933080614e15663517c7291
10
+ def normalize_name
11
+ # 日本語などの非ASCII文字を保持するための修正
12
+ # parameterizeを使わず、空白の除去と小文字化のみを行う
13
+ self.name = name.to_s.strip.downcase
14
+ end
15
+ end
16
+ end
17
+
18
+ # Decoratorを適用
19
+ Spree::OptionValue.prepend(Spree::OptionValueDecorator)
@@ -0,0 +1,30 @@
1
+ module Spree
2
+ module OptionValueVariantDecorator
3
+ def self.prepended(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ # @gem-override spree_core-5.3.6/app/models/spree/base.rb#to_tom_select_json
9
+ # @see https://github.com/be-agile/giga-repeat/commit/108316dd2cab88a61933080614e15663517c7291
10
+ # カスタムto_tom_select_jsonメソッドの実装
11
+ # OptionValueVariantにはnameがないため、関連するOptionValueとOptionTypeから情報を取得
12
+ def to_tom_select_json
13
+ joins(:option_value)
14
+ .joins('JOIN spree_option_types ON spree_option_types.id = spree_option_values.option_type_id')
15
+ .select('spree_option_value_variants.id,
16
+ spree_option_values.presentation AS value_presentation,
17
+ spree_option_types.presentation AS type_presentation')
18
+ .map do |ov|
19
+ {
20
+ id: ov.id,
21
+ name: "#{ov.type_presentation}: #{ov.value_presentation}"
22
+ }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ # Decoratorを適用
30
+ Spree::OptionValueVariant.prepend(Spree::OptionValueVariantDecorator)
@@ -0,0 +1,14 @@
1
+ module Spree
2
+ module Order::AddressBookDecorator
3
+ # @gem-override spree_core-5.3.6/app/models/spree/order/address_book.rb#ship_address_attributes=
4
+ # @see https://github.com/be-agile/giga-repeat/commit/e57a39c4
5
+ # 本家との差分は user.reload を挟む点(チェックアップ住所ステップ通過時のデフォルト住所同期ズレ対策)。
6
+ def ship_address_attributes=(attributes)
7
+ self.ship_address = update_or_create_address(attributes)
8
+ user.reload.ship_address = ship_address if should_assign_user_default_address?(ship_address)
9
+ self.ship_address = nil if quick_checkout_address?(attributes[:quick_checkout]) && !ship_address.persisted?
10
+ end
11
+ end
12
+ end
13
+
14
+ Spree::Order::AddressBook.prepend Spree::Order::AddressBookDecorator
@@ -0,0 +1,29 @@
1
+ module Spree
2
+ module OrderMergerDecorator
3
+ # @gem-override spree_core-5.3.6/app/models/spree/order_merger.rb#merge!
4
+ # @see https://github.com/be-agile/giga-repeat/commit/d0dbe2b4524e22c1cf974bbd4829c587ef7cd254
5
+ def merge!(other_order, user = nil, discard_merged: true)
6
+ other_order.line_items.each do |other_order_line_item|
7
+ next unless other_order_line_item.currency == order.currency
8
+
9
+ current_line_item = find_matching_line_item(other_order_line_item)
10
+ handle_merge(current_line_item, other_order_line_item)
11
+ end
12
+
13
+ set_user(user)
14
+ clear_addresses(other_order) if discard_merged
15
+ persist_merge
16
+
17
+ if discard_merged
18
+ # So that the destroy doesn't take out line items which may have been re-assigned
19
+ other_order.line_items.reload
20
+ other_order.destroy
21
+ else
22
+ other_order.line_items.reload
23
+ other_order.updater.update
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ Spree::OrderMerger.prepend(Spree::OrderMergerDecorator)
@@ -0,0 +1,18 @@
1
+ module Spree
2
+ module PriceRules
3
+ module CustomerGroupRuleDecorator
4
+ module ClassMethods
5
+ # @gem-override spree_core-5.3.6/app/models/spree/price_rule.rb#human_name
6
+ # Spree 5.3: 本家 self.human_name は name.demodulize.titleize で英語固定(i18n を引かない)。
7
+ # 価格表ルール追加モーダルの見出しを日本語化するため i18n を引く。
8
+ # (description は本家 CustomerGroupRule が既に Spree.t を使い日本語化済みのため上書き不要)
9
+ # 訳語は config/locales/spree.ja.yml の spree.price_rules.customer_group_rule.name。
10
+ def human_name
11
+ Spree.t('price_rules.customer_group_rule.name')
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ Spree::PriceRules::CustomerGroupRule.singleton_class.prepend(Spree::PriceRules::CustomerGroupRuleDecorator::ClassMethods)
@@ -0,0 +1,25 @@
1
+ module Spree
2
+ module PriceRules
3
+ module UserRuleDecorator
4
+ module ClassMethods
5
+ # @gem-override spree_core-5.3.6/app/models/spree/price_rule.rb#human_name
6
+ # Spree 5.3: 本家 self.human_name は name.demodulize.titleize で英語固定(i18n を引かない)。
7
+ # 価格表ルール追加モーダルの見出しを日本語化するため i18n を引く。
8
+ # 訳語は config/locales/spree.ja.yml の spree.price_rules.user_rule.name。
9
+ def human_name
10
+ Spree.t('price_rules.user_rule.name')
11
+ end
12
+
13
+ # @gem-override spree_core-5.3.6/app/models/spree/price_rules/user_rule.rb#description
14
+ # Spree 5.3: 本家 self.description は英語文字列をハードコードしており i18n を引かない。
15
+ # CustomerGroupRule のみ Spree.t を使うため、UserRule も同流儀で i18n 化し日本語化する。
16
+ # 訳語は config/locales/spree.ja.yml の spree.price_rules.user_rule.description。
17
+ def description
18
+ Spree.t('price_rules.user_rule.description')
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ Spree::PriceRules::UserRule.singleton_class.prepend(Spree::PriceRules::UserRuleDecorator::ClassMethods)
@@ -0,0 +1,25 @@
1
+ module Spree
2
+ module PriceRules
3
+ module VolumeRuleDecorator
4
+ module ClassMethods
5
+ # @gem-override spree_core-5.3.6/app/models/spree/price_rule.rb#human_name
6
+ # Spree 5.3: 本家 self.human_name は name.demodulize.titleize で英語固定(i18n を引かない)。
7
+ # 価格表ルール追加モーダルの見出しを日本語化するため i18n を引く。
8
+ # 訳語は config/locales/spree.ja.yml の spree.price_rules.volume_rule.name。
9
+ def human_name
10
+ Spree.t('price_rules.volume_rule.name')
11
+ end
12
+
13
+ # @gem-override spree_core-5.3.6/app/models/spree/price_rules/volume_rule.rb#description
14
+ # Spree 5.3: 本家 self.description は英語文字列をハードコードしており i18n を引かない。
15
+ # CustomerGroupRule のみ Spree.t を使うため、VolumeRule も同流儀で i18n 化し日本語化する。
16
+ # 訳語は config/locales/spree.ja.yml の spree.price_rules.volume_rule.description。
17
+ def description
18
+ Spree.t('price_rules.volume_rule.description')
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ Spree::PriceRules::VolumeRule.singleton_class.prepend(Spree::PriceRules::VolumeRuleDecorator::ClassMethods)
@@ -0,0 +1,25 @@
1
+ module Spree
2
+ module PriceRules
3
+ module ZoneRuleDecorator
4
+ module ClassMethods
5
+ # @gem-override spree_core-5.3.6/app/models/spree/price_rule.rb#human_name
6
+ # Spree 5.3: 本家 self.human_name は name.demodulize.titleize で英語固定(i18n を引かない)。
7
+ # 価格表ルール追加モーダルの見出しを日本語化するため i18n を引く。
8
+ # 訳語は config/locales/spree.ja.yml の spree.price_rules.zone_rule.name。
9
+ def human_name
10
+ Spree.t('price_rules.zone_rule.name')
11
+ end
12
+
13
+ # @gem-override spree_core-5.3.6/app/models/spree/price_rules/zone_rule.rb#description
14
+ # Spree 5.3: 本家 self.description は英語文字列をハードコードしており i18n を引かない。
15
+ # CustomerGroupRule のみ Spree.t を使うため、ZoneRule も同流儀で i18n 化し日本語化する。
16
+ # 訳語は config/locales/spree.ja.yml の spree.price_rules.zone_rule.description。
17
+ def description
18
+ Spree.t('price_rules.zone_rule.description')
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ Spree::PriceRules::ZoneRule.singleton_class.prepend(Spree::PriceRules::ZoneRuleDecorator::ClassMethods)
@@ -0,0 +1,96 @@
1
+ module Spree
2
+ module Pricing
3
+ # 1商品の全 variant について、適用される実価格をまとめて解決する。
4
+ #
5
+ # 本家の Resolver は variant ごとに price_list の適用判定とクエリ(または Rails.cache 往復)を
6
+ # 行うため、多 variant 商品ではフラグメントキャッシュが冷えた際の一覧描画が極端に遅くなる。
7
+ # price_list の適用判定は variant に依存しない(active/scheduled + 期間 + rules のみ)ので、
8
+ # 商品ごとに1回だけ判定し、全 variant の価格をまとめて読んでから選び直す。
9
+ #
10
+ # 価格の選択規則は Resolver から複製しているため、Spree アップグレード時は各メソッドの
11
+ # @gem-override マーカーで本家の変更(rake gem_override_marker:diff / :check)を確認すること。
12
+ # @see https://github.com/be-agile/giga-repeat/issues/1290
13
+ class BatchResolver
14
+ attr_reader :variants, :context
15
+
16
+ # @param variants [Array<Spree::Variant>] master を含む解決対象の variant
17
+ # @param context [Spree::Pricing::Context] 商品共通の価格解決コンテキスト
18
+ def initialize(variants:, context:)
19
+ @variants = variants
20
+ @context = context
21
+ end
22
+
23
+ # @return [Array<Spree::Price>] amount が確定した価格のみ(価格未設定の variant は除外)
24
+ def resolve
25
+ return [] if variants.empty?
26
+
27
+ price_list_ids = applicable_price_lists.map(&:id)
28
+ prices_by_variant_id = prices_by_variant_id_for(currency)
29
+
30
+ variants.filter_map do |variant|
31
+ find_best_price(prices_by_variant_id[variant.id] || [], price_list_ids)
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def currency
38
+ @currency ||= context.currency&.upcase
39
+ end
40
+
41
+ # @gem-override spree_core-5.3.6/lib/spree/core/pricing/resolver.rb#find_best_price
42
+ # reason: 本家は variant ごとに price_list を1件ずつ照会するが、ここでは読み込み済みの価格配列と
43
+ # 商品単位で判定済みの price_list id から同じ優先順位で選ぶ。本家の find_price_for_list
44
+ # (.non_zero) と find_base_price (amount present) の条件もこのメソッドに畳んである。
45
+ # @see https://github.com/be-agile/giga-repeat/issues/1290
46
+ # @param prices [Array<Spree::Price>] その variant の該当通貨の価格
47
+ # @param price_list_ids [Array] 適用される price_list の id(position 昇順)
48
+ # @return [Spree::Price, nil]
49
+ def find_best_price(prices, price_list_ids)
50
+ price_list_ids.each do |price_list_id|
51
+ price = prices.find do |candidate|
52
+ candidate.price_list_id == price_list_id && candidate.amount.present? && !candidate.amount.zero?
53
+ end
54
+ return price if price
55
+ end
56
+
57
+ prices.find { |price| price.price_list_id.nil? && price.amount.present? }
58
+ end
59
+
60
+ # @gem-override spree_core-5.3.6/lib/spree/core/pricing/resolver.rb#applicable_price_lists
61
+ # reason: 本家は variant ごとの Resolver インスタンス単位でメモ化するが、適用判定は variant に
62
+ # 依存しないため商品単位(このインスタンス)で1回だけ評価する。判定そのものは本家と同一。
63
+ # @see https://github.com/be-agile/giga-repeat/issues/1290
64
+ # @return [Array<Spree::PriceList>]
65
+ def applicable_price_lists
66
+ @applicable_price_lists ||= price_lists_for_context.select { |price_list| price_list.applicable?(context) }
67
+ end
68
+
69
+ # @gem-override spree_core-5.3.6/lib/spree/core/pricing/resolver.rb#price_lists_for_context
70
+ # reason: 本家と同一。Resolver を経由しないため取得元の分岐だけ複製している。
71
+ # @see https://github.com/be-agile/giga-repeat/issues/1290
72
+ # @return [Enumerable<Spree::PriceList>]
73
+ def price_lists_for_context
74
+ if context.store == Spree::Current.store && context.currency == Spree::Current.currency
75
+ Spree::Current.price_lists
76
+ else
77
+ Spree::PriceList.for_context(context)
78
+ end
79
+ end
80
+
81
+ # variant_id ごとの該当通貨の価格。未ロードの variant があれば1クエリでまとめて読む。
82
+ # 通貨の比較は、本家が SQL(with_currency)で行い spree_prices の照合順序が
83
+ # utf8mb4_general_ci = 大文字小文字を区別しないため、Ruby 側でも区別しない。
84
+ # @param currency [String]
85
+ # @return [Hash]
86
+ def prices_by_variant_id_for(currency)
87
+ unloaded = variants.reject { |variant| variant.association(:prices).loaded? }
88
+ ActiveRecord::Associations::Preloader.new(records: unloaded, associations: :prices).call if unloaded.any?
89
+
90
+ variants.to_h do |variant|
91
+ [ variant.id, variant.prices.select { |price| price.currency&.casecmp?(currency) } ]
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,51 @@
1
+ module Spree
2
+ module PromotionHandler
3
+ module CartDecorator
4
+ private
5
+
6
+ # @gem-override spree_core-5.3.6/app/models/spree/promotion_handler/cart.rb#promotions
7
+ # @see https://github.com/be-agile/giga-repeat/issues/723
8
+ # プロモーションが大量にあるとカート処理が重くなるため、本家のスコープ取得を
9
+ # UNION の生 SQL に置き換え、カート内容にマッチしない Product ルールを除外する。
10
+ def promotions
11
+ union_sql = "#{order.promotions.active.to_sql} UNION #{promotion_scope.active.automatic.to_sql}"
12
+
13
+ if exclude_promotion_ids.present?
14
+ final_sql = "SELECT * FROM (#{union_sql}) AS unioned_promotions WHERE id NOT IN (#{exclude_promotion_ids.join(',')})"
15
+ promotion_scope.find_by_sql(final_sql)
16
+ else
17
+ promotion_scope.find_by_sql(union_sql)
18
+ end
19
+ end
20
+
21
+ def exclude_promotion_ids
22
+ cart_product_ids = order.line_items.joins(:variant).pluck('spree_variants.product_id').uniq
23
+ # Find all promotions with Product rules
24
+ all_product_rule_promotions = Spree::Promotion
25
+ .joins(:promotion_rules)
26
+ .where(promotion_rules: { type: 'Spree::Promotion::Rules::Product' })
27
+ .distinct
28
+
29
+ return all_product_rule_promotions.pluck(:id) if cart_product_ids.blank?
30
+
31
+ # Find promotions with Product rules that match cart contents
32
+ # Use ProductPromotionRule as starting point since the association exists
33
+ matching_promotion_rule_ids = Spree::ProductPromotionRule
34
+ .where(product_id: cart_product_ids)
35
+ .joins(:promotion_rule)
36
+ .where(promotion_rule: { type: 'Spree::Promotion::Rules::Product' })
37
+ .pluck(:promotion_rule_id)
38
+
39
+ matching_promotions = Spree::Promotion
40
+ .joins(:promotion_rules)
41
+ .where(promotion_rules: { id: matching_promotion_rule_ids })
42
+ .distinct
43
+
44
+ # Exclude Product rule promotions that don't match cart contents
45
+ all_product_rule_promotions.where.not(id: matching_promotions.select(:id)).pluck(:id)
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ Spree::PromotionHandler::Cart.prepend(Spree::PromotionHandler::CartDecorator)
@@ -0,0 +1,61 @@
1
+ module Spree
2
+ module Stock
3
+ module PackerDecorator
4
+ # 同一 variant の複数 line_item が order に存在する場合 (例: spree_embed_gravity_forms の
5
+ # `wp_gf_entry_id` で line_item を分離するケース) に、Spree 本家の実装では
6
+ # `inventory_units.index_by(&:variant_id)` で 1 つに集約してしまい、最初に作られた
7
+ # line_item の inventory_unit が package に渡らず orphan になる問題への対処。
8
+ # Issue #1187 を参照。
9
+ #
10
+ # 修正方針: variant ごとに qty 合計で fill_status を呼び (本家の在庫判定ロジックを維持)、
11
+ # 結果を line_item 単位で先着順に on_hand / backordered へ按分する。
12
+ # 単純な each 置換では fill_status が読み取り専用 (count_on_hand を減らさない) なため
13
+ # 過剰販売が起きてしまうので、按分配置が必須。
14
+ #
15
+ # @gem-override spree_core-5.3.6/app/models/spree/stock/packer.rb#default_package
16
+ # @see https://github.com/be-agile/giga-repeat/issues/1187
17
+ def default_package
18
+ package = Package.new(stock_location)
19
+
20
+ inventory_units.group_by(&:variant_id).each do |variant_id, units_for_variant|
21
+ variant = Spree::Variant.find(variant_id)
22
+
23
+ if variant.should_track_inventory?
24
+ next unless stock_location.stocks?(variant)
25
+
26
+ total_qty = units_for_variant.sum(&:quantity)
27
+ on_hand, _backordered = stock_location.fill_status(variant, total_qty)
28
+
29
+ allocate_units_to_package(package, units_for_variant, on_hand)
30
+ else
31
+ units_for_variant.each { |inventory_unit| package.add(inventory_unit.dup) }
32
+ end
33
+ end
34
+
35
+ package
36
+ end
37
+
38
+ private
39
+
40
+ # variant 単位の fill_status 結果 (on_hand 数) を line_item 単位の inventory_unit に
41
+ # 先着順で配分する。on_hand_remaining を使い切ったら以降は backordered になる。
42
+ def allocate_units_to_package(package, units_for_variant, on_hand)
43
+ on_hand_remaining = on_hand
44
+
45
+ units_for_variant.each do |inventory_unit|
46
+ unit = inventory_unit.dup
47
+ qty = inventory_unit.quantity
48
+
49
+ unit_on_hand = [ qty, on_hand_remaining ].min
50
+ on_hand_remaining -= unit_on_hand
51
+ unit_backordered = qty - unit_on_hand
52
+
53
+ package.add(InventoryUnit.split(unit, unit_backordered), :backordered) if unit_backordered.positive?
54
+ package.add(InventoryUnit.split(unit, unit_on_hand), :on_hand) if unit_on_hand.positive?
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ Spree::Stock::Packer.prepend(Spree::Stock::PackerDecorator)
@@ -0,0 +1,27 @@
1
+ module Spree
2
+ module Stock
3
+ module PrioritizerDecorator
4
+ # 同一 variant の複数 line_item が order に存在する場合 (例: spree_embed_gravity_forms の
5
+ # `wp_gf_entry_id` で line_item を分離するケース) に、Spree 本家の
6
+ # `hash_item` は shipment が無いと `variant.hash` のみで識別するため、
7
+ # 同 variant の inventory_unit が同じ adjuster を共有してしまい、
8
+ # 後発の inventory_unit が `Adjuster#adjust` の `fulfilled?` 判定で
9
+ # `remove_item` されて shipment から欠落する。
10
+ # Issue #1187 を参照。Packer decorator の修正と合わせて line_item 単位で adjuster を
11
+ # 個別化することで対処する。
12
+ #
13
+ # @gem-override spree_core-5.3.6/app/models/spree/stock/prioritizer.rb#hash_item
14
+ # @see https://github.com/be-agile/giga-repeat/issues/1187
15
+ def hash_item(item)
16
+ line_item_id = item.inventory_unit.line_item_id
17
+ return super unless line_item_id
18
+
19
+ shipment = item.inventory_unit.shipment
20
+ base = item.inventory_unit.variant.hash ^ line_item_id.hash
21
+ shipment.present? ? (base ^ shipment.hash) : base
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ Spree::Stock::Prioritizer.prepend(Spree::Stock::PrioritizerDecorator)
@@ -0,0 +1,93 @@
1
+ module Spree
2
+ module TaxRateDecorator
3
+ def adjust_order_level_adjustments(order)
4
+ return unless order.tax_address.nil? || zone.include?(order.tax_address)
5
+
6
+ order_adjustments = order.adjustments.eligible.non_tax
7
+ .where(adjustable_type: 'Spree::Order')
8
+
9
+ total_tax_amount = 0
10
+
11
+ order_adjustments.each do |adjustment|
12
+ tax_amount = calculate_tax_for_adjustment(adjustment)
13
+ total_tax_amount += tax_amount if tax_amount
14
+ end
15
+
16
+ return if total_tax_amount.round(0).zero?
17
+
18
+ existing_tax = find_existing_tax_adjustment_for_order(order)
19
+
20
+ if existing_tax
21
+ update_tax_adjustment(existing_tax, total_tax_amount)
22
+ else
23
+ create_tax_adjustment(order, total_tax_amount)
24
+ end
25
+ end
26
+
27
+ def calculate_tax_for_adjustment(adjustment)
28
+ tax_category = get_tax_category_for_adjustment(adjustment)
29
+ return nil unless tax_category
30
+ return nil unless tax_category == self.tax_category
31
+ return nil if adjustment.amount.zero?
32
+
33
+ pre_tax_amount = adjustment.amount / (1 + amount)
34
+ tax_amount = adjustment.amount - pre_tax_amount
35
+
36
+ tax_amount
37
+ end
38
+
39
+ def get_tax_category_for_adjustment(adjustment)
40
+ source = adjustment.source
41
+ return nil unless source
42
+
43
+ case adjustment.source_type
44
+ when 'Spree::PromotionAction'
45
+ # 定額(FlatRate)など tax_category を持たない calculator もあるため、
46
+ # respond_to? でガードして NoMethodError を避ける(try で同等)。
47
+ source.calculator.try(:tax_category)
48
+ when 'Spree::Calculator'
49
+ source.try(:tax_category)
50
+ when 'Spree::ShippingMethod'
51
+ source.try(:tax_category)
52
+ when 'Spree::PaymentMethod'
53
+ source.try(:tax_category)
54
+ else
55
+ nil
56
+ end
57
+ end
58
+
59
+ def find_existing_tax_adjustment_for_order(order)
60
+ order.adjustments.tax.find_by(
61
+ source_type: 'Spree::TaxRate',
62
+ source_id: id,
63
+ adjustable_type: 'Spree::Order'
64
+ )
65
+ end
66
+
67
+ def create_tax_adjustment(order, tax_amount)
68
+ order.adjustments.create!(
69
+ adjustable_type: 'Spree::Order',
70
+ adjustable_id: order.id,
71
+ amount: tax_amount.round(0),
72
+ included: true,
73
+ label: label,
74
+ order: order,
75
+ state: 'closed',
76
+ source_type: 'Spree::TaxRate',
77
+ source_id: id
78
+ )
79
+ end
80
+
81
+ def update_tax_adjustment(existing_tax, tax_amount)
82
+ new_amount = tax_amount.round(0)
83
+ if existing_tax.amount != new_amount
84
+ existing_tax.update_columns(
85
+ amount: new_amount,
86
+ updated_at: Time.current
87
+ )
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ Spree::TaxRate.prepend(Spree::TaxRateDecorator)