Merge branch 'master' into render_plain

This commit is contained in:
Andy Brett
2021-03-18 10:53:13 -07:00
committed by GitHub
73 changed files with 1042 additions and 342 deletions

View File

@@ -7,6 +7,7 @@ on:
env:
DISABLE_KNAPSACK: true
TIMEZONE: UTC
jobs:
test-models:

View File

@@ -123,7 +123,7 @@ GEM
atomic (1.1.101)
awesome_nested_set (3.4.0)
activerecord (>= 4.0.0, < 7.0)
awesome_print (1.8.0)
awesome_print (1.9.2)
aws-sdk (1.67.0)
aws-sdk-v1 (= 1.67.0)
aws-sdk-v1 (1.67.0)
@@ -266,7 +266,7 @@ GEM
tilt
hashdiff (1.0.1)
highline (2.0.3)
i18n (1.8.5)
i18n (1.8.9)
concurrent-ruby (~> 1.0)
i18n-js (3.8.1)
i18n (>= 0.6.6)
@@ -450,10 +450,10 @@ GEM
rspec-expectations (3.10.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.10.0)
rspec-mocks (3.10.0)
rspec-mocks (3.10.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.10.0)
rspec-rails (4.0.2)
rspec-rails (4.1.2)
actionpack (>= 4.2)
activesupport (>= 4.2)
railties (>= 4.2)
@@ -463,7 +463,7 @@ GEM
rspec-support (~> 3.10)
rspec-retry (0.6.2)
rspec-core (> 3.3)
rspec-support (3.10.1)
rspec-support (3.10.2)
rswag (2.4.0)
rswag-api (= 2.4.0)
rswag-specs (= 2.4.0)

View File

