Exemplo n.º 1
0
def do_budget_transfer(db_sess,
                       txn_date,
                       amount,
                       account,
                       from_budget,
                       to_budget,
                       notes=None):
    """
    Transfer a given amount from ``from_budget`` to ``to_budget`` on
    ``txn_date``. This method does NOT commit database changes. There are places
    where we rely on this function not committing changes.

    :param db_sess: active database session to use for queries
    :type db_sess: sqlalchemy.orm.session.Session
    :param txn_date: date to make the transfer Transactions on
    :type txn_date: datetime.date
    :param amount: amount of money to transfer
    :type amount: float
    :param account:
    :type account: biweeklybudget.models.account.Account
    :param from_budget:
    :type from_budget: biweeklybudget.models.budget_model.Budget
    :param to_budget:
    :type to_budget: biweeklybudget.models.budget_model.Budget
    :param notes: Notes to add to the Transaction
    :type notes: str
    :return: list of Transactions created for the transfer
    :rtype: :py:obj:`list` of :py:class:`~.Transaction` objects
    """
    desc = 'Budget Transfer - %s from %s (%d) to %s (%d)' % (
        amount, from_budget.name, from_budget.id, to_budget.name, to_budget.id)
    logger.info(desc)
    t1 = Transaction(date=txn_date,
                     budget_amounts={from_budget: amount},
                     budgeted_amount=amount,
                     description=desc,
                     account=account,
                     notes=notes,
                     planned_budget=from_budget)
    db_sess.add(t1)
    t2 = Transaction(date=txn_date,
                     budget_amounts={to_budget: (-1 * amount)},
                     budgeted_amount=(-1 * amount),
                     description=desc,
                     account=account,
                     notes=notes,
                     planned_budget=to_budget)
    db_sess.add(t2)
    t1.transfer = t2
    db_sess.add(t1)
    t2.transfer = t1
    db_sess.add(t2)
    db_sess.add(TxnReconcile(transaction=t1, note=desc))
    db_sess.add(TxnReconcile(transaction=t2, note=desc))
    return [t1, t2]
