spree_cm_commissioner 1.9.2 → 1.10.0.pre.pre
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.
- checksums.yaml +4 -4
- data/.github/workflows/test_and_build_gem.yml +86 -0
- data/.gitignore +2 -1
- data/Gemfile.lock +22 -1
- data/app/controllers/spree/admin/events_controller.rb +47 -0
- data/app/controllers/spree/admin/prototypes_controller_decorator.rb +20 -0
- data/app/controllers/spree/admin/stock_managements_controller.rb +17 -1
- data/app/controllers/spree/api/v2/storefront/accommodations/variants_controller.rb +42 -0
- data/app/controllers/spree/api/v2/storefront/accommodations_controller.rb +14 -31
- data/app/finders/spree_cm_commissioner/accommodations/find.rb +40 -0
- data/app/finders/spree_cm_commissioner/accommodations/find_variant.rb +35 -0
- data/app/interactors/spree_cm_commissioner/create_event.rb +65 -0
- data/app/interactors/spree_cm_commissioner/firebase_email_fetcher.rb +30 -4
- data/app/interactors/spree_cm_commissioner/firebase_email_fetcher_cron_executor.rb +23 -0
- data/app/interactors/spree_cm_commissioner/inventory_item_syncer.rb +25 -0
- data/app/interactors/spree_cm_commissioner/stock/permanent_inventory_items_generator.rb +71 -0
- data/app/interactors/spree_cm_commissioner/vattanac_bank_initiator.rb +25 -6
- data/app/jobs/spree_cm_commissioner/firebase_email_fetcher_job.rb +9 -0
- data/app/jobs/spree_cm_commissioner/inventory_item_syncer_job.rb +7 -0
- data/app/jobs/spree_cm_commissioner/stock/permanent_inventory_items_generator_job.rb +9 -0
- data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +26 -0
- data/app/models/concerns/spree_cm_commissioner/product_type.rb +10 -0
- data/app/models/spree_cm_commissioner/inventory.rb +11 -0
- data/app/models/spree_cm_commissioner/inventory_item.rb +37 -0
- data/app/models/spree_cm_commissioner/invite_user_taxon.rb +1 -0
- data/app/models/spree_cm_commissioner/line_item_decorator.rb +1 -0
- data/app/models/spree_cm_commissioner/order_decorator.rb +15 -0
- data/app/models/spree_cm_commissioner/product_decorator.rb +10 -0
- data/app/models/spree_cm_commissioner/prototype_decorator.rb +9 -0
- data/app/models/spree_cm_commissioner/redis_stock/cached_inventory_items_builder.rb +40 -0
- data/app/models/spree_cm_commissioner/redis_stock/inventory_updater.rb +114 -0
- data/app/models/spree_cm_commissioner/redis_stock/line_items_cached_inventory_items_builder.rb +42 -0
- data/app/models/spree_cm_commissioner/redis_stock/variant_cached_inventory_items_builder.rb +27 -0
- data/app/models/spree_cm_commissioner/stock/availability_checker.rb +27 -25
- data/app/models/spree_cm_commissioner/stock/availability_validator_decorator.rb +2 -1
- data/app/models/spree_cm_commissioner/stock/line_item_availability_checker.rb +3 -3
- data/app/models/spree_cm_commissioner/stock/order_availability_checker.rb +44 -0
- data/app/models/spree_cm_commissioner/stock_movement_decorator.rb +34 -0
- data/app/models/spree_cm_commissioner/taxon_decorator.rb +2 -1
- data/app/models/spree_cm_commissioner/user_decorator.rb +5 -1
- data/app/models/spree_cm_commissioner/variant_decorator.rb +24 -17
- data/app/overrides/spree/admin/prototypes/_form/description.html.erb.deface +6 -0
- data/app/overrides/spree/admin/prototypes/_form/icon.html.erb.deface +8 -0
- data/app/overrides/spree/admin/prototypes/_form/product_type.html.erb.deface +2 -2
- data/app/overrides/spree/admin/shared/sub_menu/_product/events_tab.html.erb.deface +3 -0
- data/app/request_schemas/spree_cm_commissioner/accommodation_request_schema.rb +3 -0
- data/app/request_schemas/spree_cm_commissioner/application_request_schema.rb +1 -1
- data/app/request_schemas/spree_cm_commissioner/variant_request_schema.rb +19 -0
- data/app/serializers/spree/v2/storefront/accommodation_serializer.rb +2 -0
- data/app/serializers/spree/v2/tenant/payment_method_serializer.rb +12 -0
- data/app/serializers/spree/v2/tenant/user_serializer.rb +1 -1
- data/app/views/spree/admin/events/_search_form.html.erb +61 -0
- data/app/views/spree/admin/events/_tab.html.erb +25 -0
- data/app/views/spree/admin/events/_table.html.erb +27 -0
- data/app/views/spree/admin/events/index.html.erb +15 -0
- data/app/views/spree/admin/stock_managements/_events_popover.html.erb +17 -0
- data/app/views/spree/admin/stock_managements/calendar.html.erb +32 -0
- data/app/views/spree/admin/stock_managements/index.html.erb +31 -5
- data/app/views/spree_cm_commissioner/crew_invite_mailer/_mailer_stylesheets.html.erb +2 -2
- data/app/views/spree_cm_commissioner/crew_invite_mailer/send_crew_invite_email.html.erb +1 -1
- data/config/initializers/user_manager_decorator.rb +26 -0
- data/config/routes.rb +12 -2
- data/db/migrate/20250304293518_create_cm_inventory_items.rb +21 -0
- data/db/migrate/20250425084929_add_description_to_spree_prototypes.rb +5 -0
- data/db/migrate/20250425085938_add_preferences_to_spree_prototypes.rb +5 -0
- data/db/migrate/20250428025645_add_slug_to_spree_prototypes.rb +6 -0
- data/db/migrate/20250429094228_add_lock_version_to_cm_inventory_items.rb +5 -0
- data/docker-compose.yml +1 -1
- data/lib/generators/spree_cm_commissioner/install/install_generator.rb +2 -2
- data/lib/generators/spree_cm_commissioner/install/templates/app/javascript/{spree_cm_commissioner → spree_dashboard/spree_cm_commissioner}/utilities.js +4 -0
- data/lib/spree_cm_commissioner/cached_inventory_item.rb +23 -0
- data/lib/spree_cm_commissioner/calendar_event.rb +11 -1
- data/lib/spree_cm_commissioner/test_helper/factories/inventory_item_factory.rb +9 -0
- data/lib/spree_cm_commissioner/test_helper/factories/variant_factory.rb +28 -6
- data/lib/spree_cm_commissioner/version.rb +1 -1
- data/lib/spree_cm_commissioner.rb +34 -0
- data/lib/tasks/create_default_non_permanent_inventory_items.rake +16 -0
- data/lib/tasks/fetch_email.rake +7 -0
- data/lib/tasks/generate_inventory_items.rake +7 -0
- data/spree_cm_commissioner.gemspec +5 -0
- metadata +88 -7
- data/app/queries/spree_cm_commissioner/variant_availability/non_permanent_stock_query.rb +0 -45
- data/app/queries/spree_cm_commissioner/variant_availability/permanent_stock_query.rb +0 -55
@@ -1,47 +1,49 @@
|
|
1
1
|
module SpreeCmCommissioner
|
2
2
|
module Stock
|
3
3
|
class AvailabilityChecker
|
4
|
-
attr_reader :variant, :error_message
|
4
|
+
attr_reader :variant, :options, :error_message
|
5
5
|
|
6
|
-
def initialize(variant)
|
6
|
+
def initialize(variant, options = {})
|
7
7
|
@variant = variant
|
8
|
+
@options = options
|
8
9
|
@error_message = nil
|
9
10
|
end
|
10
11
|
|
11
|
-
def can_supply?(quantity = 1
|
12
|
+
def can_supply?(quantity = 1)
|
12
13
|
return false unless variant.available?
|
13
14
|
return true unless variant.should_track_inventory?
|
14
15
|
return true if variant.backorderable?
|
15
16
|
return true if variant.need_confirmation?
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
return variant.stock_items.sum(:count_on_hand) >= quantity if variant.delivery_required?
|
18
|
+
variant_available?(quantity)
|
19
|
+
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
def variant_available?(quantity = 1)
|
22
|
+
return false if cached_inventory_items.empty?
|
23
|
+
|
24
|
+
cached_inventory_items.all? do |cached_inventory_item|
|
25
|
+
cached_inventory_item.active? && cached_inventory_item.quantity_available >= quantity
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
29
|
+
def cached_inventory_items
|
30
|
+
return @cached_inventory_items if defined?(@cached_inventory_items)
|
31
|
+
|
32
|
+
if variant.permanent_stock?
|
33
|
+
return [] if options[:from_date].blank? || options[:to_date].blank?
|
34
|
+
|
35
|
+
@cached_inventory_items = builder_klass.new(
|
36
|
+
variant_id: variant.id,
|
37
|
+
from_date: options[:from_date].to_date,
|
38
|
+
to_date: options[:to_date].to_date
|
39
|
+
).call
|
40
|
+
else
|
41
|
+
@cached_inventory_items = builder_klass.new(variant_id: variant.id).call
|
42
|
+
end
|
36
43
|
end
|
37
44
|
|
38
|
-
def
|
39
|
-
SpreeCmCommissioner::
|
40
|
-
variant: variant,
|
41
|
-
from_date: options[:from_date].to_date,
|
42
|
-
to_date: options[:to_date].to_date,
|
43
|
-
except_line_item_id: options[:except_line_item_id]
|
44
|
-
).available?(quantity)
|
45
|
+
def builder_klass
|
46
|
+
::SpreeCmCommissioner::RedisStock::VariantCachedInventoryItemsBuilder
|
45
47
|
end
|
46
48
|
end
|
47
49
|
end
|
@@ -3,7 +3,8 @@ module SpreeCmCommissioner
|
|
3
3
|
module AvailabilityValidatorDecorator
|
4
4
|
# override
|
5
5
|
def item_available?(line_item, quantity)
|
6
|
-
SpreeCmCommissioner::Stock::LineItemAvailabilityChecker.new(line_item)
|
6
|
+
SpreeCmCommissioner::Stock::LineItemAvailabilityChecker.new(line_item)
|
7
|
+
.can_supply?(quantity)
|
7
8
|
end
|
8
9
|
end
|
9
10
|
end
|
@@ -8,14 +8,14 @@ module SpreeCmCommissioner
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def can_supply?(quantity)
|
11
|
-
AvailabilityChecker.new(line_item.variant
|
11
|
+
::SpreeCmCommissioner::Stock::AvailabilityChecker.new(line_item.variant, options)
|
12
|
+
.can_supply?(quantity)
|
12
13
|
end
|
13
14
|
|
14
15
|
def options
|
15
16
|
{
|
16
17
|
from_date: line_item.from_date,
|
17
|
-
to_date: line_item.to_date
|
18
|
-
except_line_item_id: line_item.id
|
18
|
+
to_date: line_item.to_date
|
19
19
|
}
|
20
20
|
end
|
21
21
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# On top of SpreeCmCommissioner::Stock::AvailabilityChecker, this class does the same thing but in bulk check with redis.
|
2
|
+
# Which mean, it check basic condition with db first, once pass, it check with redis.
|
3
|
+
module SpreeCmCommissioner
|
4
|
+
module Stock
|
5
|
+
class OrderAvailabilityChecker
|
6
|
+
attr_reader :order
|
7
|
+
|
8
|
+
def initialize(order)
|
9
|
+
@order = order
|
10
|
+
end
|
11
|
+
|
12
|
+
def can_supply_all?
|
13
|
+
insufficient_stock_lines.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
def insufficient_stock_lines
|
17
|
+
cached_inventory_items_group_by_line_item_id.map do |line_item_id, cached_inventory_items|
|
18
|
+
line_item = order.line_items.find { |item| item.id == line_item_id }
|
19
|
+
line_item unless sufficient_stock_for?(line_item, cached_inventory_items)
|
20
|
+
end.compact
|
21
|
+
end
|
22
|
+
|
23
|
+
# {
|
24
|
+
# 1: [ {inventory_key: "inventory:1", active: true, quantity_available: 5, inventory_item_id: 1, variant_id: 1}],
|
25
|
+
# 2: [ {inventory_key: "inventory:2", active: true, quantity_available: 5, inventory_item_id: 2, variant_id: 2}],
|
26
|
+
# }
|
27
|
+
def cached_inventory_items_group_by_line_item_id
|
28
|
+
@cached_inventory_items_group_by_line_item_id ||=
|
29
|
+
::SpreeCmCommissioner::RedisStock::LineItemsCachedInventoryItemsBuilder.new(
|
30
|
+
line_item_ids: order.line_items.pluck(:id)
|
31
|
+
).call
|
32
|
+
end
|
33
|
+
|
34
|
+
def sufficient_stock_for?(line_item, cached_inventory_items)
|
35
|
+
return false unless line_item.variant.available?
|
36
|
+
return true unless line_item.variant.should_track_inventory?
|
37
|
+
return true if line_item.variant.backorderable?
|
38
|
+
return true if line_item.variant.need_confirmation?
|
39
|
+
|
40
|
+
cached_inventory_items.all? { |item| item.active? && item.quantity_available >= line_item.quantity }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
module StockMovementDecorator
|
3
|
+
def self.prepended(base)
|
4
|
+
base.after_create :adjust_inventory_items, if: -> { stock_item.should_track_inventory? }
|
5
|
+
end
|
6
|
+
|
7
|
+
def adjust_inventory_items
|
8
|
+
if !variant.permanent_stock? && !variant.default_inventory_item_exist?
|
9
|
+
variant.create_default_non_permanent_inventory_item!
|
10
|
+
else
|
11
|
+
adjust_existing_inventory_items!
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def should_create_default_non_permanent_inventory_item?
|
18
|
+
return false if variant.permanent_stock?
|
19
|
+
return false if variant.inventory_items.exists?(inventory_date: nil)
|
20
|
+
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
def adjust_existing_inventory_items!
|
25
|
+
variant.inventory_items.active.find_each(batch_size: 100) do |inventory_item|
|
26
|
+
inventory_item.adjust_quantity!(quantity)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
unless Spree::StockMovement.included_modules.include?(SpreeCmCommissioner::StockMovementDecorator)
|
33
|
+
Spree::StockMovement.prepend(SpreeCmCommissioner::StockMovementDecorator)
|
34
|
+
end
|
@@ -46,7 +46,8 @@ module SpreeCmCommissioner
|
|
46
46
|
base.before_save :set_kind
|
47
47
|
base.before_save :set_slug
|
48
48
|
|
49
|
-
base.
|
49
|
+
base.whitelisted_ransackable_associations |= %w[vendor]
|
50
|
+
base.whitelisted_ransackable_attributes |= %w[kind from_date to_date]
|
50
51
|
|
51
52
|
base.enum purchasable_on: { both: 0, web: 1, app: 2 }
|
52
53
|
base.has_many :crew_invites, class_name: 'SpreeCmCommissioner::CrewInvite', dependent: :destroy
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# rubocop:disable Metrics/MethodLength
|
2
2
|
module SpreeCmCommissioner
|
3
3
|
module UserDecorator
|
4
|
-
def self.prepended(base)
|
4
|
+
def self.prepended(base) # rubocop:disable Metrics/AbcSize
|
5
5
|
base.include SpreeCmCommissioner::UserNotification
|
6
6
|
base.include SpreeCmCommissioner::UserIdentity
|
7
7
|
base.include SpreeCmCommissioner::UserPreference
|
@@ -18,6 +18,10 @@ module SpreeCmCommissioner
|
|
18
18
|
base.has_many :events, through: :user_events, class_name: 'Spree::Taxon', source: 'taxon'
|
19
19
|
base.has_many :guests, class_name: 'SpreeCmCommissioner::Guest', dependent: :destroy
|
20
20
|
|
21
|
+
base.has_many :google_user_identity_providers,
|
22
|
+
-> { where(identity_type: :google) },
|
23
|
+
class_name: 'SpreeCmCommissioner::UserIdentityProvider'
|
24
|
+
|
21
25
|
base.has_many :wished_items, class_name: 'Spree::WishedItem', through: :wishlists
|
22
26
|
base.has_many :promotions, through: :promotion_rules, class_name: 'Spree::Promotion'
|
23
27
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module SpreeCmCommissioner
|
2
2
|
module VariantDecorator
|
3
3
|
def self.prepended(base)
|
4
|
+
base.include SpreeCmCommissioner::ProductType
|
4
5
|
base.include SpreeCmCommissioner::ProductDelegation
|
5
6
|
base.include SpreeCmCommissioner::VariantOptionsConcern
|
6
7
|
|
@@ -21,7 +22,12 @@ module SpreeCmCommissioner
|
|
21
22
|
base.has_many :variant_guest_card_class, class_name: 'SpreeCmCommissioner::VariantGuestCardClass'
|
22
23
|
base.has_many :guest_card_classes, class_name: 'SpreeCmCommissioner::GuestCardClass', through: :variant_guest_card_class
|
23
24
|
|
25
|
+
base.has_many :inventory_items, class_name: 'SpreeCmCommissioner::InventoryItem'
|
26
|
+
|
24
27
|
base.scope :subscribable, -> { active.joins(:product).where(product: { subscribable: true, status: :active }) }
|
28
|
+
base.scope :with_permanent_stock, -> { joins(:product).where(product: { product_type: base::PERMANENT_STOCK_PRODUCT_TYPES }) }
|
29
|
+
base.scope :with_non_permanent_stock, -> { joins(:product).where.not(product: { product_type: base::PERMANENT_STOCK_PRODUCT_TYPES }) }
|
30
|
+
|
25
31
|
base.has_one :trip,
|
26
32
|
class_name: 'SpreeCmCommissioner::Trip'
|
27
33
|
base.accepts_nested_attributes_for :option_values
|
@@ -41,14 +47,26 @@ module SpreeCmCommissioner
|
|
41
47
|
super || product.discontinued?
|
42
48
|
end
|
43
49
|
|
44
|
-
def permanent_stock?
|
45
|
-
accommodation?
|
46
|
-
end
|
47
|
-
|
48
50
|
def event
|
49
51
|
taxons.event.first
|
50
52
|
end
|
51
53
|
|
54
|
+
def default_inventory_item_exist?
|
55
|
+
inventory_items.exists?(inventory_date: nil)
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_default_non_permanent_inventory_item!(quantity_available: nil, max_capacity: nil)
|
59
|
+
return if product_type.blank? # handle in case product not exist for variant.
|
60
|
+
return unless should_track_inventory?
|
61
|
+
return if default_inventory_item_exist?
|
62
|
+
|
63
|
+
inventory_items.create!(
|
64
|
+
product_type: product_type,
|
65
|
+
quantity_available: [0, quantity_available || total_on_hand].max,
|
66
|
+
max_capacity: [0, max_capacity || total_on_hand].max
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
52
70
|
# override
|
53
71
|
def options_text
|
54
72
|
@options_text ||= Spree::Variants::VisableOptionsPresenter.new(self).to_sentence
|
@@ -69,23 +87,12 @@ module SpreeCmCommissioner
|
|
69
87
|
end
|
70
88
|
|
71
89
|
# override
|
72
|
-
def in_stock?
|
73
|
-
|
90
|
+
def in_stock?(options = {})
|
91
|
+
SpreeCmCommissioner::Stock::AvailabilityChecker.new(self, options).can_supply?
|
74
92
|
end
|
75
93
|
|
76
94
|
private
|
77
95
|
|
78
|
-
def total_purchases
|
79
|
-
Spree::LineItem.complete.where(variant_id: id).sum(:quantity).to_i
|
80
|
-
end
|
81
|
-
|
82
|
-
def available_quantity
|
83
|
-
stock_count = stock_items.sum(&:count_on_hand)
|
84
|
-
return stock_count if delivery_required?
|
85
|
-
|
86
|
-
stock_count - total_purchases
|
87
|
-
end
|
88
|
-
|
89
96
|
def update_vendor_price
|
90
97
|
return unless vendor.present? && product&.product_type == vendor&.primary_product_type
|
91
98
|
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<!-- insert_bottom "[data-hook='admin_prototype_form_fields']" -->
|
2
|
+
|
3
|
+
<div id='icon'>
|
4
|
+
<%= f.field_container :icon do %>
|
5
|
+
<%= f.label :preferred_icon, Spree.t(:icon_name) %> <%= link_to Spree.t(:icons), spree.admin_vectors_icons_url %>
|
6
|
+
<%= f.select :preferred_icon, @icons.map(&:path), { include_blank: Spree.t(:None) }, { class: "select2" } %>
|
7
|
+
<% end %>
|
8
|
+
</div>
|
@@ -1,3 +1,3 @@
|
|
1
|
-
<!-- insert_bottom "
|
1
|
+
<!-- insert_bottom "#taxons" -->
|
2
2
|
|
3
|
-
<%= render 'shared/product_type_field', field: :product_type, f: f %>
|
3
|
+
<%= render 'shared/product_type_field', field: :product_type, f: f %>
|
@@ -3,6 +3,9 @@ module SpreeCmCommissioner
|
|
3
3
|
params do
|
4
4
|
required(:from_date).value(:date)
|
5
5
|
required(:to_date).value(:date)
|
6
|
+
required(:state_id).value(:integer)
|
7
|
+
required(:number_of_adults).value(:integer)
|
8
|
+
required(:number_of_kids).value(:integer)
|
6
9
|
end
|
7
10
|
|
8
11
|
rule(:from_date, :to_date) do
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
class VariantRequestSchema < ApplicationRequestSchema
|
3
|
+
params do
|
4
|
+
required(:from_date).value(:date)
|
5
|
+
required(:to_date).value(:date)
|
6
|
+
required(:number_of_adults).value(:integer)
|
7
|
+
required(:number_of_kids).value(:integer)
|
8
|
+
end
|
9
|
+
|
10
|
+
rule(:from_date, :to_date) do
|
11
|
+
from_date = values[:from_date]
|
12
|
+
to_date = values[:to_date]
|
13
|
+
|
14
|
+
key.failure(:must_be_in_future) if from_date < Time.zone.today
|
15
|
+
key.failure(:must_be_later_than_start_date) if from_date > to_date
|
16
|
+
key.failure(:stay_is_too_long) if (to_date - from_date).to_i > ENV.fetch('ACCOMMODATION_MAX_STAY_DAYS', 10).to_i
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -9,10 +9,12 @@ module Spree
|
|
9
9
|
|
10
10
|
attributes :total_inventory, :service_availabilities
|
11
11
|
|
12
|
+
# Deprecated
|
12
13
|
attribute :total_booking do |vendor|
|
13
14
|
vendor.respond_to?(:total_booking) ? vendor.total_booking : 0
|
14
15
|
end
|
15
16
|
|
17
|
+
# Deprecated
|
16
18
|
attribute :remaining do |vendor|
|
17
19
|
vendor.respond_to?(:remaining) ? vendor.remaining : vendor.total_inventory
|
18
20
|
end
|
@@ -7,6 +7,18 @@ module Spree
|
|
7
7
|
attribute :preferences do |object|
|
8
8
|
object.public_preferences.as_json
|
9
9
|
end
|
10
|
+
|
11
|
+
attribute :icon_name do |payment_method|
|
12
|
+
return nil if payment_method.preferences.blank?
|
13
|
+
|
14
|
+
payment_method.preferences[:icon_name]
|
15
|
+
end
|
16
|
+
|
17
|
+
attribute :payment_option do |payment_method|
|
18
|
+
pref = payment_method.preferences
|
19
|
+
|
20
|
+
pref.blank? || pref[:payment_option].blank? ? payment_method.method_type : pref[:payment_option]
|
21
|
+
end
|
10
22
|
end
|
11
23
|
end
|
12
24
|
end
|
@@ -2,7 +2,7 @@ module Spree
|
|
2
2
|
module V2
|
3
3
|
module Tenant
|
4
4
|
class UserSerializer < BaseSerializer
|
5
|
-
attributes :email, :first_name, :last_name, :gender,
|
5
|
+
attributes :email, :first_name, :last_name, :gender, :dob,
|
6
6
|
:public_metadata, :completed_orders,
|
7
7
|
:phone_number, :intel_phone_number, :country_code,
|
8
8
|
:otp_enabled, :otp_email, :otp_phone_number,
|
@@ -0,0 +1,61 @@
|
|
1
|
+
<% content_for :table_filter do %>
|
2
|
+
<%= search_form_for [:admin, @search], url: admin_events_path do |f| %>
|
3
|
+
<div class="row">
|
4
|
+
|
5
|
+
<div class="date-range-filter col-12 col-lg-8">
|
6
|
+
<div class="form-group">
|
7
|
+
<%= label_tag :q_from_date_gt, Spree.t(:date_range) %>
|
8
|
+
<div class="row pb-0">
|
9
|
+
<div class="col-12 col-md-6 mb-3 mb-md-0">
|
10
|
+
<div class="input-group datePickerFrom"
|
11
|
+
data-wrap="true"
|
12
|
+
data-alt-format="<%= Spree.t(:fpr_human_friendly_date_format, scope: 'date_picker', default: 'M j, Y') %>"
|
13
|
+
data-max-date="<%= params[:q][:from_date_lt] %>">
|
14
|
+
<%= f.text_field :from_date_gt,
|
15
|
+
class: 'form-control js-filterable',
|
16
|
+
value: params[:q][:from_date_gt],
|
17
|
+
placeholder: Spree.t(:starting_from),
|
18
|
+
'data-input':'' %>
|
19
|
+
<%= render partial: 'spree/admin/shared/cal_close' %>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
<div class="col-12 col-md-6 mt-3 mt-md-0">
|
23
|
+
<div class="input-group datePickerTo"
|
24
|
+
data-wrap="true"
|
25
|
+
data-alt-format="<%= Spree.t(:fpr_human_friendly_date_format, scope: 'date_picker', default: 'M j, Y') %>"
|
26
|
+
data-min-date="<%= params[:q][:from_date_gt] %>">
|
27
|
+
|
28
|
+
<%= f.text_field :from_date_lt,
|
29
|
+
class: 'form-control js-filterable',
|
30
|
+
value: params[:q][:from_date_lt],
|
31
|
+
placeholder: Spree.t(:ending_at),
|
32
|
+
'data-input':'' %>
|
33
|
+
<%= render partial: 'spree/admin/shared/cal_close' %>
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
</div>
|
39
|
+
|
40
|
+
<div class="col-md-6">
|
41
|
+
<div class="form-group">
|
42
|
+
<%= label_tag :q_name_cont, "Event Name" %>
|
43
|
+
<%= f.text_field :name_cont, class: "form-control js-quick-search-target js-filterable" %>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
|
47
|
+
<div class="col-md-6">
|
48
|
+
<div class="form-group">
|
49
|
+
<%= label_tag :q_vendor_name_cont, "Vendor" %>
|
50
|
+
<%= f.text_field :vendor_name_cont, class: "form-control js-quick-search-target js-filterable" %>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
|
54
|
+
|
55
|
+
</div>
|
56
|
+
<div class="form-actions">
|
57
|
+
<%= button Spree.t(:filter_results), 'search' %>
|
58
|
+
<%= link_to Spree.t(:clear_filters), admin_events_path(q: {}), class: "btn btn-secondary" %>
|
59
|
+
</div>
|
60
|
+
<% end %>
|
61
|
+
<% end %>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<% content_for :page_tabs do %>
|
2
|
+
<li class="nav-item">
|
3
|
+
<%= link_to 'All',
|
4
|
+
admin_events_path,
|
5
|
+
class: "nav-link #{'active' if params[:q][:status_eq].blank? }" %>
|
6
|
+
</li>
|
7
|
+
<li class="nav-item">
|
8
|
+
<%= link_to_with_icon 'calendar.svg',
|
9
|
+
Spree.t(:"admin.events.upcoming"),
|
10
|
+
admin_events_path(q: { from_date_gt: Date.today, status_eq: 'Upcoming' }),
|
11
|
+
class: "nav-link #{'active' if params[:q][:status_eq] == 'Upcoming'}" %>
|
12
|
+
</li>
|
13
|
+
<li class="nav-item">
|
14
|
+
<%= link_to_with_icon 'clock.svg',
|
15
|
+
Spree.t(:"admin.events.happening"),
|
16
|
+
admin_events_path(q: { from_date_lteq: Date.today, to_date_gteq: Date.today, status_eq: 'Happening' }),
|
17
|
+
class: "nav-link #{'active' if params[:q][:status_eq] == 'Happening'}" %>
|
18
|
+
</li>
|
19
|
+
<li class="nav-item">
|
20
|
+
<%= link_to_with_icon 'clock.svg',
|
21
|
+
Spree.t(:"admin.events.past"),
|
22
|
+
admin_events_path(q: { to_date_lt: Date.today, status_eq: 'Past' }),
|
23
|
+
class: "nav-link #{'active' if params[:q][:status_eq] == 'Past'}" %>
|
24
|
+
</li>
|
25
|
+
<% end %>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<div class="table-responsive border rounded bg-white mb-4">
|
2
|
+
<table class="table " id="listing_products">
|
3
|
+
<thead class="text-muted">
|
4
|
+
<tr data-hook="admin_products_index_headers">
|
5
|
+
<th><%= sort_link @search,:name, Spree.t(:name), { default_order: "asc" } %></th>
|
6
|
+
<th class="col-2">Vendor</th>
|
7
|
+
<th><%= sort_link @search,:from_date, Spree.t(:from_date), { default_order: "asc" } %></th>
|
8
|
+
<th><%= sort_link @search,:to_date, Spree.t(:to_date), { default_order: "asc" } %></th>
|
9
|
+
<th class="col-1 text-center" data-hook="admin_event_header_actions">Actions</th>
|
10
|
+
</tr>
|
11
|
+
</thead>
|
12
|
+
<tbody>
|
13
|
+
<% @taxons.each do |taxon| %>
|
14
|
+
<tr>
|
15
|
+
<td class="col-4 align-middle"><%= taxon.name %></td>
|
16
|
+
<td class="col-2 align-middle"><%= taxon.vendor.name if taxon.vendor.present? %></td>
|
17
|
+
<td class="col-2 align-middle"><%= taxon.from_date %></td>
|
18
|
+
<td class="col-2 align-middle"><%= taxon.to_date %></td>
|
19
|
+
<td class="col-1 align-middle text-center" data-hook="admin_products_index_row_actions">
|
20
|
+
<%= link_to_edit taxon.id, url: edit_admin_taxonomy_taxon_url(taxon.taxonomy.id, taxon.id), no_text: true if can?(:edit, taxon) %>
|
21
|
+
<%= link_to_delete taxon.id, url: admin_taxonomy_taxon_url(taxon.taxonomy.id, taxon.id), no_text: true if can?(:delete, taxon) %>
|
22
|
+
</td>
|
23
|
+
</tr>
|
24
|
+
<% end %>
|
25
|
+
</tbody>
|
26
|
+
</table>
|
27
|
+
</div>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<%= content_for :page_title do %>
|
2
|
+
<%= Spree.t(:Events) %>
|
3
|
+
<% end %>
|
4
|
+
|
5
|
+
<%= content_for :page_actions do %>
|
6
|
+
<%= button_link_to Spree.t(:new_event), new_admin_taxonomy_taxon_path(@taxonomy.id) , class: "btn btn-success", icon: 'add.svg', id: 'admin_new_taxonomy_link' %>
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
<%= render 'tab' %>
|
10
|
+
|
11
|
+
<%= render 'search_form' %>
|
12
|
+
|
13
|
+
<%= render 'table' %>
|
14
|
+
|
15
|
+
<%= paginate @taxons, theme: 'admin-twitter-bootstrap-4', params: {taxon_id: params[:taxon_id] } %>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<div>
|
2
|
+
<% events.each do |event| %>
|
3
|
+
<div>
|
4
|
+
<h6><%= event.options[:inventory_item].variant.options_text %></h6>
|
5
|
+
<ul class="list-group mt-1">
|
6
|
+
<li class="list-group-item d-flex justify-content-between align-items-center">
|
7
|
+
Max capacity
|
8
|
+
<span class="badge badge-primary badge-pill"><%= event.options[:inventory_item].max_capacity %></span>
|
9
|
+
</li>
|
10
|
+
<li class="list-group-item d-flex justify-content-between align-items-center">
|
11
|
+
Quantity available
|
12
|
+
<span class="badge badge-primary badge-pill"><%= event.options[:inventory_item].quantity_available %></span>
|
13
|
+
</li>
|
14
|
+
</ul>
|
15
|
+
</div>
|
16
|
+
<% end %>
|
17
|
+
</div>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<%= turbo_frame_tag "calendar" do %>
|
2
|
+
<%= form_with url: admin_product_stock_managements_path, method: :get, class: "mt-4" do %>
|
3
|
+
<%= select_year(params[:year]&.to_i || @year, { start_year: 2025, end_year: @year + 3 }, { name: "year", onchange: "this.form.submit()" }) %>
|
4
|
+
<% end %>
|
5
|
+
|
6
|
+
<div class="c-annual-calendar-container mt-2">
|
7
|
+
<div class="c-annual-calendar">
|
8
|
+
<% (1..12).each do |month| %>
|
9
|
+
<%= month_calendar(start_date: Date.new(@year, month, 1), attribute: :from_date, end_attribute: :to_date, events: @events) do |date, events| %>
|
10
|
+
<div class="c-annual-day"
|
11
|
+
data-date="<%= date.strftime '%Y-%m-%d' %>"
|
12
|
+
type="button"
|
13
|
+
tabindex="0"
|
14
|
+
data-toggle="popover"
|
15
|
+
data-trigger="hover"
|
16
|
+
data-placement="right"
|
17
|
+
data-html="true"
|
18
|
+
data-content="<%= raw render_escape_html partial: "events_popover", locals: { events: events } if events.any? %>">
|
19
|
+
<%= date.day %>
|
20
|
+
<ul class="p-0 m-0 list-unstyled">
|
21
|
+
<% events.each do |event| %>
|
22
|
+
<li class="badge badge-warning">
|
23
|
+
<%= event.options[:inventory_item].quantity_available %>
|
24
|
+
</li>
|
25
|
+
<% end %>
|
26
|
+
</ul>
|
27
|
+
</div>
|
28
|
+
<% end %>
|
29
|
+
<% end %>
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
<% end %>
|
@@ -19,10 +19,7 @@
|
|
19
19
|
<% @variants.each do |variant| %>
|
20
20
|
<tr id="<%= spree_dom_id variant %>" data-hook="admin_product_stock_management_index_rows">
|
21
21
|
<td class="image text-center">
|
22
|
-
|
23
|
-
<div class="mt-4">
|
24
|
-
Reserved Stock: <%= @reserved_stocks[variant.id] || 0 %></strong>
|
25
|
-
</div>
|
22
|
+
<%= small_image(variant) %>
|
26
23
|
</td>
|
27
24
|
<td>
|
28
25
|
<%= variant.sku_and_options_text %>
|
@@ -35,14 +32,37 @@
|
|
35
32
|
<% end %>
|
36
33
|
</div>
|
37
34
|
<% end if can?(:update, @product) && can?(:update, variant) %>
|
35
|
+
|
36
|
+
<% if defined?(@reserved_stocks) %>
|
37
|
+
<div>
|
38
|
+
<span type="button" data-toggle="popover" data-trigger="hover" data-placement="right" data-content="This product stock will renew every day">
|
39
|
+
<%= svg_icon name: "cart-check.svg", width: '14', height: '14' %>
|
40
|
+
</span>
|
41
|
+
<%= label_tag "reserved_stock#{variant.id}", "Reserved Stock: #{@reserved_stocks[variant.id] || 0}", class: "m-0" %>
|
42
|
+
</div>
|
43
|
+
<% end %>
|
44
|
+
|
38
45
|
<% if variant.permanent_stock? %>
|
39
46
|
<div>
|
40
47
|
<span type="button" data-toggle="popover" data-trigger="hover" data-placement="right" data-content="This product stock will renew every day">
|
41
48
|
<%= svg_icon name: "info-circle-fill.svg", width: '14', height: '14' %>
|
42
49
|
</span>
|
43
|
-
<%= label_tag "permanent_stock_#{ variant.id }", Spree.t(:permanent_stock) %>
|
50
|
+
<%= label_tag "permanent_stock_#{ variant.id }", Spree.t(:permanent_stock), class: "m-0" %>
|
44
51
|
</div>
|
45
52
|
<% end %>
|
53
|
+
|
54
|
+
<% if defined?(@inventory_items) %>
|
55
|
+
<% @inventory_items[variant.id]&.each do |inventory_item| %>
|
56
|
+
<div>
|
57
|
+
<%= svg_icon name: "handbag.svg", width: '14', height: '14' %>
|
58
|
+
<%= label_tag "max_capacity_#{ inventory_item.id }", "Max capacity: #{inventory_item.max_capacity}", class: "m-0" %>
|
59
|
+
</div>
|
60
|
+
<div>
|
61
|
+
<%= svg_icon name: "approve.svg", width: '14', height: '14' %>
|
62
|
+
<%= label_tag "quantity_available_#{ inventory_item.id }", "Quantity available: #{inventory_item.quantity_available}", class: "m-0" %>
|
63
|
+
</div>
|
64
|
+
<% end %>
|
65
|
+
<% end %>
|
46
66
|
</td>
|
47
67
|
|
48
68
|
<td colspan="3" class="stock_location_info">
|
@@ -53,3 +73,9 @@
|
|
53
73
|
</tbody>
|
54
74
|
</table>
|
55
75
|
</div>
|
76
|
+
|
77
|
+
<%= turbo_frame_tag "calendar", src: calendar_admin_product_stock_managements_path(year: params[:year]) do %>
|
78
|
+
<div class="spinner-border mt-2" role="status">
|
79
|
+
<span class="sr-only">Loading...</span>
|
80
|
+
</div>
|
81
|
+
<% end if @product.permanent_stock? %>
|