@@ -4,8 +4,8 @@
footer {
.row {
p a {
font-size: 0.875rem;
p {
font-size: 1rem;
}
a, a * {
@@ -107,9 +107,14 @@ footer {
color: rgba($disabled-med, 0.35);
}
p.text-big {
font-size: 1.5rem;
font-weight: 300;
}
.social-icons {
margin-bottom: 0.25rem;
margin-top: 0.75rem;
margin-bottom: 0.9rem;
padding-top: 0.1rem;
a {
i {
@@ -128,5 +133,9 @@ footer {
}
}
}
.legal p {
font-size: 0.875rem;
}
}
}

View File

@@ -25,15 +25,14 @@ module Admin
protected
def build_resource_with_address
enterprise_group = build_resource_without_address
def build_resource
enterprise_group = super
enterprise_group.address = Spree::Address.new
enterprise_group.address.country = Spree::Country.find_by(
id: Spree::Config[:default_country_id]
)
enterprise_group
end
alias_method_chain :build_resource, :address
# Overriding method on Spree's resource controller,
# so that resources are found using permalink.

View File

@@ -114,13 +114,12 @@ module Admin
protected
def build_resource_with_address
enterprise = build_resource_without_address
def build_resource
enterprise = super
enterprise.address ||= Spree::Address.new
enterprise.address.country ||= Spree::Country.find_by(id: Spree::Config[:default_country_id])
enterprise
end
alias_method_chain :build_resource, :address
# Overriding method on Spree's resource controller,
# so that resources are found using permalink

View File

@@ -6,9 +6,9 @@ class CheckoutController < ::BaseController
layout 'darkswarm'
include OrderStockCheck
include CheckoutHelper
include OrderCyclesHelper
include EnterprisesHelper
helper 'terms_and_conditions'
helper 'checkout'
ssl_required

View File

@@ -136,7 +136,7 @@ module Spree
#
# Otherwise redirect user to that step
def can_transition_to_payment
return if @order.payment? || @order.complete? || @order.canceled?
return if @order.payment? || @order.complete? || @order.canceled? || @order.resumed?
flash[:notice] = Spree.t(:fill_in_customer_info)
redirect_to spree.edit_admin_order_customer_url(@order)

View File

@@ -15,4 +15,10 @@ module FooterLinksHelper
target: '_blank',
rel: 'noopener' )
end
def show_social_icons?
ContentConfig.footer_facebook_url.present? || ContentConfig.footer_twitter_url.present? ||
ContentConfig.footer_instagram_url.present? || ContentConfig.footer_linkedin_url.present? ||
ContentConfig.footer_googleplus_url.present? || ContentConfig.footer_pinterest_url.present?
end
end

View File

@@ -2,6 +2,8 @@ require 'open_food_network/enterprise_injection_data'
module InjectionHelper
include SerializerHelper
include EnterprisesHelper
include OrderCyclesHelper
def inject_enterprises(enterprises = nil)
inject_json_array(

View File

@@ -2,7 +2,7 @@
module Spree
class OrderMailer < BaseMailer
helper ::CheckoutHelper
helper 'checkout'
helper SpreeCurrencyHelper
helper Spree::Admin::PaymentsHelper
helper OrderHelper

View File

@@ -1,5 +1,5 @@
class SubscriptionMailer < Spree::BaseMailer
helper CheckoutHelper
helper 'checkout'
helper MailerHelper
helper ShopMailHelper
helper OrderHelper

View File

@@ -0,0 +1,43 @@
# frozen_string_literal: true
require 'active_support/concern'
# Contains the methods to compute an order balance form the point of view of the enterprise and not
# the individual shopper.
module Balance
FINALIZED_NON_SUCCESSFUL_STATES = %w(canceled returned).freeze
# Returns the order balance by considering the total as money owed to the order distributor aka.
# the shop, and as a positive balance of said enterprise. If the customer pays it all, they
# distributor and customer are even.
#
# Note however, this is meant to be used only in the context of a single order object. When
# working with a collection of orders, such an index controller action, please consider using
# `app/queries/oustanding_balance.rb` instead so we avoid potential N+1s.
def new_outstanding_balance
if state.in?(FINALIZED_NON_SUCCESSFUL_STATES)
-payment_total
else
total - payment_total
end
end
# This method is the one we're gradually replacing with `#new_outstanding_balance`. Having them
# separate enables us to choose which implementation we want depending on the context using
# a feature toggle. This avoids incosistent behavior across the app during that incremental
# refactoring.
#
# It is meant to be removed as soon as we get product approval that the new implementation has
# been working correctly in production.
def outstanding_balance
total - payment_total
end
def outstanding_balance?
!outstanding_balance.zero?
end
def display_outstanding_balance
Spree::Money.new(outstanding_balance, currency: currency)
end
end

View File

@@ -2,15 +2,15 @@ class Customer < ActiveRecord::Base
acts_as_taggable
belongs_to :enterprise
belongs_to :user, class_name: Spree.user_class
has_many :orders, class_name: Spree::Order
belongs_to :user, class_name: Spree.user_class.to_s
has_many :orders, class_name: "Spree::Order"
before_destroy :check_for_orders
belongs_to :bill_address, foreign_key: :bill_address_id, class_name: Spree::Address
belongs_to :bill_address, foreign_key: :bill_address_id, class_name: "Spree::Address"
alias_attribute :billing_address, :bill_address
accepts_nested_attributes_for :bill_address
belongs_to :ship_address, foreign_key: :ship_address_id, class_name: Spree::Address
belongs_to :ship_address, foreign_key: :ship_address_id, class_name: "Spree::Address"
alias_attribute :shipping_address, :ship_address
accepts_nested_attributes_for :ship_address

View File

@@ -1,5 +1,5 @@
class DistributorShippingMethod < ActiveRecord::Base
self.table_name = "distributors_shipping_methods"
belongs_to :shipping_method, class_name: Spree::ShippingMethod, touch: true
belongs_to :distributor, class_name: Enterprise, touch: true
belongs_to :shipping_method, class_name: "Spree::ShippingMethod", touch: true
belongs_to :distributor, class_name: "Enterprise", touch: true
end

View File

@@ -250,7 +250,7 @@ class Enterprise < ActiveRecord::Base
def plus_relatives_and_oc_producers(order_cycles)
oc_producer_ids = Exchange.in_order_cycle(order_cycles).incoming.pluck :sender_id
Enterprise.relatives_of_one_union_others(id, oc_producer_ids | [id])
Enterprise.is_primary_producer.relatives_of_one_union_others(id, oc_producer_ids | [id])
end
def relatives_including_self

View File

@@ -1,5 +1,5 @@
class EnterpriseRole < ActiveRecord::Base
belongs_to :user, class_name: Spree.user_class
belongs_to :user, class_name: Spree.user_class.to_s
belongs_to :enterprise
validates :user_id, :enterprise_id, presence: true

View File

@@ -36,8 +36,6 @@ module Spree
preference :allow_ssl_in_development_and_test, :boolean, default: false
preference :allow_ssl_in_production, :boolean, default: true
preference :allow_ssl_in_staging, :boolean, default: true
# Automatically capture the credit card (as opposed to just authorize and capture later)
preference :auto_capture, :boolean, default: false
# Replace with the name of a zone if you would like to limit the countries
preference :checkout_zone, :string, default: nil
preference :currency, :string, default: "USD"

View File

@@ -31,10 +31,6 @@ module Spree
provider_class.new
end
def auto_capture?
true
end
def method_type
'paypal'
end

View File

@@ -174,11 +174,11 @@ module Spree
end
def has_tax?
adjustments.included_tax.any?
adjustments.tax.any?
end
def included_tax
adjustments.included_tax.sum(:included_tax)
adjustments.tax.inclusive.sum(:amount)
end
def tax_rates

View File

@@ -9,7 +9,9 @@ require 'concerns/order_shipment'
module Spree
class Order < ActiveRecord::Base
prepend OrderShipment
include Checkout
include Balance
checkout_flow do
go_to_state :address
@@ -125,7 +127,7 @@ module Spree
}
scope :not_state, lambda { |state|
where("state != ?", state)
where.not(state: state)
}
# All the states an order can be in after completing the checkout
@@ -166,10 +168,6 @@ module Spree
self[:currency] || Spree::Config[:currency]
end
def display_outstanding_balance
Spree::Money.new(outstanding_balance, currency: currency)
end
def display_item_total
Spree::Money.new(item_total, currency: currency)
end
@@ -374,14 +372,6 @@ module Spree
Spree::TaxRate.adjust(self)
end
def outstanding_balance
total - payment_total
end
def outstanding_balance?
outstanding_balance != 0
end
def name
address = bill_address || ship_address
return unless address
@@ -672,7 +662,9 @@ module Spree
end
def total_tax
all_adjustments.sum(:included_tax)
adjustments.sum(:included_tax) +
shipment_adjustments.sum(:included_tax) +
line_item_adjustments.tax.sum(:amount)
end
def has_taxes_included

View File

@@ -154,8 +154,10 @@ module Spree
return unless adjustment.try(:reload)
return if adjustment.finalized?
adjustment.update_attribute(:eligible, false)
adjustment.finalize!
adjustment.update(
eligible: false,
state: "finalized"
)
end
def validate_source

View File

@@ -6,22 +6,14 @@ module Spree
def process!
return unless validate!
if payment_method.auto_capture?
purchase!
else
authorize!
end
purchase!
end
def process_offline!
return unless validate!
return if authorization_action_required?
if payment_method.auto_capture?
charge_offline!
else
authorize!
end
charge_offline!
end
def authorize!(return_url = nil)

View File

@@ -89,10 +89,6 @@ module Spree
true
end
def auto_capture?
Spree::Config[:auto_capture]
end
def supports?(_source)
true
end

View File

@@ -84,7 +84,7 @@ module Spree
order.line_items.reload
# TaxRate adjustments (order.adjustments.tax)
# and line item adjustments (tax included on line items) consist of 100% tax
(order.adjustments.tax + order.line_item_adjustments.reload).each do |adjustment|
order.adjustments.tax.additional.each do |adjustment|
adjustment.set_absolute_included_tax! adjustment.amount
end
end

View File

@@ -8,8 +8,8 @@ class Subscription < ActiveRecord::Base
belongs_to :schedule
belongs_to :shipping_method, class_name: 'Spree::ShippingMethod'
belongs_to :payment_method, class_name: 'Spree::PaymentMethod'
belongs_to :bill_address, foreign_key: :bill_address_id, class_name: Spree::Address
belongs_to :ship_address, foreign_key: :ship_address_id, class_name: Spree::Address
belongs_to :bill_address, foreign_key: :bill_address_id, class_name: "Spree::Address"
belongs_to :ship_address, foreign_key: :ship_address_id, class_name: "Spree::Address"
has_many :subscription_line_items, inverse_of: :subscription
has_many :order_cycles, through: :schedule
has_many :proxy_orders

View File

@@ -0,0 +1,15 @@
# frozen_string_literal: true
class CompleteVisibleOrders
def initialize(order_permissions)
@order_permissions = order_permissions
end
def query
order_permissions.visible_orders.complete
end
private
attr_reader :order_permissions
end

View File

@@ -8,6 +8,9 @@
# cases.
#
# See CompleteOrdersWithBalance or CustomersWithBalance as examples.
#
# Note this query object and `app/models/concerns/balance.rb` should implement the same behavior
# until we find a better way. If you change one, please, change the other too.
class OutstandingBalance
# All the states of a finished order but that shouldn't count towards the balance (the customer
# didn't get the order for whatever reason). Note it does not include complete

View File

@@ -1,4 +1,8 @@
class InvoiceRenderer
def initialize(renderer = ApplicationController.new)
@renderer = renderer
end
def render_to_string(order)
renderer.instance_variable_set(:@order, order)
renderer.render_to_string(args(order))
@@ -15,9 +19,7 @@ class InvoiceRenderer
private
def renderer
@renderer ||= ApplicationController.new
end
attr_reader :renderer
def invoice_template
if Spree::Config.invoice_style2?

View File

@@ -21,7 +21,6 @@ class OrderTaxAdjustmentsFetcher
def all
Spree::Adjustment
.with_tax
.where(order_adjustments.or(line_item_adjustments).or(shipment_adjustments))
.order('created_at ASC')
end
@@ -50,11 +49,27 @@ class OrderTaxAdjustmentsFetcher
Hash[tax_rates.collect do |tax_rate|
tax_amount = if tax_rates.one?
adjustment.included_tax
adjustment_tax_amount(adjustment)
else
tax_rate.compute_tax(adjustment.amount)
end
[tax_rate, tax_amount]
end]
end
def adjustment_tax_amount(adjustment)
if no_tax_adjustments?(adjustment)
adjustment.included_tax
else
adjustment.amount
end
end
def no_tax_adjustments?(adjustment)
# Enterprise Fees, Admin Adjustments, and Shipping Fees currently do not have tax adjustments.
# The tax amount is stored in the included_tax attribute.
adjustment.originator_type == "EnterpriseFee" ||
adjustment.originator_type == "Spree::ShippingMethod" ||
(adjustment.source_type.nil? && adjustment.originator_type.nil?)
end
end

View File

@@ -75,7 +75,7 @@ class TaxRateFinder
# to the included tax.
def find_closest_tax_rates_from_included_tax(amount, included_tax)
approximation = (included_tax / (amount - included_tax))
return [] if approximation.infinite? || approximation.zero?
return [] if approximation.infinite? || approximation.zero? || approximation.nan?
[Spree::TaxRate.order("ABS(amount - #{approximation})").first]
end

View File

@@ -31,25 +31,26 @@
// This is the instance-managed set of links:
%h4
= t '.footer_contact_headline'
%p.social-icons
- if ContentConfig.footer_facebook_url.present?
%a{href: ContentConfig.footer_facebook_url}
%i.ofn-i_044-facebook
- if ContentConfig.footer_twitter_url.present?
%a{href: ContentConfig.footer_twitter_url}
%i.ofn-i_041-twitter
- if ContentConfig.footer_instagram_url.present?
%a{href: ContentConfig.footer_instagram_url}
%i.ofn-i_043-instagram
- if ContentConfig.footer_linkedin_url.present?
%a{href: ContentConfig.footer_linkedin_url}
%i.ofn-i_042-linkedin
- if ContentConfig.footer_googleplus_url.present?
%a{href: ContentConfig.footer_googleplus_url}
%i.ofn-i_046-g
- if ContentConfig.footer_pinterest_url.present?
%a{href: ContentConfig.footer_pinterest_url}
%i.ofn-i_045-pintrest
- if show_social_icons?
%p.social-icons
- if ContentConfig.footer_facebook_url.present?
%a{href: ContentConfig.footer_facebook_url}
%i.ofn-i_044-facebook
- if ContentConfig.footer_twitter_url.present?
%a{href: ContentConfig.footer_twitter_url}
%i.ofn-i_041-twitter
- if ContentConfig.footer_instagram_url.present?
%a{href: ContentConfig.footer_instagram_url}
%i.ofn-i_043-instagram
- if ContentConfig.footer_linkedin_url.present?
%a{href: ContentConfig.footer_linkedin_url}
%i.ofn-i_042-linkedin
- if ContentConfig.footer_googleplus_url.present?
%a{href: ContentConfig.footer_googleplus_url}
%i.ofn-i_046-g
- if ContentConfig.footer_pinterest_url.present?
%a{href: ContentConfig.footer_pinterest_url}
%i.ofn-i_045-pintrest
- if ContentConfig.footer_email.present?
%p
%a{href: ContentConfig.footer_email.reverse, mailto: true, target: '_blank'}
@@ -92,7 +93,7 @@
%hr.hr-light
%br
.row
.row.legal
.small-12.medium-3.medium-offset-2.columns.text-left
%a{href: main_app.root_path}
%img{src: ContentConfig.footer_logo.url, width: "220"}
@@ -107,10 +108,9 @@
%p.text-small
= t '.footer_legal_text_html', {content_license: link_to('CC BY-SA 3.0', 'https://creativecommons.org/licenses/by-sa/3.0/'), code_license: link_to('AGPL 3', 'https://tldrlegal.com/license/gnu-affero-general-public-license-v3-(agpl-3.0)' )}
%p.text-small
%div
- if Spree::Config.privacy_policy_url.present?
= t '.footer_data_text_with_privacy_policy_html', {cookies_policy: cookies_policy_link.html_safe, privacy_policy: privacy_policy_link.html_safe }
- else
= t '.footer_data_text_without_privacy_policy_html', {cookies_policy: cookies_policy_link.html_safe }
- if Spree::Config.privacy_policy_url.present?
= t '.footer_data_text_with_privacy_policy_html', {cookies_policy: cookies_policy_link.html_safe, privacy_policy: privacy_policy_link.html_safe }
- else
= t '.footer_data_text_without_privacy_policy_html', {cookies_policy: cookies_policy_link.html_safe }
.medium-2.columns.text-center
/ Placeholder

View File

@@ -200,7 +200,5 @@ module Openfoodnetwork
config.active_support.escape_html_entities_in_json = true
config.active_job.queue_adapter = :delayed_job
config.active_record.raise_in_transactional_callbacks = true
end
end

View File

@@ -11,7 +11,7 @@ Openfoodnetwork::Application.configure do
config.action_controller.perform_caching = true
# Disable Rails's static asset server (Apache or nginx will already do this)
config.serve_static_files = false
config.public_file_server.enabled = false
# Compress JavaScripts and CSS
config.assets.compress = true

View File

@@ -11,7 +11,7 @@ Openfoodnetwork::Application.configure do
config.action_controller.perform_caching = true
# Disable Rails's static asset server (Apache or nginx will already do this)
config.serve_static_files = false
config.public_file_server.enabled = false
# Compress JavaScripts and CSS
config.assets.compress = true

View File

@@ -10,8 +10,8 @@ Openfoodnetwork::Application.configure do
config.eager_load = false
# Configure static asset server for tests with Cache-Control for performance
config.serve_static_files = true
config.static_cache_control = "public, max-age=3600"
config.public_file_server.enabled = true
config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=3600' }
# Separate cache stores when running in parallel
config.cache_store = :file_store, Rails.root.join("tmp", "cache", "paralleltests#{ENV['TEST_ENV_NUMBER']}")

View File

@@ -22,10 +22,6 @@ Spree.config do |config|
config.address_requires_state = true
config.admin_interface_logo = '/default_images/ofn-logo.png'
# -- spree_paypal_express
# Auto-capture payments. Without this option, payments must be manually captured in the paypal interface.
config.auto_capture = true
# S3 settings
config.s3_bucket = ENV['S3_BUCKET'] if ENV['S3_BUCKET']
config.s3_access_key = ENV['S3_ACCESS_KEY'] if ENV['S3_ACCESS_KEY']

View File

@@ -1186,6 +1186,7 @@ en_CA:
mailers:
powered_by:
open_food_network: "Open Food Network"
powered_html: "%{open_food_network}"
menu:
cart:
cart: "Cart"
@@ -1252,7 +1253,7 @@ en_CA:
invoice_tax_total: "Sales Tax Total:"
tax_invoice: "TAX INVOICE"
tax_total: "Total tax (%{rate}):"
total_excl_tax: "Total (Excl. tax):"
total_excl_tax: "Total (incl. shipping tax if applicable)"
total_incl_tax: "Total (Incl. tax):"
abn: "Business Number:"
acn: "Business Number:"
@@ -2882,6 +2883,7 @@ en_CA:
name_or_sku: "Name or SKU (enter at least first 4 characters of product name)"
resend: "Resend"
back_to_orders_list: "Back to Orders List"
back_to_payments_list: "Back To Payments List"
return_authorizations: "Return Authoriations"
cannot_create_returns: "Cannot create returns as this order has no shipped units"
select_stock: "Select stock"
@@ -3179,7 +3181,7 @@ en_CA:
code: "Code"
from: "From"
to: "Bill to"
shipping: "Shipping"
shipping: "Delivery/Pick-up"
form:
distribution_fields:
title: "Distribution"
@@ -3451,6 +3453,7 @@ en_CA:
pending: pending
ready: ready
shipped: shipped
canceled: canceled
payment_states:
balance_due: balance due
completed: completed
@@ -3572,6 +3575,8 @@ en_CA:
past_orders: Past Orders
transactions:
transaction_history: Transaction History
authorisation_required: Authorisation Required
authorise: Authorize
open_orders:
order: Order
shop: Shop

View File

@@ -1186,6 +1186,7 @@ en_IE:
mailers:
powered_by:
open_food_network: "Open Food Network"
powered_html: "Your shopping experience is powered by the %{open_food_network}."
menu:
cart:
cart: "Basket"
@@ -1552,6 +1553,7 @@ en_IE:
set_a_password: "You will then be prompted to set a password before you are able to administer the enterprise."
mistakenly_sent: "Not sure why you have received this email? Please contact %{owner_email} for more information."
producer_mail_greeting: "Dear"
producer_mail_text_before: "Please find below an update about the order cycle ready for:"
producer_mail_order_text: "Here is a summary of the orders for your products:"
producer_mail_delivery_instructions: "Stock pickup/delivery instructions:"
producer_mail_signoff: "Thanks and best wishes"
@@ -2888,6 +2890,7 @@ en_IE:
name_or_sku: "Name or SKU (enter at least first 4 characters of product name)"
resend: "Resend"
back_to_orders_list: "Back To Orders List"
back_to_payments_list: "Back To Payments List"
return_authorizations: "Return Authorisations"
cannot_create_returns: "Cannot create returns as this order has no shipped units."
select_stock: "Select stock"
@@ -3457,6 +3460,7 @@ en_IE:
pending: pending
ready: ready
shipped: shipped
canceled: canceled
payment_states:
balance_due: balance due
completed: completed
@@ -3578,6 +3582,8 @@ en_IE:
past_orders: Past Orders
transactions:
transaction_history: Transaction History
authorisation_required: Authorisation Required
authorise: Authorize
open_orders:
order: Order
shop: Shop

View File

@@ -1188,6 +1188,7 @@ fr_CA:
mailers:
powered_by:
open_food_network: "Open Food Network "
powered_html: "Cette commande a été rendue possible par %{open_food_network}."
menu:
cart:
cart: "Panier"
@@ -2896,6 +2897,7 @@ fr_CA:
name_or_sku: "Nom ou Ref Produit (entrer au moins les 4 premiers caractères du nom du produit)"
resend: "Renvoyer"
back_to_orders_list: "Retour à la liste des commandes"
back_to_payments_list: "Retour à la liste des paiements"
return_authorizations: "Autorisations de retours"
cannot_create_returns: "Impossible de créer une autorisation de retour car aucun produit n'a été livré pour cette commande."
select_stock: "Sélectionner le stock"
@@ -3465,6 +3467,7 @@ fr_CA:
pending: en attente
ready: prêt
shipped: envoyé
canceled: Annulé
payment_states:
balance_due: solde dû
completed: effectué
@@ -3587,6 +3590,8 @@ fr_CA:
past_orders: Commandes Passées
transactions:
transaction_history: Historique des Transactions
authorisation_required: Autorisation nécessaire
authorise: Autorise
open_orders:
order: Commander
shop: Faire mes courses

View File

@@ -3,8 +3,8 @@
module OrderManagement
module Reports
class BulkCoopController < Spree::Admin::BaseController
before_filter :load_report_parameters
before_filter :load_permissions
before_action :load_report_parameters
before_action :load_permissions
def new; end

View File

@@ -3,8 +3,8 @@
module OrderManagement
module Reports
class EnterpriseFeeSummariesController < Spree::Admin::BaseController
before_filter :load_report_parameters
before_filter :load_permissions
before_action :load_report_parameters
before_action :load_permissions
def new; end

View File

@@ -59,7 +59,7 @@ module OrderManagement
def update_adjustment_total
order.adjustment_total = all_adjustments.additional.eligible.sum(:amount)
order.additional_tax_total = all_adjustments.tax.additional.sum(:amount)
order.included_tax_total = order.line_item_adjustments.tax.sum(:included_tax) +
order.included_tax_total = order.line_item_adjustments.tax.inclusive.sum(:amount) +
all_adjustments.enterprise_fee.sum(:included_tax) +
all_adjustments.shipping.sum(:included_tax) +
adjustments.admin.sum(:included_tax)

View File

@@ -14,6 +14,7 @@ module OrderManagement
].freeze
attr_reader :params
def initialize(user, params = {}, render_table = false)
@params = params
@user = user
@@ -21,6 +22,7 @@ module OrderManagement
@supplier_report = BulkCoopSupplierReport.new
@allocation_report = BulkCoopAllocationReport.new
@filter_canceled = false
end
def header
@@ -137,6 +139,8 @@ module OrderManagement
private
attr_reader :filter_canceled
def line_item_includes
[
{
@@ -148,25 +152,35 @@ module OrderManagement
end
def order_permissions
return @order_permissions unless @order_permissions.nil?
@order_permissions = ::Permissions::Order.new(@user, @params[:q])
@order_permissions ||= ::Permissions::Order.new(@user, filter_canceled)
end
def report_line_items
@report_line_items ||= OpenFoodNetwork::Reports::LineItems.new(order_permissions, @params)
@report_line_items ||= OpenFoodNetwork::Reports::LineItems.new(
order_permissions,
@params,
CompleteVisibleOrders.new(order_permissions).query
)
end
def customer_payments_total_cost(line_items)
line_items.map(&:order).uniq.sum(&:total)
unique_orders(line_items).sum(&:total)
end
def customer_payments_amount_owed(line_items)
line_items.map(&:order).uniq.sum(&:outstanding_balance)
if OpenFoodNetwork::FeatureToggle.enabled?(:customer_balance, @user)
unique_orders(line_items).sum(&:new_outstanding_balance)
else
unique_orders(line_items).sum(&:outstanding_balance)
end
end
def customer_payments_amount_paid(line_items)
line_items.map(&:order).uniq.sum(&:payment_total)
unique_orders(line_items).sum(&:payment_total)
end
def unique_orders(line_items)
line_items.map(&:order).uniq
end
def empty_cell(_line_items)

View File

@@ -141,114 +141,119 @@ module OrderManagement
updater.update
end
it "is failed if no valid payments" do
allow(order).to receive_message_chain(:payments, :valid, :empty?).and_return(true)
describe "#update_payment_state" do
context "when the order has no valid payments" do
it "is failed" do
allow(order).to receive_message_chain(:payments, :valid, :empty?).and_return(true)
updater.update_payment_state
expect(order.payment_state).to eq('failed')
end
context "payment total is greater than order total" do
it "is credit_owed" do
order.payment_total = 2
order.total = 1
expect {
updater.update_payment_state
}.to change { order.payment_state }.to 'credit_owed'
end
end
context "order total is greater than payment total" do
it "is credit_owed" do
order.payment_total = 1
order.total = 2
expect {
updater.update_payment_state
}.to change { order.payment_state }.to 'balance_due'
end
end
context "order total equals payment total" do
it "is paid" do
order.payment_total = 30
order.total = 30
expect {
updater.update_payment_state
}.to change { order.payment_state }.to 'paid'
end
end
context "order is canceled" do
before do
order.state = 'canceled'
end
context "and is still unpaid" do
it "is void" do
order.payment_total = 0
order.total = 30
expect {
updater.update_payment_state
}.to change { order.payment_state }.to 'void'
expect(order.payment_state).to eq('failed')
end
end
context "and is paid" do
context "payment total is greater than order total" do
it "is credit_owed" do
order.payment_total = 30
order.total = 30
allow(order).to receive_message_chain(:payments, :valid, :empty?).and_return(false)
allow(order).to receive_message_chain(:payments, :completed, :empty?).and_return(false)
order.payment_total = 2
order.total = 1
expect {
updater.update_payment_state
}.to change { order.payment_state }.to 'credit_owed'
end
end
context "and payment is refunded" do
it "is void" do
order.payment_total = 0
order.total = 30
allow(order).to receive_message_chain(:payments, :valid, :empty?).and_return(false)
allow(order).to receive_message_chain(:payments, :completed, :empty?).and_return(false)
context "order total is greater than payment total" do
it "is credit_owed" do
order.payment_total = 1
order.total = 2
expect {
updater.update_payment_state
}.to change { order.payment_state }.to 'void'
end
end
end
context 'when the set payment_state does not match the last payment_state' do
before { order.payment_state = 'previous_to_paid' }
context 'and the order is being updated' do
before { allow(order).to receive(:persisted?) { true } }
it 'creates a new state_change for the order' do
expect { updater.update_payment_state }
.to change { order.state_changes.size }.by(1)
}.to change { order.payment_state }.to 'balance_due'
end
end
context 'and the order is being created' do
before { allow(order).to receive(:persisted?) { false } }
context "order total equals payment total" do
it "is paid" do
order.payment_total = 30
order.total = 30
it 'creates a new state_change for the order' do
expect {
updater.update_payment_state
}.to change { order.payment_state }.to 'paid'
end
end
context "order is canceled" do
before { order.state = 'canceled' }
context "and is still unpaid" do
it "is void" do
order.payment_total = 0
order.total = 30
expect {
updater.update_payment_state
}.to change { order.payment_state }.to 'void'
end
end
context "and is paid" do
it "is credit_owed" do
order.payment_total = 30
order.total = 30
allow(order).to receive_message_chain(:payments, :valid, :empty?) { false }
allow(order).to receive_message_chain(:payments, :completed, :empty?) { false }
expect {
updater.update_payment_state
}.to change { order.payment_state }.to 'credit_owed'
end
end
context "and payment is refunded" do
it "is void" do
order.payment_total = 0
order.total = 30
allow(order).to receive_message_chain(:payments, :valid, :empty?) { false }
allow(order).to receive_message_chain(:payments, :completed, :empty?) { false }
expect {
updater.update_payment_state
}.to change { order.payment_state }.to 'void'
end
end
end
context 'when the set payment_state matches the last payment_state' do
before { order.payment_state = 'paid' }
it 'does not create any state_change' do
expect { updater.update_payment_state }
.not_to change { order.state_changes.size }
end
end
end
context 'when the set payment_state matches the last payment_state' do
before { order.payment_state = 'paid' }
context 'when the set payment_state does not match the last payment_state' do
before { order.payment_state = 'previous_to_paid' }
it 'does not create any state_change' do
expect { updater.update_payment_state }
.not_to change { order.state_changes.size }
context 'and the order is being updated' do
before { allow(order).to receive(:persisted?) { true } }
it 'creates a new state_change for the order' do
expect { updater.update_payment_state }
.to change { order.state_changes.size }.by(1)
end
end
context 'and the order is being created' do
before { allow(order).to receive(:persisted?) { false } }
it 'creates a new state_change for the order' do
expect { updater.update_payment_state }
.not_to change { order.state_changes.size }
end
end
end
end

View File

@@ -3,7 +3,12 @@
require 'spec_helper'
describe OrderManagement::Reports::BulkCoop::BulkCoopReport do
describe "fetching orders" do
subject { OrderManagement::Reports::BulkCoop::BulkCoopReport.new user, params, true }
let(:user) { create(:admin_user) }
describe '#table_items' do
let(:params) { {} }
let(:d1) { create(:distributor_enterprise) }
let(:oc1) { create(:simple_order_cycle) }
let(:o1) { create(:order, completed_at: 1.day.ago, order_cycle: oc1, distributor: d1) }
@@ -12,19 +17,38 @@ describe OrderManagement::Reports::BulkCoop::BulkCoopReport do
before { o1.line_items << li1 }
context "as a site admin" do
let(:user) { create(:admin_user) }
subject { OrderManagement::Reports::BulkCoop::BulkCoopReport.new user, {}, true }
context 'when searching' do
let(:params) { { q: { completed_at_gt: '', completed_at_lt: '', distributor_id_in: [] } } }
it "fetches completed orders" do
o2 = create(:order)
o2.line_items << build(:line_item)
expect(subject.table_items).to eq([li1])
it "fetches completed orders" do
o2 = create(:order, state: 'cart')
o2.line_items << build(:line_item)
expect(subject.table_items).to eq([li1])
end
it 'shows canceled orders' do
o2 = create(:order, state: 'canceled', completed_at: 1.day.ago, order_cycle: oc1, distributor: d1)
line_item = build(:line_item_with_shipment)
o2.line_items << line_item
expect(subject.table_items).to include(line_item)
end
end
it "does not show cancelled orders" do
o2 = create(:order, state: "canceled", completed_at: 1.day.ago)
o2.line_items << build(:line_item_with_shipment)
expect(subject.table_items).to eq([li1])
context 'when not searching' do
let(:params) { {} }
it "fetches completed orders" do
o2 = create(:order, state: 'cart')
o2.line_items << build(:line_item)
expect(subject.table_items).to eq([li1])
end
it 'shows canceled orders' do
o2 = create(:order, state: 'canceled', completed_at: 1.day.ago, order_cycle: oc1, distributor: d1)
line_item = build(:line_item_with_shipment)
o2.line_items << line_item
expect(subject.table_items).to include(line_item)
end
end
end
@@ -124,4 +148,56 @@ describe OrderManagement::Reports::BulkCoop::BulkCoopReport do
end
end
end
describe '#columns' do
context 'when report type is bulk_coop_customer_payments' do
let(:params) { { report_type: 'bulk_coop_customer_payments' } }
it 'returns' do
expect(subject.columns).to eq(
[
:order_billing_address_name,
:order_completed_at,
:customer_payments_total_cost,
:customer_payments_amount_owed,
:customer_payments_amount_paid,
]
)
end
end
end
# Yes, I know testing a private method is bad practice but report's design, tighly coupling
# OpenFoodNetwork::OrderGrouper and OrderManagement::Reports::BulkCoop::BulkCoopReport, makes it
# very hard to make things testeable without ending up in a wormwhole. This is a trade-off.
describe '#customer_payments_amount_owed' do
let(:params) { {} }
let(:user) { build(:user) }
let!(:line_item) { create(:line_item) }
let(:order) { line_item.order }
context 'when the customer_balance feature is enabled' do
before do
allow(OpenFoodNetwork::FeatureToggle)
.to receive(:enabled?).with(:customer_balance, user) { true }
end
it 'calls #new_outstanding_balance' do
expect_any_instance_of(Spree::Order).to receive(:new_outstanding_balance)
subject.send(:customer_payments_amount_owed, [line_item])
end
end
context 'when the customer_balance feature is disabled' do
before do
allow(OpenFoodNetwork::FeatureToggle)
.to receive(:enabled?).with(:customer_balance, user) { false }
end
it 'calls #outstanding_balance' do
expect_any_instance_of(Spree::Order).to receive(:outstanding_balance)
subject.send(:customer_payments_amount_owed, [line_item])
end
end
end
end

View File

@@ -155,7 +155,7 @@ feature "Cookies", js: true do
end
def click_footer_cookies_policy_link_and_wait
find("div > a", text: "cookies policy").click
find(".legal a", text: "cookies policy").click
sleep 2
end

View File

@@ -49,7 +49,7 @@ module OpenFoodNetwork
if FeatureToggle.enabled?(:customer_balance, @user)
Spree::Order.
finalized.
where.not(spree_orders: { state: :canceled }).
not_state(:canceled).
distributed_by_user(@user).
managed_by(@user).
search(params[:q])

View File

@@ -2,9 +2,11 @@ module OpenFoodNetwork
module Reports
# shared code to search and list line items
class LineItems
def initialize(order_permissions, params)
def initialize(order_permissions, params, orders_relation = nil)
@order_permissions = order_permissions
@params = params
complete_not_canceled_visible_orders = CompleteVisibleOrders.new(order_permissions).query.not_state(:canceled)
@orders_relation = orders_relation || complete_not_canceled_visible_orders
end
def orders
@@ -12,7 +14,7 @@ module OpenFoodNetwork
end
def list(line_item_includes = nil)
line_items = @order_permissions.visible_line_items.in_orders(orders.result)
line_items = order_permissions.visible_line_items.in_orders(orders.result)
if @params[:supplier_id_in].present?
line_items = line_items.supplied_by_any(@params[:supplier_id_in])
@@ -22,11 +24,9 @@ module OpenFoodNetwork
line_items = line_items.includes(*line_item_includes).references(:line_items)
end
editable_line_items = editable_line_items(line_items)
without_editable_line_items = line_items - editable_line_items(line_items)
line_items.reject{ |li|
editable_line_items.include? li
}.each do |line_item|
without_editable_line_items.each do |line_item|
OrderDataMasker.new(line_item.order).call
end
@@ -35,13 +35,15 @@ module OpenFoodNetwork
private
attr_reader :orders_relation, :order_permissions
def search_orders
@order_permissions.visible_orders.complete.not_state(:canceled).search(@params[:q])
orders_relation.search(@params[:q])
end
# From the line_items given, returns the ones that are editable by the user
def editable_line_items(line_items)
editable_line_items_ids = @order_permissions.editable_line_items.select(:id)
editable_line_items_ids = order_permissions.editable_line_items.select(:id)
# Although merge could take a relation, here we convert line_items to array
# because, if we pass a relation, merge will overwrite the conditions on the same field

View File

@@ -100,7 +100,7 @@ module OpenFoodNetwork
end
def tax_included_in(line_item)
line_item.adjustments.sum(:included_tax)
line_item.adjustments.tax.inclusive.sum(:amount)
end
def shipment_inc_vat

View File

@@ -7,7 +7,7 @@ module Spree
extend ActiveSupport::Concern
included do
before_filter :ensure_api_key
before_action :ensure_api_key
rescue_from CanCan::AccessDenied do
unauthorized

View File

@@ -12,7 +12,7 @@ module Spree
layout :get_layout
before_filter :set_user_language
before_action :set_user_language
protected

View File

@@ -10,7 +10,7 @@ module Spree
base.class_eval do
helper_method :current_order
helper_method :current_currency
before_filter :set_current_order
before_action :set_current_order
end
end

View File

@@ -7,7 +7,7 @@ module Spree
extend ActiveSupport::Concern
included do
before_filter :force_non_ssl_redirect, if: proc { Spree::Config[:redirect_https_to_http] }
before_action :force_non_ssl_redirect, if: proc { Spree::Config[:redirect_https_to_http] }
def self.ssl_allowed(*actions)
class_attribute :ssl_allowed_actions

View File

@@ -84,6 +84,18 @@ describe LineItemsController, type: :controller do
expect(response.status).to eq 204
expect { item.reload }.to raise_error ActiveRecord::RecordNotFound
end
context "after a payment is captured" do
let(:payment) { create(:check_payment, amount: order.total, order: order, state: 'completed') }
before { payment.capture! }
it 'updates the payment state' do
expect(order.payment_state).to eq 'paid'
delete :destroy, params
order.reload
expect(order.payment_state).to eq 'credit_owed'
end
end
end
end
end

View File

@@ -5,7 +5,7 @@ require 'spec_helper'
describe Spree::Admin::BaseController, type: :controller do
controller(Spree::Admin::BaseController) do
def index
before_filter :unauthorized
before_action :unauthorized
render plain: ""
end
end

View File

@@ -297,6 +297,17 @@ describe Spree::Admin::PaymentsController, type: :controller do
spree_get :index, order_id: order.number
expect(response.status).to eq 200
end
context "order is then resumed" do
before do
order.resume
end
it "still renders the payments tab" do
spree_get :index, order_id: order.number
expect(response.status).to eq 200
end
end
end
end
end

View File

@@ -54,7 +54,7 @@ feature "Uploading Terms and Conditions PDF" do
go_to_business_details
expect(page).to have_selector "a[href*='logo-white.pdf'][target=\"_blank\"]"
expect(page).to have_content time.strftime("%F %T %z")
expect(page).to have_content time.strftime("%F %T")
# Replace PDF
attach_file "enterprise[terms_and_conditions]", black_pdf_file_name

View File

@@ -90,7 +90,7 @@ feature "Using embedded shopfront functionality", js: true do
end
end
it "redirects to embedded hub on logout when embedded" do
xit "redirects to embedded hub on logout when embedded" do
on_embedded_page do
wait_for_cart
find('#login-link a').click

View File

@@ -0,0 +1,54 @@
require 'spec_helper'
require 'open_food_network/reports/line_items'
describe OpenFoodNetwork::Reports::LineItems do
subject(:reports_line_items) { described_class.new(order_permissions, params) }
# This object lets us add some test coverage despite the very deep coupling between the class
# under test and the various objects it depends on. Other more common moking strategies where very
# hard.
class FakeOrderPermissions
def initialize(line_item, orders_relation)
@relation = Spree::LineItem.where(id: line_item.id)
@orders_relation = orders_relation
end
def visible_line_items
relation
end
def editable_line_items
line_item = FactoryBot.create(:line_item)
Spree::LineItem.where(id: line_item.id)
end
def visible_orders
orders_relation
end
private
attr_reader :relation, :orders_relation
end
describe '#list' do
let!(:order) do
create(
:order,
distributor: create(:enterprise),
completed_at: 1.day.ago,
shipments: [build(:shipment)]
)
end
let!(:line_item) { create(:line_item, order: order) }
let(:orders_relation) { Spree::Order.where(id: order.id) }
let(:order_permissions) { FakeOrderPermissions.new(line_item, orders_relation) }
let(:params) { {} }
it 'returns masked data' do
line_items = reports_line_items.list
expect(line_items.first.order.email).to eq(I18n.t('admin.reports.hidden'))
end
end
end

View File

@@ -5,27 +5,77 @@ require 'spec_helper'
describe Spree::OrderMailer do
include OpenFoodNetwork::EmailHelper
context "basic behaviour" do
describe '#confirm_email_for_customer' do
subject(:email) { described_class.confirm_email_for_customer(order) }
let(:order) { build(:order_with_totals_and_distribution) }
context ":from not set explicitly" do
it "falls back to spree config" do
message = Spree::OrderMailer.confirm_email_for_customer(order)
expect(message.from).to eq [Spree::Config[:mails_from]]
it 'renders the shared/_payment.html.haml partial' do
expect(email.body).to include(I18n.t(:email_payment_summary))
end
context 'when the order has outstanding balance' do
before { allow(order).to receive(:outstanding_balance) { 123 } }
it 'renders the amount as money' do
expect(email.body).to include('$123')
end
end
it "doesn't aggressively escape double quotes in confirmation body" do
confirmation_email = Spree::OrderMailer.confirm_email_for_customer(order)
expect(confirmation_email.body).to_not include("&quot;")
context 'when the order has no outstanding balance' do
before { allow(order).to receive(:outstanding_balance) { 0 } }
it 'displays the payment status' do
expect(email.body).to include(I18n.t(:email_payment_not_paid))
end
end
it "confirm_email_for_customer accepts an order id as an alternative to an Order object" do
context "when :from is not set explicitly" do
it "falls back to spree config" do
expect(email.from).to eq [Spree::Config[:mails_from]]
end
end
it "doesn't aggressively escape double quotes body" do
expect(email.body).to_not include("&quot;")
end
it "accepts an order id as an alternative to an Order object" do
expect(Spree::Order).to receive(:find).with(order.id).and_return(order)
expect {
Spree::OrderMailer.confirm_email_for_customer(order.id).deliver_now
described_class.confirm_email_for_customer(order.id).deliver_now
}.to_not raise_error
end
end
describe '#confirm_email_for_shop' do
subject(:email) { described_class.confirm_email_for_shop(order) }
let(:order) { build(:order_with_totals_and_distribution) }
it 'renders the shared/_payment.html.haml partial' do
expect(email.body).to include(I18n.t(:email_payment_summary))
end
context 'when the order has outstanding balance' do
before { allow(order).to receive(:outstanding_balance) { 123 } }
it 'renders the amount as money' do
expect(email.body).to include('$123')
end
end
context 'when the order has no outstanding balance' do
before { allow(order).to receive(:outstanding_balance) { 0 } }
it 'displays the payment status' do
expect(email.body).to include(I18n.t(:email_payment_not_paid))
end
end
end
context "basic behaviour" do
let(:order) { build(:order_with_totals_and_distribution) }
it "cancel_email accepts an order id as an alternative to an Order object" do
expect(Spree::Order).to receive(:find).with(order.id).and_return(order)

View File

@@ -8,7 +8,10 @@ describe SubscriptionMailer, type: :mailer do
before { setup_email }
describe "order placement" do
describe '#placement_email' do
subject(:email) { SubscriptionMailer.placement_email(order, changes) }
let(:changes) { {} }
let(:shop) { create(:enterprise) }
let(:customer) { create(:customer, enterprise: shop) }
let(:subscription) { create(:subscription, shop: shop, customer: customer, with_items: true) }
@@ -16,31 +19,24 @@ describe SubscriptionMailer, type: :mailer do
let!(:order) { proxy_order.initialise_order! }
context "when changes have been made to the order" do
let(:changes) { {} }
before do
changes[order.line_items.first.id] = 2
expect do
SubscriptionMailer.placement_email(order, changes).deliver_now
end.to change{ SubscriptionMailer.deliveries.count }.by(1)
end
before { changes[order.line_items.first.id] = 2 }
it "sends the email, which notifies the customer of changes made" do
expect { email.deliver_now }.to change { SubscriptionMailer.deliveries.count }.by(1)
body = SubscriptionMailer.deliveries.last.body.encoded
expect(body).to include "This order was automatically created for you."
expect(body).to include "Unfortunately, not all products that you requested were available."
end
end
context "and changes have not been made to the order" do
before do
expect do
SubscriptionMailer.placement_email(order, {}).deliver_now
end.to change{ SubscriptionMailer.deliveries.count }.by(1)
end
it "sends the email" do
expect { email.deliver_now }.to change { SubscriptionMailer.deliveries.count }.by(1)
body = SubscriptionMailer.deliveries.last.body.encoded
expect(body).to include "This order was automatically created for you."
expect(body).to_not include "Unfortunately, not all products that you requested were available."
end
@@ -52,12 +48,9 @@ describe SubscriptionMailer, type: :mailer do
let(:shop) { create(:enterprise, allow_order_changes: true) }
let(:email) { SubscriptionMailer.deliveries.last }
let(:body) { email.body.encoded }
let(:body) { SubscriptionMailer.deliveries.last.body.encoded }
before do
SubscriptionMailer.placement_email(order, {}).deliver_now
end
before { email.deliver_now }
context "when the customer has a user account" do
let(:customer) { create(:customer, enterprise: shop) }
@@ -93,21 +86,36 @@ describe SubscriptionMailer, type: :mailer do
end
end
end
context 'when the order has outstanding balance' do
before { allow(order).to receive(:outstanding_balance) { 123 } }
it 'renders the amount as money' do
expect(email.body).to include('$123')
end
end
context 'when the order has no outstanding balance' do
before { allow(order).to receive(:outstanding_balance) { 0 } }
it 'displays the payment status' do
expect(email.body).to include(I18n.t(:email_payment_not_paid))
end
end
end
describe "order confirmation" do
describe '#confirmation_email' do
subject(:email) { SubscriptionMailer.confirmation_email(order) }
let(:customer) { create(:customer) }
let(:subscription) { create(:subscription, customer: customer, with_items: true) }
let(:proxy_order) { create(:proxy_order, subscription: subscription) }
let!(:order) { proxy_order.initialise_order! }
before do
expect do
SubscriptionMailer.confirmation_email(order).deliver_now
end.to change{ SubscriptionMailer.deliveries.count }.by(1)
end
let(:user) { order.user }
it "sends the email" do
expect { email.deliver_now }.to change{ SubscriptionMailer.deliveries.count }.by(1)
body = SubscriptionMailer.deliveries.last.body.encoded
expect(body).to include "This order was automatically placed for you"
end
@@ -115,14 +123,11 @@ describe SubscriptionMailer, type: :mailer do
describe "linking to order page" do
let(:order_link_href) { "href=\"#{order_url(order)}\"" }
let(:email) { SubscriptionMailer.deliveries.last }
let(:body) { email.body.encoded }
context "when the customer has a user account" do
let(:customer) { create(:customer) }
it "provides link to view details" do
expect(body).to match /#{order_link_href}/
expect(email.body.encoded).to include(order_url(order))
end
end
@@ -130,10 +135,26 @@ describe SubscriptionMailer, type: :mailer do
let(:customer) { create(:customer, user: nil) }
it "does not provide link" do
expect(body).to_not match /#{order_link_href}/
expect(email.body).to_not match /#{order_link_href}/
end
end
end
context 'when the order has outstanding balance' do
before { allow(order).to receive(:outstanding_balance) { 123 } }
it 'renders the amount as money' do
expect(email.body).to include('$123')
end
end
context 'when the order has no outstanding balance' do
before { allow(order).to receive(:outstanding_balance) { 0 } }
it 'displays the payment status' do
expect(email.body).to include(I18n.t(:email_payment_not_paid))
end
end
end
describe "empty order notification" do

View File

@@ -0,0 +1,241 @@
require 'spec_helper'
describe Balance do
context "#new_outstanding_balance" do
context 'when orders are in cart state' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'cart') }
it 'returns the order balance' do
expect(order.new_outstanding_balance).to eq(100 - 10)
end
end
context 'when orders are in address state' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'address') }
it 'returns the order balance' do
expect(order.new_outstanding_balance).to eq(100 - 10)
end
end
context 'when orders are in delivery state' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'delivery') }
it 'returns the order balance' do
expect(order.new_outstanding_balance).to eq(100 - 10)
end
end
context 'when orders are in payment state' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'payment') }
it 'returns the order balance' do
expect(order.new_outstanding_balance).to eq(100 - 10)
end
end
context 'when no orders where paid' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'complete') }
it 'returns the customer balance' do
expect(order.new_outstanding_balance).to eq(100 - 10)
end
end
context 'when an order was paid' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'complete') }
it 'returns the customer balance' do
expect(order.new_outstanding_balance).to eq(100 - 10)
end
end
context 'when an order is canceled' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'canceled') }
it 'returns the customer balance' do
expect(order.new_outstanding_balance).to eq(-10)
end
end
context 'when an order is resumed' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'resumed') }
it 'returns the customer balance' do
expect(order.new_outstanding_balance).to eq(100 - 10)
end
end
context 'when an order is in payment' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'payment') }
it 'returns the customer balance' do
expect(order.new_outstanding_balance).to eq(100 - 10)
end
end
context 'when an order is awaiting_return' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'awaiting_return') }
it 'returns the customer balance' do
expect(order.new_outstanding_balance).to eq(100 - 10)
end
end
context 'when an order is returned' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'returned') }
it 'returns the balance' do
expect(order.new_outstanding_balance).to eq(-10)
end
end
context 'when payment_total is less than total' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'complete') }
it "returns positive" do
expect(order.new_outstanding_balance).to eq(100 - 10)
end
end
context 'when payment_total is greater than total' do
let(:order) { create(:order, total: 8.20, payment_total: 10.20, state: 'complete') }
it "returns negative amount" do
expect(order.new_outstanding_balance).to eq(-2.00)
end
end
end
context '#outstanding_balance?' do
context 'when total is greater than payment_total' do
let(:order) { build(:order, total: 10.10, payment_total: 9.50) }
it 'returns true' do
expect(order.outstanding_balance?).to eq(true)
end
end
context 'when total is less than payment_total' do
let(:order) { build(:order, total: 8.25, payment_total: 10.44) }
it 'returns true' do
expect(order.outstanding_balance?).to eq(true)
end
end
context "when total equals payment_total" do
let(:order) { build(:order, total: 10.10, payment_total: 10.10) }
it 'returns false' do
expect(order.outstanding_balance?).to eq(false)
end
end
end
context "#outstanding_balance" do
context 'when orders are in cart state' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'cart') }
it 'returns the order balance' do
expect(order.outstanding_balance).to eq(100 - 10)
end
end
context 'when orders are in address state' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'address') }
it 'returns the order balance' do
expect(order.outstanding_balance).to eq(100 - 10)
end
end
context 'when orders are in delivery state' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'delivery') }
it 'returns the order balance' do
expect(order.outstanding_balance).to eq(100 - 10)
end
end
context 'when orders are in payment state' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'payment') }
it 'returns the order balance' do
expect(order.outstanding_balance).to eq(100 - 10)
end
end
context 'when no orders where paid' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'complete') }
it 'returns the customer balance' do
expect(order.outstanding_balance).to eq(100 - 10)
end
end
context 'when an order was paid' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'complete') }
it 'returns the customer balance' do
expect(order.outstanding_balance).to eq(100 - 10)
end
end
context 'when an order is canceled' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'canceled') }
it 'returns the customer balance' do
expect(order.outstanding_balance).to eq(100 - 10)
end
end
context 'when an order is resumed' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'resumed') }
it 'returns the customer balance' do
expect(order.outstanding_balance).to eq(100 - 10)
end
end
context 'when an order is in payment' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'payment') }
it 'returns the customer balance' do
expect(order.outstanding_balance).to eq(100 - 10)
end
end
context 'when an order is awaiting_return' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'awaiting_return') }
it 'returns the customer balance' do
expect(order.outstanding_balance).to eq(100 - 10)
end
end
context 'when an order is returned' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'returned') }
it 'returns the balance' do
expect(order.outstanding_balance).to eq(100 - 10)
end
end
context 'when payment_total is less than total' do
let(:order) { build(:order, total: 100, payment_total: 10, state: 'complete') }
it "returns positive" do
expect(order.outstanding_balance).to eq(100 - 10)
end
end
context 'when payment_total is greater than total' do
let(:order) { create(:order, total: 8.20, payment_total: 10.20, state: 'complete') }
it "returns negative amount" do
expect(order.outstanding_balance).to eq(-2.00)
end
end
end
end

