sharetribe/sharetribe

View on GitHub
app/helpers/transaction_helper.rb

Summary

Maintainability
D
2 days
Test Coverage
module TransactionHelper

  def icon_for(status)
    case status
    when "confirmed"
      "ss-check"
    when "rejected"
      "ss-delete"
    when "canceled"
      "ss-delete"
    when "accepted"
      "ss-check"
    when "paid"
      "ss-check"
    when "preauthorized"
      "ss-check"
    when "pending_ext"
      "ss-alert"
    when "accept_preauthorized"
      "ss-check"
    when "reject_preauthorized"
      "ss-delete"
    when "errored"
      "ss-delete"
    end
  end

  # Give `status`, `is_author` and `other_party` and get back icon and text for current status
  # rubocop:disable all
  def conversation_icon_and_status(status, is_author, other_party_name, waiting_feedback, status_meta)
    icon_waiting_you = icon_tag("alert", ["icon-fix-rel", "waiting-you"])
    icon_waiting_other = icon_tag("clock", ["icon-fix-rel", "waiting-other"])

    # Split "confirmed" status into "waiting_feedback" and "completed"
    status = if waiting_feedback
      "waiting_feedback"
    else
      "completed"
    end if status == "confirmed"

    status_hash = {
      pending: ->() {
        ActiveSupport::Deprecation.warn("Transaction state 'pending' is deprecated and will be removed in the future.")
        {
          author: {
            icon: icon_waiting_you,
            text: t("conversations.status.waiting_for_you_to_accept_request")
          },
          starter: {
            icon: icon_waiting_other,
            text: t("conversations.status.waiting_for_listing_author_to_accept_request", listing_author_name: other_party_name)
          }
        } },
      preauthorized: ->() { {
        author: {
          icon: icon_waiting_you,
          text: t("conversations.status.waiting_for_you_to_accept_request")
        },
        starter: {
          icon: icon_waiting_other,
          text: t("conversations.status.waiting_for_listing_author_to_accept_request", listing_author_name: other_party_name)
        }
      } },
      accepted: ->() {
        ActiveSupport::Deprecation.warn("Transaction state 'accepted' is deprecated and will be removed in the future.")
          {
          author: {
            icon: icon_waiting_other,
            text: t("conversations.status.waiting_payment_from_requester", requester_name: other_party_name)
          },
          starter: {
            icon: icon_waiting_you,
            text: t("conversations.status.waiting_payment_from_you")
          }
        } },
      pending_ext: ->() {
        case status_meta[:paypal_pending_reason]
        when "multicurrency"
          {
            author: {
              icon: icon_waiting_you,
              # Make this aware of the reason
              text: t("conversations.status.pending_external_inbox.paypal.multicurrency")
            },
            starter: {
              icon: icon_waiting_other,
              text: t("conversations.status.waiting_for_listing_author_to_accept_request", listing_author_name: other_party_name)
            }
          }
        when "intl"
          {
            author: {
              icon: icon_waiting_you,
              # Make this aware of the reason
              text: t("conversations.status.pending_external_inbox.paypal.intl")
            },
            starter: {
              icon: icon_waiting_other,
              text: t("conversations.status.waiting_for_listing_author_to_accept_request", listing_author_name: other_party_name)
            }
          }
        when "verify"
          {
            author: {
              icon: icon_waiting_you,
              # Make this aware of the reason
              text: t("conversations.status.pending_external_inbox.paypal.verify")
            },
            starter: {
              icon: icon_waiting_other,
              text: t("conversations.status.waiting_for_listing_author_to_accept_request", listing_author_name: other_party_name)
            }
          }
        else # some pending_ext reason we don't have special message for
          {
            author: {
              icon: icon_waiting_you,
              text: t("conversations.status.pending_external_inbox.paypal.unknown_reason")
            },
            starter: {
              icon: icon_waiting_other,
              text: t("conversations.status.waiting_for_listing_author_to_accept_request", listing_author_name: other_party_name)
            }
          }
        end
      },

      rejected: -> () { {
        both: {
          icon: icon_tag("cross", ["icon-fix-rel", "rejected"]),
          text: t("conversations.status.request_rejected")
        }
      } },

      paid: ->() { {
        author: {
          icon: icon_waiting_other,
          text: t("conversations.status.waiting_confirmation_from_requester", requester_name: other_party_name)
        },
        starter: {
          icon: icon_waiting_you,
          text: t("conversations.status.waiting_confirmation_from_you")
        }
      } },

      waiting_feedback: ->() { {
        both: {
          icon: icon_waiting_you,
          text: t("conversations.status.waiting_feedback_from_you")
        }
      } },

      completed: ->() { {
        both: {
          icon: icon_tag("check", ["icon-fix-rel", "confirmed"]),
          text: t("conversations.status.request_confirmed")
        }
      } },

      canceled: ->() { {
        both: {
          icon: icon_tag("cross", ["icon-fix-rel", "canceled"]),
          text: t("conversations.status.request_canceled")
        }
      } },

      disputed: ->() { {
        both: {
          icon: icon_tag("clock", ["icon-fix-rel", "canceled"]),
          text: t("conversations.status.waiting_for_marketplace_review")
        }
      } },

      errored: ->() { {
        both: {
          icon: icon_tag("cross", ["icon-fix-rel", "canceled"]),
          text: t("conversations.status.payment_errored")
         }
      } },

      refunded: ->() { {
        both: {
          icon: icon_tag("refund", ["icon-fix-rel", "confirmed"]),
          text: t("conversations.status.refunded")
         }
      } },

      dismissed: ->() { {
        both: {
          icon: icon_tag("dismiss", ["icon-fix-rel", "confirmed"]),
          text: t("conversations.status.dismissed")
         }
      } },
    }

    Maybe(status_hash)[status.to_sym]
      .map { |s| s.call }
      .map { |s| Maybe(is_author ? s[:author] : s[:starter]).or_else { s[:both] } }
      .values
      .get
  end
  # rubocop:enable all

  #
  # Returns statuses in Hash format
  # statuses = [
  #   {
  #     type: :status_info,
  #     content: {
  #       info_text_part: 'msg',
  #       info_icon_tag: ''               # e.g. icon_tag("testimonial", ["icon-with-text"])
  #     }
  #   },
  #   {
  #     type: :status_info,
  #     content: {
  #       info_text_part: 'msg',
  #       info_icon_part_classes: 'class1 class2'
  #     }
  #   },
  #   {
  #     type: :status_links,
  #     content: [{
  #         link_href: "/path/to/somewhere" # url
  #         link_classes: '[if-any]'
  #         link_data: {},                  # e.g. {:method => "put", :remote => "true"}
  #         link_icon_tag: '[some-tag]>',   # OR link_icon_with_text_classes: icon_for("accepted")
  #         link_text_with_icon: 'Something'
  #       },
  #       {}
  #     ]
  #   }
  # }
  # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
  def get_conversation_statuses(conversation, is_author)
    statuses = if conversation.listing && !conversation.status.eql?("free")
      status_hash = {
        paid: -> { {
          both: [
            status_info(t("conversations.status.request_paid"), icon_classes: icon_for("paid")),
            delivery_status(conversation),
            paid_status(conversation)
          ]
        }
              },
        preauthorized: -> { {
          both: [
            status_info(t("conversations.status.request_preauthorized"), icon_classes: icon_for("preauthorized")),
            preauthorized_status(conversation)
          ]
        }
                       },
        pending_ext: -> {
          ## This is so wrong place to call services...
          #TODO Deprecated call, update to use PaypalService::API:Api.payments.get_payment
          paypal_payment = PaypalService::Store::PaypalPayment.for_transaction(conversation.id)

          reason = Maybe(conversation).transaction_transitions.last.metadata["paypal_pending_reason"]

          case reason
          when Some("multicurrency")
            {
              author: [
                status_info(t("conversations.status.pending_external.paypal.multicurrency", currency: paypal_payment[:payment_total].currency, paypal_url: link_to("https://www.paypal.com", "https://www.paypal.com")).html_safe, icon_classes: icon_for("pending_ext"))
              ],
              starter: [
                status_info(t("conversations.status.request_preauthorized"), icon_classes: icon_for("preauthorized")),
                preauthorized_status(conversation)
              ]
            }
          when Some("intl")
            {
              author: [
                status_info(t("conversations.status.pending_external.paypal.intl", paypal_url: link_to("https://www.paypal.com", "https://www.paypal.com")).html_safe, icon_classes: icon_for("pending_ext"))
              ],
              starter: [
                status_info(t("conversations.status.request_preauthorized"), icon_classes: icon_for("preauthorized")),
                preauthorized_status(conversation)
              ]
            }
          when Some("verify")
            {
              author: [
                status_info(t("conversations.status.pending_external.paypal.verify", paypal_url: link_to("https://www.paypal.com", "https://www.paypal.com")).html_safe, icon_classes: icon_for("pending_ext"))
              ],
              starter: [
                status_info(t("conversations.status.request_preauthorized"), icon_classes: icon_for("preauthorized")),
                preauthorized_status(conversation)
              ]
            }
          end
        },
        confirmed: -> { {
          both: [
            status_info(t("conversations.status.request_confirmed"), icon_classes: icon_for("confirmed")),
            feedback_status(conversation)
          ]
        }
                   },
        canceled: -> {
          {
            both: [
              status_info(t("conversations.status.request_canceled"), icon_classes: icon_for("canceled")),
              feedback_status(conversation)
            ]
          }
                  },
        disputed: -> {
          contact_link = link_to t("conversations.status.contact_them"), new_user_feedback_path
          {
            both: [
              status_info(t("conversations.status.order_canceled"), icon_classes: icon_for("canceled")),
              status_info(t("conversations.status.waiting_for_marketplace_review"), icon_classes: 'ss-clock'),
              status_info(t("conversations.status.marketplace_notified", contact_link: contact_link).html_safe, icon_classes: 'ss-mail'),
            ]
          }
                  },
        rejected: -> { {
          both: [
            status_info(t("conversations.status.request_rejected"), icon_classes: icon_for(conversation.status))
          ]
        }
                  },
        errored: -> { {
          author: [
            status_info(t("conversations.status.payment_errored_author", starter_name: conversation.starter.name(conversation.community)), icon_classes: icon_for("errored"))
          ],
          starter: [
            status_info(t("conversations.status.payment_errored_starter"), icon_classes: icon_for("errored"))
          ]
        }
                 },
        refunded: -> {
          contact_link = link_to t("conversations.status.contact_the_marketpalce_team"), new_user_feedback_path
          {
          author: [
            status_info(t("conversations.status.order_refunded"), icon_classes: icon_class('refund')),
            status_info(t('conversations.status.do_you_disagree', contact_link: contact_link).html_safe, icon_classes: icon_class('mail')),
            feedback_status(conversation)
          ],
          starter: [
            status_info("#{t('conversations.status.order_refunded')} #{t('conversations.status.you_should_receive_refund_soon')}", icon_classes: icon_class('refund')),
            status_info(t('conversations.status.issue_with_the_refund', contact_link: contact_link).html_safe, icon_classes: icon_class('mail')),
            feedback_status(conversation)

          ]
          }
                  },
        dismissed: -> {
          contact_link = link_to t("conversations.status.contact_the_marketpalce_team"), new_user_feedback_path
          {
          author: [
            status_info(t("conversations.status.order_cancellation_dismissed"), icon_classes: icon_class('dismiss')),
            status_info(t('conversations.status.do_you_disagree', contact_link: contact_link).html_safe, icon_classes: icon_class('mail')),
            feedback_status(conversation)
          ],
          starter: [
            status_info(t("conversations.status.order_cancellation_dismissed"), icon_classes: icon_class('dismiss')),
            status_info(t('conversations.status.do_you_disagree', contact_link: contact_link).html_safe, icon_classes: icon_class('mail')),
            feedback_status(conversation)
          ]
          }
                   }
      }

      Maybe(status_hash)[conversation.status.to_sym]
        .map { |s| s.call }
        .map { |s| Maybe(is_author ? s[:author] : s[:starter]).or_else { s[:both] } }
        .or_else([])
    else
      []
    end

    statuses.flatten.compact
  end
  # rubocop:enable Metrics/MethodLength, Metrics/AbcSize

  private

  def paid_status(conversation)
    if conversation.seller == @current_user
      waiting_for_buyer_to_confirm(conversation)
    else
      waiting_for_current_user_to_confirm(conversation)
    end
  end

  def delivery_status(conversation)
    if current_user?(conversation.author)
      status_info(
        t("conversations.status.waiting_for_current_user_to_deliver_listing",
          :listing_title => link_to(conversation.listing.title, conversation.listing)
        ).html_safe,
        icon_classes: "ss-clockwise"
      )
    else
      status_info(
        t("conversations.status.waiting_for_listing_author_to_deliver_listing",
          :listing_title => link_to(conversation.listing.title, conversation.listing),
          :listing_author_name => link_to(PersonViewUtils.person_display_name(conversation.author, conversation.community))
        ).html_safe,
        icon_classes: "ss-clockwise"
      )
    end
  end

  def preauthorized_status(transaction)
    if current_user?(transaction.listing.author)
      waiting_for_current_user_to_accept_preauthorized(transaction)
    else
      waiting_for_author_to_accept_preauthorized(transaction)
    end
  end

  def feedback_status(conversation)
    if conversation.has_feedback_from?(@current_user)
      feedback_given_status
    elsif conversation.feedback_skipped_by?(@current_user)
      feedback_skipped_status
    else
      feedback_pending_status(conversation)
    end
  end

  def waiting_for_current_user_to_confirm(conversation)
    status_links([
      {
        link_href: confirm_person_message_path(@current_user, :id => conversation.id),
        link_classes: "confirm",
        link_icon_with_text_classes: icon_for("confirmed"),
        link_text_with_icon: link_text_with_icon(conversation, "confirm")
      },
      {
        link_href: cancel_person_message_path(@current_user, :id => conversation.id),
        link_classes: "cancel",
        link_icon_with_text_classes: icon_for("canceled"),
        link_text_with_icon: link_text_with_icon(conversation, "cancel")
      }
    ])
  end

  def waiting_for_current_user_to_accept_preauthorized(transaction)
    status_links([
      {
        link_href: accept_preauthorized_person_message_path(@current_user, :id => transaction.id),
        link_classes: "accept_preauthorized",
        link_icon_with_text_classes: icon_for("accept_preauthorized"),
        link_text_with_icon: link_text_with_icon(transaction, "accept_preauthorized")
      },
      {
        link_href: reject_preauthorized_person_message_path(@current_user, :id => transaction.id),
        link_classes: "reject_preauthorized",
        link_icon_with_text_classes: icon_for("reject_preauthorized"),
        link_text_with_icon: link_text_with_icon(transaction, "reject_preauthorized")
      }
    ])
  end

  def feedback_pending_status(conversation)
    status_links([
      {
        link_href: new_person_message_feedback_path(@current_user, :message_id => conversation.id),
        link_classes: "accept",
        link_icon_tag: icon_tag("testimonial", ["icon-with-text"]),
        link_text_with_icon: t("conversations.status.give_feedback")
      },
      {
        link_href: skip_person_message_feedbacks_path(@current_user, :message_id => conversation.id),
        link_classes: "cancel",
        link_icon_with_text_classes: "ss-skipforward",
        link_text_with_icon: t("conversations.status.skip_feedback"),
        link_data: { :method => "put", :remote => "true"}
      }
    ])
  end

  def status_links(content)
    {
      type: :status_links,
      content: content
    }
  end

  def link_text_with_icon(conversation, status_link_name)
    if ["accept", "reject", "accept_preauthorized", "reject_preauthorized"].include?(status_link_name)
      t("conversations.status_link.#{status_link_name}_request")
    else
      t("conversations.status_link.#{status_link_name}")
    end
  end

  def waiting_for_buyer_to_confirm(conversation)
    key = conversation.payment_gateway == :stripe ? "conversations.status.stripe.waiting_confirmation_from_requester" : "conversations.status.waiting_confirmation_from_requester"
    link = t(key,
      :requester_name => link_to(
        PersonViewUtils.person_display_name(conversation.other_party(@current_user), conversation.community),
        conversation.other_party(@current_user)
      )
    ).html_safe

    status_info(link, icon_classes: 'ss-clock')
  end

  def waiting_for_author_to_accept_preauthorized(transaction)
    text = t("conversations.status.waiting_for_listing_author_to_accept_request",
      listing_author_name: link_to(
        PersonViewUtils.person_display_name(transaction.author, transaction.community),
        transaction.author
      )
    ).html_safe

    status_info(text, icon_classes: 'ss-clock')
  end

  def feedback_given_status
    status_info(t("conversations.status.feedback_given"), icon_tag: icon_tag("testimonial", ["icon-part"]))
  end

  def feedback_skipped_status
    status_info(t("conversations.status.feedback_skipped"), icon_classes: "ss-skipforward")
  end

  def status_info(text, icon_tag: nil, icon_classes: nil)
    hash = {
      type: :status_info,
      content: {
        info_text_part: text
      }
    }

    if icon_tag
      hash.deep_merge(content: {info_icon_tag: icon_tag})
    elsif icon_classes
      hash.deep_merge(content: {info_icon_part_classes: icon_classes})
    else
      hash
    end
  end
end