"""Logic for Payment Group Payment Batch.""" from decimal import Decimal import typing from payment.constants.constants import PAYMENT_TYPES, VALID_PAYMENT_BATCH_STATUSES from payment.constants.error import ERROR_INVALID_PAYMENT_BATCH_STATUS from payment.logic.exceptions import LogicError from payment.models import ( PaymentDebitCreditDataEntry, PaymentGroupPayment, PaymentGroupPaymentAccountDetail, PaymentGroupPaymentBatch, ) from payment.repository import payment_group_payment_account_detail as repository def create_payment_group_payment_batch( payment_group_payment_id: int, payoneer_program_id: int, batch_num: int, ) -> PaymentGroupPaymentBatch: """Create a payment_group_payment_batch. Args: payment_group_payment_id: Id of the payment_group_payment payoneer_program_id: Id of the Payoneer's Program Id batch_num: Batch number of payment_group_payment_account batch Returns: Newly created PaymentGroupPaymentBatch record. """ PaymentGroupPayment.get_by_id_or_error(payment_group_payment_id) return PaymentGroupPaymentBatch.create( payment_group_payment_id=payment_group_payment_id, payoneer_program_id=payoneer_program_id, batch_num=batch_num, ) def get_payment_group_payment_batches( payment_group_payment_id: int, payment_batch_status: str = None ) -> typing.List[PaymentGroupPaymentBatch]: """Get payment batches by payment_group_payment_id and payment status of batch. Arg: payment_group_payment_id(int): id of the payment_group_payment payment_batch_status(str)(Optional): status of the payment batch is either failed, pending, or success Returns: A List of payment batches """ if payment_batch_status: _validate_payment_batch_status(payment_batch_status) return PaymentGroupPaymentBatch.get_by_payment_group_payment_and_batch_status( payment_group_payment_id, payment_batch_status ) def _validate_payment_batch_status(payment_batch_status: str): """Validate payment batch status. Arg: payment_batch_status(str): status of the payment batch """ valid_batch_statuses = VALID_PAYMENT_BATCH_STATUSES if payment_batch_status not in valid_batch_statuses: raise LogicError( ERROR_INVALID_PAYMENT_BATCH_STATUS.format( batch_status=', '.join(valid_batch_statuses) ) ) return True def get_payment_batch_by_id(batch_id: int) -> PaymentGroupPaymentBatch: """Get payment group payment batch entity. Arg: batch_id(int): batch id """ return PaymentGroupPaymentBatch.get_by_id_or_error(batch_id) def get_batch_debit_data( batch_id: int, payment_type: str ) -> typing.List[PaymentDebitCreditDataEntry]: """Get batch debit data. Arg: batch_id(int): batch id payment_type(str): `wht`, `vat` or `payment` """ details = repository.get_by_payment_group_payment_batch(batch_id) results = [ PaymentDebitCreditDataEntry( d.account_id, d.contract_id, d.currency_code, _get_debit_amount_using_type(payment_type, d), d.worksheet_account_contract_payable_after_tax_id, ) for d in details # This should be flexible filter for any case, including corrections. if _get_debit_amount_using_type(payment_type, d) ] return results def _get_debit_amount_using_type( payment_type: str, account_detail: PaymentGroupPaymentAccountDetail ) -> Decimal: """Get debit amount. Debit of vat should be positive, while payment and wht are negative. Then it’s flipped for credit. Arg: payment_type(int): payment type `wht`, `vat` or `payment` account_detail(str): detail record """ match payment_type: case PAYMENT_TYPES.WHT: # negative according to formula, return as is return account_detail.tax_withholding_amount case PAYMENT_TYPES.VAT: # positive according to formula, return as is return account_detail.vat_amount case _: # positive according to formula, make negative return ( account_detail.payable_amount_post_tax * -1 if account_detail.payable_amount_post_tax else account_detail.payable_amount_post_tax )