Skip to content

Commit

Permalink
close #166 support promotion, adjustment, tax for caculate commission…
Browse files Browse the repository at this point in the history
… for payout
  • Loading branch information
theachoem committed Jul 17, 2024
1 parent 60c25d1 commit 6c8a1ce
Show file tree
Hide file tree
Showing 21 changed files with 365 additions and 129 deletions.
28 changes: 25 additions & 3 deletions app/models/spree/payout.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,31 @@ class Payout < Base

enum state: { created: 0, confirmed: 1 }

validates :payout_profile_id, uniqueness: { scope: %i[line_item_id payment_id] }

extend DisplayMoney
money_methods :amount, :outstanding_amount
money_methods :amount, :outstanding_amount,
:commission_amount,
:pre_commission_amount,
:subtotal_with_vendor_adjustment_total,
:vendor_adjustment_total

def commission_rate
private_metadata&.dig('commission_rate')&.to_f
end

def commission_amount
private_metadata&.dig('commission_amount')&.to_f
end

def pre_commission_amount
private_metadata&.dig('pre_commission_amount')&.to_f
end

def subtotal_with_vendor_adjustment_total
private_metadata&.dig('subtotal_with_vendor_adjustment_total')&.to_f
end

def vendor_adjustment_total
private_metadata&.dig('vendor_adjustment_total')&.to_f
end
end
end
30 changes: 30 additions & 0 deletions app/models/vpago/adjustment_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module Vpago
module AdjustmentDecorator
def self.prepended(base)
base.enum handle_by: { unspecified: 0, store: 1, vendor: 2 }, _prefix: true

base.scope :handle_by_vendor, -> { eligible.where(handle_by: :vendor) }
base.scope :handle_by_store, -> { eligible.where(handle_by: :store) }

base.before_save :set_handle_by

def base.total
sum(:amount) || 0
end
end

private

def set_handle_by
if source.is_a?(::Spree::PromotionAction)
self.handle_by = source.run_by
elsif source.is_a?(::Spree::TaxRate)
self.handle_by = source.tax_category.collect_by
end
end
end
end

unless Spree::Adjustment.included_modules.include?(Vpago::AdjustmentDecorator)
Spree::Adjustment.prepend(Vpago::AdjustmentDecorator)
end
11 changes: 11 additions & 0 deletions app/models/vpago/inventory_unit_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Vpago
module InventoryUnitDecorator
def self.prepended(base)
base.has_one :selected_shipping_rate, through: :shipment, class_name: 'Spree::ShippingRate'
end
end
end

unless Spree::InventoryUnit.included_modules.include?(Vpago::InventoryUnitDecorator)
Spree::InventoryUnit.prepend(Vpago::InventoryUnitDecorator)
end
20 changes: 18 additions & 2 deletions app/models/vpago/line_item_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ def self.prepended(base)
base.has_many :active_payway_payout_profiles, class_name: 'Spree::PayoutProfile', through: :product

base.has_many :required_active_payout_profiles, class_name: 'Spree::PayoutProfile', through: :product

base.has_many :shipments, class_name: 'Spree::Shipment', through: :inventory_units
base.has_many :selected_shipping_rates, class_name: 'Spree::ShippingRate', through: :inventory_units
end

# considred required when there are any required profiles.
Expand All @@ -23,11 +26,24 @@ def commission_rate
end

def commission_amount
pre_tax_amount * commission_rate / 100.0
subtotal_with_vendor_adjustment_total * commission_rate / 100.0
end

def pre_commission_amount
pre_tax_amount - commission_amount
subtotal_with_vendor_adjustment_total - commission_amount
end

# using subtotal instead of pre_tax_amount since pre_tax_amount already include adjustments amount in it.
# we want raw amount to add only vendor adjustment amount.
def subtotal_with_vendor_adjustment_total
subtotal + vendor_adjustment_total
end

def vendor_adjustment_total
@vendor_adjustment_total ||= begin
order_adjustment = order.line_items_count.zero? ? 0 : order.adjustments.handle_by_vendor.total / order.line_items_count
adjustments.handle_by_vendor.total + order_adjustment
end
end
end
end
Expand Down
61 changes: 35 additions & 26 deletions app/models/vpago/order_decorator.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
module Vpago
module OrderDecorator
def self.prepended(base)
base.has_many :payouts, class_name: 'Spree::Payout', through: :payments
end

