"""Tests for account logic.""" import datetime from unittest.mock import patch from abacus_common_logic.utils.dates import safe_format_datetime from marshmallow import ValidationError from owsresponse import response import pytest from abacus_account.constants import constants from abacus_account.constants import error from abacus_account.logic import account as logic from tests.utils.factories import AccountFactory from tests.utils.factories import AccountPayeeFactory from tests.utils.factories import AccountPaymentTermFactory from tests.utils.factories import AccountPaymentTermTemplateFactory from tests.utils.factories import AccountTaxInfoFactory from tests.utils.factories import PaymentHoldFactory @pytest.mark.parametrize( 'country_of_tax_residence,omit_country_of_tax_residence', (('USA', False), (None, False), (None, False)) ) @patch('abacus_account.logic.account.models') @patch('abacus_account.logic.account.create_account_payee') @patch('abacus_account.logic.account' '.create_payment_term_from_template_by_currency_code') @patch('abacus_account.logic.account.create_account_tax_info') @patch('abacus_account.logic.account._validate_account_creation') def test_create_account_success( mock_validation, mock_create_account_tax_info, mock_create_payment_term, mock_create_account_payee, mock_models, country_of_tax_residence, omit_country_of_tax_residence, reference_payment_entity_fixture ): """Test success response of create_account method.""" mock_account = AccountFactory.create() mock_account_payee = AccountPayeeFactory.create(account=mock_account) mock_template = AccountPaymentTermTemplateFactory.create() mock_account_payment_term = AccountPaymentTermFactory.create(account=mock_account) mock_account_tax_info = AccountTaxInfoFactory.create( account=mock_account, country_of_tax_residence=country_of_tax_residence ) mock_validation.return_value = None mock_models.Account.create.return_value = mock_account mock_create_account_payee.return_value = response.Response( message=mock_account_payee, status=201 ) mock_create_payment_term.return_value = mock_account_payment_term mock_create_account_tax_info.return_value = response.Response( message=mock_account_tax_info, status=201 ) post_data = { 'account_id': mock_account.account_id, 'account_name': mock_account.account_name, 'currency_code': mock_template.currency_code, 'created_by': mock_account.created_by, } if not omit_country_of_tax_residence: post_data[ 'country_of_tax_residence' ] = mock_account_tax_info.country_of_tax_residence res = logic.create_account(**post_data) assert res.status == 201 assert res.message == { 'account_id': mock_account.account_id, 'account_name': mock_account.account_name, 'account_payee_id': mock_account_payee.account_payee_id, 'account_payment_term_id': mock_account_payment_term.account_payment_term_id, 'created_by': mock_account.created_by, 'sap_created_at': None } mock_validation.assert_called_once_with( post_data.get('account_id'), post_data.get('currency_code'), post_data.get('country_of_tax_residence') ) mock_models.Account.create.assert_called_once_with( account_id=post_data['account_id'], account_name=post_data['account_name'], created_by=post_data['created_by'] ) mock_create_account_payee.assert_called_once_with( account_id=post_data['account_id'] ) mock_create_payment_term.assert_called_once_with( post_data['currency_code'], post_data['account_id'] ) mock_create_account_tax_info.assert_called_once_with( account_id=post_data['account_id'], country_of_tax_residence=post_data.get('country_of_tax_residence') ) @pytest.mark.parametrize( 'country_of_tax_residence,omit_country_of_tax_residence', (('USA', False), (None, False), (None, False)) ) @patch('abacus_account.logic.account.models') @patch('abacus_account.logic.account.create_account_payee') @patch('abacus_account.logic.account' '.create_payment_term_from_template_by_currency_code') @patch('abacus_account.logic.account' '.create_account_payment_term') @patch('abacus_account.logic.account.create_account_tax_info') @patch('abacus_account.logic.account._validate_account_creation') def test_create_account_skip_payment_term_template_creation_success( mock_validation, mock_create_account_tax_info, mock_create_payment_term, mock_create_payment_term_from_template, mock_create_account_payee, mock_models, country_of_tax_residence, omit_country_of_tax_residence, reference_payment_entity_fixture ): """Test success response of create_account method.""" mock_account = AccountFactory.create() mock_account_payee = AccountPayeeFactory.create(account=mock_account) mock_template = AccountPaymentTermTemplateFactory.create() mock_account_tax_info = AccountTaxInfoFactory.create( account=mock_account, country_of_tax_residence=country_of_tax_residence ) mock_validation.return_value = None mock_models.Account.create.return_value = mock_account mock_create_account_payee.return_value = response.Response( message=mock_account_payee, status=201 ) mock_create_account_tax_info.return_value = response.Response( message=mock_account_tax_info, status=201 ) post_data = { 'account_id': mock_account.account_id, 'account_name': mock_account.account_name, 'currency_code': mock_template.currency_code, 'created_by': mock_account.created_by, 'creation_source': constants.SKIP_PAYMENT_TERM_TEMPLATE_CREATION_SOURCES[0] } if not omit_country_of_tax_residence: post_data[ 'country_of_tax_residence' ] = mock_account_tax_info.country_of_tax_residence res = logic.create_account(**post_data) assert res.status == 201 assert res.message == { 'account_id': mock_account.account_id, 'account_name': mock_account.account_name, 'account_payee_id': mock_account_payee.account_payee_id, 'account_payment_term_id': None, 'created_by': mock_account.created_by, 'sap_created_at': None } mock_validation.assert_called_once_with( post_data.get('account_id'), post_data.get('currency_code'), post_data.get('country_of_tax_residence') ) mock_models.Account.create.assert_called_once_with( account_id=post_data['account_id'], account_name=post_data['account_name'], created_by=post_data['created_by'] ) mock_create_account_payee.assert_called_once_with( account_id=post_data['account_id'] ) mock_create_payment_term_from_template.assert_not_called() mock_create_payment_term.assert_called_once_with( account_id=post_data['account_id'], currency_code=post_data.get('currency_code'), ) mock_create_account_tax_info.assert_called_once_with( account_id=post_data['account_id'], country_of_tax_residence=post_data.get('country_of_tax_residence') ) @patch('abacus_account.logic.account.models') @patch('abacus_account.logic.account.create_account_payee') @patch('abacus_account.logic.account' '.create_payment_term_from_template_by_currency_code') @patch('abacus_account.logic.account.create_account_tax_info') @patch('abacus_account.logic.account._validate_account_creation') def test_create_account_error( mock_validation, mock_create_account_tax_info, mock_create_payment_term, mock_create_account_payee, mock_models, reference_payment_entity_fixture ): """Test error message is returned when creation data is invalid.""" mock_account = AccountFactory.create() mock_validation.side_effect = ValidationError('You shall not pass!') post_data = { 'account_id': mock_account.account_id, 'account_name': mock_account.account_name } res = logic.create_account(**post_data) assert res.status == 400 assert res.errors mock_models.Account.create.assert_not_called() mock_create_account_payee.assert_not_called() mock_create_payment_term.assert_not_called() mock_create_account_tax_info.assert_not_called() mock_validation.assert_called_once_with(post_data.get('account_id'), None, None) @patch('abacus_account.logic.account.models') def test_get_eligible_accounts_for_group_id_when_ff_enabled( mock_models ): """Test for get_eligible_accounts method.""" mock_account = AccountFactory.create() mock_models.Account.get_eligible_for_payment.return_value = [mock_account] response = logic.get_eligible_accounts_for_group_id(1) assert response.status == 200 mock_models.Account.get_eligible_for_payment.assert_called_once_with(1) @patch('abacus_account.logic.account.models') def test_get_payment_eligibility_status_no_payment_hold(mock_models): """Test get_payment_eligibility_status method when there is no payment_hold.""" account = AccountFactory.create() mock_models.Account.get_by_id_or_error.return_value = account res = logic.get_payment_eligibility_status(account.account_id) assert res.status == 200 assert res.message['eligibility_status'] == constants.ELIGIBILITY_STATUSES.ACTIVE @patch('abacus_account.logic.account.models') def test_get_payment_eligibility_status_on_hold(mock_models): """Test get_payment_eligibility_status method when there is a payment hold.""" payment_hold = PaymentHoldFactory.create() mock_models.Account.get_by_id_or_error.return_value = payment_hold.account res = logic.get_payment_eligibility_status(payment_hold.account_id) assert res.status == 200 assert res.message['eligibility_status'] == constants.ELIGIBILITY_STATUSES.ON_HOLD @patch('abacus_account.logic.account.models') def test_get_payment_eligibility_status_removed_hold(mock_models): """Test get_payment_eligibility_status method when payment hold has been removed.""" payment_hold = PaymentHoldFactory.create(is_on_hold=False) mock_models.Account.get_by_id_or_error.return_value = payment_hold.account res = logic.get_payment_eligibility_status(payment_hold.account_id) assert res.status == 200 assert res.message['eligibility_status'] == constants.ELIGIBILITY_STATUSES.ACTIVE @patch('abacus_account.logic.account.models') def test_get_payment_eligibility_status_future_hold(mock_models): """Test get_payment_eligibility_status method when hold has not taken affect yet.""" payment_hold = PaymentHoldFactory.create(is_on_hold=True, start_date='2099-01-01') mock_models.Account.get_by_id_or_error.return_value = payment_hold.account res = logic.get_payment_eligibility_status(payment_hold.account_id) assert res.status == 200 assert res.message['eligibility_status'] == constants.ELIGIBILITY_STATUSES.ACTIVE @patch('abacus_account.logic.account.models') def test_get_payment_eligibility_status_future_remove_hold(mock_models): """Test get_payment_eligibility_status method when remove hold's start date is future date.""" # noqa: #501 payment_hold = PaymentHoldFactory.create(is_on_hold=False, start_date='2099-01-01') mock_models.Account.get_by_id_or_error.return_value = payment_hold.account res = logic.get_payment_eligibility_status(payment_hold.account_id) assert res.status == 200 assert res.message['eligibility_status'] == constants.ELIGIBILITY_STATUSES.ON_HOLD @patch('abacus_account.logic.account.models') @patch('abacus_account.logic.account.validate_country_code') @patch('abacus_account.logic.account.get_payment_term_template_by_currency_code') def test_validate_account_creation_valid( mock_get_payment_term_template, mock_validate_country_code, mock_models ): """Test validate_account_creation returns nothing when data is valid.""" template = AccountPaymentTermTemplateFactory.create() currency_code = template.currency_code country_code = 'USA' account_id = 123 mock_models.Account.get_by_id.return_value = None mock_get_payment_term_template.return_value = template mock_validate_country_code.return_value = None res = logic._validate_account_creation(account_id, currency_code, country_code) assert res is None mock_models.Account.get_by_id.assert_called_once_with(account_id) mock_get_payment_term_template.assert_called_once_with(currency_code) mock_validate_country_code.assert_called_once_with(country_code) @patch('abacus_account.logic.account.models') @patch('abacus_account.logic.account.validate_country_code') def test_validate_account_creation_invalid_account( mock_validate_country_code, mock_models ): """Test error message is returned when account already exists.""" account = AccountFactory.create() mock_models.Account.get_by_id.return_value = account with pytest.raises(ValidationError): logic._validate_account_creation(account.account_id) mock_models.Account.get_by_id.assert_called_once_with(account.account_id) mock_models.AccountPaymentTermTemplate.get_by_id_or_error.assert_not_called() mock_validate_country_code.assert_not_called() @patch('abacus_account.logic.account.models') @patch('abacus_account.logic.account.validate_country_code') def test_validate_account_creation_invalid_template( mock_validate_country_code, mock_models ): """Test currency_code is validated.""" account_id = 1 currency_code = 'AOA' mock_models.Account.get_by_id.return_value = None with pytest.raises( ValidationError, match=error.ERROR_PAYMENT_TERM_TEMPLATE_DOES_NOT_EXISTS.format( currency_code=currency_code ) ): logic._validate_account_creation(account_id, currency_code) mock_models.Account.get_by_id.assert_called_once_with(account_id) mock_validate_country_code.assert_not_called() @patch('abacus_account.logic.account.models') @patch('abacus_account.logic.account.validate_country_code') @patch('abacus_account.logic.account.get_payment_term_template_by_currency_code') def test_validate_account_creation_invalid_country_code( mock_get_payment_term_template, mock_validate_country_code, mock_models ): """Test country_code is validated.""" template = AccountPaymentTermTemplateFactory.create() currency_code = template.currency_code country_code = 'BUG' account_id = 123 mock_models.Account.get_by_id.return_value = None mock_get_payment_term_template.return_value = template logic._validate_account_creation(account_id, currency_code, country_code) mock_models.Account.get_by_id.assert_called_once_with(account_id) mock_get_payment_term_template.assert_called_once_with(currency_code) mock_validate_country_code.assert_called_once_with(country_code) @patch('abacus_account.logic.account.models') def test_get_sap_formatted_account_info(mock_models): """Test get_sap_formatted_account_info function.""" mock_account = AccountFactory.create() mock_models.Account.get_by_id_or_error.return_value = mock_account response = logic.get_sap_formatted_account_info(mock_account.account_id) assert response.status == 200 assert response.message == { 'AccountId': str(mock_account.account_id), 'AcctName': mock_account.account_name, 'Kunnr': None, 'Lifnr': None, 'Zzfield1': None, 'Zzfield2': None } @patch('abacus_account.logic.account.models') def test_update_account(mock_model): """Test update_account function.""" mock_account = AccountFactory.create() sap_created_at_datetime = datetime.date(2022, 2, 1) put_body = { 'sap_created_at': sap_created_at_datetime, 'account_name': 'Test account' } res = logic.update_account(mock_account, **put_body) assert res.status == 200 assert res.message == { 'account_id': mock_account.account_id, 'account_name': put_body['account_name'], 'account_payee_id': None, 'account_payment_term_id': None, 'created_by': mock_account.created_by, 'sap_created_at': safe_format_datetime(sap_created_at_datetime) } mock_model.Account.commit_changes.assert_called_once()