mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-25 20:46:48 +00:00
145 lines
4.0 KiB
Ruby
145 lines
4.0 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Spree
|
|
class Zone < ActiveRecord::Base
|
|
has_many :zone_members, dependent: :destroy, class_name: "Spree::ZoneMember"
|
|
has_many :tax_rates, dependent: :destroy
|
|
has_and_belongs_to_many :shipping_methods, join_table: 'spree_shipping_methods_zones'
|
|
|
|
validates :name, presence: true, uniqueness: true
|
|
after_save :remove_defunct_members
|
|
after_save :remove_previous_default
|
|
|
|
alias :members :zone_members
|
|
accepts_nested_attributes_for :zone_members, allow_destroy: true,
|
|
reject_if: proc { |a| a['zoneable_id'].blank? }
|
|
|
|
def kind
|
|
return unless members.any? && members.none? { |member| member.try(:zoneable_type).nil? }
|
|
|
|
members.last.zoneable_type.demodulize.underscore
|
|
end
|
|
|
|
def kind=(value)
|
|
# do nothing - just here to satisfy the form
|
|
end
|
|
|
|
def include?(address)
|
|
return false unless address
|
|
|
|
members.any? do |zone_member|
|
|
case zone_member.zoneable_type
|
|
when 'Spree::Country'
|
|
zone_member.zoneable_id == address.country_id
|
|
when 'Spree::State'
|
|
zone_member.zoneable_id == address.state_id
|
|
else
|
|
false
|
|
end
|
|
end
|
|
end
|
|
|
|
# Returns the matching zone with the highest priority zone type (State, Country, Zone.)
|
|
# Returns nil in the case of no matches.
|
|
def self.match(address)
|
|
return unless matches = includes(:zone_members).
|
|
order('zone_members_count', 'created_at').
|
|
select { |zone| zone.include? address }
|
|
|
|
['state', 'country'].each do |zone_kind|
|
|
if match = matches.detect { |zone| zone_kind == zone.kind }
|
|
return match
|
|
end
|
|
end
|
|
matches.first
|
|
end
|
|
|
|
# convenience method for returning the countries contained within a zone
|
|
def countries
|
|
@countries ||= case kind
|
|
when 'country' then zoneables
|
|
when 'state' then zoneables.collect(&:country)
|
|
end.flatten.compact.uniq
|
|
end
|
|
|
|
def <=>(other)
|
|
name <=> other.name
|
|
end
|
|
|
|
# All zoneables belonging to the zone members. Will be a collection of either
|
|
# countries or states depending on the zone type.
|
|
def zoneables
|
|
members.collect(&:zoneable)
|
|
end
|
|
|
|
def country_ids
|
|
if kind == 'country'
|
|
members.collect(&:zoneable_id)
|
|
else
|
|
[]
|
|
end
|
|
end
|
|
|
|
def state_ids
|
|
if kind == 'state'
|
|
members.collect(&:zoneable_id)
|
|
else
|
|
[]
|
|
end
|
|
end
|
|
|
|
def country_ids=(ids)
|
|
zone_members.destroy_all
|
|
ids.reject(&:blank?).map do |id|
|
|
member = ZoneMember.new
|
|
member.zoneable_type = 'Spree::Country'
|
|
member.zoneable_id = id
|
|
members << member
|
|
end
|
|
end
|
|
|
|
def state_ids=(ids)
|
|
zone_members.destroy_all
|
|
ids.reject(&:blank?).map do |id|
|
|
member = ZoneMember.new
|
|
member.zoneable_type = 'Spree::State'
|
|
member.zoneable_id = id
|
|
members << member
|
|
end
|
|
end
|
|
|
|
def self.default_tax
|
|
find_by(default_tax: true)
|
|
end
|
|
|
|
# Indicates whether the specified zone falls entirely within the zone performing
|
|
# the check.
|
|
def contains?(target)
|
|
return false if kind == 'state' && target.kind == 'country'
|
|
return false if zone_members.empty? || target.zone_members.empty?
|
|
|
|
if kind == target.kind
|
|
if target.zoneables.any? { |target_zoneable| zoneables.exclude?(target_zoneable) }
|
|
return false
|
|
end
|
|
elsif target.zoneables.any? { |target_state| zoneables.exclude?(target_state.country) }
|
|
return false
|
|
end
|
|
true
|
|
end
|
|
|
|
private
|
|
|
|
def remove_defunct_members
|
|
return unless zone_members.any?
|
|
|
|
zone_members.where('zoneable_id IS NULL OR zoneable_type != ?',
|
|
"Spree::#{kind.capitalize}").destroy_all
|
|
end
|
|
|
|
def remove_previous_default
|
|
Spree::Zone.where('id != ?', id).update_all(default_tax: false) if default_tax
|
|
end
|
|
end
|
|
end
|