Exemplo n.º 2
0
    def submit(self, data):
        """
        Handle form submission; create or update models in the DB. Raises an
        Exception for any errors.

        :param data: submitted form data
        :type data: dict
        :return: message describing changes to DB (i.e. link to created record)
        :rtype: str
        """
        st_id = int(data['id'])
        st = db_session.query(ScheduledTransaction).get(st_id)
        d = datetime.strptime(data['payperiod_start_date'], '%Y-%m-%d').date()
        desc = 'Skip ScheduledTransaction %d in period %s' % (
            st_id, data['payperiod_start_date'])
        t = Transaction(date=d,
                        budget_amounts={st.budget: Decimal('0.0')},
                        budgeted_amount=Decimal('0.0'),
                        description=desc,
                        notes=data['notes'],
                        account=st.account,
                        scheduled_trans=st,
                        planned_budget=st.budget)
        db_session.add(t)
        db_session.add(TxnReconcile(transaction=t, note=desc))
        db_session.commit()
        logger.info(
            'Created Transaction %d to skip '
            'ScheduledTransaction %d', t.id, st.id)
        return 'Successfully created Transaction %d to skip ' \
               'ScheduledTransaction %d.' % (t.id, st.id)
 def test_0_update_db(self, testdb):
     b = testdb.query(Budget).get(4)
     b.current_balance = 1617.56
     testdb.add(b)
     acct = testdb.query(Account).get(1)
     budget = testdb.query(Budget).get(1)
     pp = BiweeklyPayPeriod.period_for_date(dtnow(), testdb)
     tdate = pp.start_date + timedelta(days=2)
     stmt1 = OFXStatement(account=acct,
                          filename='a1.ofx',
                          file_mtime=dtnow(),
                          as_of=dtnow(),
                          currency='USD',
                          acctid='1',
                          bankid='b1',
                          routing_number='r1')
     testdb.add(stmt1)
     o = OFXTransaction(account=acct,
                        statement=stmt1,
                        fitid='OFX8',
                        trans_type='Purchase',
                        date_posted=dtnow(),
                        amount=-600.0,
                        name='ofx8-trans4')
     testdb.add(o)
     t = Transaction(date=tdate,
                     actual_amount=600.00,
                     description='trans6',
                     account=acct,
                     budget=budget)
     testdb.add(t)
     testdb.add(TxnReconcile(transaction=t, ofx_trans=o))
     testdb.add(
         Transaction(date=tdate,
                     actual_amount=34000.00,
                     description='transFoo',
                     account=acct,
                     budget=budget))
     testdb.flush()
     testdb.commit()
 def test_2_add_data(self, testdb):
     acct = testdb.query(Account).get(1)
     budg = testdb.query(Budget).get(1)
     st_daynum = ScheduledTransaction(amount=111.11,
                                      description='ST_day_9',
                                      account=acct,
                                      budget=budg,
                                      day_of_month=9)
     testdb.add(st_daynum)
     t_daynum = Transaction(
         actual_amount=111.33,
         budgeted_amount=111.11,
         date=date(2017, 4, 9),
         description='Trans_ST_day_9',
         account=acct,
         budget=budg,
         scheduled_trans=st_daynum,
     )
     testdb.add(t_daynum)
     testdb.add(TxnReconcile(note='foo', transaction=t_daynum))
     st_pp1 = ScheduledTransaction(amount=222.22,
                                   description='ST_pp_1',
                                   account=acct,
                                   budget=budg,
                                   num_per_period=1)
     testdb.add(st_pp1)
     st_pp3 = ScheduledTransaction(amount=333.33,
                                   description='ST_pp_3',
                                   account=acct,
                                   budget=budg,
                                   num_per_period=3)
     testdb.add(st_pp3)
     t_pp3A = Transaction(
         actual_amount=333.33,
         budgeted_amount=333.33,
         date=date(2017, 4, 14),
         description='Trans_ST_pp_3_A',
         account=acct,
         budget=budg,
         scheduled_trans=st_pp3,
     )
     testdb.add(t_pp3A)
     t_pp3B = Transaction(actual_amount=333.33,
                          budgeted_amount=333.33,
                          date=date(2017, 4, 15),
                          description='Trans_ST_pp_3_B',
                          account=acct,
                          budget=budg,
                          scheduled_trans=st_pp3)
     testdb.add(t_pp3B)
     st_date = ScheduledTransaction(amount=444.44,
                                    description='ST_date',
                                    account=acct,
                                    budget=budg,
                                    date=date(2017, 4, 12))
     testdb.add(st_date)
     t_date = Transaction(actual_amount=444.44,
                          budgeted_amount=444.44,
                          date=date(2017, 4, 12),
                          description='Trans_ST_date',
                          account=acct,
                          budget=budg,
                          scheduled_trans=st_date)
     testdb.add(t_date)
     t_foo = Transaction(actual_amount=555.55,
                         date=date(2017, 4, 8),
                         description='Trans_foo',
                         account=acct,
                         budget=budg)
     testdb.add(t_foo)
     t_bar = Transaction(actual_amount=666.66,
                         date=date(2017, 4, 16),
                         description='Trans_bar',
                         account=acct,
                         budget=budg)
     testdb.add(t_bar)
     testdb.flush()
     testdb.commit()
Exemplo n.º 5
0
 def test_2_add_data(self, testdb):
     acct = testdb.query(Account).get(1)
     budg = testdb.query(Budget).get(1)
     budg2 = testdb.query(Budget).get(2)
     st_daynum = ScheduledTransaction(amount=Decimal('111.11'),
                                      description='ST_day_9',
                                      account=acct,
                                      budget=budg,
                                      day_of_month=9)
     testdb.add(st_daynum)
     t_daynum = Transaction(
         budget_amounts={budg: Decimal('111.33')},
         budgeted_amount=Decimal('111.11'),
         date=date(2017, 4, 9),
         description='Trans_ST_day_9',
         account=acct,
         planned_budget=budg,
         scheduled_trans=st_daynum,
     )
     testdb.add(t_daynum)
     testdb.add(TxnReconcile(note='foo', transaction=t_daynum))
     st_pp1 = ScheduledTransaction(amount=Decimal('222.22'),
                                   description='ST_pp_1',
                                   account=acct,
                                   budget=budg,
                                   num_per_period=1)
     testdb.add(st_pp1)
     st_pp3 = ScheduledTransaction(amount=Decimal('333.33'),
                                   description='ST_pp_3',
                                   account=acct,
                                   budget=budg,
                                   num_per_period=3)
     testdb.add(st_pp3)
     t_pp3A = Transaction(
         budget_amounts={budg: Decimal('333.33')},
         budgeted_amount=Decimal('333.33'),
         date=date(2017, 4, 14),
         description='Trans_ST_pp_3_A',
         account=acct,
         planned_budget=budg,
         scheduled_trans=st_pp3,
     )
     testdb.add(t_pp3A)
     t_pp3B = Transaction(budget_amounts={budg: Decimal('333.33')},
                          budgeted_amount=Decimal('333.33'),
                          date=date(2017, 4, 15),
                          description='Trans_ST_pp_3_B',
                          account=acct,
                          planned_budget=budg,
                          scheduled_trans=st_pp3)
     testdb.add(t_pp3B)
     st_date = ScheduledTransaction(amount=Decimal('444.44'),
                                    description='ST_date',
                                    account=acct,
                                    budget=budg,
                                    date=date(2017, 4, 12))
     testdb.add(st_date)
     t_date = Transaction(budget_amounts={budg: Decimal('444.44')},
                          budgeted_amount=Decimal('444.44'),
                          date=date(2017, 4, 12),
                          description='Trans_ST_date',
                          account=acct,
                          planned_budget=budg,
                          scheduled_trans=st_date)
     testdb.add(t_date)
     t_foo = Transaction(budget_amounts={budg: Decimal('555.55')},
                         date=date(2017, 4, 8),
                         description='Trans_foo',
                         account=acct)
     testdb.add(t_foo)
     t_bar = Transaction(budget_amounts={
         budg: Decimal('666.66'),
         budg2: Decimal('100.00')
     },
                         date=date(2017, 4, 16),
                         description='Trans_bar',
                         account=acct)
     testdb.add(t_bar)
     testdb.flush()
     testdb.commit()