View File

@@ -599,4 +599,19 @@ describe Enterprise do
end
end
end
describe "#plus_relatives_and_oc_producers" do
it "does not find non-produders " do
supplier = create(:supplier_enterprise)
distributor = create(:distributor_enterprise, is_primary_producer: false)
product = create(:product)
order_cycle = create(
:simple_order_cycle,
suppliers: [supplier],
distributors: [distributor],
variants: [product.master]
)
expect(distributor.plus_relatives_and_oc_producers(order_cycle)).to eq([supplier])
end
end
end

View File

@@ -185,9 +185,9 @@ module Spree
tax_rate.adjust(order)
end
it "has 100% tax included" do
it "has tax included" do
expect(adjustment.amount).to be > 0
expect(adjustment.included_tax).to eq(adjustment.amount)
expect(adjustment.included).to be true
end
it "does not crash when order data has been updated previously" do
@@ -365,7 +365,7 @@ module Spree
# so tax on the enterprise_fee adjustment will be 0
# Tax on line item is: 0.2/1.2 x $10 = $1.67
expect(adjustment.included_tax).to eq(0.0)
expect(line_item.adjustments.first.included_tax).to eq(1.67)
expect(line_item.adjustments.tax.first.amount).to eq(1.67)
end
end
@@ -395,7 +395,7 @@ module Spree
# gives tax on fee of 0.2/1.2 x $50 = $8.33
# Tax on line item is: 0.2/1.2 x $10 = $1.67
expect(adjustment.included_tax).to eq(8.33)
expect(line_item.adjustments.first.included_tax).to eq(1.67)
expect(line_item.adjustments.tax.first.amount).to eq(1.67)
end
end

