from sqlalchemy import func, select from payment.api import db """Payment Group Payment functional tests.""" from payment.constants.constants import VALID_PAYMENT_BATCH_STATUSES from payment.constants.error import ERROR_INVALID_PAYMENT_BATCH_STATUS from payment.models.payment_group_payment_account import PaymentGroupPaymentAccount from payment.models.payment_group_payment_account_detail import ( PaymentGroupPaymentAccountDetail, ) from payment.models.report_payment_group_payment import ReportPaymentGroupPayment from tests.utils.factories import ( PaymentGroupFactory, PaymentGroupPaymentAccountDetailFactory, PaymentGroupPaymentAccountFactory, PaymentGroupPaymentBatchFactory, PaymentGroupPaymentFactory, ReportPaymentGroupPaymentFactory, WorksheetAccountContractClosingBalanceFactory, WorksheetPayableBalanceAfterTaxFactory, ) def test_create_payment_group_payment_success(fixture_client): """Successful creation of a payment group payment.""" payment_group = PaymentGroupFactory.create() post_body = { 'payment_group_id': payment_group.payment_group_id, 'payment_name': 'Payment Group Payment Name', } res = fixture_client.post('/payment-group-payment/', json=post_body) assert res.status_code == 201 assert res.json['payment_group_id'] == payment_group.payment_group_id assert res.json['payment_group_payment_id'] assert res.json['payment_name'] == post_body['payment_name'] def test_get_payment_group_payment(fixture_client): """Test getting specified payment group payment details.""" pgp = PaymentGroupPaymentFactory.create() res = fixture_client.get(f'/payment-group-payment/{pgp.payment_group_payment_id}') assert res.status_code == 200 assert res.json['payment_group_id'] == pgp.payment_group_id assert res.json['payment_group_payment_id'] == pgp.payment_group_payment_id assert res.json['payment_name'] == pgp.payment_name def test_get_payment_group_payments_list(fixture_client): """Test GET /payment-group-payment endpoint.""" payment_group_payments = PaymentGroupPaymentFactory.create_batch(5) limit = 3 res = fixture_client.get(f'/payment-group-payment?limit={limit}&offset=0') assert res.status_code == 200 items = res.json.get('items') assert len(items) == limit assert res.json.get('total_count') == len(payment_group_payments) assert all( payment_group_payment.get('created_at') for payment_group_payment in items ) assert all( payment_group_payment.get('payment_group_payment_id') for payment_group_payment in items ) assert all( payment_group_payment.get('payment_group_id') for payment_group_payment in items ) assert all( payment_group_payment.get('payment_name') for payment_group_payment in items ) assert all( 'account_count' in payment_group_payment for payment_group_payment in items ) def test_delete_payment_group_payment(mock_accounts, fixture_client): """Test DELETE /payment-group-payment endpoint.""" payment_group_payment = PaymentGroupPaymentFactory.create() payment_group_payment_id = payment_group_payment.payment_group_payment_id payment_payee = PaymentGroupPaymentAccountFactory.create( account_id=1, payment_group_payment=payment_group_payment ) payment_account = PaymentGroupPaymentAccountFactory.create( payment_group_payment=payment_group_payment, account_id=2 ) res = fixture_client.delete(f'/payment-group-payment/{payment_group_payment_id}') assert res.status_code == 204 assert payment_group_payment.deleted_at assert payment_group_payment.deleted_by assert payment_payee.deleted_at assert payment_payee.deleted_by assert payment_account.deleted_at assert payment_account.deleted_by def test_get_payment_totals_grouped_by_program_id( mock_accounts, mock_signing_entities, mock_account_payment_terms, fixture_client ): """Test getting payment totals grouped by program_id.""" account_amounts = { 1: {'amount': 100, 'program_id': 100158970}, 2: {'amount': 200, 'program_id': 100158970}, 3: {'amount': 300, 'program_id': 100158970}, 4: {'amount': 400, 'program_id': 1001}, } payment_group_payment = PaymentGroupPaymentFactory.create() for account_id in account_amounts: PaymentGroupPaymentAccountFactory.create( account_id=account_id, balance_after_tax=account_amounts[account_id]['amount'], payment_group_payment=payment_group_payment, payoneer_program_id=account_amounts[account_id]['program_id'], ) payment_group_payment_id = payment_group_payment.payment_group_payment_id result = fixture_client.get( f'/payment-group-payment/{payment_group_payment_id}/program-currency-totals' ) assert result.status_code == 200 data = result.json assert data == [ { 'payoneer_program_id': 100158970, 'account_count': 3, 'currency_total': '600.00', 'payment_entity_name': 'AWAL-UK', 'currency_code': 'USD', }, { 'payoneer_program_id': 1001, 'account_count': 1, 'currency_total': '400.00', 'payment_entity_name': 'AWAL-UK', 'currency_code': 'USD', }, ] def test_get_failed_payment_group_payment_batches( fixture_client, mock_reference_payoneer_program, create_mock_payment_state ): """Test to get a list of payment batches that have failed while sending payment to Payoneer.""" # noqa: E501 payment_group_payment = PaymentGroupPaymentFactory.create() payment_group_payment_id = payment_group_payment.payment_group_payment_id payment_batch_status = 'failed' [ PaymentGroupPaymentBatchFactory.create( payment_group_payment_batch_id=i, batch_num=1, payment_group_payment=payment_group_payment, payoneer_program_id=100158970, ) for i in [1, 3] ] res = fixture_client.get( f'/payment-group-payment/{payment_group_payment_id}/payment-batches/{payment_batch_status}' # noqa: E501 ) assert res.status_code == 200 assert len(res.json) == 1 assert res.json == [ { 'payment_group_payment_batch_id': 1, 'payment_group_payment_id': 1, 'payoneer_program_id': 100158970, 'batch_num': 1, 'abacus_state_id': 19, 'action_status': 'error', 'error_message': 'Batch Failed', } ] def test_get_successful_payment_group_payment_batches( fixture_client, mock_reference_payoneer_program, create_mock_payment_state ): """Test to get a list of payment batches that have been successfully sent to payoneer.""" # noqa: E501 payment_group_payment = PaymentGroupPaymentFactory.create() payment_group_payment_id = payment_group_payment.payment_group_payment_id payment_batch_status = 'success' [ PaymentGroupPaymentBatchFactory.create( payment_group_payment_batch_id=i, batch_num=1, payment_group_payment=payment_group_payment, payoneer_program_id=100158970, ) for i in range(1, 4) ] res = fixture_client.get( f'/payment-group-payment/{payment_group_payment_id}/payment-batches/{payment_batch_status}' # noqa: E501 ) assert res.status_code == 200 assert len(res.json) == 2 assert res.json == [ { 'payment_group_payment_batch_id': 2, 'payment_group_payment_id': 1, 'payoneer_program_id': 100158970, 'batch_num': 1, 'abacus_state_id': 20, 'action_status': 'complete', 'error_message': None, }, { 'payment_group_payment_batch_id': 3, 'payment_group_payment_id': 1, 'payoneer_program_id': 100158970, 'batch_num': 1, 'abacus_state_id': 21, 'action_status': 'complete', 'error_message': None, }, ] def test_get_all_payment_group_payment_batches( fixture_client, mock_reference_payoneer_program, create_mock_payment_state ): """Test to get all payment batches for a payment_group_payment_id.""" payment_group_payment = PaymentGroupPaymentFactory.create() payment_group_payment_id = payment_group_payment.payment_group_payment_id [ PaymentGroupPaymentBatchFactory.create( payment_group_payment_batch_id=i, batch_num=1, payment_group_payment=payment_group_payment, payoneer_program_id=100158970, ) for i in range(1, 4) ] res = fixture_client.get( f'/payment-group-payment/{payment_group_payment_id}/payment-batches/' ) assert res.status_code == 200 assert len(res.json) == 3 assert res.json == [ { 'payment_group_payment_batch_id': 1, 'payment_group_payment_id': 1, 'payoneer_program_id': 100158970, 'batch_num': 1, 'abacus_state_id': 19, 'action_status': 'error', 'error_message': 'Batch Failed', }, { 'payment_group_payment_batch_id': 2, 'payment_group_payment_id': 1, 'payoneer_program_id': 100158970, 'batch_num': 1, 'abacus_state_id': 20, 'action_status': 'complete', 'error_message': None, }, { 'payment_group_payment_batch_id': 3, 'payment_group_payment_id': 1, 'payoneer_program_id': 100158970, 'batch_num': 1, 'abacus_state_id': 21, 'action_status': 'complete', 'error_message': None, }, ] def test_get_batches_invalid_batch_status( fixture_client, mock_reference_payoneer_program, create_mock_payment_state ): """Test to get a list of payment batches for an invalid payment batch status.""" payment_group_payment = PaymentGroupPaymentFactory.create() payment_group_payment_id = payment_group_payment.payment_group_payment_id res = fixture_client.get( f'/payment-group-payment/{payment_group_payment_id}/payment-batches/testing' ) assert res.status_code == 400 assert res.json['message'] == ERROR_INVALID_PAYMENT_BATCH_STATUS.format( batch_status=', '.join(VALID_PAYMENT_BATCH_STATUSES) ) def test_get_payment_status_overview(fixture_client, mock_payments): """Test to get the payment status overview for a payment_group_payment.""" payment_group_payment_id = 501 res = fixture_client.get( f'/payment-group-payment/{payment_group_payment_id}/payment-status-overview/' ) assert res.status_code == 200 assert res.json == { 'number_of_canceled_payments': 1, 'number_of_failed_payments': 1, 'number_of_pending_payments': 2, 'number_of_successful_payments': 1, 'number_of_payments_failed_with_batch': 2, 'payment_group_payment_id': 501, } def test_get_payment_status_overview_no_group(fixture_client, mock_payments): """Test to get the payment status overview for a payment_group_payment with no group.""" payment_group_payment_id = 99999999999999 res = fixture_client.get( f'/payment-group-payment/{payment_group_payment_id}/payment-status-overview/' ) assert res.status_code == 200 assert res.json == {} def test_irrevocable_revert_payment_group_payment( mock_accounts, mock_contracts, mock_abacus_event, mock_ledger_account_contracts, fixture_client, ): """Test irrevocable revert endpoint hard deletes related data.""" payment_group_payment = PaymentGroupPaymentFactory.create() payment_group_payment_id = payment_group_payment.payment_group_payment_id payment_account1 = PaymentGroupPaymentAccountFactory.create( account_id=1, payment_group_payment=payment_group_payment ) payment_account2 = PaymentGroupPaymentAccountFactory.create( account_id=2, payment_group_payment=payment_group_payment ) worksheet_closing_balance = WorksheetAccountContractClosingBalanceFactory.create() worksheet_after_tax = WorksheetPayableBalanceAfterTaxFactory.create( worksheet_account_contract_closing_balance_id=worksheet_closing_balance.worksheet_account_contract_closing_balance_id ) detail1 = PaymentGroupPaymentAccountDetailFactory.create( payment_group_payment_account=payment_account1, worksheet_account_contract_payable_after_tax_id=worksheet_after_tax.worksheet_account_contract_payable_after_tax_id, ) detail2 = PaymentGroupPaymentAccountDetailFactory.create( payment_group_payment_account=payment_account2, worksheet_account_contract_payable_after_tax_id=worksheet_after_tax.worksheet_account_contract_payable_after_tax_id, ) report = ReportPaymentGroupPaymentFactory.create( payment_group_payment=payment_group_payment ) payment_account1_id = payment_account1.payment_group_payment_account_id payment_account2_id = payment_account2.payment_group_payment_account_id detail1_id = detail1.payment_group_payment_account_detail_id detail2_id = detail2.payment_group_payment_account_detail_id report_id = report.report_payment_group_payment_id assert ( db.session.execute( select(func.count()).select_from( select(PaymentGroupPaymentAccount) .where( PaymentGroupPaymentAccount.payment_group_payment_account_id.in_( [payment_account1_id, payment_account2_id] ) ) .subquery() ) ).scalar_one() == 2 ) assert ( db.session.execute( select(func.count()).select_from( select(PaymentGroupPaymentAccountDetail) .where( PaymentGroupPaymentAccountDetail.payment_group_payment_account_detail_id.in_( [detail1_id, detail2_id] ) ) .subquery() ) ).scalar_one() == 2 ) assert ( db.session.execute( select(func.count()).select_from( select(ReportPaymentGroupPayment) .where( ReportPaymentGroupPayment.report_payment_group_payment_id == report_id ) .subquery() ) ).scalar_one() == 1 ) res = fixture_client.delete( f'/payment-group-payment/{payment_group_payment_id}/irrevocable_revert' ) assert res.status_code == 204 assert ( db.session.execute( select(func.count()).select_from( select(PaymentGroupPaymentAccount) .where( PaymentGroupPaymentAccount.payment_group_payment_account_id.in_( [payment_account1_id, payment_account2_id] ) ) .subquery() ) ).scalar_one() == 0 ) assert ( db.session.execute( select(func.count()).select_from( select(PaymentGroupPaymentAccountDetail) .where( PaymentGroupPaymentAccountDetail.payment_group_payment_account_detail_id.in_( [detail1_id, detail2_id] ) ) .subquery() ) ).scalar_one() == 0 ) assert ( db.session.execute( select(func.count()).select_from( select(ReportPaymentGroupPayment) .where( ReportPaymentGroupPayment.report_payment_group_payment_id == report_id ) .subquery() ) ).scalar_one() == 0 )