"""Payee Ledger Processor. Calculates and saves all ledger entries for a payee's sales in an accounting run """ from decimal import Decimal, getcontext, ROUND_HALF_EVEN class PayeeLedgerProcessor: """Payee ledger processor.""" NEGATIVE_ERROR = 'Negative sales found for payee id: {} run id: {}' def __init__(self, payee_id, event, sf_executor, entry_queue): """Init command.""" self._payee_id = payee_id self._payee_currency_code = '' self._event = event self._entries = entry_queue self._payee_ledger_amount = Decimal('0.00') self._general_ledger_amount = Decimal('0.00000000000000000') # TODO: check if we need to declare a # non-zero decimal to use in quantize() self._payee_ledger_precision = Decimal('0.01') self._sf_executor = sf_executor def validate_row(self, row): """Validate a row of sales.""" contract_sales_amount = row['AMOUNT'] if contract_sales_amount < 0: raise Exception(self.NEGATIVE_ERROR.format( self._payee_id, self._event.get('target_id'))) def process(self, rows): """Process the ledger entries for a payee in an accounting run.""" getcontext().prec = 18 for row in rows: self.validate_row(row) self.process_row(row) self._entries.append(self.format_payee_ledger_entry()) self._entries.append(self.format_general_ledger_entry()) def process_row(self, row): """Process a row returned from snowflake.""" self._payee_currency_code = row['CURRENCY_CODE'] rounded_amount = row['AMOUNT'].quantize( self._payee_ledger_precision, rounding=ROUND_HALF_EVEN) remainder = row['AMOUNT'] - rounded_amount self._payee_ledger_amount += rounded_amount self._general_ledger_amount -= row['AMOUNT'] self._entries.append( self.format_deposit_ledger_entry( row['CONTRACT_ID'], rounded_amount, remainder)) def format_deposit_ledger_entry( self, contract_id, currency_amount, remainder): """Format a deposit ledger entry.""" return { 'model_type': 'deposit', 'ledger_type': 'credit', 'abacus_event_id': self._event.get('abacus_event_id'), 'contract_id': contract_id, 'payee_id': self._payee_id, 'currency_code': self._payee_currency_code, 'rounded_amount': currency_amount, 'remaining_amount': remainder } def format_payee_ledger_entry(self): """Format a payee_ledger entry.""" return { 'model_type': 'payee', 'ledger_type': 'credit', 'abacus_event_id': self._event.get('abacus_event_id'), 'payee_id': self._payee_id, 'currency_code': self._payee_currency_code, 'currency_amount': self._payee_ledger_amount } def format_general_ledger_entry(self): """Format a general ledger entry.""" return { 'model_type': 'general', 'ledger_type': 'debit', 'abacus_event_id': self._event.get('abacus_event_id'), 'currency_code': self._payee_currency_code, 'currency_amount': self._general_ledger_amount }