Class: SpreeCmCommissioner::DraftOrder::Create

Inherits:
Object
  • Object
show all
Includes:
Spree::ServiceModule::Base
Defined in:
app/services/spree_cm_commissioner/draft_order/create.rb

Instance Method Summary collapse

Instance Method Details

#build_line_item!(order, store, line_item_params) ⇒ Object

Builds (unsaved) an order.line_items member — persisted together with the order via order.save's cascading autosave of new has_many records, same as TransitOrder::Create.



84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'app/services/spree_cm_commissioner/draft_order/create.rb', line 84

def build_line_item!(order, store, line_item_params)
  variant = store.variants.find(line_item_params[:variant_id])
  options = (line_item_params[:options] || {}).to_h.with_indifferent_access
  permitted_opts = ::Spree::PermittedAttributes.line_item_attributes.flatten.index_with { |attribute| options[attribute] }
  opts = permitted_opts.merge(currency: order.currency).compact

  order.line_items.new(
    variant: variant,
    quantity: line_item_params[:quantity],
    options: opts,
    public_metadata: (line_item_params[:public_metadata] || {}).to_h,
    private_metadata: (line_item_params[:private_metadata] || {}).to_h
  )
end

#call(line_items:, user: nil, store: nil, currency: nil, tenant: nil, options: {}) ⇒ Object

rubocop:disable Metrics/ParameterLists



30
31
32
33
34
35
36
37
# File 'app/services/spree_cm_commissioner/draft_order/create.rb', line 30

def call(line_items:, user: nil, store: nil, currency: nil, tenant: nil, options: {}) # rubocop:disable Metrics/ParameterLists
  return failure(nil, I18n.t('spree_cm_commissioner.draft_order.line_items_missing')) if line_items.blank?

  order = create_order!(line_items, user, store, currency, tenant, options)
  success(order: order)
rescue StandardError => e
  failure(nil, e.message)
end

#create_order!(line_items_params, user, store, currency, tenant, options) ⇒ Object

rubocop:disable Metrics/ParameterLists



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'app/services/spree_cm_commissioner/draft_order/create.rb', line 39

def create_order!(line_items_params, user, store, currency, tenant, options) # rubocop:disable Metrics/ParameterLists
  store ||= Spree::Store.default
  currency ||= store&.default_currency
  order = Spree::Order.new(
    state: 'cart',
    use_billing: true,
    user: user,
    store: store,
    currency: currency,
    tenant: tenant,
    device_id: options[:device_id],
    last_ip_address: options[:last_ip_address]
  )
  line_items_params.each { |line_item_params| build_line_item!(order, store, line_item_params) }

  ActiveRecord::Base.transaction do
    raise StandardError, order_error_message(order) unless order.save

    # Same as the regular add-to-cart path (Spree::Cart::Recalculate): activate eligible
    # promotions first and persist their totals, then (re)compute tax on the now-discounted
    # base — both batched once for the whole order instead of relying on LineItem's per-item
    # tax callbacks. The interim update_with_updater! is required: TaxRate.adjust computes
    # each item's taxable base off its `taxable_adjustment_total` column, which only gets
    # (re)persisted by OrderUpdater#recalculate_adjustments — without it tax would still be
    # computed pre-discount. The reload is required too: recalculate_adjustments persists
    # those columns onto adjustable objects it fetches itself (via all_adjustments), not onto
    # the LineItem instances already loaded in order.line_items, which create_tax_charge! reads.
    ::Spree::PromotionHandler::Cart.new(order).activate
    order.update_with_updater!
    order.line_items.reload
    order.create_tax_charge!

    order.update_with_updater!

    # Build blank guests per line item so the client can render guest fields offline.
    order.line_items.each(&:generate_remaining_guests)

    raise StandardError, order_error_message(order) if order.has_checkout_step?('address') && !order.next
  end

  order
end

#order_error_message(order) ⇒ Object

order.errors already includes line item errors (accepts_nested_attributes_for autosaves them in). Skip full_message's attribute prefix when the validator already wrote a full sentence, e.g. before: "Quantity Not enough stock" -> after: "Not enough stock".



102
103
104
105
106
# File 'app/services/spree_cm_commissioner/draft_order/create.rb', line 102

def order_error_message(order)
  order.errors.map do |error|
    error.raw_type.is_a?(String) || error.options[:message].is_a?(String) ? error.message : error.full_message
  end.uniq.to_sentence
end