Class: Dscf::Marketplace::OrderSplittingService

Inherits:
Object
  • Object
show all
Defined in:
app/services/dscf/marketplace/order_splitting_service.rb

Class Method Summary collapse

Class Method Details

.accept_item_adjustment(order, order_item) ⇒ Object

Aggregator accepts the supplier's proposal: the adjusted price/quantity become the line's actual terms, the item is confirmed, and the order total recomputes (Order#calculate_total_amount runs on save). Advances once all items responded.



89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'app/services/dscf/marketplace/order_splitting_service.rb', line 89

def self.accept_item_adjustment(order, order_item)
  order_item.unit_price = order_item.supplier_adjusted_unit_price if order_item.supplier_adjusted_unit_price.present?
  order_item.quantity = order_item.supplier_adjusted_quantity if order_item.supplier_adjusted_quantity.present?
  order_item.supplier_adjusted_unit_price = nil
  order_item.supplier_adjusted_quantity = nil
  order_item.status = :confirmed
  order_item.save!

  order.reload
  order.save! # recompute total_amount from the revised line
  order.mark_waiting_retailer_confirmation! if order.supplier_confirmation_complete?
  order_item
end

.adjust_item(order, order_item, unit_price: nil, quantity: nil, note: nil) ⇒ Object

Supplier proposes revised terms instead of confirming/rejecting outright. The item goes to :adjusted holding the proposal; because :adjusted is neither confirmed nor cancelled, supplier_confirmation_complete? keeps the order in waiting_supplier_confirmation until the aggregator follows up (accept/reject).



77
78
79
80
81
82
83
84
# File 'app/services/dscf/marketplace/order_splitting_service.rb', line 77

def self.adjust_item(order, order_item, unit_price: nil, quantity: nil, note: nil)
  order_item.supplier_adjusted_unit_price = unit_price if unit_price.present?
  order_item.supplier_adjusted_quantity = quantity if quantity.present?
  order_item.supplier_adjustment_note = note
  order_item.status = :adjusted
  order_item.save!
  order_item
end

.assign_source(order_item, source_type:, source_id:) ⇒ Object

Handles source assignment and the split operation per requirements. Sources can be:

  • Aggregator self (via aggregator listing or direct)
  • Sub-supplier
  • Other supplier


10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'app/services/dscf/marketplace/order_splitting_service.rb', line 10

def self.assign_source(order_item, source_type:, source_id:)
  # "self" is the frontend SourcePicker's sentinel for "the aggregator
  # fulfils it directly" — it is not a real polymorphic class name, so
  # storing it literally corrupts the source association (any later
  # read of order_item.source tries "self".constantize and crashes).
  # A self-fulfilled line has no external source at all: clear it,
  # matching the frontend's own isAggregatorManaged check (!source_type).
  if source_type.to_s == "self"
    order_item.source_type = nil
    order_item.source_id = nil
  else
    order_item.source_type = source_type
    order_item.source_id = source_id
  end
  order_item.save!
  order_item
end

.confirm_item(order, order_item, confirmed: true, reason: nil) ⇒ Object

Per-source confirmation (requirements doc: "Supplier and aggregator order confirmation"). The aggregator confirms each sub-supplier / aggregator-store allocation individually; other-supplier items are confirmed by that supplier. Once every item has responded (confirmed or cancelled), the order advances to retailer confirmation.



64
65
66
67
68
69
70
71
# File 'app/services/dscf/marketplace/order_splitting_service.rb', line 64

def self.confirm_item(order, order_item, confirmed: true, reason: nil)
  order_item.status = confirmed ? :confirmed : :cancelled
  order_item.save!

  order.reload
  order.mark_waiting_retailer_confirmation! if order.supplier_confirmation_complete?
  order_item
end

.perform_split(order) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'app/services/dscf/marketplace/order_splitting_service.rb', line 28

def self.perform_split(order)
  raise "Cannot split order that is not ready" unless order.all_items_validated?

  # Mark items ready for source assignment / supplier confirmation
  order.order_items.each do |item|
    if item.status.to_s == "pending" || item.status.to_s == "confirmed"
      item.status = :processing
    end
    item.save! if item.changed?
  end

  order.mark_waiting_supplier_confirmation!

  order
end

.reject_item_adjustment(order, order_item, reason: nil) ⇒ Object

Aggregator rejects the proposal — the line can't be fulfilled on agreeable terms, so it is cancelled (mirrors a not-confirm). Advances if all responded.



105
106
107
108
109
110
111
112
113
# File 'app/services/dscf/marketplace/order_splitting_service.rb', line 105

def self.reject_item_adjustment(order, order_item, reason: nil)
  order_item.supplier_adjustment_note = reason if reason.present?
  order_item.status = :cancelled
  order_item.save!

  order.reload
  order.mark_waiting_retailer_confirmation! if order.supplier_confirmation_complete?
  order_item
end

.reopen_item(order, order_item) ⇒ Object

Re-source loop (requirements doc: when a supplier doesn't confirm, "search for other sources" then go back to change-product-source). Reopens a cancelled line for a fresh source assignment: clears its source and any stale adjustment proposal, and puts it back in :processing. If the order had already advanced (because every other line had responded), pull it back to waiting_supplier_confirmation so the reassigned line gets a chance to be confirmed before the retailer sees the order.



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'app/services/dscf/marketplace/order_splitting_service.rb', line 122

def self.reopen_item(order, order_item)
  raise "Only a cancelled item can be reopened" unless order_item.cancelled?

  order_item.status = :processing
  order_item.source_type = nil
  order_item.source_id = nil
  order_item.supplier_adjusted_unit_price = nil
  order_item.supplier_adjusted_quantity = nil
  order_item.supplier_adjustment_note = nil
  order_item.save!

  order.reload
  order.mark_waiting_supplier_confirmation! unless order.waiting_supplier_confirmation?
  order_item
end

.supplier_confirm(order, confirmed: true, reason: nil) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'app/services/dscf/marketplace/order_splitting_service.rb', line 44

def self.supplier_confirm(order, confirmed: true, reason: nil)
  if confirmed
    # In full impl, mark specific allocation as confirmed
    # For now advance whole if appropriate
  else
    order.order_items.where(status: :processing).update_all(status: OrderItem.statuses[:cancelled])
  end

  # Check if supplier side is done
  if order.supplier_confirmation_complete?
    order.mark_waiting_retailer_confirmation!
  end
  order
end