def __refund_entry(self, entry):
        """
        refund a single entry.

        THIS DOES NOT remove the entry from a contest!

        :param entry:
        :return:
        """

        buyin = entry.contest_pool.prize_structure.buyin
        bm = BuyinManager(entry.user)
        transaction = None

        # Create a cash or ticket deposit as a refund,
        # based on what the user used to get into the contest
        if bm.entry_did_use_ticket(entry):
            tm = TicketManager(entry.user)
            tm.deposit(buyin)
            transaction = tm.transaction
            refund = self.__create_refund(transaction, entry)
        else:
            ct = CashTransaction(entry.user)
            ct.deposit(buyin)
            transaction = ct.transaction
            refund = self.__create_refund(transaction, entry)

        # Create refund transaction from escrow
        escrow_ct = CashTransaction(self.get_escrow_user())
        escrow_ct.withdraw(buyin, trans=transaction)
        return refund
示例#2
0
 def create_deposit(user, amount, balance_result):
     test = CashTransaction(user)
     test.deposit(amount)
     tran_det= CashTransactionDetail.objects.get(
                 user=test.transaction_detail.user,
                 transaction=test.transaction_detail.transaction)
     self.assertAlmostEquals(tran_det.amount, amount)
     bal = CashBalance.objects.get(
         user=test.transaction_detail.user)
     self.assertAlmostEquals(bal.amount,balance_result)
示例#3
0
    def setUp(self):
        super().setUp()
        self.user = self.get_admin_user()  # get a superuser
        self.withdraw_amount = 10.00  # using float here on purpose
        self.account_balance = 10000.00  # balance starting amount (once we add it)

        ct = CashTransaction(self.user)  # get a new CashTransaction instance
        ct.deposit(self.account_balance
                   )  # start the balance with self.account_balance

        tm = TaxManager(self.user)
        tm.set_tax_id("123456789")
 def create_user(self, username):
     """
     creates a user and gives them $10,000
     :param username:
     :return:
     """
     user = User.objects.create(username=username)
     user.set_password(self.DEFAULT_USER_PASSWORD)
     user.save()
     Information.objects.create(user=user)
     ct = CashTransaction(user)
     ct.deposit(10000.00)
     return user
示例#5
0
    def setUp(self):
        super().setUp()
        self.withdraw_amount = decimal.Decimal(10.00)  # a decimal amount
        self.account_balance = 1000.00  # balance starting amount (once we add it)
        self.user = self.get_admin_user()  #
        r = self.move_time(days=365, hours=1)  # adjust time test
        ct = CashTransaction(self.user)  # get a new CashTransaction instance

        ct.deposit(self.account_balance
                   )  # start the balance with self.account_balance
        information = AccountInformation(
            self.user)  # give self.user an Information object
        information.set_fields(fullname='Ryan',
                               address1='address1',
                               city='city',
                               state='NH',
                               zipcode='03820')
        r.restore()
示例#6
0
    def cancel(self):  # old args: , withdraw_pk, status_pk ):
        """
        Cancels a withdraw assuming the status is still pending. Releases the funds back to user.

        :param withdraw_pk:
        :param status_pk:
        """
        self.__check_status_pending()
        self.withdraw_object.status = self.get_withdraw_status(
            WithdrawStatusConstants.CancelledAdminDefault.value)
        self.withdraw_object.save()

        #
        # Creates a new transaction for the refunded amount.
        category = TransactionType.objects.get(
            pk=TransactionTypeConstants.AdminCancelWithdraw.value)
        ct = CashTransaction(self.user)
        ct.deposit(abs(self.withdraw_object.cash_transaction_detail.amount),
                   category)
示例#7
0
    def __convert_bonus_cash(user, rake_paid, transaction):
        """
        Creates the conversion from bonus cash to real cash
        based on the rake_paid for the given entry
        :param user:
        :param rake_paid:
        """
        bct = BonusCashTransaction(user)
        balance = bct.get_balance_amount()

        #
        #  Create the conversion if there is a balance
        # to the user's bonus cash account
        if balance > 0:
            #
            # get the conversion amount based on rake paid
            amount = rake_paid * settings.BONUS_CASH_RAKE_PERCENTAGE

            #
            # round to the nearest cent
            val = math.floor(amount * 100)
            amount = val / 100.0

            #
            # if the amount is greater than the balance make the
            # amount the balance
            if balance < amount:
                amount = balance
            #
            # create the withdraw from the bonus cash
            bct.withdraw(amount, transaction)

            #
            # create the deposit from the bonus cash
            ct = CashTransaction(user)
            ct.deposit(amount, trans=bct.transaction)
