mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-13 04:00:21 +00:00
Bring modesl from spree_core
This commit is contained in:
123
app/models/spree/address.rb
Normal file
123
app/models/spree/address.rb
Normal file
@@ -0,0 +1,123 @@
|
||||
module Spree
|
||||
class Address < ActiveRecord::Base
|
||||
belongs_to :country, class_name: "Spree::Country"
|
||||
belongs_to :state, class_name: "Spree::State"
|
||||
|
||||
has_many :shipments
|
||||
|
||||
validates :firstname, :lastname, :address1, :city, :country, presence: true
|
||||
validates :zipcode, presence: true, if: :require_zipcode?
|
||||
validates :phone, presence: true, if: :require_phone?
|
||||
|
||||
validate :state_validate
|
||||
|
||||
alias_attribute :first_name, :firstname
|
||||
alias_attribute :last_name, :lastname
|
||||
|
||||
def self.default
|
||||
country = Spree::Country.find(Spree::Config[:default_country_id]) rescue Spree::Country.first
|
||||
new(country: country)
|
||||
end
|
||||
|
||||
# Can modify an address if it's not been used in an order (but checkouts controller has finer control)
|
||||
# def editable?
|
||||
# new_record? || (shipments.empty? && checkouts.empty?)
|
||||
# end
|
||||
|
||||
def full_name
|
||||
"#{firstname} #{lastname}".strip
|
||||
end
|
||||
|
||||
def state_text
|
||||
state.try(:abbr) || state.try(:name) || state_name
|
||||
end
|
||||
|
||||
def same_as?(other)
|
||||
return false if other.nil?
|
||||
attributes.except('id', 'updated_at', 'created_at') == other.attributes.except('id', 'updated_at', 'created_at')
|
||||
end
|
||||
|
||||
alias same_as same_as?
|
||||
|
||||
def to_s
|
||||
"#{full_name}: #{address1}"
|
||||
end
|
||||
|
||||
def clone
|
||||
self.class.new(self.attributes.except('id', 'updated_at', 'created_at'))
|
||||
end
|
||||
|
||||
def ==(other_address)
|
||||
self_attrs = self.attributes
|
||||
other_attrs = other_address.respond_to?(:attributes) ? other_address.attributes : {}
|
||||
|
||||
[self_attrs, other_attrs].each { |attrs| attrs.except!('id', 'created_at', 'updated_at', 'order_id') }
|
||||
|
||||
self_attrs == other_attrs
|
||||
end
|
||||
|
||||
def empty?
|
||||
attributes.except('id', 'created_at', 'updated_at', 'order_id', 'country_id').all? { |_, v| v.nil? }
|
||||
end
|
||||
|
||||
# Generates an ActiveMerchant compatible address hash
|
||||
def active_merchant_hash
|
||||
{
|
||||
name: full_name,
|
||||
address1: address1,
|
||||
address2: address2,
|
||||
city: city,
|
||||
state: state_text,
|
||||
zip: zipcode,
|
||||
country: country.try(:iso),
|
||||
phone: phone
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
def require_phone?
|
||||
true
|
||||
end
|
||||
|
||||
def require_zipcode?
|
||||
true
|
||||
end
|
||||
|
||||
def state_validate
|
||||
# Skip state validation without country (also required)
|
||||
# or when disabled by preference
|
||||
return if country.blank? || !Spree::Config[:address_requires_state]
|
||||
return unless country.states_required
|
||||
|
||||
# ensure associated state belongs to country
|
||||
if state.present?
|
||||
if state.country == country
|
||||
self.state_name = nil #not required as we have a valid state and country combo
|
||||
else
|
||||
if state_name.present?
|
||||
self.state = nil
|
||||
else
|
||||
errors.add(:state, :invalid)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# ensure state_name belongs to country without states, or that it matches a predefined state name/abbr
|
||||
if state_name.present?
|
||||
if country.states.present?
|
||||
states = country.states.find_all_by_name_or_abbr(state_name)
|
||||
|
||||
if states.size == 1
|
||||
self.state = states.first
|
||||
self.state_name = nil
|
||||
else
|
||||
errors.add(:state, :invalid)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# ensure at least one state field is populated
|
||||
errors.add :state, :blank if state.blank? && state_name.blank?
|
||||
end
|
||||
end
|
||||
end
|
||||
8
app/models/spree/shipping_category.rb
Normal file
8
app/models/spree/shipping_category.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
module Spree
|
||||
class ShippingCategory < ActiveRecord::Base
|
||||
validates :name, presence: true
|
||||
has_many :products
|
||||
has_many :shipping_method_categories
|
||||
has_many :shipping_methods, through: :shipping_method_categories
|
||||
end
|
||||
end
|
||||
60
app/models/spree/shipping_method.rb
Normal file
60
app/models/spree/shipping_method.rb
Normal file
@@ -0,0 +1,60 @@
|
||||
module Spree
|
||||
class ShippingMethod < ActiveRecord::Base
|
||||
include Spree::Core::CalculatedAdjustments
|
||||
DISPLAY = [:both, :front_end, :back_end]
|
||||
|
||||
default_scope -> { where(deleted_at: nil) }
|
||||
|
||||
has_many :shipments
|
||||
has_many :shipping_method_categories
|
||||
has_many :shipping_categories, through: :shipping_method_categories
|
||||
has_many :shipping_rates
|
||||
|
||||
has_and_belongs_to_many :zones, :join_table => 'spree_shipping_methods_zones',
|
||||
:class_name => 'Spree::Zone',
|
||||
:foreign_key => 'shipping_method_id'
|
||||
|
||||
validates :name, presence: true
|
||||
|
||||
validate :at_least_one_shipping_category
|
||||
|
||||
def adjustment_label
|
||||
Spree.t(:shipping)
|
||||
end
|
||||
|
||||
def include?(address)
|
||||
return false unless address
|
||||
zones.any? do |zone|
|
||||
zone.include?(address)
|
||||
end
|
||||
end
|
||||
|
||||
def build_tracking_url(tracking)
|
||||
tracking_url.gsub(/:tracking/, tracking) unless tracking.blank? || tracking_url.blank?
|
||||
end
|
||||
|
||||
def self.calculators
|
||||
spree_calculators.send(model_name_without_spree_namespace).select{ |c| c < Spree::ShippingCalculator }
|
||||
end
|
||||
|
||||
# Some shipping methods are only meant to be set via backend
|
||||
def frontend?
|
||||
self.display_on != "back_end"
|
||||
end
|
||||
|
||||
private
|
||||
def at_least_one_shipping_category
|
||||
if self.shipping_categories.empty?
|
||||
self.errors[:base] << "You need to select at least one shipping category"
|
||||
end
|
||||
end
|
||||
|
||||
def self.on_backend_query
|
||||
"#{table_name}.display_on != 'front_end' OR #{table_name}.display_on IS NULL"
|
||||
end
|
||||
|
||||
def self.on_frontend_query
|
||||
"#{table_name}.display_on != 'back_end' OR #{table_name}.display_on IS NULL"
|
||||
end
|
||||
end
|
||||
end
|
||||
6
app/models/spree/shipping_method_category.rb
Normal file
6
app/models/spree/shipping_method_category.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
module Spree
|
||||
class ShippingMethodCategory < ActiveRecord::Base
|
||||
belongs_to :shipping_method, class_name: 'Spree::ShippingMethod'
|
||||
belongs_to :shipping_category, class_name: 'Spree::ShippingCategory'
|
||||
end
|
||||
end
|
||||
32
app/models/spree/shipping_rate.rb
Normal file
32
app/models/spree/shipping_rate.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
module Spree
|
||||
class ShippingRate < ActiveRecord::Base
|
||||
belongs_to :shipment, class_name: 'Spree::Shipment'
|
||||
belongs_to :shipping_method, class_name: 'Spree::ShippingMethod'
|
||||
|
||||
scope :frontend,
|
||||
-> { includes(:shipping_method).
|
||||
where(ShippingMethod.on_frontend_query).
|
||||
references(:shipping_method).
|
||||
order("cost ASC") }
|
||||
scope :backend,
|
||||
-> { includes(:shipping_method).
|
||||
where(ShippingMethod.on_backend_query).
|
||||
references(:shipping_method).
|
||||
order("cost ASC") }
|
||||
|
||||
delegate :order, :currency, to: :shipment
|
||||
delegate :name, to: :shipping_method
|
||||
|
||||
def display_price
|
||||
if Spree::Config[:shipment_inc_vat]
|
||||
price = (1 + Spree::TaxRate.default) * cost
|
||||
else
|
||||
price = cost
|
||||
end
|
||||
|
||||
Spree::Money.new(price, { currency: currency })
|
||||
end
|
||||
|
||||
alias_method :display_cost, :display_price
|
||||
end
|
||||
end
|
||||
228
spec/models/spree/address_spec.rb
Normal file
228
spec/models/spree/address_spec.rb
Normal file
@@ -0,0 +1,228 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Spree::Address do
|
||||
describe "clone" do
|
||||
it "creates a copy of the address with the exception of the id, updated_at and created_at attributes" do
|
||||
state = create(:state)
|
||||
original = create(:address,
|
||||
:address1 => 'address1',
|
||||
:address2 => 'address2',
|
||||
:alternative_phone => 'alternative_phone',
|
||||
:city => 'city',
|
||||
:country => Spree::Country.first,
|
||||
:firstname => 'firstname',
|
||||
:lastname => 'lastname',
|
||||
:company => 'company',
|
||||
:phone => 'phone',
|
||||
:state_id => state.id,
|
||||
:state_name => state.name,
|
||||
:zipcode => 'zip_code')
|
||||
|
||||
cloned = original.clone
|
||||
|
||||
cloned.address1.should == original.address1
|
||||
cloned.address2.should == original.address2
|
||||
cloned.alternative_phone.should == original.alternative_phone
|
||||
cloned.city.should == original.city
|
||||
cloned.country_id.should == original.country_id
|
||||
cloned.firstname.should == original.firstname
|
||||
cloned.lastname.should == original.lastname
|
||||
cloned.company.should == original.company
|
||||
cloned.phone.should == original.phone
|
||||
cloned.state_id.should == original.state_id
|
||||
cloned.state_name.should == original.state_name
|
||||
cloned.zipcode.should == original.zipcode
|
||||
|
||||
cloned.id.should_not == original.id
|
||||
cloned.created_at.should_not == original.created_at
|
||||
cloned.updated_at.should_not == original.updated_at
|
||||
end
|
||||
end
|
||||
|
||||
context "aliased attributes" do
|
||||
let(:address) { Spree::Address.new }
|
||||
|
||||
it "first_name" do
|
||||
address.firstname = "Ryan"
|
||||
address.first_name.should == "Ryan"
|
||||
end
|
||||
|
||||
it "last_name" do
|
||||
address.lastname = "Bigg"
|
||||
address.last_name.should == "Bigg"
|
||||
end
|
||||
end
|
||||
|
||||
context "validation" do
|
||||
before do
|
||||
configure_spree_preferences do |config|
|
||||
config.address_requires_state = true
|
||||
end
|
||||
end
|
||||
|
||||
let(:country) { mock_model(Spree::Country, :states => [state], :states_required => true) }
|
||||
let(:state) { stub_model(Spree::State, :name => 'maryland', :abbr => 'md') }
|
||||
let(:address) { build(:address, :country => country) }
|
||||
|
||||
before do
|
||||
country.states.stub :find_all_by_name_or_abbr => [state]
|
||||
end
|
||||
|
||||
it "state_name is not nil and country does not have any states" do
|
||||
address.state = nil
|
||||
address.state_name = 'alabama'
|
||||
address.should be_valid
|
||||
end
|
||||
|
||||
it "errors when state_name is nil" do
|
||||
address.state_name = nil
|
||||
address.state = nil
|
||||
address.should_not be_valid
|
||||
end
|
||||
|
||||
it "full state name is in state_name and country does contain that state" do
|
||||
address.state_name = 'alabama'
|
||||
# called by state_validate to set up state_id.
|
||||
# Perhaps this should be a before_validation instead?
|
||||
address.should be_valid
|
||||
address.state.should_not be_nil
|
||||
address.state_name.should be_nil
|
||||
end
|
||||
|
||||
it "state abbr is in state_name and country does contain that state" do
|
||||
address.state_name = state.abbr
|
||||
address.should be_valid
|
||||
address.state_id.should_not be_nil
|
||||
address.state_name.should be_nil
|
||||
end
|
||||
|
||||
it "state is entered but country does not contain that state" do
|
||||
address.state = state
|
||||
address.country = stub_model(Spree::Country)
|
||||
address.valid?
|
||||
address.errors["state"].should == ['is invalid']
|
||||
end
|
||||
|
||||
it "both state and state_name are entered but country does not contain the state" do
|
||||
address.state = state
|
||||
address.state_name = 'maryland'
|
||||
address.country = stub_model(Spree::Country)
|
||||
address.should be_valid
|
||||
address.state_id.should be_nil
|
||||
end
|
||||
|
||||
it "both state and state_name are entered and country does contain the state" do
|
||||
address.state = state
|
||||
address.state_name = 'maryland'
|
||||
address.should be_valid
|
||||
address.state_name.should be_nil
|
||||
end
|
||||
|
||||
it "address_requires_state preference is false" do
|
||||
Spree::Config.set :address_requires_state => false
|
||||
address.state = nil
|
||||
address.state_name = nil
|
||||
address.should be_valid
|
||||
end
|
||||
|
||||
it "requires phone" do
|
||||
address.phone = ""
|
||||
address.valid?
|
||||
address.errors["phone"].should == ["can't be blank"]
|
||||
end
|
||||
|
||||
it "requires zipcode" do
|
||||
address.zipcode = ""
|
||||
address.valid?
|
||||
address.should have(1).error_on(:zipcode)
|
||||
end
|
||||
|
||||
context "phone not required" do
|
||||
before { address.instance_eval{ self.stub :require_phone? => false } }
|
||||
|
||||
it "shows no errors when phone is blank" do
|
||||
address.phone = ""
|
||||
address.valid?
|
||||
address.should have(:no).errors_on(:phone)
|
||||
end
|
||||
end
|
||||
|
||||
context "zipcode not required" do
|
||||
before { address.instance_eval{ self.stub :require_zipcode? => false } }
|
||||
|
||||
it "shows no errors when phone is blank" do
|
||||
address.zipcode = ""
|
||||
address.valid?
|
||||
address.should have(:no).errors_on(:zipcode)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context ".default" do
|
||||
before do
|
||||
@default_country_id = Spree::Config[:default_country_id]
|
||||
new_country = create(:country)
|
||||
Spree::Config[:default_country_id] = new_country.id
|
||||
end
|
||||
|
||||
after do
|
||||
Spree::Config[:default_country_id] = @default_country_id
|
||||
end
|
||||
it "sets up a new record with Spree::Config[:default_country_id]" do
|
||||
Spree::Address.default.country.should == Spree::Country.find(Spree::Config[:default_country_id])
|
||||
end
|
||||
|
||||
# Regression test for #1142
|
||||
it "uses the first available country if :default_country_id is set to an invalid value" do
|
||||
Spree::Config[:default_country_id] = "0"
|
||||
Spree::Address.default.country.should == Spree::Country.first
|
||||
end
|
||||
end
|
||||
|
||||
context '#full_name' do
|
||||
context 'both first and last names are present' do
|
||||
let(:address) { stub_model(Spree::Address, :firstname => 'Michael', :lastname => 'Jackson') }
|
||||
specify { address.full_name.should == 'Michael Jackson' }
|
||||
end
|
||||
|
||||
context 'first name is blank' do
|
||||
let(:address) { stub_model(Spree::Address, :firstname => nil, :lastname => 'Jackson') }
|
||||
specify { address.full_name.should == 'Jackson' }
|
||||
end
|
||||
|
||||
context 'last name is blank' do
|
||||
let(:address) { stub_model(Spree::Address, :firstname => 'Michael', :lastname => nil) }
|
||||
specify { address.full_name.should == 'Michael' }
|
||||
end
|
||||
|
||||
context 'both first and last names are blank' do
|
||||
let(:address) { stub_model(Spree::Address, :firstname => nil, :lastname => nil) }
|
||||
specify { address.full_name.should == '' }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context '#state_text' do
|
||||
context 'state is blank' do
|
||||
let(:address) { stub_model(Spree::Address, :state => nil, :state_name => 'virginia') }
|
||||
specify { address.state_text.should == 'virginia' }
|
||||
end
|
||||
|
||||
context 'both name and abbr is present' do
|
||||
let(:state) { stub_model(Spree::State, :name => 'virginia', :abbr => 'va') }
|
||||
let(:address) { stub_model(Spree::Address, :state => state) }
|
||||
specify { address.state_text.should == 'va' }
|
||||
end
|
||||
|
||||
context 'only name is present' do
|
||||
let(:state) { stub_model(Spree::State, :name => 'virginia', :abbr => nil) }
|
||||
let(:address) { stub_model(Spree::Address, :state => state) }
|
||||
specify { address.state_text.should == 'virginia' }
|
||||
end
|
||||
end
|
||||
|
||||
context "defines require_phone? helper method" do
|
||||
let(:address) { stub_model(Spree::Address) }
|
||||
specify { address.instance_eval{ require_phone? }.should be_true}
|
||||
end
|
||||
end
|
||||
38
spec/models/spree/shipping_rate_spec.rb
Normal file
38
spec/models/spree/shipping_rate_spec.rb
Normal file
@@ -0,0 +1,38 @@
|
||||
# encoding: utf-8
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Spree::ShippingRate do
|
||||
let(:shipment) { create(:shipment) }
|
||||
let(:shipping_method) { create(:shipping_method) }
|
||||
let(:shipping_rate) { Spree::ShippingRate.new(:shipment => shipment,
|
||||
:shipping_method => shipping_method,
|
||||
:cost => 10.55) }
|
||||
before { Spree::TaxRate.stub(:default => 0.05) }
|
||||
|
||||
context "#display_price" do
|
||||
context "when shipment includes VAT" do
|
||||
before { Spree::Config[:shipment_inc_vat] = true }
|
||||
it "displays the correct price" do
|
||||
shipping_rate.display_price.to_s.should == "$11.08" # $10.55 * 1.05 == $11.08
|
||||
end
|
||||
end
|
||||
|
||||
context "when shipment does not include VAT" do
|
||||
before { Spree::Config[:shipment_inc_vat] = false }
|
||||
it "displays the correct price" do
|
||||
shipping_rate.display_price.to_s.should == "$10.55"
|
||||
end
|
||||
end
|
||||
|
||||
context "when the currency is JPY" do
|
||||
let(:shipping_rate) { shipping_rate = Spree::ShippingRate.new(:cost => 205)
|
||||
shipping_rate.stub(:currency => "JPY")
|
||||
shipping_rate }
|
||||
|
||||
it "displays the price in yen" do
|
||||
shipping_rate.display_price.to_s.should == "¥205"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user