extend Spree::DisplayMoney
money_methods :order_adjustment_total, :shipping_discount

Expand All @@ -10,7 +14,7 @@ def finalize!

# update payment and shipment(s) states, and save
updater.update_payment_state

shipments.each do |shipment|
shipment.update!(self)
shipment.finalize! if paid? || authorized?
Expand All @@ -22,46 +26,52 @@ def finalize!

touch :completed_at

if !confirmation_delivered? && (paid? || authorized?)
deliver_order_confirmation_email
end
deliver_order_confirmation_email if !confirmation_delivered? && (paid? || authorized?)

consider_risk
end

# currently does not support payout when having adjustment/promotion or having tax.
def allowed_payout?
return false if adjustment_total > 0
return false if included_tax_total > 0
return false if additional_tax_total > 0
return false if promo_total > 0
def required_payway_payout?
line_items.any?(&:required_payway_payout?)
end

true
def line_items_count
line_items.size
end

def required_payway_payout?
allowed_payout? && line_items.any?(&:required_payway_payout?)
def generate_commissions_to_line_items
line_items.each do |line_item|
private_metadata = line_item.private_metadata || {}

private_metadata[:commission_rate] = line_item.commission_rate
private_metadata[:commission_amount] = line_item.commission_amount.to_f
private_metadata[:pre_commission_amount] = line_item.pre_commission_amount.to_f
private_metadata[:subtotal_with_vendor_adjustment_total] = line_item.subtotal_with_vendor_adjustment_total.to_f
private_metadata[:vendor_adjustment_total] = line_item.vendor_adjustment_total.to_f

line_item.update!(private_metadata: private_metadata)
end
end

# override
def available_payment_methods(store = nil)
payment_methods = collect_payment_methods(store)

@available_payment_methods ||= if required_payway_payout?
payment_methods.select { |payment| payment.type_payway_v2? }
else
payment_methods
end
payment_methods.select(&:type_payway_v2?)
else
payment_methods
end
end

def send_confirmation_email!
if !confirmation_delivered? && (paid? || authorized?)
deliver_order_confirmation_email
end
return unless !confirmation_delivered? && (paid? || authorized?)

deliver_order_confirmation_email
end

def successful_payment
paid? || payments.any? {|p| p.after_pay_method? && p.authorized?}
paid? || payments.any? { |p| p.after_pay_method? && p.authorized? }
end

alias paid_or_authorized? successful_payment
Expand All @@ -73,11 +83,10 @@ def authorized?
def order_adjustment_total
adjustments.eligible.sum(:amount)
end

def has_order_adjustments?
order_adjustment_total.abs > 0
end
end
end

Spree::Order.prepend(Vpago::OrderDecorator)
if Spree::Order.included_modules.exclude?(Vpago::OrderDecorator)
Spree::Order.register_update_hook(:generate_commissions_to_line_items)
Spree::Order.prepend(Vpago::OrderDecorator)
end
6 changes: 5 additions & 1 deletion app/models/vpago/payment_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ module Vpago
module PaymentDecorator
def self.prepended(base)
base.has_many :payouts, class_name: 'Spree::Payout', inverse_of: :payment
base.after_create -> { Vpago::PayoutsGenerator.new(self).call }, if: :support_payout?
base.after_create -> { Vpago::PayoutsGenerator.new(self).call }, if: :should_generate_payouts?
end

def should_generate_payouts?
support_payout? && payouts.empty?
end

def support_payout?
Expand Down
5 changes: 4 additions & 1 deletion app/models/vpago/payouts_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ def vendor_payouts
line_item: line_item,
payment: payment,
payout_profile: payout_profile,
currency: payment.currency,
amount: payout_amount,
outstanding_amount: outstanding_amount
outstanding_amount: outstanding_amount,
private_metadata: line_item.private_metadata
)

