c80_shared 0.1.59 → 0.1.60
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/repositories/accounts/admin_account_repository.rb +35 -0
- data/app/repositories/accounts/central_agent_account_repository.rb +46 -0
- data/app/repositories/accounts/client_account_repository.rb +43 -0
- data/app/repositories/accounts/manager_account_repository.rb +35 -0
- data/app/repositories/accounts/manager_guest_account_repository.rb +35 -0
- data/app/repositories/accounts/moderator_account_repository.rb +35 -0
- data/app/schemas/base.rb +67 -0
- data/app/schemas/base_boat_schema.rb +235 -0
- data/app/schemas/central_agent/save_boat_schema.rb +57 -0
- data/app/services/abstract_prices_service.rb +70 -0
- data/app/services/boats/boat_prices_save_service.rb +81 -0
- data/app/services/boats/boat_sale_prices_save_service.rb +60 -0
- data/app/services/boats/dimension_service.rb +44 -0
- data/app/services/central_agent/save_boat_service.rb +111 -0
- data/app/services/lease/create_broadcast_inquiry_service.rb +62 -0
- data/app/services/lease/destroy_inquiry_service.rb +5 -0
- data/config/initializers/core_ext/active_record_log_subscriber.rb +60 -0
- data/config/initializers/core_ext/string.rb +62 -0
- data/lib/c80_shared/dry/errors.rb +77 -0
- data/lib/c80_shared/dry/rule.rb +69 -0
- data/lib/c80_shared/dry/rules/and.rb +11 -0
- data/lib/c80_shared/dry/rules/between.rb +20 -0
- data/lib/c80_shared/dry/rules/binary.rb +18 -0
- data/lib/c80_shared/dry/rules/collection.rb +16 -0
- data/lib/c80_shared/dry/rules/composite.rb +19 -0
- data/lib/c80_shared/dry/rules/equal.rb +18 -0
- data/lib/c80_shared/dry/rules/format.rb +18 -0
- data/lib/c80_shared/dry/rules/greater_than.rb +18 -0
- data/lib/c80_shared/dry/rules/greater_than_or_equal.rb +18 -0
- data/lib/c80_shared/dry/rules/included.rb +18 -0
- data/lib/c80_shared/dry/rules/length_between.rb +18 -0
- data/lib/c80_shared/dry/rules/length_equal.rb +18 -0
- data/lib/c80_shared/dry/rules/less_than.rb +18 -0
- data/lib/c80_shared/dry/rules/less_than_or_equal.rb +18 -0
- data/lib/c80_shared/dry/rules/max_length.rb +18 -0
- data/lib/c80_shared/dry/rules/min_length.rb +18 -0
- data/lib/c80_shared/dry/rules/not_equal.rb +18 -0
- data/lib/c80_shared/dry/rules/or.rb +15 -0
- data/lib/c80_shared/dry/rules/present.rb +21 -0
- data/lib/c80_shared/dry/rules/then.rb +13 -0
- data/lib/c80_shared/dry/rules_factory.rb +118 -0
- data/lib/c80_shared/dry/schema.rb +148 -0
- data/lib/c80_shared/version.rb +1 -1
- data/lib/c80_shared.rb +1 -1
- metadata +44 -3
- data/scratch_105___02.txt +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 041f6a36b9dc94e33939939f808ffa80f7f6d69d34bcef15edc1eb49cbda7d81
|
4
|
+
data.tar.gz: 0c92d58789c52d96427c2b92911474f25d139e23f636faca8cb86ea59a9dbc00
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 698924b53ce5b331eb76555154bad6fe1015ceb33f87170de0b5249acce6c702de9a2efc1bcd146080d34e779734b3b444a59b9c3035eafdb1894a317e6a5c0b
|
7
|
+
data.tar.gz: 96996cb21f8db1cae96f2ecaedbd7d42165e0a9879253e34e15796ec88ddd4437d8d356bc495a77a7677c782dbf2b98c5a267933a29ae7b505f1fe9e07e6064d
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Accounts
|
2
|
+
class AdminAccountRepository
|
3
|
+
|
4
|
+
def create_account(user)
|
5
|
+
return unless user
|
6
|
+
return if user.accounts.admins.any?
|
7
|
+
|
8
|
+
account = ::Account.new(type_id: ::Dictionaries::AccountType::ADMIN)
|
9
|
+
admin = ::Accounts::Admin.new(account: account)
|
10
|
+
|
11
|
+
account.user = user
|
12
|
+
account.name = user.name.to_s
|
13
|
+
account.active = true
|
14
|
+
|
15
|
+
account.transaction do
|
16
|
+
account.save validate: false
|
17
|
+
admin.save validate: false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def destroy_account(user)
|
22
|
+
return unless user
|
23
|
+
return unless user.accounts.admins.any?
|
24
|
+
|
25
|
+
account = user.accounts.admins.first
|
26
|
+
admin = account.admin
|
27
|
+
|
28
|
+
ActiveRecord::Base.transaction do
|
29
|
+
admin.destroy
|
30
|
+
account.destroy
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Accounts
|
2
|
+
class CentralAgentAccountRepository
|
3
|
+
|
4
|
+
# todo-my: перевести в self.
|
5
|
+
def create_account(user)
|
6
|
+
return unless user
|
7
|
+
return if user.accounts.central_agents.any?
|
8
|
+
|
9
|
+
account = ::Account.new(type_id: ::Dictionaries::AccountType::CENTRAL_AGENT)
|
10
|
+
central = ::Accounts::CentralAgent.new(account: account)
|
11
|
+
|
12
|
+
account.user = user
|
13
|
+
account.name = user.name.to_s
|
14
|
+
account.active = true
|
15
|
+
user.is_notify = true
|
16
|
+
|
17
|
+
ActiveRecord::Base.transaction do
|
18
|
+
account.save
|
19
|
+
central.save
|
20
|
+
user.save
|
21
|
+
end
|
22
|
+
|
23
|
+
central
|
24
|
+
end
|
25
|
+
|
26
|
+
# todo-my: перевести в self.
|
27
|
+
def destroy_account(user)
|
28
|
+
return unless user
|
29
|
+
return unless user.accounts.central_agents.any?
|
30
|
+
|
31
|
+
account = user.accounts.central_agents.first
|
32
|
+
central = account.central_agent
|
33
|
+
|
34
|
+
ActiveRecord::Base.transaction do
|
35
|
+
central.destroy
|
36
|
+
account.destroy
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.fetch_or_create(user)
|
41
|
+
return unless user
|
42
|
+
return user.accounts.central_agents.first.central_agent if user.accounts.central_agents.any?
|
43
|
+
new.create_account(user)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Accounts
|
2
|
+
class ClientAccountRepository
|
3
|
+
|
4
|
+
def fetch_or_create(user)
|
5
|
+
return unless user
|
6
|
+
return user.accounts.clients.first.client if user.accounts.clients.any?
|
7
|
+
create_account(user)
|
8
|
+
end
|
9
|
+
|
10
|
+
def create_account(user)
|
11
|
+
return unless user
|
12
|
+
return if user.accounts.clients.any?
|
13
|
+
|
14
|
+
account = ::Account.new(type_id: ::Dictionaries::AccountType::CLIENT)
|
15
|
+
client = ::Accounts::Client.new(account: account)
|
16
|
+
|
17
|
+
account.user = user
|
18
|
+
account.name = user.name.to_s
|
19
|
+
account.active = true
|
20
|
+
|
21
|
+
account.transaction do
|
22
|
+
account.save validate: false
|
23
|
+
client.save validate: false
|
24
|
+
end
|
25
|
+
|
26
|
+
client
|
27
|
+
end
|
28
|
+
|
29
|
+
def destroy_account(user)
|
30
|
+
return unless user
|
31
|
+
return unless user.accounts.clients.any?
|
32
|
+
|
33
|
+
account = user.accounts.clients.first
|
34
|
+
client = account.client
|
35
|
+
|
36
|
+
ActiveRecord::Base.transaction do
|
37
|
+
client.destroy
|
38
|
+
account.destroy
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Accounts
|
2
|
+
class ManagerAccountRepository
|
3
|
+
|
4
|
+
def create_account(user)
|
5
|
+
return unless user
|
6
|
+
return if user.accounts.managers.any?
|
7
|
+
|
8
|
+
account = ::Account.new(type_id: ::Dictionaries::AccountType::MANAGER)
|
9
|
+
manager = ::Accounts::Manager.new(account: account)
|
10
|
+
|
11
|
+
account.user = user
|
12
|
+
account.name = user.name.to_s
|
13
|
+
account.active = true
|
14
|
+
|
15
|
+
account.transaction do
|
16
|
+
account.save validate: false
|
17
|
+
manager.save validate: false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def destroy_account(user)
|
22
|
+
return unless user
|
23
|
+
return unless user.accounts.managers.any?
|
24
|
+
|
25
|
+
account = user.accounts.managers.first
|
26
|
+
manager = account.manager
|
27
|
+
|
28
|
+
ActiveRecord::Base.transaction do
|
29
|
+
manager.destroy
|
30
|
+
account.destroy
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Accounts
|
2
|
+
class ManagerGuestAccountRepository
|
3
|
+
|
4
|
+
def create_account(user)
|
5
|
+
return unless user
|
6
|
+
return if user.accounts.managers_guests.any?
|
7
|
+
|
8
|
+
account = ::Account.new(type_id: ::Dictionaries::AccountType::MANAGER_GUEST)
|
9
|
+
manager = ::Accounts::ManagerGuest.new(account: account)
|
10
|
+
|
11
|
+
account.user = user
|
12
|
+
account.name = user.name.to_s
|
13
|
+
account.active = true
|
14
|
+
|
15
|
+
account.transaction do
|
16
|
+
account.save validate: false
|
17
|
+
manager.save validate: false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def destroy_account(user)
|
22
|
+
return unless user
|
23
|
+
return unless user.accounts.managers_guests.any?
|
24
|
+
|
25
|
+
account = user.accounts.managers_guests.first
|
26
|
+
manager = account.manager_guest
|
27
|
+
|
28
|
+
ActiveRecord::Base.transaction do
|
29
|
+
manager.destroy
|
30
|
+
account.destroy
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Accounts
|
2
|
+
class ModeratorAccountRepository
|
3
|
+
|
4
|
+
def create_account(user)
|
5
|
+
return unless user
|
6
|
+
return if user.accounts.moderators.any?
|
7
|
+
|
8
|
+
account = ::Account.new(type_id: ::Dictionaries::AccountType::MODERATOR)
|
9
|
+
moderator = ::Accounts::Moderator.new(account: account)
|
10
|
+
|
11
|
+
account.user = user
|
12
|
+
account.name = user.name.to_s
|
13
|
+
account.active = true
|
14
|
+
|
15
|
+
account.transaction do
|
16
|
+
account.save validate: false
|
17
|
+
moderator.save validate: false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def destroy_account(user)
|
22
|
+
return unless user
|
23
|
+
return unless user.accounts.moderators.any?
|
24
|
+
|
25
|
+
account = user.accounts.moderators.first
|
26
|
+
moderator = account.moderator
|
27
|
+
|
28
|
+
ActiveRecord::Base.transaction do
|
29
|
+
moderator.destroy
|
30
|
+
account.destroy
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
data/app/schemas/base.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
module Schemas
|
2
|
+
class Base
|
3
|
+
|
4
|
+
include Dry::Schema
|
5
|
+
|
6
|
+
def normalize_integer(value)
|
7
|
+
return nil if value.blank?
|
8
|
+
Integer(value.to_s.gsub(/\s/, '')) rescue value
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
def strip_string(value, empty_to_nil = true)
|
13
|
+
value = value.to_s.strip
|
14
|
+
value = nil if empty_to_nil && value.blank?
|
15
|
+
value
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def to_date(value)
|
20
|
+
value.to_date rescue value
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def clear_html(value)
|
25
|
+
Rails::Html::WhiteListSanitizer.new.sanitize(value)
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def normalize_url(value)
|
30
|
+
return if value.blank?
|
31
|
+
value.to_s.strip.scan(/^https?:\/\//).present? ? value : sprintf('https://%s', value.to_s.strip)
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def to_bool(value)
|
36
|
+
value.to_s.to_bool
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def to_float(value)
|
41
|
+
return unless value.present?
|
42
|
+
BigDecimal.new(value.to_s.gsub(/\s/, '')) rescue value
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def to_big_decimal(value)
|
47
|
+
return unless value.present?
|
48
|
+
BigDecimal.new(value.to_s.gsub(/\s/, '')) rescue value
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def forced_array(value)
|
53
|
+
[value.present? ? value : nil].flatten.compact
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def forced_hash(value)
|
58
|
+
value.blank? ? {} : value
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def nullify_empty(value)
|
63
|
+
value.present? ? value : nil
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,235 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
module Schemas
|
4
|
+
class BaseBoatSchema < ::Schemas::Base
|
5
|
+
def schema
|
6
|
+
result = {
|
7
|
+
properties: {
|
8
|
+
id: {
|
9
|
+
type: %i[null number],
|
10
|
+
cast: ->(value) { normalize_integer(value) }
|
11
|
+
},
|
12
|
+
name: {
|
13
|
+
type: [:null, :string],
|
14
|
+
cast: ->(value) { clear_html(strip_string(value)) },
|
15
|
+
rule: ->(parents) { rule_name(parents) }
|
16
|
+
},
|
17
|
+
boat_type_id: {
|
18
|
+
enum: [nil, -1] + Dicts::BoatType::ALL.collect(&:id),
|
19
|
+
cast: ->(value) { normalize_integer(value) },
|
20
|
+
rule: ->(parents) { rule_boat_type_id(parents) }
|
21
|
+
},
|
22
|
+
boat_model: {
|
23
|
+
type: %i[null string],
|
24
|
+
cast: ->(value) { clear_html(strip_string(value)) }
|
25
|
+
},
|
26
|
+
builder: {
|
27
|
+
type: %i[null string],
|
28
|
+
cast: ->(value) { clear_html(strip_string(value)) }
|
29
|
+
},
|
30
|
+
length: {
|
31
|
+
type: :hash,
|
32
|
+
required: %i[value length_id],
|
33
|
+
properties: {
|
34
|
+
value: {
|
35
|
+
type: %i[null number string],
|
36
|
+
cast: ->(value) { to_float(value) }
|
37
|
+
},
|
38
|
+
length_id: {
|
39
|
+
enum: Dicts::Length.all.collect(&:id),
|
40
|
+
cast: ->(value) { normalize_integer(value) }
|
41
|
+
}
|
42
|
+
}
|
43
|
+
},
|
44
|
+
crew_total: {
|
45
|
+
type: %i[null number],
|
46
|
+
cast: ->(value) { normalize_integer(value) }
|
47
|
+
},
|
48
|
+
guest_cabins: {
|
49
|
+
type: %i[null number],
|
50
|
+
cast: ->(value) { normalize_integer(value) }
|
51
|
+
},
|
52
|
+
guests_total: {
|
53
|
+
type: %i[null number],
|
54
|
+
cast: ->(value) { normalize_integer(value) }
|
55
|
+
},
|
56
|
+
maximum_guests_during_cruise: {
|
57
|
+
type: %i[null number],
|
58
|
+
cast: ->(value) { normalize_integer(value) },
|
59
|
+
rule: ->(parents) { rule_maximum_guests_during_cruise(parents) }
|
60
|
+
},
|
61
|
+
boat_locations_attributes: {
|
62
|
+
type: %i[null array],
|
63
|
+
items: {
|
64
|
+
type: :hash,
|
65
|
+
required: %i[latitude longitude address is_main _destroy],
|
66
|
+
cast: ->(value) do
|
67
|
+
value[:latitude] = to_big_decimal value[:latitude]
|
68
|
+
value[:longitude] = to_big_decimal value[:longitude]
|
69
|
+
value[:address] = strip_string value[:address]
|
70
|
+
value[:is_main] = to_bool value[:is_main]
|
71
|
+
value[:_destroy] = to_bool value[:_destroy]
|
72
|
+
|
73
|
+
value[:latitude].nil? || value[:longitude].nil? || !value[:address].present? ? nil : value
|
74
|
+
end
|
75
|
+
},
|
76
|
+
cast: ->(value) { forced_array(value) }
|
77
|
+
}
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
result[:properties][:rent_prices] = {
|
82
|
+
type: [:null, :array], # это первая версия, где я пробую разобраться с массивом объектов (версия покруче тут - app/getboat/schemas/base_boat_schema.rb - с помощью :items)
|
83
|
+
cast: ->(value) do
|
84
|
+
value.inject([]) do |res, el|
|
85
|
+
el[:_delete] = to_bool el[:_delete]
|
86
|
+
el[:currency] = normalize_integer el[:currency]
|
87
|
+
el[:discount] = to_float el[:discount]
|
88
|
+
el[:duration] = to_float el[:duration]
|
89
|
+
el[:season] = normalize_integer el[:season]
|
90
|
+
el[:uom] = normalize_integer el[:uom]
|
91
|
+
el[:value] = normalize_integer(el[:value].is_a?(String) ? el[:value].split(/[, ]/).join : el[:value])
|
92
|
+
res << el
|
93
|
+
res
|
94
|
+
end
|
95
|
+
end
|
96
|
+
}
|
97
|
+
|
98
|
+
result[:properties][:sale_price] = {
|
99
|
+
type: [:null, :hash],
|
100
|
+
properties: {
|
101
|
+
currency_id: {
|
102
|
+
enum: Dicts::Currency.all.collect(&:id),
|
103
|
+
cast: ->(value) { normalize_integer(value) }
|
104
|
+
},
|
105
|
+
value: {
|
106
|
+
type: %i[number null string],
|
107
|
+
cast: ->(value) do
|
108
|
+
val = value.is_a?(String) ? value.split(/[, ]/).join : value
|
109
|
+
normalize_integer val
|
110
|
+
end
|
111
|
+
},
|
112
|
+
discount: {
|
113
|
+
type: %i[number null string],
|
114
|
+
cast: ->(value) { to_float(value) }
|
115
|
+
},
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
result[:properties][:boat_photos_attributes] = {
|
120
|
+
type: [:null, :hash],
|
121
|
+
cast: ->(value) { nullify_empty(value) },
|
122
|
+
rule: ->(parents) { rule_boat_photos_attributes(parents)}
|
123
|
+
}
|
124
|
+
|
125
|
+
result
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
def rule_name(parents)
|
130
|
+
messages = {
|
131
|
+
present: I18n.t('errors.boat.name.blank'),
|
132
|
+
length_between: I18n.t('errors.boat.name.length_invalid', min: 2, max: 255)
|
133
|
+
}
|
134
|
+
rule(:name, parents, messages) { present? & length_between?(2..255) }
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
def rule_boat_type_id(parents)
|
139
|
+
messages = {
|
140
|
+
present: I18n.t('errors.boat.boat_type_id.blank'),
|
141
|
+
gt: I18n.t('errors.boat.boat_type_id.blank')
|
142
|
+
}
|
143
|
+
rule(:boat_type_id, parents, messages) { present? & gt?(0) }
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
def rule_maximum_guests_during_cruise(parents)
|
148
|
+
messages = {
|
149
|
+
present: I18n.t('errors.boat.maximum_guests_during_cruise.blank'),
|
150
|
+
gt: I18n.t('errors.boat.maximum_guests_during_cruise.blank')
|
151
|
+
}
|
152
|
+
rule(:maximum_guests_during_cruise, parents, messages) { present? & gt?(0) }
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
def rule_boat_photos_attributes(parents)
|
157
|
+
messages = { present: I18n.t('errors.boat.boat_photos_attributes.blank') }
|
158
|
+
rule(:boat_photos_attributes, parents, messages) { present? }
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
def validate_location
|
163
|
+
unless boat_locations_attributes
|
164
|
+
errors.add :boat_locations, I18n.t('errors.boat.boat_locations.blank')
|
165
|
+
return
|
166
|
+
end
|
167
|
+
|
168
|
+
bls = boat_locations_attributes.compact.select { |loc| !loc[:_destroy] }
|
169
|
+
mbls = bls.select { |loc| loc[:is_main] }
|
170
|
+
|
171
|
+
if bls.size.zero?
|
172
|
+
errors.add :boat_locations, I18n.t('errors.boat.boat_locations.blank')
|
173
|
+
return
|
174
|
+
end
|
175
|
+
|
176
|
+
if bls.size > 5
|
177
|
+
errors.add :boat_locations, I18n.t('errors.boat.boat_locations.limit', max: 5)
|
178
|
+
return
|
179
|
+
end
|
180
|
+
|
181
|
+
if mbls.size == 0
|
182
|
+
errors.add :boat_locations, I18n.t('errors.boat.boat_locations.is_main')
|
183
|
+
return
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
def validate_length
|
189
|
+
errors.add(:length_value, I18n.t('errors.boat.length.blank')) unless length[:value]&.nonzero?
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
def validate_crew_total
|
194
|
+
errors.add(:crew_total, I18n.t('errors.boat.crew_total.blank')) unless crew_total.present?
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
def validate_guests_total
|
199
|
+
errors.add(:guests_total, I18n.t('errors.boat.guests_total.blank')) unless guests_total.present?
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
def validate_guest_cabins
|
204
|
+
errors.add(:guest_cabins, I18n.t('errors.boat.guest_cabins.blank')) unless guest_cabins.present?
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
def validate_prices_and_rent_prices
|
209
|
+
rent_prices_present = rent_prices.present? && rent_prices.count { |price| price[:_delete] } < rent_prices.count
|
210
|
+
sale_price_present = sale_price.present? && sale_price[:value]&.>(0)
|
211
|
+
return if rent_prices_present && sale_price_present
|
212
|
+
|
213
|
+
if !(rent_prices_present || sale_price_present)
|
214
|
+
errors.add :rent_prices, I18n.t('errors.boat.prices.any')
|
215
|
+
errors.add :sale_price, I18n.t('errors.boat.prices.any')
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
def validate_rent_prices
|
221
|
+
return unless rent_prices.present?
|
222
|
+
|
223
|
+
rent_prices.each do |price|
|
224
|
+
if price[:value].nil? || price[:value].zero?
|
225
|
+
errors.add :rent_prices, I18n.t('errors.boat.rent_prices.invalid')
|
226
|
+
break
|
227
|
+
end
|
228
|
+
if price[:duration].nil? || price[:duration].zero?
|
229
|
+
errors.add :rent_prices, I18n.t('errors.boat.rent_prices.invalid_duration')
|
230
|
+
break
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require_relative '../base_boat_schema'
|
2
|
+
|
3
|
+
module Schemas
|
4
|
+
module CentralAgent
|
5
|
+
class SaveBoatSchema < Schemas::BaseBoatSchema
|
6
|
+
def schema
|
7
|
+
boat_schema = super
|
8
|
+
boat_schema[:required] = %i[
|
9
|
+
name
|
10
|
+
boat_type_id
|
11
|
+
]
|
12
|
+
|
13
|
+
boat_schema[:properties][:boat_photos_attributes] = {
|
14
|
+
type: [:null, :hash],
|
15
|
+
cast: ->(value) { nullify_empty(value) }
|
16
|
+
}
|
17
|
+
|
18
|
+
# второй вариант реализации property boat_length, который присутствует в базовой схеме
|
19
|
+
%i[boat_length_metrics boat_beam_metrics boat_draft_metrics].product([:meters, :ft]).map { |f,d| '%s_%s' % [f,d] }.each do |prop|
|
20
|
+
boat_schema[:properties][prop.to_sym] = {
|
21
|
+
type: %i[null string number],
|
22
|
+
cast: ->(value) do
|
23
|
+
val = value.is_a?(String) ? value.split(/[, ]/).join : value
|
24
|
+
to_float val
|
25
|
+
end
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
boat_schema
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def validate_additional
|
34
|
+
validate_boat_photos
|
35
|
+
validate_prices_and_rent_prices
|
36
|
+
validate_rent_prices
|
37
|
+
validate_length
|
38
|
+
validate_location
|
39
|
+
validate_guests_total
|
40
|
+
# validate_guest_cabins
|
41
|
+
validate_crew_total
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate_boat_photos
|
45
|
+
errors.add :boat_photos, I18n.t('errors.boat.boat_photos_attributes.blank') unless boat_photos_attributes.present?
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def validate_length
|
50
|
+
val = boat_length_metrics_meters || boat_length_metrics_ft
|
51
|
+
unless val.present? && val > 0 && val < 1000
|
52
|
+
errors.add(:boat_length_metrics, I18n.t('errors.messages.invalid'))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
class AbstractPricesService
|
2
|
+
|
3
|
+
attr_reader :built_prices
|
4
|
+
|
5
|
+
def initialize(all_currencies = ::Dicts::Currency::ALL2) # all_currencies - список валют, в которых надо сохранить цены
|
6
|
+
@all_currencies = all_currencies
|
7
|
+
_reset_ivars
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def _model
|
13
|
+
raise NotImplementedError
|
14
|
+
end
|
15
|
+
|
16
|
+
def _fields_for_create
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
19
|
+
|
20
|
+
def _save(prices) # помещаем в базу всё одним запросом
|
21
|
+
filtered = prices.select { |el| !el[:_delete] && el[:value].to_i != 0 } # игнорируя цены, которые были помечены "к удалению" или те, у которых значение 0 (0 можно подавать, если хотим "удалить" цену продажи)
|
22
|
+
return if filtered.size.zero?
|
23
|
+
|
24
|
+
values = filtered.map do |p|
|
25
|
+
_fields_for_create.map do |field|
|
26
|
+
p[field.to_sym]
|
27
|
+
end.compact * ','
|
28
|
+
end * '),('
|
29
|
+
values = '(%s)' % values
|
30
|
+
sql = 'INSERT INTO %s(%s) VALUES %s' % [_model.table_name, _fields_for_create * ',', values]
|
31
|
+
ActiveRecord::Base.connection.execute sql
|
32
|
+
|
33
|
+
filtered
|
34
|
+
end
|
35
|
+
|
36
|
+
# из базы удаляем все записи, принадлежащие данной лодке
|
37
|
+
# т.к. считаем, что с формы приходит полная картина по ценам
|
38
|
+
# кроме того, юзер может просто изменить, например, период в уже
|
39
|
+
# существующей строке с ценами, которую он видит на экране
|
40
|
+
def _delete_existing_records(boat_id)
|
41
|
+
sql = 'DELETE FROM %s WHERE boat_id = %s' % [_model.table_name, boat_id]
|
42
|
+
ActiveRecord::Base.connection.execute sql
|
43
|
+
end
|
44
|
+
|
45
|
+
def _build_other(price) # отталкиваясь от валюты входных данных, рассчитываем "те же" цены в других валютах
|
46
|
+
return if price[:_delete] || price[:value].to_i == 0 # игнорируя цены, которые были помечены "к удалению" или те, у которых значение 0 (0 можно подавать, если хотим "удалить" цену продажи)
|
47
|
+
|
48
|
+
incoming_curr = ::Dicts::Currency.find(price[:currency_id])
|
49
|
+
currs_to_build = @all_currencies - [incoming_curr]
|
50
|
+
rates = Currency.fresh(incoming_curr.index).rates
|
51
|
+
rates = JSON.parse(rates)['rates']
|
52
|
+
|
53
|
+
currs_to_build.each do |curr|
|
54
|
+
rate = rates[curr.index]
|
55
|
+
res = price.clone
|
56
|
+
res[:currency_id] = curr.id
|
57
|
+
res[:value] = res[:value].to_f * rate
|
58
|
+
res[:is_orig] = false
|
59
|
+
@built_prices << res
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def _delete_bad_values(prices)
|
64
|
+
prices.delete_if { |price| price[:value].nil? || price[:value].to_f.zero? }
|
65
|
+
end
|
66
|
+
|
67
|
+
def _reset_ivars
|
68
|
+
@built_prices = []
|
69
|
+
end
|
70
|
+
end
|