View File

@@ -443,7 +443,10 @@ module Spree
let(:li_no_tax) { create(:line_item) }
let(:li_tax) { create(:line_item) }
let(:tax_rate) { create(:tax_rate, calculator: ::Calculator::DefaultTax.new) }
let!(:adjustment) { create(:adjustment, adjustable: li_tax, originator: tax_rate, label: "TR", amount: 123, included_tax: 10.00) }
let!(:adjustment) {
create(:adjustment, adjustable: li_tax, originator: tax_rate, label: "TR",
amount: 10.00, included: true)
}
context "checking if a line item has tax included" do
it "returns true when it does" do

View File

@@ -9,9 +9,6 @@ module Spree
let(:bogus) { create(:bogus_payment_method, distributors: [create(:enterprise)]) }
before do
# So that Payment#purchase! is called during processing
Spree::Config[:auto_capture] = true
allow(order).to receive_message_chain(:line_items, :empty?).and_return(false)
allow(order).to receive_messages total: 100
end

View File

@@ -234,37 +234,6 @@ describe Spree::Order do
end
end
context "#outstanding_balance" do
it "should return positive amount when payment_total is less than total" do
order.payment_total = 20.20
order.total = 30.30
expect(order.outstanding_balance).to eq 10.10
end
it "should return negative amount when payment_total is greater than total" do
order.total = 8.20
order.payment_total = 10.20
expect(order.outstanding_balance).to be_within(0.001).of(-2.00)
end
end
context "#outstanding_balance?" do
it "should be true when total greater than payment_total" do
order.total = 10.10
order.payment_total = 9.50
expect(order.outstanding_balance?).to be_truthy
end
it "should be true when total less than payment_total" do
order.total = 8.25
order.payment_total = 10.44
expect(order.outstanding_balance?).to be_truthy
end
it "should be false when total equals payment_total" do
order.total = 10.10
order.payment_total = 10.10
expect(order.outstanding_balance?).to be_falsy
end
end
context "#completed?" do
it "should indicate if order is completed" do
order.completed_at = nil

