"""WorksheetPaymentCustom delete functional tests.""" from decimal import Decimal from abacus_common_logic.connectors.database import db from tests.conftest import _exec_sql from tests.utils.factories import WorksheetPaymentCustomFactory def test_worksheet_payment_custom_delete_success( fixture_client, mock_accounts, mock_contracts, mock_statement_periods ): """Test successful deletion of WorksheetPaymentCustom that has not been sent.""" worksheet = WorksheetPaymentCustomFactory.create( account_id=1, contract_id=1, currency_code='USD', amount=Decimal('100.00'), payment_name='test payment to delete', ) worksheet_id = worksheet.worksheet_payment_custom_id # Verify the record exists before deletion res = fixture_client.get(f'/payment/custom/{worksheet_id}') assert res.status_code == 200 # Delete the record res = fixture_client.delete(f'/payment/custom/{worksheet_id}') assert res.status_code == 204 assert res.data == b'' # Verify the record is soft deleted (404 when trying to get) res = fixture_client.get(f'/payment/custom/{worksheet_id}') assert res.status_code == 404 def test_worksheet_payment_custom_delete_with_init_status( fixture_client, mock_accounts, mock_contracts, mock_statement_periods ): """Test deletion succeeds when payment status is 'init'.""" worksheet = WorksheetPaymentCustomFactory.create( account_id=1, contract_id=1, currency_code='USD', amount=Decimal('100.00'), payment_name='test payment init', ) worksheet_id = worksheet.worksheet_payment_custom_id # Create abacus_state with 'init' status SQL_QUERY = f""" INSERT INTO abacus_state( `parent_table_name`, `parent_table_id`, `action_name`, `action_status`, `message`, `created_by`, `created_at`, `last_modified_by`, `last_modified` ) VALUES ('worksheet_payment_custom', {worksheet_id}, 'send_payments', 'init', NULL, '2', NOW(), '2', NOW()) """ _exec_sql(SQL_QUERY) db.session.commit() # Delete should succeed with 'init' status res = fixture_client.delete(f'/payment/custom/{worksheet_id}') assert res.status_code == 204 def test_worksheet_payment_custom_delete_with_error_status( fixture_client, mock_accounts, mock_contracts, mock_statement_periods ): """Test deletion succeeds when payment status is 'error'.""" worksheet = WorksheetPaymentCustomFactory.create( account_id=1, contract_id=1, currency_code='USD', amount=Decimal('100.00'), payment_name='test payment error', ) worksheet_id = worksheet.worksheet_payment_custom_id # Create abacus_state with 'error' status SQL_QUERY = f""" INSERT INTO abacus_state( `parent_table_name`, `parent_table_id`, `action_name`, `action_status`, `message`, `created_by`, `created_at`, `last_modified_by`, `last_modified` ) VALUES ('worksheet_payment_custom', {worksheet_id}, 'send_payments', 'error', 'Payment failed', '2', NOW(), '2', NOW()) """ _exec_sql(SQL_QUERY) db.session.commit() # Delete should succeed with 'error' status res = fixture_client.delete(f'/payment/custom/{worksheet_id}') assert res.status_code == 204 def test_worksheet_payment_custom_delete_with_rejected_status( fixture_client, mock_accounts, mock_contracts, mock_statement_periods ): """Test deletion succeeds when payment status is 'rejected'.""" worksheet = WorksheetPaymentCustomFactory.create( account_id=1, contract_id=1, currency_code='USD', amount=Decimal('100.00'), payment_name='test payment rejected', ) worksheet_id = worksheet.worksheet_payment_custom_id # Create abacus_state with 'rejected' status SQL_QUERY = f""" INSERT INTO abacus_state( `parent_table_name`, `parent_table_id`, `action_name`, `action_status`, `message`, `created_by`, `created_at`, `last_modified_by`, `last_modified` ) VALUES ('worksheet_payment_custom', {worksheet_id}, 'send_payments', 'rejected', 'Payment rejected', '2', NOW(), '2', NOW()) """ _exec_sql(SQL_QUERY) db.session.commit() # Delete should succeed with 'rejected' status res = fixture_client.delete(f'/payment/custom/{worksheet_id}') assert res.status_code == 204 def test_worksheet_payment_custom_delete_fails_when_complete( fixture_client, mock_accounts, mock_contracts, mock_statement_periods ): """Test deletion fails when payment status is 'complete'.""" worksheet = WorksheetPaymentCustomFactory.create( account_id=1, contract_id=1, currency_code='USD', amount=Decimal('100.00'), payment_name='test payment complete', ) worksheet_id = worksheet.worksheet_payment_custom_id # Create abacus_state with 'complete' status (payment sent) SQL_QUERY = f""" INSERT INTO abacus_state( `parent_table_name`, `parent_table_id`, `action_name`, `action_status`, `message`, `created_by`, `created_at`, `last_modified_by`, `last_modified` ) VALUES ('worksheet_payment_custom', {worksheet_id}, 'send_payments', 'complete', NULL, '2', NOW(), '2', NOW()) """ _exec_sql(SQL_QUERY) db.session.commit() # Delete should fail res = fixture_client.delete(f'/payment/custom/{worksheet_id}') assert res.status_code == 400 # Verify the record still exists res = fixture_client.get(f'/payment/custom/{worksheet_id}') assert res.status_code == 200 def test_worksheet_payment_custom_delete_fails_when_running( fixture_client, mock_accounts, mock_contracts, mock_statement_periods ): """Test deletion fails when payment status is 'running'.""" worksheet = WorksheetPaymentCustomFactory.create( account_id=1, contract_id=1, currency_code='USD', amount=Decimal('100.00'), payment_name='test payment running', ) worksheet_id = worksheet.worksheet_payment_custom_id # Create abacus_state with 'running' status (payment in progress) SQL_QUERY = f""" INSERT INTO abacus_state( `parent_table_name`, `parent_table_id`, `action_name`, `action_status`, `message`, `created_by`, `created_at`, `last_modified_by`, `last_modified` ) VALUES ('worksheet_payment_custom', {worksheet_id}, 'send_payments', 'running', NULL, '2', NOW(), '2', NOW()) """ _exec_sql(SQL_QUERY) db.session.commit() # Delete should fail res = fixture_client.delete(f'/payment/custom/{worksheet_id}') assert res.status_code == 400 # Verify the record still exists res = fixture_client.get(f'/payment/custom/{worksheet_id}') assert res.status_code == 200 def test_worksheet_payment_custom_delete_nonexistent_id( fixture_client, mock_accounts, mock_contracts, mock_statement_periods ): """Test deletion fails with 404 for non-existent ID.""" nonexistent_id = 99999 res = fixture_client.delete(f'/payment/custom/{nonexistent_id}') assert res.status_code == 404 def test_worksheet_payment_custom_delete_already_deleted( fixture_client, mock_accounts, mock_contracts, mock_statement_periods ): """Test deletion fails with 404 for already deleted record.""" worksheet = WorksheetPaymentCustomFactory.create( account_id=1, contract_id=1, currency_code='USD', amount=Decimal('100.00'), payment_name='test payment', ) worksheet_id = worksheet.worksheet_payment_custom_id # Delete the record first time res = fixture_client.delete(f'/payment/custom/{worksheet_id}') assert res.status_code == 204 # Try to delete again res = fixture_client.delete(f'/payment/custom/{worksheet_id}') assert res.status_code == 404 def test_worksheet_payment_custom_delete_multiple_records( fixture_client, mock_accounts, mock_contracts, mock_statement_periods ): """Test deleting multiple WorksheetPaymentCustom records independently.""" worksheet1 = WorksheetPaymentCustomFactory.create( account_id=1, contract_id=1, currency_code='USD', amount=Decimal('100.00'), payment_name='test payment 1', ) worksheet2 = WorksheetPaymentCustomFactory.create( account_id=2, contract_id=2, currency_code='EUR', amount=Decimal('200.00'), payment_name='test payment 2', ) worksheet3 = WorksheetPaymentCustomFactory.create( account_id=3, contract_id=3, currency_code='GBP', amount=Decimal('300.00'), payment_name='test payment 3', ) # Delete first record res = fixture_client.delete( f'/payment/custom/{worksheet1.worksheet_payment_custom_id}' ) assert res.status_code == 204 # Delete third record res = fixture_client.delete( f'/payment/custom/{worksheet3.worksheet_payment_custom_id}' ) assert res.status_code == 204 # Verify first and third are deleted res = fixture_client.get( f'/payment/custom/{worksheet1.worksheet_payment_custom_id}' ) assert res.status_code == 404 res = fixture_client.get( f'/payment/custom/{worksheet3.worksheet_payment_custom_id}' ) assert res.status_code == 404 # Verify second still exists res = fixture_client.get( f'/payment/custom/{worksheet2.worksheet_payment_custom_id}' ) assert res.status_code == 200 assert res.json['payment_name'] == 'test payment 2' def test_worksheet_payment_custom_delete_not_in_bulk_list( fixture_client, mock_accounts, mock_contracts, mock_statement_periods ): """Test deleted records do not appear in bulk list endpoint.""" worksheet1 = WorksheetPaymentCustomFactory.create( account_id=1, contract_id=1, currency_code='USD', amount=Decimal('100.00'), payment_name='test payment 1', ) worksheet2 = WorksheetPaymentCustomFactory.create( account_id=2, contract_id=2, currency_code='EUR', amount=Decimal('200.00'), payment_name='test payment 2', ) # Delete first record res = fixture_client.delete( f'/payment/custom/{worksheet1.worksheet_payment_custom_id}' ) assert res.status_code == 204 # Get bulk list res = fixture_client.post('/payments/custom/bulk', json={'filters': {}}) assert res.status_code == 200 assert res.json['total_count'] == 1 assert len(res.json['items']) == 1 assert res.json['items'][0]['payment_name'] == 'test payment 2' def test_worksheet_payment_custom_delete_with_different_amounts( fixture_client, mock_accounts, mock_contracts, mock_statement_periods ): """Test deletion works for records with various amount configurations.""" # Record with negative WHT and positive VAT worksheet1 = WorksheetPaymentCustomFactory.create( account_id=1, contract_id=1, currency_code='USD', amount=Decimal('100.00'), withholding_tax_amount=Decimal('-10.00'), vat_amount=Decimal('20.00'), amount_after_withholding_and_vat=Decimal('110.00'), payment_name='test payment 1', ) # Record with null optional fields worksheet2 = WorksheetPaymentCustomFactory.create( account_id=2, contract_id=2, currency_code='EUR', amount=Decimal('200.00'), withholding_tax_amount=Decimal('0'), vat_amount=Decimal('0'), amount_after_withholding_and_vat=Decimal('200.00'), payment_name='test payment 2', ) # Delete both res = fixture_client.delete( f'/payment/custom/{worksheet1.worksheet_payment_custom_id}' ) assert res.status_code == 204 res = fixture_client.delete( f'/payment/custom/{worksheet2.worksheet_payment_custom_id}' ) assert res.status_code == 204 # Verify both are deleted res = fixture_client.get( f'/payment/custom/{worksheet1.worksheet_payment_custom_id}' ) assert res.status_code == 404 res = fixture_client.get( f'/payment/custom/{worksheet2.worksheet_payment_custom_id}' ) assert res.status_code == 404 def test_worksheet_payment_custom_delete_with_different_currencies( fixture_client, mock_accounts, mock_contracts, mock_statement_periods ): """Test deletion works for records with different currencies.""" currencies = ['USD', 'EUR', 'GBP', 'JPY', 'CAD'] created_ids = [] for idx, currency in enumerate(currencies, start=1): worksheet = WorksheetPaymentCustomFactory.create( account_id=idx, contract_id=idx, currency_code=currency, amount=Decimal('100.00'), payment_name=f'test payment {currency}', ) created_ids.append(worksheet.worksheet_payment_custom_id) # Delete all records for worksheet_id in created_ids: res = fixture_client.delete(f'/payment/custom/{worksheet_id}') assert res.status_code == 204 # Verify all are deleted for worksheet_id in created_ids: res = fixture_client.get(f'/payment/custom/{worksheet_id}') assert res.status_code == 404 def test_worksheet_payment_custom_delete_invalid_id_format( fixture_client, mock_accounts, mock_contracts, mock_statement_periods ): """Test deletion with invalid ID format.""" # Test with string instead of integer res = fixture_client.delete('/payment/custom/invalid') assert res.status_code == 404 def test_worksheet_payment_custom_delete_negative_id( fixture_client, mock_accounts, mock_contracts, mock_statement_periods ): """Test deletion with negative ID.""" res = fixture_client.delete('/payment/custom/-1') assert res.status_code == 404 def test_worksheet_payment_custom_delete_zero_id( fixture_client, mock_accounts, mock_contracts, mock_statement_periods ): """Test deletion with zero ID.""" res = fixture_client.delete('/payment/custom/0') assert res.status_code == 404 def test_worksheet_payment_custom_delete_with_complete_and_other_actions( fixture_client, mock_accounts, mock_contracts, mock_statement_periods ): """Test deletion fails even when there are other actions but send_payments is complete.""" worksheet = WorksheetPaymentCustomFactory.create( account_id=1, contract_id=1, currency_code='USD', amount=Decimal('100.00'), payment_name='test payment multiple actions', ) worksheet_id = worksheet.worksheet_payment_custom_id # Create multiple abacus_state records with different actions SQL_QUERY = f""" INSERT INTO abacus_state( `parent_table_name`, `parent_table_id`, `action_name`, `action_status`, `message`, `created_by`, `created_at`, `last_modified_by`, `last_modified` ) VALUES ('worksheet_payment_custom', {worksheet_id}, 'generate_export', 'complete', NULL, '2', NOW(), '2', NOW()), ('worksheet_payment_custom', {worksheet_id}, 'send_payments', 'complete', NULL, '2', NOW(), '2', NOW()) """ _exec_sql(SQL_QUERY) db.session.commit() # Delete should fail because send_payments is complete res = fixture_client.delete(f'/payment/custom/{worksheet_id}') assert res.status_code == 400 def test_worksheet_payment_custom_delete_preserves_other_records( fixture_client, mock_accounts, mock_contracts, mock_statement_periods ): """Test that deleting one record doesn't affect others with similar properties.""" # Create multiple records with the same account and contract worksheet1 = WorksheetPaymentCustomFactory.create( account_id=1, contract_id=1, currency_code='USD', amount=Decimal('100.00'), payment_name='test payment 1', statement_period_id=1, ) worksheet2 = WorksheetPaymentCustomFactory.create( account_id=1, contract_id=1, currency_code='USD', amount=Decimal('200.00'), payment_name='test payment 2', statement_period_id=1, ) worksheet3 = WorksheetPaymentCustomFactory.create( account_id=1, contract_id=1, currency_code='USD', amount=Decimal('300.00'), payment_name='test payment 3', statement_period_id=2, ) # Delete the middle record res = fixture_client.delete( f'/payment/custom/{worksheet2.worksheet_payment_custom_id}' ) assert res.status_code == 204 # Verify only the targeted record is deleted res = fixture_client.get( f'/payment/custom/{worksheet1.worksheet_payment_custom_id}' ) assert res.status_code == 200 res = fixture_client.get( f'/payment/custom/{worksheet2.worksheet_payment_custom_id}' ) assert res.status_code == 404 res = fixture_client.get( f'/payment/custom/{worksheet3.worksheet_payment_custom_id}' ) assert res.status_code == 200 def test_worksheet_payment_custom_delete_idempotency_check( fixture_client, mock_accounts, mock_contracts, mock_statement_periods ): """Test that deletion is idempotent at the endpoint level (second call returns 404).""" worksheet = WorksheetPaymentCustomFactory.create( account_id=1, contract_id=1, currency_code='USD', amount=Decimal('100.00'), payment_name='test payment', ) worksheet_id = worksheet.worksheet_payment_custom_id # First deletion res = fixture_client.delete(f'/payment/custom/{worksheet_id}') assert res.status_code == 204 # Second deletion attempt res = fixture_client.delete(f'/payment/custom/{worksheet_id}') assert res.status_code == 404 # Third deletion attempt res = fixture_client.delete(f'/payment/custom/{worksheet_id}') assert res.status_code == 404