Exemplo n.º 6
0
    def post(self):
        """
        Handle POST ``/ajax/reconcile``

        Request is a JSON dict with two keys, "reconciled" and "ofxIgnored".
        "reconciled" value is a dict of integer transaction ID keys, to
        values which are either a string reason why the Transaction is being
        reconciled as "No OFX" or a 2-item list of OFXTransaction acct_id
        and fitid.
        "ofxIgnored" is a dict with string keys which are strings identifying
        an OFXTransaction in the form "<ACCT_ID>%<FITID>", and values are a
        string reason why the OFXTransaction is being reconciled without a
        matching Transaction.

        Response is a JSON dict. Keys are ``success`` (boolean) and either
        ``error_message`` (string) or ``success_message`` (string).

        :return: JSON response
        """
        raw = request.get_json()
        data = {
            'reconciled':
            {int(x): raw['reconciled'][x]
             for x in raw['reconciled']},
            'ofxIgnored': raw.get('ofxIgnored', {})
        }
        logger.debug('POST /ajax/reconcile: %s', data)
        rec_count = 0
        for trans_id in sorted(data['reconciled'].keys()):
            trans = db_session.query(Transaction).get(trans_id)
            if trans is None:
                logger.error('Invalid transaction ID: %s', trans_id)
                return jsonify({
                    'success':
                    False,
                    'error_message':
                    'Invalid Transaction ID: %s' % trans_id
                }), 400
            if not isinstance(data['reconciled'][trans_id], type([])):
                # it's a string; reconcile without OFX
                db_session.add(
                    TxnReconcile(txn_id=trans_id,
                                 note=data['reconciled'][trans_id]))
                logger.info('Reconcile %s as NoOFX; note=%s', trans,
                            data['reconciled'][trans_id])
                rec_count += 1
                continue
            # else reconcile with OFX
            ofx_key = (data['reconciled'][trans_id][0],
                       data['reconciled'][trans_id][1])
            ofx = db_session.query(OFXTransaction).get(ofx_key)
            if ofx is None:
                logger.error('Invalid OFXTransaction: %s', ofx_key)
                return jsonify({
                    'success':
                    False,
                    'error_message':
                    'Invalid OFXTransaction: (%s, \'%s\')' %
                    (ofx_key[0], ofx_key[1])
                }), 400
            db_session.add(
                TxnReconcile(txn_id=trans_id,
                             ofx_account_id=data['reconciled'][trans_id][0],
                             ofx_fitid=data['reconciled'][trans_id][1]))
            logger.info('Reconcile %s with %s', trans, ofx)
            rec_count += 1
        # handle OFXTransactions to reconcile with no Transaction
        for ofxkey in sorted(data['ofxIgnored'].keys()):
            note = data['ofxIgnored'][ofxkey]
            acct_id, fitid = ofxkey.split('%', 1)
            db_session.add(
                TxnReconcile(ofx_account_id=acct_id,
                             ofx_fitid=fitid,
                             note=note))
            logger.info(
                'Reconcile OFXTransaction (%s, %s) as NoTransaction; note=%s',
                acct_id, fitid, note)
            rec_count += 1
        try:
            db_session.flush()
            db_session.commit()
        except Exception as ex:
            logger.error('Exception committing transaction reconcile',
                         exc_info=True)
            return jsonify({
                'success':
                False,
                'error_message':
                'Exception committing reconcile(s): %s' % ex
            }), 400
        return jsonify({
            'success':
            True,
            'success_message':
            'Successfully reconciled '
            '%d transactions' % rec_count
        })