self.remaining_amount -= payout_amount
Expand All @@ -58,6 +60,7 @@ def store_payouts
state: :created,
payment: payment,
payout_profile: Spree::PayoutProfiles::PaywayV2.default,
currency: payment.currency,
amount: self.remaining_amount
)
]
Expand Down
11 changes: 11 additions & 0 deletions app/models/vpago/promotion_action_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Vpago
module PromotionActionDecorator
def self.prepended(base)
base.enum run_by: { unspecified: 0, store: 1, vendor: 2 }, _prefix: true
end
end
end

unless Spree::PromotionAction.included_modules.include?(Vpago::PromotionActionDecorator)
Spree::PromotionAction.prepend(Vpago::PromotionActionDecorator)
end
11 changes: 11 additions & 0 deletions app/models/vpago/shipping_method_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Vpago
module ShippingMethodDecorator
def self.prepended(base)
base.enum handle_by: { unspecified: 0, store: 1, vendor: 2 }, _prefix: true
end
end
end

unless Spree::ShippingMethod.included_modules.include?(Vpago::ShippingMethodDecorator)
Spree::ShippingMethod.prepend(Vpago::ShippingMethodDecorator)
end
22 changes: 22 additions & 0 deletions app/models/vpago/shipping_rate_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module Vpago
module ShippingRateDecorator
def self.prepended(base)
base.enum handle_by: { unspecified: 0, store: 1, vendor: 2 }, _prefix: true

base.scope :handle_by_vendor, -> { where(handle_by: :vendor) }
base.scope :handle_by_store, -> { where(handle_by: :store) }

base.before_save :set_handle_by
end

private

def set_handle_by
self.handle_by = shipping_method.handle_by
end
end
end

unless Spree::ShippingRate.included_modules.include?(Vpago::ShippingRateDecorator)
Spree::ShippingRate.prepend(Vpago::ShippingRateDecorator)
end
11 changes: 11 additions & 0 deletions app/models/vpago/tax_category_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Vpago
module TaxCategoryDecorator
def self.prepended(base)
base.enum collect_by: { unspecified: 0, store: 1, vendor: 2 }, _prefix: true
end
end
end

unless Spree::TaxCategory.included_modules.include?(Vpago::TaxCategoryDecorator)
Spree::TaxCategory.prepend(Vpago::TaxCategoryDecorator)
end
11 changes: 10 additions & 1 deletion db/migrate/20240715091228_create_spree_payouts.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
class CreateSpreePayouts < ActiveRecord::Migration[7.0]
def change
create_table :spree_payouts do |t|
create_table :spree_payouts, if_not_exists: true do |t|
t.references :payout_profile, foreign_key: { to_table: :spree_payout_profiles }
t.references :line_item, foreign_key: { to_table: :spree_line_items }
t.references :payment, foreign_key: { to_table: :spree_payments }

t.integer :state, default: 0, null: false
t.boolean :default, default: false, null: false

t.string :currency
t.decimal :outstanding_amount, precision: 10, scale: 2
t.decimal :amount, precision: 10, scale: 2, default: '0.0', null: false

if t.respond_to? :jsonb
t.jsonb :public_metadata
t.jsonb :private_metadata
else
t.json :public_metadata
t.json :private_metadata
end

t.timestamps
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddHandleByToSpreeAdjustments < ActiveRecord::Migration[7.0]
def change
add_column :spree_adjustments, :handle_by, :integer, null: false, default: 0, if_not_exists: true
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddRunByToSpreePromotionActions < ActiveRecord::Migration[7.0]
def change
add_column :spree_promotion_actions, :run_by, :integer, null: false, default: 0, if_not_exists: true
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddCollectByToSpreeTaxCategories < ActiveRecord::Migration[7.0]
def change
add_column :spree_tax_categories, :collect_by, :integer, null: false, default: 0, if_not_exists: true
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddHandleByToSpreeShippingMethod < ActiveRecord::Migration[7.0]
def change
add_column :spree_shipping_methods, :handle_by, :integer, null: false, default: 0, if_not_exists: true
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddHandleByToSpreeShippingRates < ActiveRecord::Migration[7.0]
def change
add_column :spree_shipping_rates, :handle_by, :integer, null: false, default: 0, if_not_exists: true
end
end
Loading

0 comments on commit 6c8a1ce

Please sign in to comment.