示例#8
0
    def __update_accounts(self, place, contest, entry, amount):
        """
        Updates the accounts for Payout, FPP, Bonus, and Rake


        This gets run on each contest entry once the contest is finished.
         It:
            1. Withdraws the payout amount from escrow's account.
            2. Deposits that amount into the entry owner's account.
            3. Pays out any Frequent Player Points earned.
            4.


        :param place:
        :param contest:
        :param entry:
        :param amount:
        :return:
        """
        payout = Payout()
        payout.rank = place
        payout.contest = contest
        payout.entry = entry
        tm = CashTransaction(entry.lineup.user)
        tm.deposit(amount)
        #
        # Take cash out of escrow
        ct = CashTransaction(self.get_escrow_user())
        ct.withdraw(amount, tm.transaction)

        payout.transaction = tm.transaction
        payout.save()

        user = payout.entry.lineup.user
        rake_paid = contest.buyin * .10

        #
        # Pays out FPP
        lsm = LoyaltyStatusManager(user)
        fppt = FppTransaction(user)
        # rake * base loyalty multiplier * the multiplier
        fppt.deposit((rake_paid * lsm.BASE_LOYALTY_MULTIPLIER) *
                     lsm.get_fpp_multiplier(),
                     trans=ct.transaction)
        fpp = FPP()
        fpp.contest = contest
        fpp.transaction = ct.transaction
        fpp.save()

        #
        # convert the bonus_cash for the user
        self.__convert_bonus_cash(user, rake_paid, payout.transaction)

        #
        # Create a rake transaction for the user
        rpt = RakepaidTransaction(user)
        rpt.deposit(rake_paid, trans=payout.transaction)

        msg = "User[" + payout.entry.lineup.user.username + "] was ranked #" + str(
            payout.rank) + " for contest #" + str(
                payout.contest.pk) + " and was paid out."
        Logger.log(ErrorCodes.INFO, "Contest Payout", msg)