View File

@@ -81,20 +81,12 @@ describe Spree::Payment do
end
context "#process!" do
it "should purchase if with auto_capture" do
it "should call purchase!" do
payment = build_stubbed(:payment, payment_method: gateway)
expect(payment.payment_method).to receive(:auto_capture?).and_return(true)
expect(payment).to receive(:purchase!)
payment.process!
end
it "should authorize without auto_capture" do
payment = build_stubbed(:payment, payment_method: gateway)
expect(payment.payment_method).to receive(:auto_capture?).and_return(false)
expect(payment).to receive(:authorize!)
payment.process!
end
it "should make the state 'processing'" do
expect(payment).to receive(:started_processing!)
payment.process!

View File

@@ -0,0 +1,40 @@
# frozen_string_literal: true
require 'spec_helper'
describe CompleteVisibleOrders do
subject(:complete_visible_orders) { described_class.new(order_permissions) }
let(:filter_canceled) { false }
describe '#query' do
let(:user) { create(:user) }
let(:enterprise) { create(:enterprise) }
let(:order_permissions) { ::Permissions::Order.new(user, filter_canceled) }
before do
user.enterprises << enterprise
user.save!
end
context 'when an order has no completed_at' do
let(:cart_order) { create(:order, distributor: enterprise) }
it 'does not return it' do
expect(complete_visible_orders.query).not_to include(cart_order)
end
end
context 'when an order has complete_at' do
let(:complete_order) { create(:order, completed_at: 1.day.ago, distributor: enterprise) }
it 'does not return it' do
expect(complete_visible_orders.query).to include(complete_order)
end
end
it 'calls #visible_orders' do
expect(order_permissions).to receive(:visible_orders).and_call_original
complete_visible_orders.query
end
end
end

