def check(info_date, current_amount, month_pay): assert get_credit_info(info_date, **credit_config) == Credit( get_date(credit_config["start_date"]), get_date(credit_config["end_date"]), Decimal(credit_config["amount"]), Decimal(current_amount), Decimal(credit_config["interest"]), None if month_pay is None else Decimal(month_pay), _calculate(credit_config["start_date"], credit_config["end_date"], credit_config["amount"], credit_config["interest"], credit_config["payments"]))
def test_iter_months_from_year_to_year(): assert list(_iter_months("15.11.2013", "15.02.2014")) == [ get_date("15.11.2013"), get_date("15.12.2013"), get_date("15.01.2014"), get_date("15.02.2014") ]
def get_credit_info(info_date, start_date, end_date, amount, interest, payments={}): info_date = get_date(info_date) start_date = get_date(start_date) end_date = get_date(end_date) amount = Decimal(amount) interest = Decimal(interest) payment_schedule = _calculate(start_date, end_date, amount, interest, payments) month_pay = None if info_date <= end_date: prev_date = start_date current_amount = amount for payment in payment_schedule: if not (payment.date <= info_date or prev_date < info_date): break if payment.date <= info_date: current_amount = payment.credit month_pay = payment.month_pay prev_date = payment.date else: current_amount = 0 return Credit(start_date, end_date, amount, current_amount, interest, month_pay, payment_schedule)
def _calculate(start_date, end_date, credit, interest, payments={}): start_date = get_date(start_date) end_date = get_date(end_date) credit = Decimal(credit) payments = {get_date(date): Decimal(payment) for date, payment in payments.items()} schedule = [] prev_date = start_date cur_month_pay = month_pay = None for date, month_interest in _iter_month_interest(start_date, end_date, interest): if month_pay is None or cur_month_pay != month_pay: month_pay = _get_month_pay(prev_date, end_date, credit, interest) cur_month_pay = payments.pop(date, month_pay) if cur_month_pay < month_pay: raise InvalidPaymentError("Invalid payment for {}.", format_date(date)) interest_pay = _round_payment(credit * month_interest) credit_pay = cur_month_pay - interest_pay credit -= credit_pay schedule.append(Payment(date, credit_pay, interest_pay, cur_month_pay, credit)) prev_date = date if payments: raise InvalidPaymentDateError("Invalid payment date: {}.", format_date(payments.popitem()[0])) if credit: payment = schedule[-1] schedule[-1] = Payment(payment.date, payment.credit_pay + credit, interest_pay, payment.month_pay + credit, 0) return schedule
def test_iter_months_with_non_existing_days_in_months(): assert list(_iter_months("31.12.2012", "30.04.2013")) == [ get_date("31.12.2012"), get_date("31.01.2013"), get_date("28.02.2013"), get_date("31.03.2013"), get_date("30.04.2013") ]
def _calculate(start_date, end_date, credit, interest, payments={}): start_date = get_date(start_date) end_date = get_date(end_date) credit = Decimal(credit) payments = { get_date(date): Decimal(payment) for date, payment in payments.items() } schedule = [] prev_date = start_date cur_month_pay = month_pay = None for date, month_interest in _iter_month_interest(start_date, end_date, interest): if month_pay is None or cur_month_pay != month_pay: month_pay = _get_month_pay(prev_date, end_date, credit, interest) cur_month_pay = payments.pop(date, month_pay) if cur_month_pay < month_pay: raise InvalidPaymentError("Invalid payment for {}.", format_date(date)) interest_pay = _round_payment(credit * month_interest) credit_pay = cur_month_pay - interest_pay credit -= credit_pay schedule.append( Payment(date, credit_pay, interest_pay, cur_month_pay, credit)) prev_date = date if payments: raise InvalidPaymentDateError("Invalid payment date: {}.", format_date(payments.popitem()[0])) if credit: payment = schedule[-1] schedule[-1] = Payment(payment.date, payment.credit_pay + credit, interest_pay, payment.month_pay + credit, 0) return schedule
def _iter_months(start_date_string, end_date_string): date = get_date(start_date_string) end_date = get_date(end_date_string) start_day = date.day yield date while date != end_date: if date > end_date: raise InvalidDateRangeError(start_date_string, end_date_string) if date.month == 12: year = date.year + 1 month = 1 else: year = date.year month = date.month + 1 date = _nearest_valid_date(year, month, start_day) yield date
def _check_payment(payment, date, credit_pay, interest_pay, month_pay, overall_precision="0.01"): def check(payment, right_payment, precision): right_payment = Decimal(right_payment) assert right_payment - precision <= payment <= right_payment + precision exact_precision = Decimal("0.01") overall_precision = Decimal(overall_precision) assert payment.date == get_date(date) check(payment.credit_pay, credit_pay, overall_precision) check(payment.interest_pay, interest_pay, exact_precision) check(payment.credit_pay + payment.interest_pay, month_pay, overall_precision) check(payment.month_pay, month_pay, overall_precision)
def test_get_date(): date = Date(2013, 3, 13) assert get_date(date) is date
def test_iter_months_simple(): assert list(_iter_months("15.03.2013", "15.05.2013")) == [ get_date("15.03.2013"), get_date("15.04.2013"), get_date("15.05.2013") ]
def test_get_date_invalid(): with pytest.raises(InvalidDateError): get_date("31.02.2013")
def test_get_date_from_string(): assert get_date("13.03.2013") == Date(2013, 3, 13)