當我們要做開立發票、發票折讓的時候,或者對第三方如 POS
整合系統要同步資料時,可能會遇到需要使用『攤提』的情境。在開始介紹攤提以前,我們先介紹基本情境,以及各個資料表之間的關係
#======= 訂單 Order
columns:
used_rebate: 紀錄使用的回饋點
used_birth: 紀錄使用的生日點
target_price: 滿額贈實際扣點
#======= 賣出商品 OrderItem
columns:
quantity: 賣出數量
price: 折價後價錢
#======= 退貨商品 ReturnOrderItem
columns:
quantity: 退貨數量
#======= 訂單與賣出商品之間的關係
order has_many order_items
order_item belongs_to order
#======= 賣出商品、退貨商品之間的關係
order_item has_one return_order_item
return_order_item belongs_to order_item
decorator pattern 的精神是,在不動用該實體的行為的同時,對該實體新增一些顯示方法。以該情境來說,我們不動訂單、退貨單的金額,用現有的方法算攤提後的價格。
下列為將計算退款的Decorator
寫在module,並透過 module#included
在被include
之後對Order
, OrderItem
加入實體方法
module OrderSyncDecorator
def self.included(base)
Order.class_eval do
# 減項: 使用點數、滿額贈
define_method :minus, -> { used_rebate.to_i + target_price.to_i }
end
OrderItem.class_eval do
# 退貨完後的數量
define_method :qty_after_return, -> { quantity.to_i - return_order_item&.quantity.to_i }
# 比率分子
define_method :item_numerator, -> { qty_after_return * price }
end
Order.class_eval do
# 比率分母
define_method :item_denominator, -> { order_items.sum(&:item_numerator) }
end
OrderItem.class_eval do
# 比率
define_method :item_ratio, -> { item_numerator.to_f / order.item_denominator.to_f }
end
Order.class_eval do
# 攤平金額
define_method :flatten_minus, -> { order_items.map { |_| {id: _.id, price: (_.item_ratio * minus.to_f).to_i} } }
# 誤差數
define_method :deviation_minus, -> { minus - flatten_minus.sum { |_| _[:price] } }
# 修正金額 (+在首位)
define_method :corr_flatten_minus, -> { e = flatten_minus; [e[0].merge(price: e[0][:price] + deviation_minus), *e[1..-1]] }
end
OrderItem.class_eval do
# 商品小計
define_method :subtotal, -> { quantity * price - minus }
# minus
define_method :minus, -> { order.corr_flatten_minus.find { |_| _[:id] == id }.try(:[], :price) }
end
# 單項折讓
define_method :rebate, -> { variant.price - subtotal.to_f }
# 單項折扣
define_method :discount, -> { 100 * (subtotal.to_f / variant.price.to_f) }
end
end
end
以上例來說,攤提的算法為
訂單Order
➡️ 先將訂單的減項算出來
訂單項OrderItem
➡️ 將退貨後的數量算出來
訂單項OrderItem
➡️ 將攤提比率的分子算出來
訂單Order
➡️ 先將訂單攤提比率的分母算出來
訂單項OrderItem
➡️ 將攤提比率算出來
訂單Order
➡️ 將攤提金額算出來flatten_minus
➡️ 將誤差算出來deviation_minus
➡️ 修正誤差corr_flatten_minus
:將誤差修正的結果
訂單項OrderItem
➡️ 算商品小計
➡️ 算用比率
/ 修正
後的減項 ? 從corr_flatten_minus
取得結果
➡️ 算單項折讓
➡️ 算單項折扣
搭配之前介紹的繼承,只要有分銷相關的邏輯,包含後台畫面、發票折讓、同步訂單等,只要繼承了OrderSyncDecorator
,就可以共用包含在OrderSyncDecorator
裡面所有的實體方法。