spree_cm_commissioner 2.5.14.pre.pre1 → 2.5.14.pre.pre2
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/Gemfile.lock +1 -1
- data/app/controllers/spree/admin/classifications_controller.rb +3 -4
- data/app/jobs/spree_cm_commissioner/import_order_job.rb +2 -2
- data/app/models/spree_cm_commissioner/exports/operator_guest_json_gzip.rb +1 -4
- data/app/services/spree_cm_commissioner/check_ins/create_bulk.rb +15 -4
- data/app/services/spree_cm_commissioner/imports/base_import_order_service.rb +55 -0
- data/app/services/spree_cm_commissioner/imports/create_order_service.rb +99 -0
- data/app/services/spree_cm_commissioner/imports/update_order_service.rb +41 -0
- data/app/services/spree_cm_commissioner/organizer/export_guest_csv_service.rb +36 -0
- data/lib/spree_cm_commissioner/version.rb +1 -1
- metadata +4 -4
- data/app/services/spree_cm_commissioner/imports/orders/base.rb +0 -70
- data/app/services/spree_cm_commissioner/imports/orders/create.rb +0 -134
- data/app/services/spree_cm_commissioner/imports/orders/update.rb +0 -77
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9fc822ca647b6847cc80bc5d4f24f68312c67c3cf772e8422801f29d36b8c871
|
|
4
|
+
data.tar.gz: ac27932993e5c366efcfe5d1f15491c7b0248a886f0d804af69545a28a6a83cd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6030cee41764d55a73430c81849bc91b12dcf12c04097795c3646297e75c53b3206ca186a704a6603eddcb6a57e4226d437e25227bf6a005fe514ea784891d26
|
|
7
|
+
data.tar.gz: d48e1f3ec06affa85260babeea658b9a9cb95ddffffe01175fa9dd24837df25c2a087fd5e4dbc3849ce2592178db9d71793719c21e783ca425820b226f142862
|
data/Gemfile.lock
CHANGED
|
@@ -7,10 +7,9 @@ module Spree
|
|
|
7
7
|
if @taxon.parent.event?
|
|
8
8
|
SpreeCmCommissioner::MaintenanceTasks::Event.pending.find_or_create_by(
|
|
9
9
|
maintainable_type: 'Spree::Taxon',
|
|
10
|
-
maintainable_id: @taxon.parent.id
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
end.async_execute
|
|
10
|
+
maintainable_id: @taxon.parent.id,
|
|
11
|
+
manually_triggered: true
|
|
12
|
+
).async_execute
|
|
14
13
|
|
|
15
14
|
flash[:success] = flash_message_for(@taxon, :successfully_updated)
|
|
16
15
|
else
|
|
@@ -2,12 +2,12 @@ module SpreeCmCommissioner
|
|
|
2
2
|
class ImportOrderJob < ApplicationUniqueJob
|
|
3
3
|
def perform(options = {})
|
|
4
4
|
if options[:import_type] == 'new_order'
|
|
5
|
-
SpreeCmCommissioner::Imports::
|
|
5
|
+
SpreeCmCommissioner::Imports::CreateOrderService.new(
|
|
6
6
|
import_order_id: options[:import_order_id],
|
|
7
7
|
import_by_user_id: options[:import_by_user_id]
|
|
8
8
|
).call
|
|
9
9
|
else
|
|
10
|
-
SpreeCmCommissioner::Imports::
|
|
10
|
+
SpreeCmCommissioner::Imports::UpdateOrderService.new(
|
|
11
11
|
import_order_id: options[:import_order_id]
|
|
12
12
|
).call
|
|
13
13
|
end
|
|
@@ -33,8 +33,7 @@ module SpreeCmCommissioner
|
|
|
33
33
|
output_gz_file = Tempfile.new(['export', '.json.gz'])
|
|
34
34
|
|
|
35
35
|
begin
|
|
36
|
-
|
|
37
|
-
self.guests_count = total_processed
|
|
36
|
+
stream_export_data_and_includes_to_separate_files(guests_query, guests_file, included_file)
|
|
38
37
|
stream_export_merge_and_compress_data_and_includes_to_a_single_file(guests_file, included_file, output_gz_file)
|
|
39
38
|
attach_file_to_record(output_gz_file)
|
|
40
39
|
ensure
|
|
@@ -99,8 +98,6 @@ module SpreeCmCommissioner
|
|
|
99
98
|
update(track_processed_messages: track_processed_messages.last(50))
|
|
100
99
|
end
|
|
101
100
|
end
|
|
102
|
-
|
|
103
|
-
total_processed_guests
|
|
104
101
|
end
|
|
105
102
|
|
|
106
103
|
def stream_export_merge_and_compress_data_and_includes_to_a_single_file(guests_file, included_file, output_gz_file)
|
|
@@ -8,12 +8,23 @@ module SpreeCmCommissioner
|
|
|
8
8
|
|
|
9
9
|
confirmed_at ||= Time.current
|
|
10
10
|
guest_ids = check_ins_attributes.pluck(:guest_id)
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
indexed_guests = SpreeCmCommissioner::Guest
|
|
13
|
+
.includes(:event, line_item: :order)
|
|
14
|
+
.where(id: guest_ids)
|
|
15
|
+
.index_by(&:id)
|
|
12
16
|
|
|
13
17
|
check_ins = ActiveRecord::Base.transaction do
|
|
14
18
|
check_ins_attributes.map do |attrs|
|
|
15
19
|
guest = indexed_guests[attrs[:guest_id].to_i]
|
|
16
|
-
raise ActiveRecord::RecordNotFound, "Couldn't find Guest
|
|
20
|
+
raise ActiveRecord::RecordNotFound, "Couldn't find Guest to check in" if guest.blank?
|
|
21
|
+
|
|
22
|
+
order = guest.line_item.order
|
|
23
|
+
if order.canceled?
|
|
24
|
+
error = ActiveRecord::RecordInvalid.new(guest)
|
|
25
|
+
error.record.errors.add(:base, 'Cannot check in Guest from cancelled order')
|
|
26
|
+
raise error
|
|
27
|
+
end
|
|
17
28
|
|
|
18
29
|
check_in = check_in!(guest, attrs, check_in_by, confirmed_at)
|
|
19
30
|
update_guest!(guest, attrs[:guest_attributes]) if attrs[:guest_attributes].present?
|
|
@@ -25,8 +36,8 @@ module SpreeCmCommissioner
|
|
|
25
36
|
success(check_ins: check_ins)
|
|
26
37
|
rescue ActiveRecord::RecordInvalid => e
|
|
27
38
|
failure(:invalid_record, e.record.errors.full_messages.join(', '))
|
|
28
|
-
rescue ActiveRecord::RecordNotFound
|
|
29
|
-
failure(:record_not_found,
|
|
39
|
+
rescue ActiveRecord::RecordNotFound
|
|
40
|
+
failure(:record_not_found, 'Guest or related record not found')
|
|
30
41
|
end
|
|
31
42
|
|
|
32
43
|
private
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module Imports
|
|
3
|
+
class BaseImportOrderService
|
|
4
|
+
attr_reader :import_order_id, :fail_row_numbers
|
|
5
|
+
|
|
6
|
+
def initialize(import_order_id:)
|
|
7
|
+
@import_order_id = import_order_id
|
|
8
|
+
@fail_row_numbers = []
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def call
|
|
12
|
+
update_import_status_when_start(:progress)
|
|
13
|
+
import_orders
|
|
14
|
+
save_fail_rows
|
|
15
|
+
update_import_status_when_finish(:done)
|
|
16
|
+
rescue StandardError => e
|
|
17
|
+
update_import_status_when_finish(:failed)
|
|
18
|
+
raise e
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def save_fail_rows
|
|
22
|
+
import_order.update!(preferred_fail_rows: @fail_row_numbers.join(', ')) unless @fail_row_numbers.empty?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def record_failure(row_number)
|
|
26
|
+
@fail_row_numbers << row_number
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def import_order
|
|
30
|
+
@import_order ||= SpreeCmCommissioner::Imports::ImportOrder.find(import_order_id)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def update_import_status_when_start(status)
|
|
34
|
+
import_order.update(status: status, started_at: Time.zone.now)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def update_import_status_when_finish(status)
|
|
38
|
+
import_order.update(status: status, finished_at: Time.zone.now)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def fetch_content
|
|
42
|
+
url = import_order.imported_file.url
|
|
43
|
+
response = Faraday.get(url)
|
|
44
|
+
|
|
45
|
+
raise "Failed to fetch content: #{response.status}" unless response.success?
|
|
46
|
+
|
|
47
|
+
response.body
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def cleaned_value(value)
|
|
51
|
+
value.to_s.gsub(/\s+/, '')
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module Imports
|
|
3
|
+
class CreateOrderService < BaseImportOrderService
|
|
4
|
+
attr_reader :import_by_user_id
|
|
5
|
+
|
|
6
|
+
def initialize(import_order_id:, import_by_user_id:)
|
|
7
|
+
super(import_order_id: import_order_id)
|
|
8
|
+
@import_by_user_id = import_by_user_id
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def import_orders
|
|
12
|
+
content = fetch_content
|
|
13
|
+
|
|
14
|
+
CSV.parse(content, headers: true).each.with_index(2) do |row, index|
|
|
15
|
+
order_data = row.to_hash.symbolize_keys
|
|
16
|
+
process_order(order_data, index)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def process_order(order_data, index)
|
|
21
|
+
ActiveRecord::Base.transaction do
|
|
22
|
+
params = build_order_params(order_data)
|
|
23
|
+
order = create_order(params, index)
|
|
24
|
+
record_failure(index) if order && !complete_order(order)
|
|
25
|
+
end
|
|
26
|
+
rescue StandardError
|
|
27
|
+
record_failure(index)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def build_order_params(order_data)
|
|
33
|
+
guest_attributes = order_data.slice(*SpreeCmCommissioner::Guest.csv_importable_columns)
|
|
34
|
+
if order_data[:variant_id].present?
|
|
35
|
+
{
|
|
36
|
+
channel: construct_channel('google_form'),
|
|
37
|
+
email: order_data[:email],
|
|
38
|
+
phone_number: order_data[:phone_number],
|
|
39
|
+
line_items_attributes: [
|
|
40
|
+
{
|
|
41
|
+
quantity: 1,
|
|
42
|
+
variant_id: order_data[:variant_id].to_i,
|
|
43
|
+
guests_attributes: Array.new(1, guest_attributes)
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
else
|
|
48
|
+
{
|
|
49
|
+
channel: construct_channel(order_data[:order_channel]),
|
|
50
|
+
email: order_data[:email],
|
|
51
|
+
phone_number: order_data[:phone_number],
|
|
52
|
+
line_items_attributes: [
|
|
53
|
+
{
|
|
54
|
+
quantity: order_data[:quantity].to_i,
|
|
55
|
+
sku: order_data[:variant_sku],
|
|
56
|
+
guests_attributes: Array.new(order_data[:quantity].to_i, guest_attributes)
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def create_order(params, index)
|
|
64
|
+
order = Spree::Core::Importer::Order.import(import_by, params)
|
|
65
|
+
unless order
|
|
66
|
+
record_failure(index)
|
|
67
|
+
return nil
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
order
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def complete_order(order)
|
|
74
|
+
# Wraps order completion and inventory update in a transaction to ensure atomicity.
|
|
75
|
+
# If either the order update or inventory update fails, both operations will be rolled back.
|
|
76
|
+
# This prevents partial updates that could lead to data inconsistency.
|
|
77
|
+
order.unstock_inventory! do
|
|
78
|
+
order.update!(
|
|
79
|
+
completed_at: Time.zone.now,
|
|
80
|
+
state: 'complete',
|
|
81
|
+
payment_total: order.total,
|
|
82
|
+
payment_state: 'paid'
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
rescue StandardError => e
|
|
86
|
+
AppLogger.error("#{self.class.name}::#complete_order failed for Order ID #{order.id}: #{e.message}")
|
|
87
|
+
false
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def import_by
|
|
91
|
+
@import_by ||= Spree::User.find(import_by_user_id)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def construct_channel(order_channel)
|
|
95
|
+
"#{order_channel}-#{import_order.id}-#{import_order.slug}"
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module Imports
|
|
3
|
+
class UpdateOrderService < BaseImportOrderService
|
|
4
|
+
def import_orders
|
|
5
|
+
content = fetch_content
|
|
6
|
+
|
|
7
|
+
CSV.parse(content, headers: true).each.with_index(2) do |row, index|
|
|
8
|
+
order_number = cleaned_value(row['order_number'])
|
|
9
|
+
order = Spree::Order.find_by(number: order_number)
|
|
10
|
+
|
|
11
|
+
unless order
|
|
12
|
+
record_failure(index)
|
|
13
|
+
next
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
guest_id = cleaned_value(row['guest_id'])
|
|
17
|
+
seat_number = cleaned_value(row['seat_number'])
|
|
18
|
+
guest = order.guests.find_by(seat_number: seat_number) || order.guests.find_by(id: guest_id)
|
|
19
|
+
|
|
20
|
+
unless guest
|
|
21
|
+
record_failure(index)
|
|
22
|
+
next
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
guest_data = row.to_hash.symbolize_keys.slice(*SpreeCmCommissioner::Guest.csv_importable_columns)
|
|
26
|
+
guest_data.compact_blank!
|
|
27
|
+
|
|
28
|
+
if guest.update(guest_data)
|
|
29
|
+
recalculate_order(order)
|
|
30
|
+
else
|
|
31
|
+
record_failure(index)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def recalculate_order(order)
|
|
37
|
+
order.update_with_updater!
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -29,6 +29,8 @@ module SpreeCmCommissioner
|
|
|
29
29
|
def fetch_value(guest, column)
|
|
30
30
|
if guest_field?(column)
|
|
31
31
|
fetch_guest_value(guest, column)
|
|
32
|
+
elsif dynamic_field?(column)
|
|
33
|
+
fetch_dynamic_field_value(guest, column)
|
|
32
34
|
else
|
|
33
35
|
fetch_option_value(guest, column)
|
|
34
36
|
end
|
|
@@ -38,6 +40,39 @@ module SpreeCmCommissioner
|
|
|
38
40
|
SpreeCmCommissioner::KycBitwise::ORDERED_BIT_FIELDS.include?(column.to_sym)
|
|
39
41
|
end
|
|
40
42
|
|
|
43
|
+
def dynamic_field?(column)
|
|
44
|
+
return false unless dynamic_fields_exist?
|
|
45
|
+
|
|
46
|
+
dynamic_field_labels.include?(column)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def dynamic_field_labels
|
|
50
|
+
return [] unless dynamic_fields_exist?
|
|
51
|
+
|
|
52
|
+
@dynamic_field_labels ||= load_dynamic_field_labels
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def dynamic_fields_exist?
|
|
56
|
+
return @has_dynamic_fields unless @has_dynamic_fields.nil?
|
|
57
|
+
|
|
58
|
+
@has_dynamic_fields = event.event_products.joins(:product_dynamic_fields).exists?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def load_dynamic_field_labels
|
|
62
|
+
event.event_products.joins(product_dynamic_fields: :dynamic_field).distinct.pluck('label')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def fetch_dynamic_field_value(guest, column)
|
|
66
|
+
guest_dynamic_field = guest.guest_dynamic_fields.find { |gdf| gdf.dynamic_field&.label == column }
|
|
67
|
+
return '' unless guest_dynamic_field
|
|
68
|
+
|
|
69
|
+
if guest_dynamic_field.dynamic_field_option.present?
|
|
70
|
+
guest_dynamic_field.dynamic_field_option.value
|
|
71
|
+
else
|
|
72
|
+
guest_dynamic_field.value || ''
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
41
76
|
def fetch_guest_value(guest, column)
|
|
42
77
|
column_mappings(guest)[column] || ''
|
|
43
78
|
end
|
|
@@ -82,6 +117,7 @@ module SpreeCmCommissioner
|
|
|
82
117
|
:occupation,
|
|
83
118
|
:nationality,
|
|
84
119
|
:id_card,
|
|
120
|
+
guest_dynamic_fields: %i[dynamic_field dynamic_field_option],
|
|
85
121
|
line_item: { variant: { option_values: :option_type } }
|
|
86
122
|
).complete
|
|
87
123
|
scope = scope.where('cm_guests.created_at >= ?', formatted_date_time(filters[:from_date])) if filters[:from_date].present?
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spree_cm_commissioner
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.5.14.pre.
|
|
4
|
+
version: 2.5.14.pre.pre2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- You
|
|
@@ -2032,9 +2032,9 @@ files:
|
|
|
2032
2032
|
- app/services/spree_cm_commissioner/guests/preload_check_in_session_ids.rb
|
|
2033
2033
|
- app/services/spree_cm_commissioner/homepage_data.rb
|
|
2034
2034
|
- app/services/spree_cm_commissioner/homepage_data_loader.rb
|
|
2035
|
-
- app/services/spree_cm_commissioner/imports/
|
|
2036
|
-
- app/services/spree_cm_commissioner/imports/
|
|
2037
|
-
- app/services/spree_cm_commissioner/imports/
|
|
2035
|
+
- app/services/spree_cm_commissioner/imports/base_import_order_service.rb
|
|
2036
|
+
- app/services/spree_cm_commissioner/imports/create_order_service.rb
|
|
2037
|
+
- app/services/spree_cm_commissioner/imports/update_order_service.rb
|
|
2038
2038
|
- app/services/spree_cm_commissioner/integrations/base/sync_manager.rb
|
|
2039
2039
|
- app/services/spree_cm_commissioner/integrations/base/sync_result.rb
|
|
2040
2040
|
- app/services/spree_cm_commissioner/integrations/cleanup_sync_sessions.rb
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
require 'tempfile'
|
|
2
|
-
|
|
3
|
-
module SpreeCmCommissioner
|
|
4
|
-
module Imports
|
|
5
|
-
module Orders
|
|
6
|
-
class Base
|
|
7
|
-
attr_reader :import_order_id, :fail_row_numbers
|
|
8
|
-
|
|
9
|
-
def initialize(import_order_id:)
|
|
10
|
-
@import_order_id = import_order_id
|
|
11
|
-
@fail_row_numbers = []
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def call
|
|
15
|
-
update_import_status_when_start(:progress)
|
|
16
|
-
import_orders
|
|
17
|
-
save_fail_rows
|
|
18
|
-
update_import_status_when_finish(:done)
|
|
19
|
-
rescue StandardError => e
|
|
20
|
-
update_import_status_when_finish(:failed)
|
|
21
|
-
raise e
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def save_fail_rows
|
|
25
|
-
import_order.update!(preferred_fail_rows: @fail_row_numbers.join(', ')) unless @fail_row_numbers.empty?
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def record_failure(row_number)
|
|
29
|
-
@fail_row_numbers << row_number
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def import_order
|
|
33
|
-
@import_order ||= SpreeCmCommissioner::Imports::ImportOrder.find(import_order_id)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def update_import_status_when_start(status)
|
|
37
|
-
import_order.update(status: status, started_at: Time.zone.now)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def update_import_status_when_finish(status)
|
|
41
|
-
import_order.update(status: status, finished_at: Time.zone.now)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def fetch_content
|
|
45
|
-
url = import_order.imported_file.url
|
|
46
|
-
temp_file = Tempfile.new(['import', '.csv'], binmode: true)
|
|
47
|
-
|
|
48
|
-
begin
|
|
49
|
-
response = Faraday.get(url) do |req|
|
|
50
|
-
req.options.on_data = proc { |chunk| temp_file.write(chunk) }
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
raise "HTTP request failed with status #{response.status}" unless response.success?
|
|
54
|
-
|
|
55
|
-
temp_file.close
|
|
56
|
-
temp_file.path
|
|
57
|
-
rescue StandardError
|
|
58
|
-
temp_file.close
|
|
59
|
-
temp_file.unlink
|
|
60
|
-
raise
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def cleaned_value(value)
|
|
65
|
-
value.to_s.gsub(/\s+/, '')
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
require 'tempfile'
|
|
2
|
-
|
|
3
|
-
module SpreeCmCommissioner
|
|
4
|
-
module Imports
|
|
5
|
-
module Orders
|
|
6
|
-
class Create < Base
|
|
7
|
-
BATCH_SIZE = 100
|
|
8
|
-
|
|
9
|
-
attr_reader :import_by_user_id
|
|
10
|
-
|
|
11
|
-
def initialize(import_order_id:, import_by_user_id:)
|
|
12
|
-
super(import_order_id: import_order_id)
|
|
13
|
-
@import_by_user_id = import_by_user_id
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def import_orders
|
|
17
|
-
# Processes CSV orders in a memory-efficient streaming manner to handle large files (50k+ rows).
|
|
18
|
-
# Batches rows into groups of 100 for reduced DB transaction overhead and atomicity per batch.
|
|
19
|
-
# Uses CSV.foreach to stream from disk, avoiding memory spikes. Row indices start at 2 (post-header).
|
|
20
|
-
file_path = fetch_content
|
|
21
|
-
batch = []
|
|
22
|
-
|
|
23
|
-
begin
|
|
24
|
-
CSV.foreach(file_path, headers: true).with_index(2) do |row, index|
|
|
25
|
-
batch << [row.to_hash.symbolize_keys, index]
|
|
26
|
-
|
|
27
|
-
if batch.size >= BATCH_SIZE
|
|
28
|
-
process_batch(batch)
|
|
29
|
-
batch = []
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
process_batch(batch) unless batch.empty?
|
|
34
|
-
ensure
|
|
35
|
-
FileUtils.rm_f(file_path)
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def process_batch(batch)
|
|
40
|
-
# Processes a batch of orders with each row in its own transaction for error isolation.
|
|
41
|
-
# Failures record but don't affect other rows, allowing partial batch success.
|
|
42
|
-
batch.each do |order_data, index|
|
|
43
|
-
ActiveRecord::Base.transaction do
|
|
44
|
-
process_order(order_data, index)
|
|
45
|
-
rescue StandardError => e
|
|
46
|
-
CmAppLogger.error(
|
|
47
|
-
label: "#{self.class.name}::process_batch",
|
|
48
|
-
data: { message: e.message, row: index }
|
|
49
|
-
)
|
|
50
|
-
record_failure(index)
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def process_order(order_data, index)
|
|
56
|
-
params = build_order_params(order_data)
|
|
57
|
-
order = create_order(params, index)
|
|
58
|
-
record_failure(index) if order && !complete_order(order)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
private
|
|
62
|
-
|
|
63
|
-
def build_order_params(order_data)
|
|
64
|
-
guest_attributes = order_data.slice(*SpreeCmCommissioner::Guest.csv_importable_columns)
|
|
65
|
-
channel = construct_channel(
|
|
66
|
-
order_data[:variant_id].present? ? 'google_form' : order_data[:order_channel]
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
common_attrs = {
|
|
70
|
-
channel: channel,
|
|
71
|
-
email: order_data[:email],
|
|
72
|
-
phone_number: order_data[:phone_number]
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
line_item_attrs =
|
|
76
|
-
if order_data[:variant_id].present?
|
|
77
|
-
{
|
|
78
|
-
quantity: 1,
|
|
79
|
-
variant_id: order_data[:variant_id].to_i,
|
|
80
|
-
guests_attributes: Array.new(1, guest_attributes)
|
|
81
|
-
}
|
|
82
|
-
else
|
|
83
|
-
{
|
|
84
|
-
quantity: order_data[:quantity].to_i,
|
|
85
|
-
sku: order_data[:variant_sku],
|
|
86
|
-
guests_attributes: Array.new(order_data[:quantity].to_i, guest_attributes)
|
|
87
|
-
}
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
common_attrs.merge(line_items_attributes: [line_item_attrs])
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def create_order(params, index)
|
|
94
|
-
order = Spree::Core::Importer::Order.import(import_by, params)
|
|
95
|
-
|
|
96
|
-
unless order
|
|
97
|
-
record_failure(index)
|
|
98
|
-
return nil
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
order
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def complete_order(order)
|
|
105
|
-
# Wraps order completion and inventory update in a transaction to ensure atomicity.
|
|
106
|
-
# If either the order update or inventory update fails, both operations will be rolled back.
|
|
107
|
-
# This prevents partial updates that could lead to data inconsistency.
|
|
108
|
-
order.unstock_inventory! do
|
|
109
|
-
order.update!(
|
|
110
|
-
completed_at: Time.zone.now,
|
|
111
|
-
state: 'complete',
|
|
112
|
-
payment_total: order.total,
|
|
113
|
-
payment_state: 'paid'
|
|
114
|
-
)
|
|
115
|
-
end
|
|
116
|
-
rescue StandardError => e
|
|
117
|
-
CmAppLogger.error(
|
|
118
|
-
label: "#{self.class.name}::complete_order",
|
|
119
|
-
data: { message: e.message, order_id: order.id }
|
|
120
|
-
)
|
|
121
|
-
false
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def import_by
|
|
125
|
-
@import_by ||= Spree::User.find(import_by_user_id)
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def construct_channel(order_channel)
|
|
129
|
-
"#{order_channel}-#{import_order.id}-#{import_order.slug}"
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
end
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
module SpreeCmCommissioner
|
|
2
|
-
module Imports
|
|
3
|
-
module Orders
|
|
4
|
-
class Update < Base
|
|
5
|
-
BATCH_SIZE = 100
|
|
6
|
-
|
|
7
|
-
def import_orders
|
|
8
|
-
file_path = fetch_content
|
|
9
|
-
batch = []
|
|
10
|
-
|
|
11
|
-
begin
|
|
12
|
-
CSV.foreach(file_path, headers: true).with_index(2) do |row, index|
|
|
13
|
-
batch << [row, index]
|
|
14
|
-
|
|
15
|
-
if batch.size >= BATCH_SIZE
|
|
16
|
-
process_batch(batch)
|
|
17
|
-
batch = []
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
process_batch(batch) unless batch.empty?
|
|
22
|
-
ensure
|
|
23
|
-
FileUtils.rm_f(file_path)
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def process_batch(batch)
|
|
28
|
-
batch.each do |row, index|
|
|
29
|
-
ActiveRecord::Base.transaction do
|
|
30
|
-
process_row(row, index)
|
|
31
|
-
rescue StandardError
|
|
32
|
-
record_failure(index)
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def process_row(row, index)
|
|
38
|
-
order_number = cleaned_value(row['order_number'])
|
|
39
|
-
order = Spree::Order.find_by(number: order_number)
|
|
40
|
-
|
|
41
|
-
unless order
|
|
42
|
-
record_failure(index)
|
|
43
|
-
return
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
guest_id = cleaned_value(row['guest_id'])
|
|
47
|
-
seat_number = cleaned_value(row['seat_number'])
|
|
48
|
-
|
|
49
|
-
guest =
|
|
50
|
-
order.guests.find_by(seat_number: seat_number) ||
|
|
51
|
-
order.guests.find_by(id: guest_id)
|
|
52
|
-
|
|
53
|
-
unless guest
|
|
54
|
-
record_failure(index)
|
|
55
|
-
return
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
guest_data = row.to_hash
|
|
59
|
-
.symbolize_keys
|
|
60
|
-
.slice(*SpreeCmCommissioner::Guest.csv_importable_columns)
|
|
61
|
-
|
|
62
|
-
guest_data.compact_blank!
|
|
63
|
-
|
|
64
|
-
if guest.update(guest_data)
|
|
65
|
-
recalculate_order(order)
|
|
66
|
-
else
|
|
67
|
-
record_failure(index)
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def recalculate_order(order)
|
|
72
|
-
order.update_with_updater!
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|