def test_free_membership(self): self.user_membership.begins_at = session.utcnow().replace( day=self.membership_fee_current.booking_end.days + 1) end_date = last_day_of_month(session.utcnow().date()) self.assertEquals(0.00, estimate_balance(self.user, end_date))
def estimate_balance(session: Session, user, end_date): """Estimate the balance a user account will have at :paramref:`end_date`. :param session: :param user: The member :param end_date: Date of the end of the membership :return: Estimated balance at the end_date """ now = utcnow().date() # Use tomorrow in case that it is the last of the month, the fee for the # current month will be added later tomorrow = now + timedelta(1) last_fee = session.scalars( select(MembershipFee).order_by(MembershipFee.ends_on.desc()).limit(1) ).first() if last_fee is None: raise ValueError("no fee information available") # Bring end_date to previous month if the end_date is in grace period end_date_justified = end_date - timedelta(last_fee.booking_begin.days - 1) months_to_pay = diff_month(end_date_justified, tomorrow) # If the user has to pay a fee for the current month has_to_pay_this_month = user.has_property( 'membership_fee', tomorrow.replace(day=last_fee.booking_end.days) ) if has_to_pay_this_month: months_to_pay += 1 # If there was no fee booked yet for the last month and the user has to pay # a fee for the last month, increment months_to_pay last_month_last = tomorrow.replace(day=1) - timedelta(1) last_month_fee_outstanding = ( fee_from_valid_date(session, last_month_last, user.account) is None ) if last_month_fee_outstanding: had_to_pay_last_month = user.has_property( 'membership_fee', last_month_last.replace(day=last_fee.booking_end.days) ) if had_to_pay_last_month: months_to_pay += 1 # If there is already a fee booked for this month, decrement months_to_pay this_month_fee_outstanding = ( fee_from_valid_date(session, last_day_of_month(tomorrow), user.account) is None ) if not this_month_fee_outstanding: months_to_pay -= 1 return (-user.account.balance) - (months_to_pay * last_fee.regular_fee)
def test_last_date_of_month(self): for (d, expected) in [ (date(2019, 9, 1), 30), (date(2019, 8, 1), 31), (date(2019, 2, 1), 28), (date(2016, 2, 1), 29), ]: with self.subTest(date=d, expected=expected): self.assertEqual(last_day_of_month(d), d.replace(day=expected))
class MembershipFeeFactory(BaseFactory): class Meta: model = MembershipFee name = Faker('word') regular_fee = 5.00 booking_begin = timedelta(3) booking_end = timedelta(14) payment_deadline = timedelta(14) payment_deadline_final = timedelta(62) begins_on = LazyAttribute(lambda o: session.utcnow().date().replace(day=1)) ends_on = LazyAttribute(lambda o: last_day_of_month(session.utcnow()))
def test_free_membership(self): new_start = session.utcnow().replace( day=self.membership_fee_current.booking_end.days + 1) self.user_membership.active_during = closedopen(new_start, None) end_date = last_day_of_month(session.utcnow().date()) assert estimate_balance(self.session, self.user, end_date) == 0.00
def test_last_date_of_month(d, expected): assert last_day_of_month(d) == d.replace(day=expected)