"""Royalty Accounting Processor. Event: commit_royalty Action: Create ledger entries from the results of an accounting run. """ from commit_royalty.config import LEDGER_BATCH_SIZE from commit_royalty.config import PAYEE_BATCH_SIZE from commit_royalty.ledger_entry_queue import LedgerEntryQueue from commit_royalty.logging import app_logger from commit_royalty.payee_ledger_processor import PayeeLedgerProcessor from commit_royalty.template import get_formatted_query class CommitRoyaltyProcessor: """commit_royalty event processor.""" PAYEE_MSG = 'Processing payees {}; {} to {}' PROCESSOR_MSG = 'Starting royalties processor for accounting run {}' LEDGER_EXIT_MSG = 'Finished ledger entry creation for payee {}' LEDGER_MSG = 'Starting ledger entry creation for {} payees' GET_PAYEE_PERIOD_SALES_SQL = """ SELECT DISTINCT PAYEE_ID FROM $db.$schema.ACCOUNTING_RUN_RESULTS WHERE ACCOUNTING_RUN_ID = $accounting_run_id """ CONTRACT_SALES_SQL = """ SELECT CONTRACT_ID AS CONTRACT_ID, PAYEE_ID AS PAYEE_ID, PAYEE_CURRENCY_CODE AS CURRENCY_CODE, SUM(NET_REVENUE_PAYEE_CURRENCY) AS AMOUNT FROM $db.$schema.ACCOUNTING_RUN_RESULTS WHERE ACCOUNTING_RUN_ID = $accounting_run_id AND PAYEE_ID IN ($payee_ids) GROUP BY CONTRACT_ID, PAYEE_ID, PAYEE_CURRENCY_CODE """ def __init__(self, event, sf_executor): """Init action.""" self._entries = [] self._event = event self._sf_executor = sf_executor app_logger.info( self.PROCESSOR_MSG.format(self._event.get('target_id'))) def process(self): """Process the event.""" payee_id_batches = self.get_payee_batches() for index, payee_ids in enumerate(payee_id_batches): start_index = index * PAYEE_BATCH_SIZE + 1 end_index = min(len(payee_ids), PAYEE_BATCH_SIZE) app_logger.info(self.PAYEE_MSG.format( payee_ids, start_index, end_index)) self.generate_ledger_items(payee_ids) app_logger.debug(self.LEDGER_EXIT_MSG.format(payee_ids)) app_logger.info('Stopping royalties processor') def get_payee_batches(self): """Get a list of lists of the accounting run's payee ids.""" payee_ids = [] payee_count = 0 temp_list = [] for row in self._sf_executor.fetchall(self.get_sql(), dict_cursor=True): payee_count += 1 temp_list.append(row['PAYEE_ID']) if len(temp_list) > PAYEE_BATCH_SIZE: payee_ids.append(temp_list) temp_list = [] app_logger.info(self.LEDGER_MSG.format(payee_count)) if len(temp_list): payee_ids.append(temp_list) return payee_ids def get_sql(self): """Get formatted SQL to find an accounting run's distinct payees.""" template_parameters = { 'accounting_run_id': str(int(self._event.get('target_id'))) } return get_formatted_query( self.GET_PAYEE_PERIOD_SALES_SQL, template_parameters) def generate_ledger_items(self, payee_ids): """Generate all ledger items.""" payee_sales = {} sql = self.get_formatted_query(payee_ids) for row in self._sf_executor.fetchall(sql, dict_cursor=True): row_payee_id = row['PAYEE_ID'] if row_payee_id not in payee_sales: payee_sales[row_payee_id] = [] payee_sales[row_payee_id].append(row) self.process_payee_batches(payee_sales) def process_payee_batches(self, payee_sales): """Process payee sales batches by payee.""" ledger_entry_queue = LedgerEntryQueue() for process_payee_id in payee_sales.keys(): payee_processor = PayeeLedgerProcessor( process_payee_id, self._event, self._sf_executor, ledger_entry_queue) payee_processor.process(payee_sales[process_payee_id]) if ledger_entry_queue.length >= LEDGER_BATCH_SIZE: ledger_entry_queue.flush_entries() ledger_entry_queue.flush_entries() def get_formatted_query(self, payee_ids): """Format the SQL.""" template_parameters = { 'accounting_run_id': str(int(self._event.get('target_id'))), 'payee_ids': ','.join([str(i) for i in payee_ids]) } return get_formatted_query( self.CONTRACT_SALES_SQL, template_parameters)