dscf-marketplace 0.8.9 → 0.9.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce8356967d3398e0db71eeea4bb7c4c4ab29b7755a6709728b4e221ed66d66b7
4
- data.tar.gz: 29a36ec8dee0f6b151e204678199122ca6f4876201c3afb0eaaabceb11cd3ca4
3
+ metadata.gz: 28fd6ca06416aafd1e185ec3c143cbca9ad794a793e93b809be54bbee9779cae
4
+ data.tar.gz: 01fdbf80d424f8d053be1b3bba44780758d52efb21e8ad7a6bf0a4bd761f2e68
5
5
  SHA512:
6
- metadata.gz: 0d1ee57848f223f11bbe6953b603e639cd4e71972056a36b73c95c8265e742df4872cfc095ef373b1bf11ef1dae3c41f6be9ed64baf4957b7b36d6b119f3b74b
7
- data.tar.gz: '07086f8b707602c32ebfa535c572642edd0f8d7d12ef39443f1aff18e9670eadc8a01cdaedf20c53dc0ccb115ca86e259edfb5b71e1564077d718885e0ee378c'
6
+ metadata.gz: 2935f0370099d113ee36f2f2722357ad66115557043957a9a6152e94a20080d0c16044e57f3d101819d3ab231d7911317589598db6a947ba90e19ef40e5b0aa1
7
+ data.tar.gz: 3e7d3ce883999d57799693ecfc4ec9a52559f198de99ed512b9b80ded38d4a001afdd3e90f986d582ca34ebc9df34ff7580e4968ab04e6da3ba4943fac5ac184
@@ -104,6 +104,122 @@ module Dscf
104
104
  render_success("orders.success.index", data: orders, serializer_options: options)
105
105
  end
106
106
 
107
+ def validate
108
+ obj = find_record
109
+ authorize obj, :validate?
110
+
111
+ Dscf::Marketplace::OrderValidationService.validate(obj)
112
+ create_notification_for_order(obj, :validation_completed)
113
+ render_success(data: obj, serializer_options: { include: default_serializer_includes[:show] || [] })
114
+ end
115
+
116
+ def resolve_item
117
+ obj = find_record
118
+ authorize obj, :resolve_item?
119
+
120
+ item = obj.order_items.find(params[:order_item_id])
121
+ # Example: for price_changed, accept optional resolved_unit_price from params
122
+ if params[:resolved_unit_price].present?
123
+ item.resolved_unit_price = params[:resolved_unit_price]
124
+ item.validation_status = :validated
125
+ end
126
+ if params[:resolved_quantity].present?
127
+ item.resolved_quantity = params[:resolved_quantity]
128
+ item.validation_status = :validated
129
+ end
130
+ if params[:action_type] == "remove"
131
+ item.validation_status = :validated
132
+ item.quantity = 0 # will be cleaned or marked
133
+ end
134
+
135
+ item.save!
136
+ create_notification_for_order(obj, :item_resolved)
137
+ render_success(data: item)
138
+ end
139
+
140
+ def assign_source
141
+ obj = find_record
142
+ authorize obj, :split?
143
+
144
+ item = obj.order_items.find(params[:order_item_id])
145
+ Dscf::Marketplace::OrderSplittingService.assign_source(item, source_type: params[:source_type], source_id: params[:source_id])
146
+ render_success(data: item)
147
+ end
148
+
149
+ def split
150
+ obj = find_record
151
+ authorize obj, :split?
152
+
153
+ return render_error(errors: "All items must be validated before splitting") unless obj.all_items_validated?
154
+
155
+ Dscf::Marketplace::OrderSplittingService.perform_split(obj)
156
+ create_notification_for_order(obj, :split_ready)
157
+ render_success(data: obj, serializer_options: { include: default_serializer_includes[:show] || [] })
158
+ end
159
+
160
+ def supplier_confirm
161
+ obj = find_record
162
+ authorize obj, :supplier_confirm?
163
+
164
+ confirmed = params[:confirmed]
165
+ reason = params[:reason]
166
+ Dscf::Marketplace::OrderSplittingService.supplier_confirm(obj, confirmed: confirmed != false, reason: reason)
167
+
168
+ create_notification_for_order(obj, confirmed ? :supplier_confirmed : :supplier_rejected, reason: reason)
169
+ render_success(data: obj, serializer_options: { include: default_serializer_includes[:show] || [] })
170
+ end
171
+
172
+ def retailer_confirm
173
+ obj = find_record
174
+ authorize obj, :confirm? # reuse existing or add specific
175
+
176
+ return render_error(errors: "Order not ready for retailer confirmation") unless obj.retailer_can_confirm?
177
+
178
+ if params[:confirmed] == false
179
+ reason = params[:reason]
180
+ obj.update!(status: :cancelled)
181
+ obj.order_items.update_all(status: OrderItem.statuses[:cancelled])
182
+ create_notification_for_order(obj, :retailer_rejected, reason: reason)
183
+ else
184
+ obj.update!(status: :confirmed)
185
+ obj.order_items.update_all(status: OrderItem.statuses[:confirmed])
186
+ create_notification_for_order(obj, :retailer_confirmed)
187
+ end
188
+
189
+ render_success(data: obj, serializer_options: { include: default_serializer_includes[:show] || [] })
190
+ end
191
+
192
+
193
+ def create_notification_for_order(order, action, reason: nil)
194
+ recipient = order.ordered_by || order.user
195
+ return unless recipient
196
+
197
+ title = case action
198
+ when :validation_completed then "Order ##{order.id} validation complete"
199
+ when :item_resolved then "Item resolved in order ##{order.id}"
200
+ when :split_ready then "Order ##{order.id} ready for supplier confirmation"
201
+ when :supplier_confirmed then "Supplier confirmed order ##{order.id}"
202
+ when :supplier_rejected then "Supplier rejected item in order ##{order.id}"
203
+ when :retailer_confirmed then "Retailer confirmed order ##{order.id}"
204
+ when :retailer_rejected then "Retailer cancelled order ##{order.id}"
205
+ else "Order ##{order.id} update"
206
+ end
207
+
208
+ body = case action
209
+ when :supplier_rejected, :retailer_rejected then "Reason: #{reason}" if reason
210
+ else "Status: #{order.status}"
211
+ end
212
+
213
+ notification = Dscf::Core::Notification.create!(
214
+ notifiable: order,
215
+ recipient: recipient,
216
+ notification_type: :general,
217
+ title: title,
218
+ body: body.to_s
219
+ )
220
+ Dscf::Core::NotificationService.deliver(notification)
221
+ end
222
+
107
223
  private