示例#9
0
    def __payout_contest(self, contest):
        """
        Method assumes the contest has never been paid out and is ready
        to be paid out. This is why the method is private and should be
        called from a separate method that does the individual error checking.
        """

        try:
            c = ClosedContest.objects.get(pk=contest.pk)
            # print('%s already closed & paid out.'%str(contest))
            return  # go no further
        except:
            pass

        #
        # get the prize pool ranks for the contest
        ranks = Rank.objects.filter(
            prize_structure=contest.prize_structure).order_by('rank')

        #
        # get the entries for the contest
        entries = Entry.objects.filter(contest=contest)
        entries = entries.order_by('-lineup__fantasy_points')

        #
        # Validate the ranks are setup properly
        for rank in ranks:
            #
            # verify the abstract amount is correct type
            if not isinstance(rank.amount, AbstractAmount):
                raise mysite.exceptions.IncorrectVariableTypeException(
                    type(self).__name__, 'rank')

            #
            # Get the transaction class and verify that it can deposit
            transaction_class = rank.amount.get_transaction_class()
            if not issubclass(transaction_class, CanDeposit):
                raise mysite.exceptions.IncorrectVariableTypeException(
                    type(self).__name__, 'transaction_class')

        # print('------- entries [%s] contest [%s] -------' %(str(len(entries)), str(contest)))
        #
        # perform the payouts by going through each entry and finding
        # ties and ranks for the ties to chop.
        # print('======= ranks [%s] =======' % (str(ranks)))

        #
        # we now need to check which is shorter: the list of ranks or the list of entries,
        # and only use that many ranks for calculating winners! (its possible for
        # fewer entries than the total number of ranks!

        if len(entries) < len(ranks):
            logger.info('SUPERLAY PAYOUT CONTEST: %s' % contest)

        i = 0
        while i < len(ranks[:len(entries)]):
            # print('++++ i (rank): %s +++++' % str(i) )
            entries_to_pay = list()
            ranks_to_pay = list()
            entries_to_pay.append(entries[i])
            ranks_to_pay.append(ranks[i])
            score = entries[i].lineup.fantasy_points
            #
            # For each tie add the user to the list to chop the payment
            # and add the next payout to be split with the ties.
            while i + 1 < len(entries) and score == entries[
                    i + 1].lineup.fantasy_points:
                i += 1
                entries_to_pay.append(entries[i])
                if len(ranks) > i:
                    ranks_to_pay.append(ranks[i])

            self.__payout_spot(ranks_to_pay, entries_to_pay, contest)
            i += 1

        #
        ###############################################################
        # rank all of the entries with the same payout algorithm.
        ###############################################################
        # j = 0
        # last_fantasy_points = None
        # for entry in entries:
        #     if last_fantasy_points is None:
        #         last_fantasy_points = entry.lineup.fantasy_points
        #     count_at_rank = Entry.objects.filter(contest=contest, lineup__fantasy_points=)

        # using the fantasy_points as the key, add/increment the entry id to the list.
        # the length of that list will be the # of entries at that rank, and
        # the rank will be the order of the keys.
        entry_fantasy_points_map = {}
        for entry in entries:
            try:
                entry_fantasy_points_map[entry.lineup.fantasy_points] += [
                    entry.pk
                ]
            except KeyError:
                entry_fantasy_points_map[entry.lineup.fantasy_points] = [
                    entry.pk
                ]
        # sort the fantasy points map on the map key (ascending)
        sorted_list = sorted(entry_fantasy_points_map.items(),
                             key=lambda x: x[0])
        #  so its descending ie: [(75.5, [432, 213]), (50.25, [431234, 234534]), (25.0, [1, 123])]
        sorted_list.reverse()

        entry_rank = 1
        for fantasy_points, entry_id_list in sorted_list:
            count_at_rank = len(entry_id_list)
            Entry.objects.filter(pk__in=entry_id_list).update(
                final_rank=entry_rank)
            entry_rank += count_at_rank

        # Determine what our net rake amount was.
        rake_transaction = None
        rake_post_overlay = calculate_rake(contest)

        # We made money on rake! No overlay, yaaay
        if rake_post_overlay > 0:
            #
            # Take cash out of escrow and deposit it into draftboard
            logger.info(('We made money on this contest, creating a Rake '
                         'transaction for $%s. contest: %s') %
                        (rake_post_overlay, contest))

            escrow_withdraw_trans = CashTransaction(self.get_escrow_user())
            escrow_withdraw_trans.withdraw(rake_post_overlay)
            draftboard_deposit_trans = CashTransaction(
                self.get_draftboard_user())
            draftboard_deposit_trans.deposit(
                rake_post_overlay, trans=escrow_withdraw_trans.transaction)
            rake_transaction = escrow_withdraw_trans.transaction

        # We lost money on rake. :(
        elif rake_post_overlay < 0:
            #
            # Take cash out of draftboard and deposit it into escrow
            logger.info(('We lost money on this contest, creating a Rake '
                         'transaction for $%s. contest: %s') %
                        (rake_post_overlay, contest))

            rake_post_overlay = abs(rake_post_overlay)
            draftboard_withdraw_trans = CashTransaction(
                self.get_draftboard_user())
            draftboard_withdraw_trans.withdraw(rake_post_overlay)
            escrow_deposit_trans = CashTransaction(self.get_escrow_user())
            escrow_deposit_trans.deposit(
                rake_post_overlay, trans=draftboard_withdraw_trans.transaction)
            rake_transaction = draftboard_withdraw_trans.transaction

        # We broke even on this contest, don't create a rake transaction below.
        elif rake_post_overlay == 0:
            logger.info(
                'No rake was collected, not creating a Rake transaction. contest: %s'
                % contest)

        if rake_transaction:
            # links the contest with the rake payout
            rake = Rake()
            rake.contest = contest
            rake.transaction = rake_transaction
            rake.save()

        contest.status = Contest.CLOSED
        contest.save()
