Skip to content
14 changes: 0 additions & 14 deletions app/javascript/controllers/strip_prefix_controller.js

This file was deleted.

28 changes: 28 additions & 0 deletions app/models/concerns/has_wise_recipient.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ module HasWiseRecipient
include CountryEnumable
has_country_enum(field: :recipient_country)

before_validation(:normalize_wise_recipient_id)
validates(:wise_recipient_id, format: { with: WISE_RECIPIENT_ID_FORMAT, message: "is not a valid Wise recipient ID" }, allow_nil: true)

validate do
if POSTAL_CODE_FORMATS[recipient_country&.to_sym] && !address_postal_code.match(POSTAL_CODE_FORMATS[recipient_country.to_sym])
errors.add(:address_postal_code, "does not meet the required format for this country")
Expand Down Expand Up @@ -147,6 +150,28 @@ def unsupported_account_type_reason
HasWiseRecipient.unsupported_account_type_reason(account_type)
end

def wise_recipient_url
return nil unless wise_recipient_id.present?

"https://wise.com/recipients/#{CGI.escape(wise_recipient_id)}"
end

# Admins paste the recipient's Wise URL (e.g. https://wise.com/recipients/<uuid>?new=undefined),
# so keep only the UUID
def normalize_wise_recipient_id
Comment thread
mattsoh marked this conversation as resolved.
Resolved
return unless wise_recipient_id_changed?

normalized = wise_recipient_id&.strip.presence

if normalized.nil? || normalized.match?(WISE_RECIPIENT_ID_FORMAT)
self.wise_recipient_id = normalized
return
end

match = WISE_RECIPIENT_ID_REGEXP.match(normalized)
self.wise_recipient_id = match[1] if match
end

# Postal code formats sourced from https://column.com/docs/international-wires/country-specific-details

