dscf-marketplace 0.3.94 → 0.3.96
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/app/controllers/dscf/marketplace/delivery_orders_controller.rb +48 -0
- data/app/controllers/dscf/marketplace/quotations_controller.rb +25 -1
- data/app/models/dscf/marketplace/quotation.rb +23 -10
- data/app/models/dscf/marketplace/quotation_item.rb +6 -0
- data/app/services/dscf/marketplace/rfq_response_service.rb +1 -0
- data/config/locales/en.yml +1 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20250920105329_backfill_quotation_total_prices.rb +11 -0
- data/lib/dscf/marketplace/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 995d1749e7bc2caebbdfcaaef9ba08ca94d8ae3b6b71d7241c4d979ebdf8668c
|
4
|
+
data.tar.gz: e8725566a7aa2b5ea1d8b5b1dc78f464e87fdf2a9b8a0788918dec215b5b2fb4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64a8101ea7155a3918dcceaef775a87b24d0534b8db003c439a676956374bd9afa7e71ea66ace1be00fde9d5eecb1656741e8048819515df75823cb0ec91aece
|
7
|
+
data.tar.gz: 9c28c9fe4b743de98be18fd3d1d01471e571e8a83b792042e84080a04bb5c0912e0637d398876520f5fe6082ba2ea1a008032903460afe8994af4da48b1d1d19
|
@@ -159,6 +159,54 @@ module Dscf
|
|
159
159
|
end
|
160
160
|
end
|
161
161
|
|
162
|
+
def my_driver_deliveries
|
163
|
+
# Verify user has driver role
|
164
|
+
unless Dscf::Marketplace::RoleService.user_has_driver_role?(current_user)
|
165
|
+
render_error("delivery_orders.errors.access_denied", message: "Only drivers can access this endpoint")
|
166
|
+
return
|
167
|
+
end
|
168
|
+
|
169
|
+
# Get deliveries where user is the driver
|
170
|
+
delivery_orders = @clazz.where(driver: current_user)
|
171
|
+
|
172
|
+
# Apply Ransack filtering if q params present
|
173
|
+
if params[:q].present?
|
174
|
+
# Filter out any parameters that might reference JSON fields
|
175
|
+
filtered_params = params[:q].reject { |key, _| key.to_s.include?("optimized_route") }
|
176
|
+
if filtered_params.present?
|
177
|
+
delivery_orders = delivery_orders.ransack(filtered_params).result
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# Apply eager loading if defined
|
182
|
+
delivery_orders = delivery_orders.includes(eager_loaded_associations) if eager_loaded_associations.present?
|
183
|
+
|
184
|
+
# Apply pagination if requested
|
185
|
+
if params[:page]
|
186
|
+
total_count = delivery_orders.count
|
187
|
+
delivery_orders = delivery_orders.then(&paginate)
|
188
|
+
total_pages = (total_count.to_f / per_page).ceil
|
189
|
+
count = delivery_orders.is_a?(Array) ? delivery_orders.length : delivery_orders.count
|
190
|
+
|
191
|
+
pagination_meta = {
|
192
|
+
current_page: page_no,
|
193
|
+
per_page: per_page,
|
194
|
+
count: count,
|
195
|
+
total_count: total_count,
|
196
|
+
total_pages: total_pages,
|
197
|
+
links: pagination_links(total_pages)
|
198
|
+
}
|
199
|
+
end
|
200
|
+
|
201
|
+
# Add serializer includes
|
202
|
+
includes = serializer_includes_for_action(:index)
|
203
|
+
options = {}
|
204
|
+
options[:include] = includes if includes.present?
|
205
|
+
options[:pagination] = pagination_meta if params[:page]
|
206
|
+
|
207
|
+
render_success(data: delivery_orders, serializer_options: options)
|
208
|
+
end
|
209
|
+
|
162
210
|
private
|
163
211
|
|
164
212
|
def find_record
|
@@ -24,7 +24,31 @@ module Dscf
|
|
24
24
|
|
25
25
|
def accept
|
26
26
|
@obj = find_record
|
27
|
-
|
27
|
+
|
28
|
+
fulfillment_type = params[:fulfillment_type]
|
29
|
+
dropoff_address_id = params[:dropoff_address_id]
|
30
|
+
|
31
|
+
# Validate fulfillment_type
|
32
|
+
unless fulfillment_type.present? && Dscf::Marketplace::Order.fulfillment_types.keys.include?(fulfillment_type)
|
33
|
+
render_error("Invalid or missing fulfillment_type", status: :unprocessable_entity)
|
34
|
+
return
|
35
|
+
end
|
36
|
+
|
37
|
+
dropoff_address = nil
|
38
|
+
if dropoff_address_id.present?
|
39
|
+
dropoff_address = Dscf::Core::Address.find_by(id: dropoff_address_id) if defined?(Dscf::Core::Address)
|
40
|
+
unless dropoff_address && dropoff_address.user_id == current_user.id
|
41
|
+
render_error("Invalid dropoff_address_id", status: :unprocessable_entity)
|
42
|
+
return
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
if fulfillment_type == "delivery" && dropoff_address.nil?
|
47
|
+
render_error("dropoff_address_id required for delivery", status: :unprocessable_entity)
|
48
|
+
return
|
49
|
+
end
|
50
|
+
|
51
|
+
if @obj.accept!(fulfillment_type: fulfillment_type, dropoff_address: dropoff_address)
|
28
52
|
render_success("quotations.success.accepted", data: @obj)
|
29
53
|
else
|
30
54
|
render_error("quotations.errors.accept_failed")
|
@@ -19,6 +19,7 @@ module Dscf
|
|
19
19
|
validate :valid_until_after_delivery_date
|
20
20
|
validate :delivery_date_not_in_past
|
21
21
|
validate :valid_until_not_in_past
|
22
|
+
validate :total_price_required_for_operations
|
22
23
|
scope :by_business, ->(business_id) { where(business_id: business_id) }
|
23
24
|
scope :valid_quotations, -> { where("valid_until > ?", Time.current) }
|
24
25
|
scope :expired_quotations, -> { where("valid_until <= ?", Time.current) }
|
@@ -69,7 +70,7 @@ module Dscf
|
|
69
70
|
update_columns(status: "expired") if valid_until <= Time.current
|
70
71
|
end
|
71
72
|
|
72
|
-
def accept!
|
73
|
+
def accept!(options = {})
|
73
74
|
return false unless sent?
|
74
75
|
return false if within_validity_period? == false
|
75
76
|
|
@@ -80,17 +81,11 @@ module Dscf
|
|
80
81
|
request_for_quotation.update!(status: "selected", selected_quotation: self)
|
81
82
|
|
82
83
|
# Create order from accepted quotation
|
83
|
-
create_order_from_quotation
|
84
|
+
create_order_from_quotation(options)
|
84
85
|
|
85
86
|
true
|
86
87
|
end
|
87
88
|
|
88
|
-
def create_order_from_quotation
|
89
|
-
return if order.present?
|
90
|
-
|
91
|
-
Order.create_from_quotation(self)
|
92
|
-
end
|
93
|
-
|
94
89
|
def reject!
|
95
90
|
return false unless sent?
|
96
91
|
|
@@ -122,16 +117,21 @@ module Dscf
|
|
122
117
|
quotation_items.exists? && quotation_items.all? { |item| item.unit_price.present? }
|
123
118
|
end
|
124
119
|
|
125
|
-
def create_order_from_quotation
|
120
|
+
def create_order_from_quotation(options = {})
|
126
121
|
return if order.present?
|
127
122
|
|
123
|
+
fulfillment_type = options[:fulfillment_type] || "self_pickup"
|
124
|
+
dropoff_address = options[:dropoff_address]
|
125
|
+
|
128
126
|
order = Dscf::Marketplace::Order.create!(
|
129
127
|
order_type: :rfq_based,
|
130
128
|
status: :pending,
|
129
|
+
fulfillment_type: fulfillment_type,
|
131
130
|
quotation: self,
|
132
131
|
user: request_for_quotation.user, # Keep for backward compatibility
|
133
132
|
ordered_by: request_for_quotation.user,
|
134
133
|
ordered_to: business,
|
134
|
+
dropoff_address: dropoff_address,
|
135
135
|
total_amount: total_price
|
136
136
|
)
|
137
137
|
|
@@ -170,7 +170,9 @@ module Dscf
|
|
170
170
|
private
|
171
171
|
|
172
172
|
def calculate_total_price
|
173
|
-
|
173
|
+
# Reload association to ensure we have the latest data
|
174
|
+
quotation_items.reload
|
175
|
+
self.total_price = quotation_items.sum { |item| item.subtotal || 0 }.to_f
|
174
176
|
end
|
175
177
|
|
176
178
|
def valid_until_after_delivery_date
|
@@ -196,6 +198,17 @@ module Dscf
|
|
196
198
|
errors.add(:valid_until, "cannot be in the past")
|
197
199
|
end
|
198
200
|
end
|
201
|
+
|
202
|
+
def total_price_required_for_operations
|
203
|
+
return if total_price.present?
|
204
|
+
return if draft? # Allow null total_price for draft quotations
|
205
|
+
return if quotation_items.empty? # Allow null if no items
|
206
|
+
|
207
|
+
# Require total_price for sent, accepted, rejected, or expired quotations
|
208
|
+
if sent? || accepted? || rejected? || expired?
|
209
|
+
errors.add(:total_price, "must be calculated before quotation can be processed")
|
210
|
+
end
|
211
|
+
end
|
199
212
|
end
|
200
213
|
end
|
201
214
|
end
|
@@ -15,6 +15,8 @@ module Dscf
|
|
15
15
|
# validate :unit_compatible_with_product
|
16
16
|
validate :subtotal_matches_calculation, if: :should_validate_subtotal?
|
17
17
|
before_save :calculate_subtotal, if: :should_recalculate_subtotal?
|
18
|
+
after_save :update_quotation_total_price
|
19
|
+
after_destroy :update_quotation_total_price
|
18
20
|
|
19
21
|
scope :priced, -> { where.not(unit_price: nil) }
|
20
22
|
scope :unpriced, -> { where(unit_price: nil) }
|
@@ -86,6 +88,10 @@ module Dscf
|
|
86
88
|
self.subtotal = (unit_price * quantity).round(2)
|
87
89
|
end
|
88
90
|
|
91
|
+
def update_quotation_total_price
|
92
|
+
quotation.update_total_price! if quotation.present?
|
93
|
+
end
|
94
|
+
|
89
95
|
# Calculate subtotal automatically if not set
|
90
96
|
def calculated_subtotal
|
91
97
|
return subtotal if subtotal.present?
|
data/config/locales/en.yml
CHANGED
@@ -288,6 +288,7 @@ en:
|
|
288
288
|
orders_not_found: "Some orders were not found"
|
289
289
|
orders_conversion_failed: "Failed to convert orders to delivery"
|
290
290
|
convert_orders_failed: "Unexpected error during order conversion"
|
291
|
+
access_denied: "Access denied"
|
291
292
|
|
292
293
|
delivery_order_item:
|
293
294
|
success:
|
data/config/routes.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dscf-marketplace
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.96
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Asrat
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-09-
|
10
|
+
date: 2025-09-20 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: rails
|
@@ -503,6 +503,7 @@ files:
|
|
503
503
|
- db/migrate/20250909092700_add_notes_to_dscf_marketplace_quotation_items.rb
|
504
504
|
- db/migrate/20250916083536_make_driver_id_nullable_in_delivery_orders.rb
|
505
505
|
- db/migrate/20250917110618_add_dropoff_address_to_dscf_marketplace_orders.rb
|
506
|
+
- db/migrate/20250920105329_backfill_quotation_total_prices.rb
|
506
507
|
- lib/dscf/marketplace.rb
|
507
508
|
- lib/dscf/marketplace/engine.rb
|
508
509
|
- lib/dscf/marketplace/version.rb
|