"""Worksheet Payment Contract Advance logic.""" from decimal import Decimal, ROUND_UP from typing import List from payment.constants.constants import WORKSHEET_PAYMENT_CONTRACT_ADVANCE_STATUSES from payment.constants.error import ( ERROR_EXCHANGE_RATE_REQUIRED, ERROR_INVALID_EXCHANGE_RATE, ERROR_WORKSHEET_ALREADY_EXISTS, ) from payment.logic.exceptions import EntityDoesNotExist, LogicError from payment.models import ExchangeRate, WorksheetPaymentContractAdvance from payment.models.generic import Items from payment.utils.format_error import validate_currency from payment.utils.format_response import prepare_dataload_response def create_worksheet_payment_contract_advance( contract_advance_id: int, exchange_rate_id: int, statement_period_id: int, payment_name: str, amount: Decimal, currency_code: str, payee_currency_code: str, withholding_tax_amount: Decimal, vat_amount: Decimal, amount_after_withholding_and_vat: Decimal, us_source_income_rate: Decimal, is_internal: bool, ) -> WorksheetPaymentContractAdvance: """Create Worksheet Payment Contract Advance instance. Calculates fields in payee currency. """ exchange_rate = None rate = Decimal(1) exchange_rate_statement_period_id = statement_period_id if currency_code != payee_currency_code: try: exchange_rate = ExchangeRate.get_by_id_or_error(exchange_rate_id) except Exception: raise LogicError(ERROR_EXCHANGE_RATE_REQUIRED) rate = exchange_rate.rate exchange_rate_statement_period_id = exchange_rate.statement_period_id _validate_params( contract_advance_id, currency_code, payee_currency_code, exchange_rate ) amount_payee_currency = _round_currency_conversion(amount * rate) withholding_tax_amount_payee_currency = _round_currency_conversion( withholding_tax_amount * rate ) vat_amount_payee_currency = _round_currency_conversion(vat_amount * rate) amount_after_withholding_and_vat_payee_currency = ( amount_payee_currency + withholding_tax_amount_payee_currency + vat_amount_payee_currency ) return WorksheetPaymentContractAdvance.create( contract_advance_id=contract_advance_id, statement_period_id=statement_period_id, exchange_rate_statement_period_id=exchange_rate_statement_period_id, payment_name=payment_name, amount=amount, currency_code=currency_code, amount_payee_currency=amount_payee_currency, payee_currency_code=payee_currency_code, exchange_rate=rate, withholding_tax_amount=withholding_tax_amount, vat_amount=vat_amount, amount_after_withholding_and_vat=amount_after_withholding_and_vat, withholding_tax_amount_payee_currency=withholding_tax_amount_payee_currency, # noqa vat_amount_payee_currency=vat_amount_payee_currency, amount_after_withholding_and_vat_payee_currency=amount_after_withholding_and_vat_payee_currency, # noqa us_source_income_rate=us_source_income_rate, is_internal=is_internal, ) def _validate_params( contract_advance_id, currency_code, payee_currency_code, exchange_rate=None ): currency_error = validate_currency(currency_code) if currency_error is not None: raise LogicError(currency_error.errors.get('message')) currency_error = validate_currency(payee_currency_code) if currency_error is not None: raise LogicError(currency_error.errors.get('message')) if exchange_rate and ( currency_code != exchange_rate.from_currency_code or payee_currency_code != exchange_rate.to_currency_code ): raise LogicError(ERROR_INVALID_EXCHANGE_RATE) # allow only if the worksheets are deleted or rejected, otherwise raise if WorksheetPaymentContractAdvance.get_filtered_first( contract_advance_id=contract_advance_id, active=True, payment_statuses=[ WORKSHEET_PAYMENT_CONTRACT_ADVANCE_STATUSES.INIT, WORKSHEET_PAYMENT_CONTRACT_ADVANCE_STATUSES.RUNNING, WORKSHEET_PAYMENT_CONTRACT_ADVANCE_STATUSES.ERROR, WORKSHEET_PAYMENT_CONTRACT_ADVANCE_STATUSES.COMPLETE, None, ], ): raise LogicError(ERROR_WORKSHEET_ALREADY_EXISTS) def _round_currency_conversion(amount: Decimal): """Get rounded amount after the currency conversion.""" float_precision = Decimal('0.01') rounding = ROUND_UP return amount.quantize(float_precision, rounding) def get_worksheet_payment_contract_advance( object_id: int, ) -> WorksheetPaymentContractAdvance: """Get Worksheet Payment Contract Advance instance by ID.""" obj = WorksheetPaymentContractAdvance.get_by_id(object_id) if not obj: raise EntityDoesNotExist(WorksheetPaymentContractAdvance, object_id) return obj def update_worksheet_payment_contract_advance( object_id: int, salesforce_id: str ) -> WorksheetPaymentContractAdvance: """Update Worksheet Payment Contract Advance instance.""" obj = WorksheetPaymentContractAdvance.get_by_id(object_id) if not obj: raise EntityDoesNotExist(WorksheetPaymentContractAdvance, object_id) obj.update_attributes(salesforce_id=salesforce_id) WorksheetPaymentContractAdvance.commit_changes() return obj def delete_worksheet_payment_contract_advance(object_id: int) -> None: """Delete Worksheet Payment Contract Advance instance.""" WorksheetPaymentContractAdvance.delete_by_id_or_error(object_id) def list_worksheet_payment_contract_advances( offset: int, limit: int, salesforce_id: str, contract_advance_id: int, payment_statuses: List[str], ) -> Items: """List Worksheet Payment Contract Advance instances.""" items, total_count = ( WorksheetPaymentContractAdvance.get_filtered_active_internal_records( offset=offset, limit=limit, salesforce_id=salesforce_id, contract_advance_id=contract_advance_id, payment_statuses=payment_statuses, ) ) return Items(items, total_count) def dataload_worksheets_by_ids(worksheet_ids: List[int]) -> dict: """Dataload worksheets by ids.""" worksheets = WorksheetPaymentContractAdvance.get_filtered_all( worksheet_ids=worksheet_ids, active=True ) result = prepare_dataload_response( worksheet_ids, worksheets, 'worksheet_payment_contract_advance_id' ) return {'items': result}