diff --git a/app/controllers/admin/invoices_controller.rb b/app/controllers/admin/invoices_controller.rb index c9f1d0c46..bd54ffd0b 100644 --- a/app/controllers/admin/invoices_controller.rb +++ b/app/controllers/admin/invoices_controller.rb @@ -20,6 +20,18 @@ module Admin end end + def cancel_paid + invoice_id = params[:invoice_id] + invoice = Invoice.find(invoice_id) + + if account_activity_with_negative_sum(invoice) + flash[:notice] = t(:payment_was_cancelled) + else + flash[:alert] = t(:failed_to_payment_cancel) + end + redirect_to admin_invoices_path + end + def index @q = Invoice.includes(:account_activity).search(params[:q]) @q.sorts = 'number desc' if @q.sorts.empty? @@ -43,5 +55,21 @@ module Admin def deposit_params params.require(:deposit).permit(:amount, :description, :registrar_id) end + + def account_activity_with_negative_sum(invoice) + account_activity = AccountActivity.find_by(invoice_id: invoice.id) + account_activity_dup = account_activity.dup + account_activity_dup.sum = -account_activity.sum.to_i + account_activity_dup.save + account_activity.update(invoice_id: nil) + account_activity_dup.update(invoice_id: nil) + mark_cancelled_payment_order(invoice) + account_activity.save && account_activity_dup.save + end + + def mark_cancelled_payment_order(invoice) + payment_order = invoice.payment_orders.last + payment_order.update(notes: 'Cancelled') + end end end diff --git a/app/models/bank_transaction.rb b/app/models/bank_transaction.rb index 24bf51e0c..734075ac3 100644 --- a/app/models/bank_transaction.rb +++ b/app/models/bank_transaction.rb @@ -1,5 +1,6 @@ class BankTransaction < ApplicationRecord include Versions + include TransactionPaidInvoices belongs_to :bank_statement has_one :account_activity @@ -17,16 +18,6 @@ class BankTransaction < ApplicationRecord account_activity.invoice end - def invoice - return unless registrar - - @invoice ||= registrar.invoices - .order(created_at: :asc) - .unpaid - .non_cancelled - .find_by(total: sum) - end - def registrar @registrar ||= Invoice.find_by(reference_no: parsed_ref_number)&.buyer end diff --git a/app/models/concerns/transaction_paid_invoices.rb b/app/models/concerns/transaction_paid_invoices.rb new file mode 100644 index 000000000..19d632c1d --- /dev/null +++ b/app/models/concerns/transaction_paid_invoices.rb @@ -0,0 +1,37 @@ +module TransactionPaidInvoices + extend ActiveSupport::Concern + + def invoice + return unless registrar + + @invoice ||= registrar.invoices + .order(created_at: :asc) + .unpaid + .non_cancelled + .find_by(total: sum) + end + + def non_canceled? + paid_invoices = registrar.invoices + .order(created_at: :asc) + .non_cancelled + .where(total: sum) + paid_invoices.any? do |invoice| + return true if invoice.paid? && fresh_admin_paid_invoice(invoice) + end + end + + private + + def fresh_admin_paid_invoice(invoice) + check_for_date_paid_invoice(invoice) && does_invoice_created_by_admin?(invoice) + end + + def check_for_date_paid_invoice(invoice) + invoice.account_activity.created_at > Time.zone.today - 2.days + end + + def does_invoice_created_by_admin?(invoice) + invoice.account_activity.creator_str&.include? 'Admin' + end +end diff --git a/app/views/admin/invoices/show.haml b/app/views/admin/invoices/show.haml index d0450469f..b121c8337 100644 --- a/app/views/admin/invoices/show.haml +++ b/app/views/admin/invoices/show.haml @@ -6,6 +6,10 @@ %h1.text-right.text-center-xs - if @invoice.unpaid? = link_to(t(:payment_received), new_admin_bank_statement_path(invoice_id: @invoice.id), class: 'btn btn-default') + + - if @invoice.paid? and !@invoice.cancelled? + = link_to(t(:cancel_payment), cancel_paid_admin_invoices_path(invoice_id: @invoice.id), method: 'post', data: { confirm: t(:are_you_sure) }, class: 'btn btn-warning') + = link_to(t('.download_btn'), download_admin_invoice_path(@invoice), class: 'btn btn-default') = link_to(t('.deliver_btn'), new_admin_invoice_delivery_path(@invoice), class: 'btn btn-default') - if @invoice.cancellable? diff --git a/config/locales/en.yml b/config/locales/en.yml index 97c968996..1d5a469a2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -293,6 +293,9 @@ en: record_deleted: 'Record deleted' failed_to_delete_record: 'Failed to delete record' + payment_was_cancelled: 'Payment was cancelled' + failed_to_payment_cancel: 'Failed to payment cancel' + authentication_error: 'Authentication error' sign_in_cancelled: "Sign in cancelled" @@ -601,6 +604,7 @@ en: no_transfers_found: 'No transfers found' parameter_value_range_error: 'Parameter value range error: %{key}' payment_received: 'Payment received' + cancel_payment: 'Cancel Payment' api_user_not_found: 'API user not found' notes: Notes active_price_for_this_operation_is: 'Active price for this operation is %{price}' diff --git a/config/routes.rb b/config/routes.rb index 4e78b7c0f..a6a9f5ab3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -231,6 +231,9 @@ Rails.application.routes.draw do end resources :invoices, except: %i[edit update destroy] do + collection do + post ':id/cancel_paid', to: 'invoices#cancel_paid', as: 'cancel_paid' + end resource :delivery, controller: 'invoices/delivery', only: %i[new create] member do diff --git a/lib/tasks/invoices/process_payments.rake b/lib/tasks/invoices/process_payments.rake index edf6609b9..4ce9587f7 100644 --- a/lib/tasks/invoices/process_payments.rake +++ b/lib/tasks/invoices/process_payments.rake @@ -39,9 +39,11 @@ namespace :invoices do reference_no: incoming_transaction.payment_reference_number, description: incoming_transaction.payment_description } transaction = bank_statement.bank_transactions.create!(transaction_attributes) - Invoice.create_from_transaction!(transaction) unless transaction.autobindable? - transaction.autobind_invoice + unless transaction.non_canceled? + Invoice.create_from_transaction!(transaction) unless transaction.autobindable? + transaction.autobind_invoice + end end end else diff --git a/test/integration/admin_area/invoices_test.rb b/test/integration/admin_area/invoices_test.rb index 2aa17201d..01c1a29d7 100644 --- a/test/integration/admin_area/invoices_test.rb +++ b/test/integration/admin_area/invoices_test.rb @@ -4,6 +4,23 @@ class AdminAreaInvoicesIntegrationTest < ApplicationIntegrationTest setup do @invoice = invoices(:one) sign_in users(:admin) + + @account = accounts(:cash) + @registrar = registrars(:bestnames) + end + + def test_cancel_paid_invoice + @invoice.account_activity.update(sum: 10) + assert @invoice.paid? + + assert_equal @registrar.balance, 100 + + assert_no_difference 'Invoice.count' do + assert_difference 'AccountActivity.count' do + post cancel_paid_admin_invoices_path(id: @invoice.id) + "?invoice_id=#{@invoice.id}" + end + end + assert_equal @registrar.balance, 90 end def test_create_new_invoice diff --git a/test/tasks/invoices/process_payments_test.rb b/test/tasks/invoices/process_payments_test.rb index eeaf411cc..29f76ea7c 100644 --- a/test/tasks/invoices/process_payments_test.rb +++ b/test/tasks/invoices/process_payments_test.rb @@ -13,6 +13,9 @@ class ProcessPaymentsTaskTest < ActiveSupport::TestCase total: payment_amount, currency: @payment_currency, reference_no: @payment_reference_number) + @account_activity = account_activities(:one) + @account = accounts(:cash) + Setting.registry_iban = beneficiary_iban Lhv::ConnectApi.class_eval do @@ -29,6 +32,36 @@ class ProcessPaymentsTaskTest < ActiveSupport::TestCase end end + def test_cannot_create_new_invoice_if_transaction_binded_to_paid_invoice + assert_not @invoice.paid? + + @account_activity.update(activity_type: "add_credit", bank_transaction: nil, created_at: Time.zone.today - 1.day, creator_str: 'AdminUser') + @invoice.update(account_activity: @account_activity, total: @payment_amount) + assert @invoice.paid? + + assert_no_difference 'AccountActivity.count' do + assert_no_difference 'Invoice.count' do + assert_no_difference -> {@account.balance} do + capture_io { run_task } + end + end + end + end + + def test_if_invoice_is_overdue_than_48_hours + assert_not @invoice.paid? + + @account_activity.update(activity_type: "add_credit", bank_transaction: nil, created_at: Time.zone.today - 3.days, creator_str: 'AdminUser') + @invoice.update(account_activity: @account_activity, total: @payment_amount) + assert @invoice.paid? + + assert_difference 'AccountActivity.count' do + assert_difference 'Invoice.count' do + capture_io { run_task } + end + end + end + def test_doubles_are_valid assert Lhv::ConnectApi.method_defined?(:credit_debit_notification_messages) assert Lhv::ConnectApi::Messages::CreditDebitNotification.method_defined?(:bank_account_iban)