1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2024-12-04 15:24:23 +01:00
fab-manager/app/services/coupon_service.rb
2021-04-22 19:24:08 +02:00

87 lines
3.0 KiB
Ruby

# frozen_string_literal: true
# This class provides helper methods to deal with coupons
class CouponService
##
# Apply the provided coupon, if active, to the given price. Usability tests will be run depending on the
# provided parameters.
# If no coupon/coupon code or if the code does not match, return origin price without change
#
# @param total {Number} invoice total, before any coupon is applied
# @param coupon {String|Coupon} Coupon's code OR Coupon object
# @param user_id {Number} user's id against the coupon will be tested for usability
# @return {Number}
##
def apply(total, coupon = nil, user_id = nil)
price = total
coupon_object = if coupon.instance_of? Coupon
coupon
elsif coupon.instance_of? String
Coupon.find_by(code: coupon)
else
nil
end
return price if coupon_object.nil?
if coupon_object.status(user_id, total) == 'active'
if coupon_object.type == 'percent_off'
price -= (price * coupon_object.percent_off / 100.00).truncate
elsif coupon_object.type == 'amount_off'
# do not apply cash coupon unless it has a lower amount that the total price
price -= coupon_object.amount_off if coupon_object.amount_off <= price
end
end
price
end
##
# Find the coupon associated with the given code and check it is valid for the given user
# @param code {String} the literal code of the coupon
# @param user_id {Number} identifier of the user who is applying the coupon
# @return {Coupon}
##
def validate(code, user_id)
return nil unless code && user_id
coupon = Coupon.find_by(code: code)
raise InvalidCouponError if coupon.nil? || coupon.status(user_id) != 'active'
coupon
end
##
# Ventilate the discount of the provided coupon over the given amount proportionately to the invoice's total
# @param total {Number} total amount of the invoice expressed in centimes
# @param amount {Number} price of the invoice's sub-item expressed in centimes
# @param coupon {Coupon} coupon applied to the invoice, amount_off expressed in centimes if applicable
##
def ventilate(total, amount, coupon)
price = amount
if !coupon.nil? && total != 0
if coupon.type == 'percent_off'
price = amount - (amount * coupon.percent_off / 100.00)
elsif coupon.type == 'amount_off'
ratio = (coupon.amount_off / 100.00) / total
discount = (amount * ratio.abs) * 100
price = amount - discount
else
raise InvalidCouponError
end
end
price
end
##
# Compute the total amount of the given invoice, without the applied coupon
# Invoice.total stores the amount payed by the customer, coupon deducted
# @param invoice {Invoice} invoice object, its total before discount will be computed
# @return {Number} total in centimes
##
def invoice_total_no_coupon(invoice)
invoice.invoice_items.map(&:amount).map(&:to_i).reduce(:+) or 0
end
end