"""Tests for the revenue detail report generation.""" from decimal import Decimal from os import path from unittest.mock import MagicMock from unittest.mock import patch import pandas from config import FILE_OUTPUT_PATH from src.documents import revenue_detail_report from src.utils.constants import NumberFormat HEADERS = [ 'STATEMENT PERIOD', 'ACCOUNT ID', 'ACCOUNT NAME', 'CONTRACT ID', 'TRANSACTION DATE', 'SALE COUNTRY', 'STORE', 'SERVICE DETAIL', 'LABEL IMPRINT', 'PRODUCT ARTIST', 'PRODUCT', 'PRODUCT VERSION', 'PRODUCT CODE', 'DISPLAY UPC', 'MANUFACTURER UPC', 'TRACK ARTIST', 'TRACK', 'TRACK VERSION', 'ISRC', 'YOUTUBE VIDEO ID', 'TRANSACTION TYPE', 'TRANSACTION SUBTYPE', 'UNIT PRICE SALE CURRENCY', 'QUANTITY', 'GROSS REVENUE SALE CURRENCY', 'SALE CURRENCY', 'CURRENCY CONVERSION RATE', 'GROSS REVENUE ACCOUNT CURRENCY', 'ACCOUNT CURRENCY', 'ACCOUNT SHARE', 'NET SHARE ACCOUNT CURRENCY', 'PHYSICAL PPD USD', 'MECHANICAL DEDUCTION (US only)', 'MECHANICAL DEDUCTION ADMIN FEE (US only)', 'ROYALTY TYPE', 'ORIGINAL STATEMENT PERIOD', ] INPUT_ROW = pandas.DataFrame( [ { 'PERFORMERS': 'Performer', 'ACCOUNT_ID': 35977, 'ACCOUNT_NAME': 'SOME NAME', 'CONTRACT_ID': 537384, 'STATEMENT_PERIOD_NAME': 'Test Period', 'ACCOUNT_PAYEE_CURRENCY': 'GBP', 'SALE_CURRENCY_CODE': 'USD', 'TRANSACTION_DATE': '2022-08-16', 'ISRC': 'ES5770606294', 'DISPLAY_UPC': '191773586577', 'MANUFACTURER_UPC': '191773586577', 'ROYALTY_RATE': Decimal('0.8000'), 'UNIT_PRICE_SALE_CURRENCY': Decimal('0.000205000000'), 'GROSS_REVENUE_AFTER_WITHHOLDING_TAX_SALE_CURRENCY': Decimal('0.00000000000'), 'GROSS_REVENUE_AFTER_WITHHOLDING_TAX_PAYEE_CURRENCY': Decimal('200.0'), 'NET_SHARE_PAYEE_CURRENCY': Decimal('100.0'), 'COUNTRYNAME': 'Norway', 'SUBDISTRIBUTOR': 'SJD', 'CUSTOMER_NAME': 'TIDAL', 'LABELNAME': 'Test Label', 'RELEASENAME': 'Test Music', 'RELEASE_VERSION': 'Blockbuster Exclusive', 'PRODUCT_CODE': 'TEST1234', 'TRACKNAME': 'Just A Test', 'TRACK_VERSION': 'Remix', 'ARTISTNAME': 'Partyboi', 'QUANTITY': 42, 'TRANSACTIONTYPEDESC': 'Downloaded Ringtones', 'TRANSACTION_SUBTYPE': 'S', 'VIDEO_ID': '6g2t3KzCQqI', 'PHYS_PPD_USD': Decimal('12.34'), 'EXCHANGE_RATE': Decimal('0.98'), 'MECHANICAL_DEDUCTION_AMOUNT_PAYEE_CURRENCY': Decimal('-2.22'), 'PUBLISHER_ADMIN_FEE_PAYEE_CURRENCY': Decimal('-1.11'), 'LABEL_IMPRINT': 'label', 'ABACUS_SALE_TYPE': 'expensive', 'ORIGINAL_STATEMENT_PERIOD': 265, } ] ) def test_make_process_row(): """Test processing a fact_sales row.""" expected = { 'ACCOUNT SHARE': 0.8000, 'UNIT PRICE SALE CURRENCY': 0.000205, 'GROSS REVENUE SALE CURRENCY': 0.0, 'GROSS REVENUE ACCOUNT CURRENCY': 200.0, 'NET SHARE ACCOUNT CURRENCY': 100.0, 'PHYSICAL PPD USD': 12.34, 'CURRENCY CONVERSION RATE': 0.98, 'MECHANICAL DEDUCTION (US only)': -2.22, 'MECHANICAL DEDUCTION ADMIN FEE (US only)': -1.11, } df = INPUT_ROW.copy() process_dataframe_function = revenue_detail_report._make_process_row(None) process_dataframe_function(df) # test rounding for column in expected: assert df[column][0] == expected[column] # assert that the dataframe columns are maps correctly for header in HEADERS: assert df.__contains__(header) def test_make_process_row_subaccount(): """Test processing a fact_sales row for a subaccount.""" subaccount = { 'SUBACCOUNTNAME': 'Subbb', 'SUBACCOUNT_SPLIT_TYPE': 'Net', 'COMMISSIONOVERRIDE': Decimal('0.80'), } df = INPUT_ROW.copy() process_dataframe_function = revenue_detail_report._make_process_row(subaccount) process_dataframe_function(df) assert df['REVENUE'][0] == 80.0 def test_make_process_row_subaccount_no_data(): """Test processing a fact_sales row for a subaccount when there is no data.""" subaccount = { 'SUBACCOUNTNAME': 'Subbb', 'SUBACCOUNT_SPLIT_TYPE': 'Net', 'COMMISSIONOVERRIDE': Decimal('0.80'), } df = pandas.DataFrame( columns=[ *( key for key in revenue_detail_report._COLUMN_MAPPING.keys() if key not in revenue_detail_report._SUBACCOUNT_COLUMN_EXCLUDE ), 'REVENUE', ] ) process_dataframe_function = revenue_detail_report._make_process_row(subaccount) process_dataframe_function(df) assert df.empty @patch('src.documents.revenue_detail_report.snowflake_connector') @patch('src.documents.revenue_detail_report.create_report_file_pandas') @patch('src.documents.revenue_detail_report.OwsAbacusAccount.get_account') @patch('src.documents.revenue_detail_report.OwsRoyalties.get_statement_period') @patch('src.documents.revenue_detail_report._make_process_row') def test_build_document( _make_process_row_mock, get_statement_period_mock, get_account_mock, create_report_file_pandas_mock, snowflake_mock, account_fixture, statement_period_fixture, ): """Test building a revenue detail report.""" account_id = 24601 contract_id = 20001 statement_period_id = 123 data = [1, 2, 3] total_rows = 3 file_type = 'csv' number_format = NumberFormat.EU expected_path = path.join(FILE_OUTPUT_PATH, 'Jeans_Nest_20001_July_2022_revenue_details.csv') mock_process_row = MagicMock() get_account_mock.return_value = account_fixture get_statement_period_mock.return_value = statement_period_fixture create_report_file_pandas_mock.return_value = expected_path _make_process_row_mock.return_value = mock_process_row snowflake_mock.get_distribution_fact_sales.return_value = (data, total_rows) result = revenue_detail_report.build_document( account_id, contract_id, statement_period_id, None, file_type, number_format ) call_args = create_report_file_pandas_mock.call_args[0] actual_generator = call_args[0] assert result == expected_path create_report_file_pandas_mock.assert_called_once() assert hasattr(actual_generator, '__iter__') and hasattr(actual_generator, '__next__') assert call_args[1] == total_rows assert call_args[2] == mock_process_row assert call_args[3] == expected_path assert call_args[4] == file_type assert call_args[5] == number_format @patch('src.documents.revenue_detail_report.create_report_file_pandas') @patch('src.documents.revenue_detail_report.snowflake_connector') @patch('src.documents.revenue_detail_report.OwsRoyalties.get_statement_period') @patch('src.documents.revenue_detail_report._make_process_row') def test_build_document_subaccount( _make_process_row_mock, get_statement_period_mock, snowflake_mock, create_report_file_pandas_mock, statement_period_fixture, ): """Test building a revenue detail report with a subaccount.""" account_id = 24601 contract_id = None subaccount_id = 54321 subaccount = { 'SUBACCOUNTNAME': 'Subbb', 'SUBACCOUNT_SPLIT_TYPE': 'Net', 'COMMISSIONOVERRIDE': Decimal('0.80'), } statement_period_id = 123 total_rows = 3 fact_sales = [ {'NET_SHARE_PAYEE_CURRENCY': Decimal('10.0')}, {'NET_SHARE_PAYEE_CURRENCY': Decimal('20.0')}, {'NET_SHARE_PAYEE_CURRENCY': Decimal('30.0')}, ] expected_path = path.join(FILE_OUTPUT_PATH, 'Subbb_July_2022_revenue_details.csv') file_type = 'csv' number_format = NumberFormat.EU mock_process_row = MagicMock() snowflake_mock.get_distribution_fact_sales.return_value = (fact_sales, total_rows) get_statement_period_mock.return_value = statement_period_fixture snowflake_mock.get_subaccount.return_value = subaccount create_report_file_pandas_mock.return_value = expected_path _make_process_row_mock.return_value = mock_process_row result = revenue_detail_report.build_document( account_id, contract_id, statement_period_id, subaccount_id, file_type, number_format, # noqa: E501 ) call_args = create_report_file_pandas_mock.call_args[0] actual_generator = call_args[0] assert result == expected_path create_report_file_pandas_mock.assert_called_once() assert hasattr(actual_generator, '__iter__') and hasattr(actual_generator, '__next__') assert call_args[1] == total_rows assert call_args[2] == mock_process_row assert call_args[3] == expected_path assert call_args[4] == file_type assert call_args[5] == number_format @patch('src.documents.revenue_detail_report.snowflake_connector') @patch('src.documents.revenue_detail_report.create_report_file_pandas') @patch('src.documents.revenue_detail_report.OwsAbacusAccount.get_account') @patch('src.documents.revenue_detail_report.OwsRoyalties.get_statement_period') @patch('src.documents.revenue_detail_report._make_process_row') def test_build_document_empty_report( _make_process_row_mock, get_statement_period_mock, get_account_mock, create_report_file_pandas_mock, snowflake_mock, account_fixture, statement_period_fixture, ): """Test building a revenue detail report with 0 rows (empty report).""" account_id = 24601 contract_id = 20001 statement_period_id = 123 mock_generator = MagicMock() data = mock_generator total_rows = 0 # Empty report file_type = 'csv' number_format = NumberFormat.EU expected_path = path.join(FILE_OUTPUT_PATH, 'Jeans_Nest_20001_July_2022_revenue_details.csv') mock_process_row = MagicMock() get_account_mock.return_value = account_fixture get_statement_period_mock.return_value = statement_period_fixture create_report_file_pandas_mock.return_value = expected_path _make_process_row_mock.return_value = mock_process_row snowflake_mock.get_distribution_fact_sales.return_value = (data, total_rows) result = revenue_detail_report.build_document( account_id, contract_id, statement_period_id, None, file_type, number_format ) call_args = create_report_file_pandas_mock.call_args[0] actual_generator = call_args[0] assert result == expected_path create_report_file_pandas_mock.assert_called_once() assert hasattr(actual_generator, '__iter__') and hasattr(actual_generator, '__next__') first_df = next(actual_generator) assert isinstance(first_df, pandas.DataFrame) assert len(first_df) == 0 expected_columns = [col for col in revenue_detail_report._COLUMN_MAPPING.keys()] assert list(first_df.columns) == expected_columns assert call_args[1] == total_rows assert call_args[2] == mock_process_row assert call_args[3] == expected_path assert call_args[4] == file_type assert call_args[5] == number_format