POSTAL_CODE_FORMATS = {
Expand Down Expand Up @@ -175,6 +200,9 @@ def self.unsupported_account_type_reason(account_type)

ACCOUNT_NUMBER_FIELD = { type: :text_field, key: "account_number", placeholder: "123456789", label: "Account number" }.freeze

WISE_RECIPIENT_ID_FORMAT = /\A[\h-]{36}\z/
WISE_RECIPIENT_ID_REGEXP = /wise\.com\/recipients\/([\h-]{36})/

AVAILABLE_CURRENCIES = %w[AED ARS AUD BGN BRL CAD CHF CLP CNY COP CZK DKK EGP EUR GBP GEL HKD HUF IDR ILS JPY KES KRW LKR MAD MXN MYR NGN NOK NPR NZD PHP PKR PLN RON SEK SGD THB TND TRY UAH UYU VND ZAR].freeze

end
25 changes: 0 additions & 25 deletions app/models/wise_transfer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,6 @@ class WiseTransfer < ApplicationRecord
validates(:wise_id, format: { with: WISE_ID_FORMAT, message: "is not a valid Wise ID" }, allow_nil: true)
validates(:wise_id, presence: true, if: :processed?)

WISE_RECIPIENT_ID_FORMAT = /\A[\h-]{36}\z/
before_validation(:normalize_wise_recipient_id)
validates(:wise_recipient_id, format: { with: WISE_RECIPIENT_ID_FORMAT, message: "is not a valid Wise recipient ID" }, allow_nil: true)
validates(:wise_recipient_id, presence: true, if: :processed?)

after_create do
Expand Down Expand Up @@ -277,12 +274,6 @@ def wise_url
"https://wise.com/transactions/activities/by-resource/TRANSFER/#{CGI.escape(wise_id)}"
end

def wise_recipient_url
return nil unless wise_recipient_id.present?

"https://wise.com/recipients/#{CGI.escape(wise_recipient_id)}"
end

def can_cancel?
pending?
end
Expand Down Expand Up @@ -318,20 +309,4 @@ def normalize_wise_id
end
end

WISE_RECIPIENT_ID_REGEXP = /wise\.com\/recipients\/([\h-]{36})/

def normalize_wise_recipient_id
return unless wise_recipient_id_changed? && wise_recipient_id.present?

normalized = wise_recipient_id.strip

if normalized =~ WISE_RECIPIENT_ID_FORMAT
self.wise_recipient_id = normalized
return
end

match = WISE_RECIPIENT_ID_REGEXP.match(normalized)
self.wise_recipient_id = match[1] if match
end

end
21 changes: 21 additions & 0 deletions app/tasks/maintenance/normalize_wise_recipient_ids_task.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

module Maintenance
# One-time cleanup: reimbursement approvals used to save the pasted Wise recipient URL
# (e.g. https://wise.com/recipients/<uuid>?new=undefined) onto the payout method as-is,
# which broke the "Open <name> on Wise" button. Reduces every stored value to the bare
# UUID, matching what `HasWiseRecipient#normalize_wise_recipient_id` does now.
# Run once after deploying.
class NormalizeWiseRecipientIdsTask < MaintenanceTasks::Task
def collection
LegalEntity::PayoutMethod::WiseTransfer.where.not(wise_recipient_id: nil).where.not("wise_recipient_id ~* ?", "^[0-9a-f-]{36}$")
end

def process(payout_method)
payout_method.wise_recipient_id_will_change!
payout_method.normalize_wise_recipient_id
payout_method.update_columns(wise_recipient_id: payout_method.wise_recipient_id) if payout_method.wise_recipient_id_changed?
end

end
end
2 changes: 1 addition & 1 deletion app/views/admin/wise_transfer_process.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@
<%= form_with(model: @wise_transfer, local: true, url: mark_sent_wise_transfer_path(@wise_transfer), method: :post) do |form| %>
<div class="mb-2">
<%= form.label :wise_id, "Wise ID / URL", class: "bold mb1" %> <br>
<%= form.text_field :wise_id, class: "w-[400px]", placeholder: "https://wise.com/transactions/activities/by-resource/TRANSFER/XXXXXXXX", disabled: @wise_transfer.usd_amount_cents.nil?, data: { controller: "strip-prefix", action: "paste->strip-prefix#paste", strip_prefix_prefix_value: "https://wise.com/transactions/activities/by-resource/TRANSFER/" } %>
<%= form.text_field :wise_id, class: "w-[400px]", placeholder: "https://wise.com/transactions/activities/by-resource/TRANSFER/XXXXXXXX", disabled: @wise_transfer.usd_amount_cents.nil? %>
</div>
<%= form.submit "💸 Mark transfer as sent", disabled: @wise_transfer.usd_amount_cents.nil?, class: "mb-0" %>
<% end %>
Expand Down
4 changes: 2 additions & 2 deletions app/views/reimbursement/reports/_actions.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,8 @@
</strong>
</div>

<% if payout_method.wise_recipient_id.present? %>
<%= link_to "https://wise.com/send#?contact=#{payout_method.wise_recipient_id}", class: "btn btn-small tooltipped tooltipped--w bg-[#9FE870] text-[#163300] flex-shrink-0", style: "color: #163300!important; background: #9FE870", target: "_blank", "aria-label": "Use recipient information from previous transfer" do %>
<% if payout_method.wise_recipient_url %>
<%= link_to payout_method.wise_recipient_url, class: "btn btn-small tooltipped tooltipped--w bg-[#9FE870] text-[#163300] flex-shrink-0", style: "color: #163300!important; background: #9FE870", target: "_blank", "aria-label": "Use recipient information from previous transfer" do %>
<%= inline_icon "wise" %>
Open <%= report.user.full_name %> on Wise
<% end %>
Expand Down
2 changes: 1 addition & 1 deletion app/views/reimbursement/reports/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@

<div class="field">
<%= form.label :wise_id, "Wise ID / URL", class: "bold mb1" %>
<%= form.text_field :wise_id, class: "mt-0 !max-w-full", style: "width: 400px;", placeholder: "https://wise.com/transactions/activities/by-resource/TRANSFER/XXXXXXXX", data: { controller: "strip-prefix", action: "paste->strip-prefix#paste", strip_prefix_prefix_value: "https://wise.com/transactions/activities/by-resource/TRANSFER/" } %>
<%= form.text_field :wise_id, class: "mt-0 !max-w-full", style: "width: 400px;", placeholder: "https://wise.com/transactions/activities/by-resource/TRANSFER/XXXXXXXX" %>
</div>
<div class="field">
<%= form.label(:wise_recipient_id, "Wise recipient ID", class: "bold mb1") %>
Expand Down
5 changes: 5 additions & 0 deletions spec/models/wise_transfer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ def build_instance(**attrs)
instance.validate
expect(instance.errors[:wise_id]).to be_empty
expect(instance.wise_id).to eq("0987654321")

instance.wise_id = "https://wise.com/transactions/activities/by-resource/TRANSFER/1122334455?origin=activity-list"
instance.validate
expect(instance.errors[:wise_id]).to be_empty
expect(instance.wise_id).to eq("1122334455")
end
end

Expand Down
16 changes: 16 additions & 0 deletions spec/tasks/maintenance/normalize_wise_recipient_ids_task_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

require "rails_helper"

RSpec.describe Maintenance::NormalizeWiseRecipientIdsTask do
it "reduces a stored recipient URL to its UUID" do
payout_method = LegalEntity::PayoutMethod::WiseTransfer.new
payout_method.wise_recipient_id = "https://wise.com/recipients/86b41d15-1024-47ee-9e05-1891a912e450?new=undefined"
payout_method.clear_changes_information
allow(payout_method).to receive(:update_columns)

described_class.process(payout_method)

expect(payout_method).to have_received(:update_columns).with(wise_recipient_id: "86b41d15-1024-47ee-9e05-1891a912e450")
end
end
Loading