View File

@@ -4,19 +4,46 @@ require 'spec_helper'
describe InvoiceRenderer do
let(:service) { described_class.new }
it "creates a PDF invoice with two different templates" do
let(:order) do
order = create(:completed_order_with_fees)
order.bill_address = order.ship_address
order.save!
order
end
result = service.render_to_string(order)
expect(result).to match /^%PDF/
context 'when invoice_style2 is configured' do
before { allow(Spree::Config).to receive(:invoice_style2?).and_return(true) }
allow(Spree::Config).to receive(:invoice_style2?).and_return true
it 'uses the invoice2 template' do
renderer = instance_double(ApplicationController)
expect(renderer)
.to receive(:render_to_string)
.with(include(template: 'spree/admin/orders/invoice2'))
alternative = service.render_to_string(order)
expect(alternative).to match /^%PDF/
expect(alternative).to_not eq result
described_class.new(renderer).render_to_string(order)
end
it 'creates a PDF invoice' do
result = service.render_to_string(order)
expect(result).to match /^%PDF/
end
end
context 'when invoice_style2 is not configured' do
before { allow(Spree::Config).to receive(:invoice_style2?).and_return(false) }
it 'uses the invoice template' do
renderer = instance_double(ApplicationController)
expect(renderer)
.to receive(:render_to_string)
.with(include(template: 'spree/admin/orders/invoice'))
described_class.new(renderer).render_to_string(order)
end
it 'creates a PDF invoice' do
result = service.render_to_string(order)
expect(result).to match /^%PDF/
end
end
end