108
224
 
109
225
  def create_direct_listing_order
@@ -183,7 +299,7 @@ module Dscf
183
299
 
184
300
  def default_serializer_includes
185
301
  {
186
- index: [ :user, :ordered_by, :ordered_to, :quotation, order_items: [ :product ] ],
302
+ index: [ :user, :ordered_by, :ordered_to, :quotation, :order_items ],
187
303
  show: [ :user, :ordered_by, :ordered_to, :quotation, :listing, :delivery_order, :order_items ],
188
304
  create: [ :user, :ordered_by, :ordered_to, :quotation, :listing, :order_items ],
189
305
  update: [ :user, :ordered_by, :ordered_to, :quotation, :listing, :order_items ]
@@ -1,7 +1,18 @@
1
1
  module Dscf::Marketplace
2
2
  class Order < ApplicationRecord
3
3
  enum :order_type, {rfq_based: 0, direct_listing: 1}
4
- enum :status, {pending: 0, confirmed: 1, processing: 2, completed: 3, cancelled: 4}
4
+ # Workflow statuses aligned with requirements doc for Sprint 2 order management
5
+ enum :status, {
6
+ pending: 0, # initial / validating
7
+ validating: 1,
8
+ splitting: 2,
9
+ waiting_supplier_confirmation: 3,
10
+ waiting_retailer_confirmation: 4,
11
+ confirmed: 5,
12
+ processing: 6, # legacy / fulfillment
13
+ completed: 7,
14
+ cancelled: 8
15
+ }
5
16
  enum :fulfillment_type, {self_pickup: 0, delivery: 1}
6
17
  enum :payment_method, {cash: 0, credit: 1, bank_transfer: 2}, default: :cash
7
18
 
@@ -46,7 +57,7 @@ module Dscf::Marketplace
46
57
 
47
58
  attributes = {
48
59
  order_type: :rfq_based,
49
- status: :processing,
60
+ status: :validating,
50
61
  fulfillment_type: dropoff_address.present? ? :delivery : :self_pickup,
51
62
  quotation: quotation,
52
63
  user: quotation.request_for_quotation.user, # Keep for backward compatibility
@@ -66,19 +77,21 @@ module Dscf::Marketplace
66
77
  unit: item.unit,
67
78
  quantity: item.quantity,
68
79
  unit_price: item.unit_price,
69
- status: :pending
80
+ status: :pending,
81
+ validation_status: :validated # initial from accepted quote; validation can re-run
70
82
  )
71
83
  end
72
84
 
73
85
  order
74
86
  end
75
87
 
88
+
76
89
  def self.create_from_listing(listing, user, quantity, dropoff_address = nil, payment_method = nil)
77
90
  return nil unless listing.visible? && quantity <= listing.quantity
78
91
 
79
92
  attributes = {
80
93
  order_type: :direct_listing,
81
- status: :pending,
94
+ status: :validating,
82
95
  fulfillment_type: dropoff_address.present? ? :delivery : :self_pickup,
83
96
  listing: listing,
84
97
  user: user, # Keep for backward compatibility
@@ -97,12 +110,14 @@ module Dscf::Marketplace
97
110
  unit: listing.supplier_product.product.unit,
98
111
  quantity: quantity,
99
112
  unit_price: listing.price,
100
- status: :pending
113
+ status: :pending,
114
+ validation_status: :validated
101
115
  )
102
116
 
103
117
  order
104
118
  end
105
119
 
120
+
106
121
  def total_amount
107
122
  # Return stored value if it exists and is greater than 0, otherwise calculate
108
123
  stored_value = super
@@ -157,6 +172,40 @@ module Dscf::Marketplace
157
172
  self.total_amount = order_items.sum { |item| item.quantity * item.unit_price }
158
173
  end
159
174
 
175
+ # Workflow helpers for Sprint 2 order validation + splitting
176
+ def validating?
177
+ pending? || status.to_s == "processing"
178
+ end
179
+
180
+ def all_items_validated?
181
+ order_items.all? { |i| i.validation_status == "validated" }
182
+ end
183
+
184
+ def validation_summary
185
+ order_items.group_by(&:validation_status).transform_values(&:count)
186
+ end
187
+
188
+ def mark_splitting!
189
+ update!(status: :splitting)
190
+ end
191
+
192
+ def mark_waiting_supplier_confirmation!
193
+ update!(status: :waiting_supplier_confirmation)
194
+ end
195
+
196
+ def mark_waiting_retailer_confirmation!
197
+ update!(status: :waiting_retailer_confirmation)
198
+ end
199
+
200
+ def retailer_can_confirm?
201
+ waiting_retailer_confirmation? || confirmed? # allow re-confirm safety
202
+ end
203
+
204
+ def supplier_confirmation_complete?
205
+ # simplistic: all items have source and are confirmed or cancelled
206
+ order_items.all? { |i| i.source_id.present? && (i.status.to_s == "confirmed" || i.status.to_s == "cancelled") }
207
+ end
208
+
160
209
  private
161
210
 
162
211
  def quotation_or_listing_present
@@ -1,6 +1,12 @@
1
1
  module Dscf::Marketplace
2
2
  class OrderItem < ApplicationRecord
3
3
  enum :status, {pending: 0, confirmed: 1, processing: 2, fulfilled: 3, cancelled: 4}
4
+ enum :validation_status, {
5
+ validated: 0,
6
+ no_longer_listed: 1,
7
+ price_changed: 2,
8
+ low_quantity: 3
9
+ }, default: :validated
4
10
 
5
11
  delegate :name, :description, :thumbnail_url, :images_urls, to: :product, allow_nil: true
6
12
 
@@ -9,6 +15,7 @@ module Dscf::Marketplace
9
15
  belongs_to :listing, optional: true
10
16
  belongs_to :product
11
17
  belongs_to :unit
18
+ belongs_to :source, polymorphic: true, optional: true
12
19
 
13
20
  validates :quantity, presence: true, numericality: {greater_than: 0}
14
21
  validates :unit_price, presence: true, numericality: {greater_than_or_equal_to: 0}
@@ -19,11 +26,11 @@ module Dscf::Marketplace
19
26
 
20
27
  # Ransack configuration for secure filtering
21
28
  def self.ransackable_attributes(_auth_object = nil)
22
- %w[id order_id quotation_item_id listing_id product_id unit_id quantity unit_price status created_at updated_at]
29
+ %w[id order_id quotation_item_id listing_id product_id unit_id quantity unit_price status validation_status validation_note resolved_unit_price resolved_quantity source_type source_id created_at updated_at]
23
30
  end
24
31
 
25
32
  def self.ransackable_associations(_auth_object = nil)
26
- %w[order quotation_item listing product unit]
33
+ %w[order quotation_item listing product unit source]
27
34
  end
28
35
 
29
36
  def subtotal
@@ -20,6 +20,26 @@ module Dscf
20
20
  def my_orders?
21
21
  user.has_permission?("orders.my_orders")
22
22
  end
23
+
24
+ def validate?
25
+ user.has_permission?("orders.validate")
26
+ end
27
+
28
+ def split?
29
+ user.has_permission?("orders.split")
30
+ end
31
+
32
+ def resolve_item?
33
+ user.has_permission?("orders.resolve_item")
34
+ end
35
+
36
+ def supplier_confirm?
37
+ user.has_permission?("orders.supplier_confirm")
38
+ end
39
+
40
+ def retailer_confirm?
41
+ user.has_permission?("orders.confirm") # or dedicated permission
42
+ end
23
43
  end
24
44
  end
25
45
  end
@@ -3,7 +3,8 @@ module Dscf
3
3
  class OrderItemSerializer < ActiveModel::Serializer
4
4
  attributes :id, :order_id, :quotation_item_id, :listing_id,
5
5
  :product_id, :unit_id, :quantity, :unit_price,
6
- :status, :created_at, :updated_at,
6
+ :status, :validation_status, :validation_note, :resolved_unit_price, :resolved_quantity,
7
+ :source_type, :source_id, :source_name, :created_at, :updated_at,
7
8
  :subtotal, :product_name, :unit_name, :thumbnail_url, :images_urls
8
9
 
9
10
  belongs_to :order
@@ -11,6 +12,23 @@ module Dscf
11
12
  belongs_to :listing
12
13
  belongs_to :product
13
14
  belongs_to :unit
15
+
16
+ def source_name
17
+ return nil unless object.source
18
+ case object.source_type
19
+ when 'Dscf::Marketplace::SubSupplier'
20
+ object.source.try(:business_name) || object.source.try(:name)
21
+ when 'Dscf::Marketplace::Supplier'
22
+ object.source.try(:name)
23
+ when 'Dscf::Core::Business'
24
+ object.source.try(:name)
25
+ else
26
+ object.source.try(:name) || object.source.try(:business_name)
27
+ end
28
+ end
29
+
30
+ # Note: source association is polymorphic; use source_name + source_* attrs for most clients.
31
+ # belongs_to :source would be possible but we keep it attribute-based for simplicity.
14
32
  end
15
33
  end
16
34
  end
@@ -2,7 +2,7 @@ module Dscf
2
2
  module Marketplace
3
3
  class OrderSerializer < ActiveModel::Serializer
4
4
  attributes :id, :quotation_id, :listing_id, :user_id, :ordered_by_id, :ordered_to_id, :delivery_order_id, :dropoff_address_id,
5
- :order_type, :status, :fulfillment_type, :payment_method, :received_bank_name, :transaction_reference, :total_amount,
5
+ :order_type, :status, :workflow_status, :has_validation_issues, :fulfillment_type, :payment_method, :received_bank_name, :transaction_reference, :total_amount,
6
6
  :buyer_phone, :buyer_email, :seller_name, :seller_phone, :seller_email,
7
7
  :created_at, :updated_at
8
8
 
@@ -34,6 +34,15 @@ module Dscf
34
34
  def seller_email
35
35
  object.ordered_to&.contact_email
36
36
  end
37
+
38
+ # Sprint 2 workflow helpers for new order handling (validation + splitting)
39
+ def workflow_status
40
+ object.status
41
+ end
42
+
43
+ def has_validation_issues
44
+ object.order_items.any? { |i| i.validation_status.present? && i.validation_status.to_s != 'validated' }
45
+ end
37
46
  end
38
47
  end
39
48
  end
@@ -0,0 +1,49 @@
1
+ module Dscf
2
+ module Marketplace
3
+ class OrderSplittingService
4
+ # Handles source assignment and the split operation per requirements.
5
+ # Sources can be:
6
+ # - Aggregator self (via aggregator listing or direct)
7
+ # - Sub-supplier
8
+ # - Other supplier
9
+
10
+ def self.assign_source(order_item, source_type:, source_id:)
11
+ order_item.source_type = source_type
12
+ order_item.source_id = source_id
13
+ order_item.save!
14
+ order_item
15
+ end
16
+
17
+ def self.perform_split(order)
18
+ raise "Cannot split order that is not ready" unless order.all_items_validated?
19
+
20
+ # Mark items ready for source assignment / supplier confirmation
21
+ order.order_items.each do |item|
22
+ if item.status.to_s == "pending" || item.status.to_s == "confirmed"
23
+ item.status = :processing
24
+ end
25
+ item.save! if item.changed?
26
+ end
27
+
28
+ order.mark_waiting_supplier_confirmation!
29
+
30
+ order
31
+ end
32
+
33
+ def self.supplier_confirm(order, confirmed: true, reason: nil)
34
+ if confirmed
35
+ # In full impl, mark specific allocation as confirmed
36
+ # For now advance whole if appropriate
37
+ else
38
+ order.order_items.where(status: :processing).update_all(status: OrderItem.statuses[:cancelled])
39
+ end
40
+
41
+ # Check if supplier side is done
42
+ if order.supplier_confirmation_complete?
43
+ order.mark_waiting_retailer_confirmation!
44
+ end
45
+ order
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,68 @@
1
+ module Dscf
2
+ module Marketplace
3
+ class OrderValidationService
4
+ # Validates all items in an order against current listings / availability.
5
+ # Sets validation_status on each OrderItem per the requirements doc:
6
+ # validated, no_longer_listed, price_changed, low_quantity
7
+ # Also populates resolved_* fields where applicable.
8
+ def self.validate(order)
9
+ order.order_items.each do |item|
10
+ validate_item(item)
11
+ end
12
+ # Transition order to splitting ready if all validated
13
+ if order.order_items.all? { |i| i.validation_status == "validated" }
14
+ order.update!(status: :splitting) if order.validating?
15
+ end
16
+ order.reload
17
+ end
18
+
19
+
20
+ def self.validate_item(item)
21
+ current_listing = find_current_active_listing(item)
22
+ return mark_no_longer_listed(item) unless current_listing
23
+
24
+ current_price = current_listing.price
25
+ current_qty = current_listing.quantity
26
+
27
+ if current_price != item.unit_price
28
+ item.validation_status = :price_changed
29
+ item.validation_note = "Price changed (listed: #{current_price})"
30
+ item.resolved_unit_price = item.unit_price # keep original order price by default (user can choose listed later)
31
+ elsif item.quantity > current_qty
32
+ item.validation_status = :low_quantity
33
+ item.validation_note = "Low quantity available (listed: #{current_qty})"
34
+ item.resolved_quantity = [ item.quantity, current_qty ].min
35
+ else
36
+ item.validation_status = :validated
37
+ item.validation_note = nil
38
+ item.resolved_unit_price = nil
39
+ item.resolved_quantity = nil
40
+ end
41
+
42
+ item.save! if item.changed?
43
+ item
44
+ end
45
+
46
+ private_class_method def self.find_current_active_listing(item)
47
+ # Prefer the original listing if still active
48
+ if item.listing && Listing.active.exists?(id: item.listing.id)
49
+ return item.listing
50
+ end
51
+
52
+ # Fallback: find cheapest active listing for the product
53
+ Listing.active
54
+ .joins(:supplier_product)
55
+ .where(supplier_products: {product_id: item.product_id})
56
+ .order(price: :asc)
57
+ .first
58
+ end
59
+
60
+ private_class_method def self.mark_no_longer_listed(item)
61
+ item.validation_status = :no_longer_listed
62
+ item.validation_note = "Product no longer listed"
63
+ item.save! if item.changed?
64
+ item
65
+ end
66
+ end
67
+ end
68
+ end
data/config/routes.rb CHANGED
@@ -125,6 +125,12 @@ Dscf::Marketplace::Engine.routes.draw do
125
125
  post "cancel"
126
126
  post "complete"
127
127
  get "invoice"
128
+ post "validate"
129
+ post "resolve_item"
130
+ post "assign_source"
131
+ post "split"
132
+ post "supplier_confirm"
133
+ post "retailer_confirm"
128
134
  end
129
135
  collection do
130
136
  get "my_orders"
@@ -0,0 +1,13 @@
1
+ class AddValidationAndSourceFieldsToDscfMarketplaceOrderItems < ActiveRecord::Migration[8.0]
2
+ def change
3
+ add_column :dscf_marketplace_order_items, :validation_status, :integer, default: 0, null: false
4
+ add_column :dscf_marketplace_order_items, :validation_note, :text
5
+ add_column :dscf_marketplace_order_items, :resolved_unit_price, :decimal, precision: 15, scale: 6
6
+ add_column :dscf_marketplace_order_items, :resolved_quantity, :decimal, precision: 15, scale: 6
7
+ add_column :dscf_marketplace_order_items, :source_type, :string
8
+ add_column :dscf_marketplace_order_items, :source_id, :bigint
9
+
10
+ add_index :dscf_marketplace_order_items, :validation_status, name: "validation_status_on_dm_order_items_idx"
11
+ add_index :dscf_marketplace_order_items, [:source_type, :source_id], name: "source_on_dm_order_items_idx"
12
+ end
13
+ end
data/db/seeds.rb CHANGED
@@ -11,7 +11,7 @@ Dscf::Core::PermissionRegistry.register("dscf-marketplace") do
11
11
  resource :rfq_items, actions: %i[index show create update destroy]
12
12
  resource :quotations, actions: %i[index show create update destroy accept reject send_quotation my_quotes filter]
13
13
  resource :quotation_items, actions: %i[index show create update destroy]
14
- resource :orders, actions: %i[index show create update destroy confirm cancel complete my_orders filter]
14
+ resource :orders, actions: %i[index show create update destroy confirm cancel complete my_orders filter validate resolve_item assign_source split supplier_confirm retailer_confirm]
15
15
  resource :order_items, actions: %i[index show create update destroy]
16
16
  resource :delivery_orders, actions: %i[
17
17
  index show create update destroy pickup start_delivery complete_delivery mark_failed accept summary
@@ -33,7 +33,7 @@ module Dscf
33
33
  resource :rfq_items, actions: %i[index show create update destroy]
34
34
  resource :quotations, actions: %i[index show create update destroy accept reject send_quotation my_quotes filter]
35
35
  resource :quotation_items, actions: %i[index show create update destroy]
36
- resource :orders, actions: %i[index show create update destroy confirm cancel complete my_orders filter]
36
+ resource :orders, actions: %i[index show create update destroy confirm cancel complete my_orders filter validate resolve_item assign_source split supplier_confirm retailer_confirm]
37
37
  resource :order_items, actions: %i[index show create update destroy]
38
38
  resource :delivery_orders, actions: %i[
39
39
  index show create update destroy pickup start_delivery complete_delivery mark_failed accept summary
@@ -1,5 +1,5 @@
1
1
  module Dscf
2
2
  module Marketplace
3
- VERSION = "0.8.9".freeze
3
+ VERSION = "0.9.0".freeze
4
4
  end
5
5
  end
@@ -4,6 +4,7 @@ FactoryBot.define do
4
4
  quantity { 5 }
5
5
  unit_price { 10.0 }
6
6
  status { :pending }
7
+ validation_status { :validated }
7
8
  quotation_item { nil } # Don't create by default
8
9
  listing { nil } # Don't create by default
9
10
 
@@ -45,5 +46,10 @@ FactoryBot.define do
45
46
  trait :cancelled do
46
47
  status { :cancelled }
47
48
  end
49
+
50
+ trait :price_changed do
51
+ validation_status { :price_changed }
52
+ validation_note { "Price changed" }
53
+ end
48
54
  end
49
55
  end
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.8.9
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Asrat
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-06-19 00:00:00.000000000 Z
10
+ date: 2026-06-22 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rails
@@ -525,6 +525,8 @@ files:
525
525
  - app/services/dscf/marketplace/gebeta_service.rb
526
526
  - app/services/dscf/marketplace/invoice_pdf_generator.rb
527
527
  - app/services/dscf/marketplace/my_resource_service.rb
528
+ - app/services/dscf/marketplace/order_splitting_service.rb
529
+ - app/services/dscf/marketplace/order_validation_service.rb
528
530
  - app/services/dscf/marketplace/rfq_response_service.rb
529
531
  - app/services/dscf/marketplace/role_service.rb
530
532
  - app/services/dscf/marketplace/route_optimization_service.rb
@@ -586,6 +588,7 @@ files:
586
588
  - db/migrate/20260616000010_add_code_to_dscf_marketplace_categories_and_gender_to_dscf_marketplace_agents.rb
587
589
  - db/migrate/20260619210001_add_user_id_to_dscf_marketplace_agents.rb
588
590
  - db/migrate/20260619210002_add_user_id_to_dscf_marketplace_retailers.rb
591
+ - db/migrate/20260622000001_add_validation_and_source_fields_to_dscf_marketplace_order_items.rb
589
592
  - db/seeds.rb
590
593
  - lib/dscf/marketplace.rb
591
594
  - lib/dscf/marketplace/engine.rb