示例#10
0
    def setUp(self):
        super().setUp()
        # ensure the default ticket
        TicketManager.create_default_ticket_amounts(verbose=False)
        # add funds to user
        self.user = self.get_basic_user()
        ct = CashTransaction(self.user)
        ct.deposit(100)

        # salary_generator = Dummy.generate_salaries()
        # self.salary_pool = salary_generator.pool
        # start
        #
        #
        self.verbose = True  # set to False to disable print statements

        #
        # The sport we are going to build fake stats for.
        # Lets use nfl, but it doesnt matter what sport we use
        self.sport = 'nfl'

        #
        # Ensure there are Games by using the Dummy to generate fake stats.
        # The ScheduleManager requires that Game objects exist
        # because when it creates scheduled Contest objects
        # it is required to create a draft group.
        self.dummy = Dummy(sport=self.sport)
        self.generator = self.dummy.generate()
        self.salary_pool = self.generator.pool
        self.site_sport = self.dummy.site_sport  # stash the site_sport for easy use

        self.site_sport_manager = SiteSportManager()
        self.game_model = self.site_sport_manager.get_game_class(
            self.site_sport)  # ie: sports.nfl.models.Game
        self.games = self.game_model.objects.all()  # there should be handful now, for today
        if self.games.count() <= 0:
            raise Exception(
                'buyin.tests.BuyinTest - we meant to create games.... but none were created!')
        # end

        # create a simple prize pool
        self.first = 100.0
        self.second = 50.0
        self.third = 25.0
        self.buyin = 10
        cps = CashPrizeStructureCreator(name='test')
        cps.add(1, self.first)
        cps.add(2, self.second)
        cps.add(3, self.third)
        cps.set_buyin(self.buyin)
        cps.save()
        cps.prize_structure.save()

        self.prize_structure = cps.prize_structure
        self.ranks = cps.ranks

        #
        # create the Contest
        # now = timezone.now()
        # start = DfsDateTimeUtil.create(now.date(), time(23,0))
        # end = DfsDateTimeUtil.create(now.date() + timedelta(days=1), time(0,0))
        start = self.games[0].start + timedelta(minutes=5)
        end = self.games[self.games.count() - 1].start  # set 'end' to start of last game
        cc = ContestCreator("test_contest", self.sport, self.prize_structure, start, end)
        self.draft_group2 = DraftGroup()
        self.draft_group2.salary_pool = self.salary_pool
        self.draft_group2.start = start
        self.draft_group2.end = end
        self.draft_group2.save()

        self.contest_pool, created = ContestPoolCreator(
            self.sport,
            self.prize_structure,
            start,
            (end - start).seconds * 60,
            self.draft_group2
        ).get_or_create()
        self.contest = cc.create()
        self.contest.status = Contest.RESERVABLE
        self.contest.save()

        self.draft_group = DraftGroup()
        self.draft_group.salary_pool = self.salary_pool
        self.draft_group.start = start
        self.draft_group.end = end
        self.draft_group.save()
示例#11
0
 def fund_user_account(self, user):
     ct = CashTransaction(user)
     ct.deposit(100)
示例#12
0
 def __get_trigger_transaction(self):
     ct = CashTransaction(self.user)
     ct.deposit(1)
     return ct.transaction
示例#13
0
    def buyin(self, contest_pool, lineup=None):
        """
        Creates the buyin for the user based on the ContestPool and lineup. Lineup can
        be null or not passed to allow for reserving contest spots.

        This should be only run in a task

        :param contest_pool: the ContestPool to register in
        :param lineup: assumed the lineup is validated on creation

        :raises :class:`contest.exceptions.ContestCouldNotEnterException`: When the
        there is a race condition and the contest cannot be entered after max_retries
        :raises :class:`contest.exceptions.ContestIsNotAcceptingLineupsException`: When
            contest_pool does not have a draftgroup, because it is not accepting teams yet.
        :raises :class:`contest.exceptions.ContestLineupMismatchedDraftGroupsException`:
            When the lineup was picked from a draftgroup that does not match the contest_pool.
        :raises :class:`contest.exceptions.ContestIsInProgressOrClosedException`: When
            The contest_pool has started, or its past the start time
        :raises :class:`contest.exceptions.LineupDoesNotMatchUser`: When the lineup
            is not owned by the user.
        :raises :class:`contest.exceptions.ContestMaxEntriesReachedException`: When the
            max entries is reached by the lineup.
        """

        # validate the contest and the lineup are allowed to be created
        self.lineup_contest(contest_pool, lineup)

        # Create either the ticket or cash transaction
        # Try to pay with a ticket first, if that doesn't work because the user doesn't have any
        # tickets, then try to pay with their cash balance.
        tm = TicketManager(self.user)
        # Get the transaction type - `ContestBuyin`
        # category = TransactionType.objects.get(pk=TransactionTypeConstants.ContestBuyin)

        try:
            tm.consume(amount=contest_pool.prize_structure.buyin)
            # keep a reference of the transaction.
            transaction = tm.transaction

        except (UserDoesNotHaveTicketException, ticket.models.TicketAmount.DoesNotExist):
            # Paying via Ticket failed, Create a cash transaciton with the type of 'ContestBuyin'.
            ct = CashTransaction(user=self.user)
            # Make the transaction a withdrawal.
            ct.withdraw(amount=contest_pool.prize_structure.buyin)
            # keep a reference of the transaction for user later.
            transaction = ct.transaction

        #
        # Transfer money into escrow
        escrow_ct = CashTransaction(self.get_escrow_user())
        escrow_ct.deposit(contest_pool.prize_structure.buyin, trans=transaction)

        #
        # Create the Entry
        entry = Entry()
        entry.contest_pool = contest_pool
        # entry.contest = contest # the contest will be set later when the ContestPool starts
        entry.contest = None
        entry.lineup = lineup
        entry.user = self.user
        entry.save()

        #
        # Create the Buyin model
        buyin = Buyin()
        buyin.contest_pool = contest_pool
        buyin.transaction = transaction
        # buyin.contest = contest # the contest will be set later when the ContestPool starts (?)
        buyin.contest = None
        buyin.entry = entry
        buyin.save()

        #
        # Increment the contest_entry variable
        contest_pool.current_entries = F('current_entries') + 1
        contest_pool.save()
        contest_pool.refresh_from_db()

        msg = "User[" + self.user.username + "] bought into the contest_pool #" \
              + str(contest_pool.pk) + " with entry #" + str(entry.pk)
        Logger.log(ErrorCodes.INFO, "ContestPool Buyin", msg)

        #
        # pusher contest updates because entries were changed
        ContestPoolPush(ContestPoolSerializer(contest_pool).data).send()
        return entry