View File

@@ -0,0 +1,90 @@
require 'spec_helper'
describe OrderDataMasker do
describe '#call' do
let(:distributor) { create(:enterprise) }
let(:order) { create(:order, distributor: distributor, ship_address: create(:address)) }
context 'when displaying customer names is allowed' do
before { distributor.preferences[:show_customer_names_to_suppliers] = true }
it 'masks personal addresses and email' do
described_class.new(order).call
expect(order.bill_address.attributes).to include(
'phone' => '',
'address1' => '',
'address2' => '',
'city' => '',
'zipcode' => '',
'state_id' => nil
)
expect(order.ship_address.attributes).to include(
'phone' => '',
'address1' => '',
'address2' => '',
'city' => '',
'zipcode' => '',
'state_id' => nil
)
expect(order.email).to eq(I18n.t('admin.reports.hidden'))
end
it 'does not mask the full name' do
described_class.new(order).call
expect(order.bill_address.attributes).not_to include(
firstname: I18n.t('admin.reports.hidden'),
lastname: ''
)
expect(order.ship_address.attributes).not_to include(
firstname: I18n.t('admin.reports.hidden'),
lastname: ''
)
end
end
context 'when displaying customer names is not allowed' do
before { distributor.preferences[:show_customer_names_to_suppliers] = false }
it 'masks personal addresses and email' do
described_class.new(order).call
expect(order.bill_address.attributes).to include(
'phone' => '',
'address1' => '',
'address2' => '',
'city' => '',
'zipcode' => '',
'state_id' => nil
)
expect(order.ship_address.attributes).to include(
'phone' => '',
'address1' => '',
'address2' => '',
'city' => '',
'zipcode' => '',
'state_id' => nil
)
expect(order.email).to eq(I18n.t('admin.reports.hidden'))
end
it 'masks the full name' do
described_class.new(order).call
expect(order.bill_address.attributes).to include(
'firstname' => I18n.t('admin.reports.hidden'),
'lastname' => ''
)
expect(order.ship_address.attributes).to include(
'firstname' => I18n.t('admin.reports.hidden'),
'lastname' => ''
)
end
end
end
end

