# frozen_string_literal: true # Responsible for ensuring that any updates to a Subscription are propagated to any # orders belonging to that Subscription which have been instantiated module Orders class SyncService attr_reader :order_update_issues def initialize(subscription) @subscription = subscription @order_update_issues = Orders::UpdateIssuesService.new @line_item_syncer = LineItemSyncer.new(subscription, order_update_issues) end def sync! orders_in_order_cycles_not_closed.all? do |order| order.assign_attributes(customer_id:, email: customer&.email, distributor_id: shop_id) update_associations_for(order) line_item_syncer.sync!(order) order.update_order! order.save end end private attr_reader :subscription, :line_item_syncer delegate :orders, :bill_address, :ship_address, :subscription_line_items, to: :subscription delegate :shop_id, :customer, :customer_id, to: :subscription delegate :shipping_method, :shipping_method_id, :payment_method, :payment_method_id, to: :subscription delegate :shipping_method_id_changed?, :shipping_method_id_was, to: :subscription delegate :payment_method_id_changed?, :payment_method_id_was, to: :subscription def update_associations_for(order) update_bill_address_for(order) if bill_address.changes.keys.intersect?(relevant_address_attrs) update_shipment_for(order) if shipping_method_id_changed? update_ship_address_for(order) update_payment_for(order) if payment_method_id_changed? end def orders_in_order_cycles_not_closed return @orders_in_order_cycles_not_closed unless @orders_in_order_cycles_not_closed.nil? @orders_in_order_cycles_not_closed = orders.joins(:order_cycle). merge(OrderCycle.not_closed).readonly(false) end def update_bill_address_for(order) unless addresses_match?(order.bill_address, bill_address) return order_update_issues.add(order, I18n.t('bill_address')) end order.bill_address.update(bill_address.attributes.slice(*relevant_address_attrs)) end def update_payment_for(order) payment = order.payments. with_state('checkout').where(payment_method_id: payment_method_id_was).last if payment payment&.void_transaction! order.payments.create(payment_method_id:, amount: order.reload.total) else unless order.payments.with_state('checkout').where(payment_method_id:).any? order_update_issues.add(order, I18n.t('admin.payment_method')) end end end def update_shipment_for(order) return if pending_shipment_with?(order, shipping_method_id) # No need to do anything. if pending_shipment_with?(order, shipping_method_id_was) order.select_shipping_method(shipping_method_id) else order_update_issues.add(order, I18n.t('admin.shipping_method')) end end def update_ship_address_for(order) # The conditions here are to achieve the same behaviour in earlier versions of Spree, where # switching from pick-up to delivery affects whether simultaneous changes to shipping address # are ignored or not. pickup_to_delivery = force_ship_address_required?(order) if (!pickup_to_delivery || order.shipment.present?) && ship_address.changes.keys.intersect?(relevant_address_attrs) save_ship_address_in_order(order) end return unless !pickup_to_delivery || order.shipment.blank? order.updater.shipping_address_from_distributor end def relevant_address_attrs ["firstname", "lastname", "address1", "zipcode", "city", "state_id", "country_id", "phone"] end def addresses_match?(order_address, subscription_address) relevant_address_attrs.all? do |attr| order_address[attr] == subscription_address.public_send("#{attr}_was") || order_address[attr] == subscription_address[attr] end end def ship_address_updatable?(order) return true if force_ship_address_required?(order) return false unless order.shipping_method.require_ship_address? return true if addresses_match?(order.ship_address, ship_address) order_update_issues.add(order, I18n.t('ship_address')) false end # This returns true when the shipping method on the subscription has changed # to a delivery (ie. a shipping address is required) AND the existing shipping # address on the order matches the shop's address def force_ship_address_required?(order) return false unless shipping_method.require_ship_address? distributor_address = order.address_from_distributor relevant_address_attrs.all? do |attr| order.ship_address[attr] == distributor_address[attr] end end def save_ship_address_in_order(order) return unless ship_address_updatable?(order) order.ship_address.update(ship_address.attributes.slice(*relevant_address_attrs)) end def pending_shipment_with?(order, shipping_method_id) return false unless order.shipment.present? && order.shipment.state == "pending" order.shipping_method.id == shipping_method_id end end end