示例#14
0
class RefundTest(AbstractTest, RefundBuildWorldMixin):
    def setUp(self):
        super().setUp()
        self.user1 = self.get_basic_user("test1")
        self.user2 = self.get_basic_user("test2")
        self.user3 = self.get_basic_user("test3")
        self.build_world()
        self.user1_ct = CashTransaction(self.user1)
        self.user1_ct.deposit(100)

        self.user2_ct = CashTransaction(self.user2)
        self.user2_ct.deposit(50)

        ta = TicketAmount.objects.get(amount=10.00)
        self.user3_tm = TicketManager(self.user3)
        self.user3_tm.deposit(10)

        self.escrow_user = self.user2_ct.get_escrow_user()

        self.escrow_ct = CashTransaction(self.escrow_user)

        bm = BuyinManager(self.user1)
        bm.buyin(self.contest_pool)

        bm = BuyinManager(self.user2)
        bm.buyin(self.contest_pool)

        bm = BuyinManager(self.user3)
        bm.buyin(self.contest_pool)
        Entry.objects.filter(contest_pool=self.contest_pool).update(
            contest=self.contest)

    def test_refund(self):

        self.assertEqual(self.user1_ct.get_balance_amount(), 90)
        self.assertEqual(self.user2_ct.get_balance_amount(), 40)
        self.assertEqual(self.escrow_ct.get_balance_amount(), 30)
        self.assertEqual(self.user3_tm.get_available_tickets().count(), 0)

        refund_manager = RefundManager()
        refund_manager.refund(self.contest, force=True)

        self.assertEqual(self.user1_ct.get_balance_amount(), 100)
        self.assertEqual(self.user2_ct.get_balance_amount(), 50)
        self.assertEqual(self.escrow_ct.get_balance_amount(), 0)
        self.assertEqual(self.user3_tm.get_available_tickets().count(), 1)

    # in progress should be refundable in cases
    # where its not a GPP and it did not fill
    # def test_contest_is_in_progress(self):
    #     self.contest.status = self.contest.INPROGRESS
    #     self.contest.save()
    #     self.should_raise_contest_is_in_progress_or_closed_exception()

    def test_contest_is_cancelled(self):
        self.contest.status = self.contest.CANCELLED
        self.contest.save()
        self.should_raise_contest_is_cancelled_or_closed()

    def test_contest_is_closed(self):
        self.contest.status = self.contest.CLOSED
        self.contest.save()
        self.should_raise_contest_is_cancelled_or_closed()

    # completed is an "in progress" status, which has
    # no more live inprogress games
    # def test_contest_is_completed(self):
    #     self.contest.status = self.contest.COMPLETED
    #     self.contest.save()
    #     self.should_raise_contest_is_in_progress_or_closed_exception()

    def should_raise_contest_is_cancelled_or_closed(self):

        self.assertEqual(self.user1_ct.get_balance_amount(), 90)
        self.assertEqual(self.user2_ct.get_balance_amount(), 40)
        self.assertEqual(self.escrow_ct.get_balance_amount(), 30)
        self.assertEqual(self.user3_tm.get_available_tickets().count(), 0)

        refund_manager = RefundManager()
        self.assertRaises(
            ContestCanNotBeRefunded,
            lambda: refund_manager.refund(self.contest, force=True))

        self.assertEqual(self.user1_ct.get_balance_amount(), 90)
        self.assertEqual(self.user2_ct.get_balance_amount(), 40)
        self.assertEqual(self.escrow_ct.get_balance_amount(), 30)
        self.assertEqual(self.user3_tm.get_available_tickets().count(), 0)
示例#15
0
 def test_create_first_deposit(self):
     ct = CashTransaction(self.user)
     ct.deposit( self.amount )
     self.assertAlmostEquals( ct.get_balance_amount(), self.amount )