Last active
March 22, 2016 13:14
-
-
Save rossnelson/35e2db73d89f956accd7 to your computer and use it in GitHub Desktop.
Coupon Validator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class Store::CouponValidator < SimpleDelegator | |
| include ActiveModel::Validations | |
| include QueryUtils | |
| include Store::ValidationUser | |
| include Store::ValidationCoupon | |
| attr_accessor :user_id, :coupon_code | |
| def initialize(options) | |
| @user_id = options[:user_id] | |
| @coupon_code = options[:code] | |
| super(user) | |
| end | |
| validators = [ | |
| :has_coupon?, :expired?, :active?, :active_for_price_level?, | |
| :has_been_used?, :meets_cart_minimum?, :requires_new_customer? | |
| ] | |
| validators.each do |meth| | |
| validate meth, unless: :has_errors? | |
| end | |
| def has_errors? | |
| errors.any? | |
| end | |
| def has_coupon? | |
| return true if coupon | |
| errors.add(:base, "coupon code (#{coupon_code} invalid.") | |
| end | |
| def expired? | |
| return false if DateTime.now < coupon.end_date | |
| errors.add(:base, "expired on #{coupon.end_date.strftime("%d/%m/%Y")}") | |
| end | |
| def active? | |
| return true if DateTime.now > coupon.start_date | |
| errors.add(:base, "not active until #{coupon.start_date.strftime("%d/%m/%Y")}") | |
| end | |
| def active_for_price_level? | |
| return true if user.price_level_id == coupon.price_level_id | |
| errors.add(:base, "not active for current customer type") | |
| end | |
| def has_been_used? | |
| return handle_user_single_use if coupon.single_use == 'user' | |
| return handle_store_single_use if coupon.single_use == 'store' | |
| handle_already_in_cart | |
| end | |
| def meets_cart_minimum? | |
| applicable_skus_total = cart_data.inject(0) { |sum, i| Monetize.parse(i['total']) + sum } | |
| return true if applicable_skus_total > coupon.cart_min | |
| errors.add(:base, "minimum not met - need $#{coupon.cart_min} in cart (currently at $#{applicable_skus_total})") | |
| end | |
| def requires_new_customer? | |
| return true if user.plucked_order_ids.blank? | |
| errors.add(:base, "can only be used by new customers") | |
| end | |
| def is_it_stackable? | |
| return if current_coupons_in_cart.blank? | |
| return handle_stackable if coupon.stackable | |
| errors.add(:base, "#{coupon_code} removed - not stackable with other coupons") | |
| end | |
| def self.find_by_user_and_code(user, code) | |
| self.new({user: user, code: code}) | |
| end | |
| protected | |
| def applicable_skus | |
| @_aps = cart_data.select { |i| coupon.allowed_skus.include? i['sku'] } | |
| end | |
| def current_coupons_in_cart | |
| @_ccic = cart_data.select { |i| i['parent_type'] == 'coupon' } | |
| end | |
| def handle_stackable | |
| cart_has_unstackable = current_coupons_in_cart.any? { |i| i['parent']['stackable'] == false } | |
| return unless cart_has_unstackable | |
| errors.add(:base, "#{coupon_code} removed - not stackable with other coupons") | |
| end | |
| def handle_user_single_use | |
| return unless coupon.usages | |
| usages = coupon.usages.select { |cu| cu["user_id"] == user.id } | |
| return if usages.blank? | |
| errors.add(:base, "cant be used more than once") | |
| end | |
| def handle_store_single_use | |
| return if coupon.usages.blank? | |
| errors.add(:base, "cant be used more than once") | |
| end | |
| def handle_already_in_cart | |
| cart_has_coupon = current_coupons_in_cart.any? { |i| i['parent']['code'] == coupon_code } | |
| return unless cart_has_coupon | |
| errors.add(:base, "cant be used more than once") | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module QueryUtils | |
| def query_as_json(query, name) | |
| [ | |
| '(select array_to_json(array_agg(row_to_json(row)))', | |
| "from (#{query.to_sql}) row) as #{name}" | |
| ].join(" ") | |
| end | |
| def query_as_array(query, column, name) | |
| [ | |
| "ARRAY(select #{column}", | |
| "from (#{query.to_sql}) row) as #{name}" | |
| ].join(" ") | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module Store::ValidationCoupon | |
| def coupon | |
| @_coupon ||= Store::Coupon | |
| .select(coupon_query_selects) | |
| .find_by(code: coupon_code) | |
| end | |
| def coupon_usages | |
| Store::CouponUsage.joins(:coupon) | |
| .where('coupons.code = ?', coupon_code) | |
| end | |
| def coupon_query_selects | |
| [ | |
| 'coupons.*', | |
| query_as_json(coupon_usages, :usages), | |
| ].join(", ") | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module Store::ValidationUser | |
| def user | |
| @_user ||= User | |
| .select(user_query_selects) | |
| .joins(:trait, :cart) | |
| .find_by('users.id = ?', user_id) | |
| end | |
| def user_query_selects | |
| [ | |
| 'users.*', 'user_traits.values', | |
| 'user_traits.price_level_id', | |
| 'carts.data as cart_data', | |
| query_as_array(user_orders, :id, :plucked_order_ids), | |
| ].join(", ") | |
| end | |
| def user_orders | |
| @_user_orders ||= Store::Order.where(user_id: user_id) | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment