-
-
Notifications
You must be signed in to change notification settings - Fork 725
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5887 from luisramos0/orders
[Bye bye Spree] Bring models order, line_item and other related from spree_core
- Loading branch information
Showing
25 changed files
with
3,259 additions
and
637 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# frozen_string_literal: true | ||
|
||
module Spree | ||
class InventoryUnit < ActiveRecord::Base | ||
belongs_to :variant, class_name: "Spree::Variant" | ||
belongs_to :order, class_name: "Spree::Order" | ||
belongs_to :shipment, class_name: "Spree::Shipment" | ||
belongs_to :return_authorization, class_name: "Spree::ReturnAuthorization" | ||
|
||
scope :backordered, -> { where state: 'backordered' } | ||
scope :shipped, -> { where state: 'shipped' } | ||
scope :backordered_per_variant, ->(stock_item) do | ||
includes(:shipment) | ||
.where("spree_shipments.state != 'canceled'").references(:shipment) | ||
.where(variant_id: stock_item.variant_id) | ||
.backordered.order("#{table_name}.created_at ASC") | ||
end | ||
|
||
# state machine (see http://github.com/pluginaweek/state_machine/tree/master for details) | ||
state_machine initial: :on_hand do | ||
event :fill_backorder do | ||
transition to: :on_hand, from: :backordered | ||
end | ||
after_transition on: :fill_backorder, do: :update_order | ||
|
||
event :ship do | ||
transition to: :shipped, if: :allow_ship? | ||
end | ||
|
||
event :return do | ||
transition to: :returned, from: :shipped | ||
end | ||
end | ||
|
||
# This was refactored from a simpler query because the previous implementation | ||
# lead to issues once users tried to modify the objects returned. That's due | ||
# to ActiveRecord `joins(shipment: :stock_location)` only return readonly | ||
# objects | ||
# | ||
# Returns an array of backordered inventory units as per a given stock item | ||
def self.backordered_for_stock_item(stock_item) | ||
backordered_per_variant(stock_item).select do |unit| | ||
unit.shipment.stock_location == stock_item.stock_location | ||
end | ||
end | ||
|
||
def self.finalize_units!(inventory_units) | ||
inventory_units.map { |iu| iu.update_column(:pending, false) } | ||
end | ||
|
||
def find_stock_item | ||
Spree::StockItem.find_by(stock_location_id: shipment.stock_location_id, | ||
variant_id: variant_id) | ||
end | ||
|
||
# Remove variant default_scope `deleted_at: nil` | ||
def variant | ||
Spree::Variant.unscoped { super } | ||
end | ||
|
||
private | ||
|
||
def allow_ship? | ||
Spree::Config[:allow_backorder_shipping] || on_hand? | ||
end | ||
|
||
def update_order | ||
order.update! | ||
end | ||
end | ||
end |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'open_food_network/scope_variant_to_hub' | ||
require 'variant_units/variant_and_line_item_naming' | ||
|
||
module Spree | ||
class LineItem < ActiveRecord::Base | ||
include VariantUnits::VariantAndLineItemNaming | ||
include LineItemBasedAdjustmentHandling | ||
|
||
belongs_to :order, class_name: "Spree::Order", inverse_of: :line_items | ||
belongs_to :variant, class_name: "Spree::Variant" | ||
belongs_to :tax_category, class_name: "Spree::TaxCategory" | ||
|
||
has_one :product, through: :variant | ||
has_many :adjustments, as: :adjustable, dependent: :destroy | ||
|
||
has_and_belongs_to_many :option_values, join_table: 'spree_option_values_line_items', | ||
class_name: 'Spree::OptionValue' | ||
|
||
before_validation :adjust_quantity | ||
before_validation :copy_price | ||
before_validation :copy_tax_category | ||
|
||
validates :variant, presence: true | ||
validates :quantity, numericality: { | ||
only_integer: true, | ||
greater_than: -1, | ||
message: Spree.t('validation.must_be_int') | ||
} | ||
validates :price, numericality: true | ||
validates_with Stock::AvailabilityValidator | ||
|
||
before_save :update_inventory | ||
before_save :calculate_final_weight_volume, if: :quantity_changed?, | ||
unless: :final_weight_volume_changed? | ||
after_save :update_order | ||
after_save :update_units | ||
before_destroy :update_inventory_before_destroy | ||
after_destroy :update_order | ||
|
||
delegate :product, :unit_description, :display_name, to: :variant | ||
|
||
attr_accessor :skip_stock_check # Allows manual skipping of Stock::AvailabilityValidator | ||
attr_accessor :target_shipment | ||
|
||
# -- Scopes | ||
scope :managed_by, lambda { |user| | ||
if user.has_spree_role?('admin') | ||
where(nil) | ||
else | ||
# Find line items that are from orders distributed by the user or supplied by the user | ||
joins(variant: :product). | ||
joins(:order). | ||
where('spree_orders.distributor_id IN (?) OR spree_products.supplier_id IN (?)', | ||
user.enterprises, user.enterprises). | ||
select('spree_line_items.*') | ||
end | ||
} | ||
|
||
scope :in_orders, lambda { |orders| | ||
where(order_id: orders) | ||
} | ||
|
||
# Find line items that are from order sorted by variant name and unit value | ||
scope :sorted_by_name_and_unit_value, -> { | ||
joins(variant: :product). | ||
reorder(" | ||
lower(spree_products.name) asc, | ||
lower(spree_variants.display_name) asc, | ||
spree_variants.unit_value asc") | ||
} | ||
|
||
scope :from_order_cycle, lambda { |order_cycle| | ||
joins(order: :order_cycle). | ||
where('order_cycles.id = ?', order_cycle) | ||
} | ||
|
||
# Here we are simply joining the line item to its variant and product | ||
# We dont use joins here to avoid the default scopes, | ||
# and with that, include deleted variants and deleted products | ||
scope :supplied_by_any, lambda { |enterprises| | ||
product_ids = Spree::Product.unscoped.where(supplier_id: enterprises).select(:id) | ||
variant_ids = Spree::Variant.unscoped.where(product_id: product_ids).select(:id) | ||
where("spree_line_items.variant_id IN (?)", variant_ids) | ||
} | ||
|
||
scope :with_tax, -> { | ||
joins(:adjustments). | ||
where('spree_adjustments.originator_type = ?', 'Spree::TaxRate'). | ||
select('DISTINCT spree_line_items.*') | ||
} | ||
|
||
# Line items without a Spree::TaxRate-originated adjustment | ||
scope :without_tax, -> { | ||
joins(" | ||
LEFT OUTER JOIN spree_adjustments | ||
ON (spree_adjustments.adjustable_id=spree_line_items.id | ||
AND spree_adjustments.adjustable_type = 'Spree::LineItem' | ||
AND spree_adjustments.originator_type='Spree::TaxRate')"). | ||
where('spree_adjustments.id IS NULL') | ||
} | ||
|
||
def copy_price | ||
return unless variant | ||
|
||
self.price = variant.price if price.nil? | ||
self.cost_price = variant.cost_price if cost_price.nil? | ||
self.currency = variant.currency if currency.nil? | ||
end | ||
|
||
def copy_tax_category | ||
return unless variant | ||
|
||
self.tax_category = variant.product.tax_category | ||
end | ||
|
||
def amount | ||
price * quantity | ||
end | ||
alias total amount | ||
|
||
def single_money | ||
Spree::Money.new(price, currency: currency) | ||
end | ||
alias single_display_amount single_money | ||
|
||
def money | ||
Spree::Money.new(amount, currency: currency) | ||
end | ||
alias display_total money | ||
alias display_amount money | ||
|
||
def adjust_quantity | ||
self.quantity = 0 if quantity.nil? || quantity < 0 | ||
end | ||
|
||
# Here we skip stock check if skip_stock_check flag is active, | ||
# we skip stock check if requested quantity is zero or negative, | ||
# and we scope variants to hub and thus acivate variant overrides. | ||
def sufficient_stock? | ||
return true if skip_stock_check | ||
return true if quantity <= 0 | ||
|
||
scoper.scope(variant) | ||
variant.can_supply?(quantity) | ||
end | ||
|
||
def insufficient_stock? | ||
!sufficient_stock? | ||
end | ||
|
||
def assign_stock_changes_to=(shipment) | ||
@preferred_shipment = shipment | ||
end | ||
|
||
# Remove product default_scope `deleted_at: nil` | ||
def product | ||
variant.product | ||
end | ||
|
||
# This ensures that LineItems always have access to soft-deleted variants. | ||
# In some situations, unscoped super will be nil. In these cases, | ||
# we fetch the variant using variant_id. See issue #4946 for more details. | ||
def variant | ||
Spree::Variant.unscoped { super } || Spree::Variant.unscoped.find(variant_id) | ||
end | ||
|
||
def cap_quantity_at_stock! | ||
scoper.scope(variant) | ||
return if variant.on_demand | ||
|
||
update!(quantity: variant.on_hand) if quantity > variant.on_hand | ||
end | ||
|
||
def has_tax? | ||
adjustments.included_tax.any? | ||
end | ||
|
||
def included_tax | ||
adjustments.included_tax.sum(&:included_tax) | ||
end | ||
|
||
def tax_rates | ||
product.tax_category.andand.tax_rates || [] | ||
end | ||
|
||
def price_with_adjustments | ||
# EnterpriseFee#create_adjustment applies adjustments on line items to their parent order, | ||
# so line_item.adjustments returns an empty array | ||
return 0 if quantity.zero? | ||
|
||
line_item_adjustments = OrderAdjustmentsFetcher.new(order).line_item_adjustments(self) | ||
|
||
(price + line_item_adjustments.sum(&:amount) / quantity).round(2) | ||
end | ||
|
||
def single_display_amount_with_adjustments | ||
Spree::Money.new(price_with_adjustments, currency: currency) | ||
end | ||
|
||
def amount_with_adjustments | ||
# We calculate from price_with_adjustments here rather than building our own value because | ||
# rounding errors can produce discrepencies of $0.01. | ||
price_with_adjustments * quantity | ||
end | ||
|
||
def display_amount_with_adjustments | ||
Spree::Money.new(amount_with_adjustments, currency: currency) | ||
end | ||
|
||
def display_included_tax | ||
Spree::Money.new(included_tax, currency: currency) | ||
end | ||
|
||
def unit_value | ||
return variant.unit_value if quantity == 0 || !final_weight_volume | ||
|
||
final_weight_volume / quantity | ||
end | ||
|
||
def scoper | ||
@scoper ||= OpenFoodNetwork::ScopeVariantToHub.new(order.distributor) | ||
end | ||
|
||
private | ||
|
||
def update_inventory | ||
return unless changed? | ||
|
||
scoper.scope(variant) | ||
Spree::OrderInventory.new(order).verify(self, target_shipment) | ||
end | ||
|
||
def update_order | ||
return unless changed? || destroyed? | ||
|
||
# update the order totals, etc. | ||
order.create_tax_charge! | ||
order.update! | ||
end | ||
|
||
def update_inventory_before_destroy | ||
# This is necessary before destroying the line item | ||
# so that update_inventory will restore stock to the variant | ||
self.quantity = 0 | ||
|
||
update_inventory | ||
|
||
# This is necessary after updating inventory | ||
# because update_inventory may delete the last shipment in the order | ||
# and that makes update_order fail if we don't reload the shipments | ||
order.shipments.reload | ||
end | ||
|
||
def calculate_final_weight_volume | ||
if final_weight_volume.present? && quantity_was > 0 | ||
self.final_weight_volume = final_weight_volume * quantity / quantity_was | ||
elsif variant.andand.unit_value.present? | ||
self.final_weight_volume = variant.andand.unit_value * quantity | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.