View File

@@ -44,8 +44,9 @@ describe OrderTaxAdjustmentsFetcher do
tax_category: tax_category20,
calculator: Calculator::FlatRate.new(preferred_amount: 48.0))
end
let(:additional_adjustment) do
create(:adjustment, amount: 50.0, included_tax: tax_rate25.compute_tax(50.0))
let(:admin_adjustment) do
create(:adjustment, order: order, amount: 50.0, included_tax: tax_rate25.compute_tax(50.0),
source: nil, label: "Admin Adjustment")
end
let(:order_cycle) do
@@ -62,8 +63,7 @@ describe OrderTaxAdjustmentsFetcher do
line_items: [line_item1, line_item2],
bill_address: create(:address),
order_cycle: order_cycle,
distributor: coordinator,
adjustments: [additional_adjustment]
distributor: coordinator
)
end
@@ -80,6 +80,8 @@ describe OrderTaxAdjustmentsFetcher do
end
before do
order.reload
order.adjustments << admin_adjustment
order.create_tax_charge!
order.recreate_all_fees!
end
@@ -102,7 +104,7 @@ describe OrderTaxAdjustmentsFetcher do
expect(subject[tax_rate20]).to eq(8.0)
end
it "contains tax on order adjustment" do
it "contains tax on admin adjustment" do
expect(subject[tax_rate25]).to eq(10.0)
end
end

View File

@@ -178,7 +178,6 @@ RSpec.configure do |config|
spree_config.checkout_zone = checkout_zone
spree_config.currency = currency
spree_config.shipping_instructions = true
spree_config.auto_capture = true
end
end