Skip to content

Instantly share code, notes, and snippets.

@simmsy
Last active August 29, 2015 13:57
Show Gist options
  • Save simmsy/9684227 to your computer and use it in GitHub Desktop.
Save simmsy/9684227 to your computer and use it in GitHub Desktop.
# The fundamantal design opinion is that a Stock Return has many Returned Items
# Each Returned Item effectively has one of 3 actions associated with it return, exchange OR replace
# Any of these action can involve a refund, although the most common is that a return involves a refund
# You may wish to refund a replacement Or exchange to maintain customer relations
#
# Once all Returned Items and their actions have been set then you can commence to attempt to process the Stock Return
# Processing the Stock Return will automate the Order Adjustments, Addiitonal Shipments and any Refunds / Payments
# It should be exstensible so that developers can easily implement their own behaviour here
#
# It could be that a developer can extend this to Create New Orders instead of Additional Shipments under certain scenarios:
# When the total of the exchanged items exceed the balance on the Order, it might be preferable to require a new payment, hence new Order
class StockReturn
# return_cost
# handling_cost
# timestamps
has_many :returned_items
has_many :exchange_items
before_transistion_to :complete, :handle_return
# Validate processable?
def processable?
Spree::StockReturnHandler.factory(self).processable?
end
def handle_return
Spree::StockReturnHandler.factory(self).process!
end
end
# A Returned Item has 3 actions: return, replace or exchange
# You can refund (using refund_amount not null and > 0) with ANY of the actions above
#
# Attributes:
# - reason: provided by customer (wrong_size, not_suitable, not_as_described, arrived_late, other, etc)
# - reason_other : string
# - refund_amount: allow null, null is unknown and therefore unprocessable, must be set to 0 if no refund provided
# NOTE: No qty for line item, each one has a separate record
#
# Potentially:
# - condition: provided by receiver (faulty, damaged, good)
# - restock: boolean (null is unknown and therefore unprocessable)
class ReturnedItem
enum action: [:return, :replace, :exchange]
enum reason: REASON_CODES
belongs_to :stock_return
belongs_to :line_item
# If certain Reason Codes provided then we can auto set refund amount
before_create :auto_set_refund_amount
def refund?
refund_amount.present? && refund_amount > 0
end
end
class ExchangeItem
# price (defaults to line_item current price but if set here is overriden)
belongs_to :stock_return
belongs_to :line_item
end
class StockReturnHandler
def initialize(stock_return)
@stock_return
end
def processable?
# These methods add errors to the Stock Return object
refund_due_within_processable_boundary?
exchange_items_shipable?
replacement_items_shipable?
@stock_return.valid?
end
def perform
return false unless processable?
adjust_order_for_return_cost
adjust_order_for_handling_fee
create_shipments_for_exchanged_and_replacement_items
process_balance #(refund or charge additional)
true
end
end
@JDutil
Copy link

JDutil commented Mar 21, 2014

You said you would like the ReturnedItems to not have a quantity, and be a single record per unit quantity. That is what InventoryUnits are used for in terms of mapping LineItems to a Shipment. Following that naming convention it would make sense it using the naming as ReturnedInventoryUnit and ExchangedInventoryUnit as the way to track that your shipped InventoryUnit came back.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment