def _add_posting(self, transaction):
     split = transaction.narration.split(';')
     if len(split) >= 2:
         transaction_type = split[1].strip()
         create_simple_posting(transaction, self._mapping[transaction_type],
                               None, self.currency)
     return transaction
示例#2
0
    def gen_transaction(self, row):
        trade_type = row['交易类型']
        trade_amount = row['金额(元)'].replace('¥', '')
        trade_status = row['当前状态']
        trade_time = dateparser.parse(row['交易时间'])
        trade_account = row['支付方式']
        trade_partner = row['交易对方']
        trade_description = row['商品']
        trade_comment = row['备注']

        meta = {}
        meta['trade_time'] = row['交易时间']
        if trade_comment != '/':
            meta['comment'] = trade_comment

        entry = Transaction(meta, trade_time.strftime('%Y-%m-%d'), '*', trade_partner, trade_description,
                            data.EMPTY_SET,
                            data.EMPTY_SET, [])

        amount1 = trade_amount
        if row['收/支'] == '支出':
            amount1 = '-' + trade_amount
        data.create_simple_posting(entry, accounts.get_reality_account(row, 'wechat', trade_account), amount1,
                                       'CNY')

        if trade_type == '商户消费' or trade_type == '扫二维码付款':
            data.create_simple_posting(entry, accounts.get_account(trade_partner, trade_description, trade_time, trade_comment),
                                       trade_amount, 'CNY')


        return entry
示例#3
0
def test_json_to_entry():
    valid_accounts = ['Assets:US:ETrade:Cash', 'Assets:US:ETrade:GLD']
    json_txn = {
        'type': 'transaction',
        'date': '2017-12-12',
        'flag': '*',
        'payee': 'Test3',
        'narration': '',
        'metadata': {},
        'postings': [
            {
                'account': 'Assets:US:ETrade:Cash',
                'number': '100',
                'currency': 'USD',
            },
            {
                'account': 'Assets:US:ETrade:GLD',
            },
        ],
    }

    txn = Transaction({}, datetime.date(2017, 12, 12), '*', 'Test3', '',
                      frozenset(), frozenset(), [])
    create_simple_posting(txn, 'Assets:US:ETrade:Cash', '100', 'USD')
    create_simple_posting(txn, 'Assets:US:ETrade:GLD', None, None)
    assert json_to_entry(json_txn, valid_accounts) == txn
示例#4
0
def test_deserialise():
    postings = [
        {
            'account': 'Assets:ETrade:Cash',
            'number': '100',
            'currency': 'USD',
        },
        {
            'account': 'Assets:ETrade:GLD',
        },
    ]
    json_txn = {
        'type': 'Transaction',
        'date': '2017-12-12',
        'flag': '*',
        'payee': 'Test3',
        'narration': 'asdfasd #tag ^link',
        'meta': {},
        'postings': postings,
    }

    txn = Transaction({}, datetime.date(2017, 12, 12), '*', 'Test3', 'asdfasd',
                      frozenset(['tag']), frozenset(['link']), [])
    create_simple_posting(txn, 'Assets:ETrade:Cash', '100', 'USD')
    create_simple_posting(txn, 'Assets:ETrade:GLD', None, None)
    assert deserialise(json_txn) == txn

    with pytest.raises(KeyError):
        deserialise({})

    with pytest.raises(FavaAPIException):
        deserialise({'type': 'NoEntry'})
示例#5
0
def deserialise(json_entry):
    """Parse JSON to a Beancount entry.

    Args:
        json_entry: The entry.

    Raises:
        KeyError: if one of the required entry fields is missing.
        FavaAPIException: if the type of the given entry is not supported.
    """
    if json_entry['type'] == 'Transaction':
        date = util.date.parse_date(json_entry['date'])[0]
        narration, tags, links = extract_tags_links(json_entry['narration'])
        txn = data.Transaction(json_entry['meta'], date, json_entry['flag'],
                               json_entry['payee'], narration, tags, links, [])

        for posting in json_entry['postings']:
            data.create_simple_posting(txn, posting['account'],
                                       parse_number(posting.get('number')),
                                       posting.get('currency'))

        return txn
    if json_entry['type'] == 'Balance':
        date = util.date.parse_date(json_entry['date'])[0]
        number = parse_number(json_entry['number'])
        amount = Amount(number, json_entry['currency'])

        return data.Balance(json_entry['meta'], date, json_entry['account'],
                            amount, None, None)
    if json_entry['type'] == 'Note':
        date = util.date.parse_date(json_entry['date'])[0]
        comment = json_entry['comment'].replace('"', '')
        return data.Note(json_entry['meta'], date, json_entry['account'],
                         comment)
    raise FavaAPIException('Unsupported entry type.')
示例#6
0
    def create_sort_data(self):
        account = 'Assets:Bank:Checking'
        date1 = date(2014, 1, 15)
        date2 = date(2014, 1, 18)
        date3 = date(2014, 1, 20)
        entries = [
            data.Transaction(data.new_metadata(".", 1100), date3, FLAG, None,
                             "Next day", None, None, []),
            data.Close(data.new_metadata(".", 1000), date2, account),
            data.Balance(data.new_metadata(".", 1001), date2, account,
                         A('200.00 USD"'), None, None),
            data.Open(data.new_metadata(".", 1002), date2, account, 'USD',
                      None),
            data.Transaction(data.new_metadata(".", 1009), date2, FLAG, None,
                             "Transaction 2", None, None, []),
            data.Transaction(data.new_metadata(".", 1008), date2, FLAG, None,
                             "Transaction 1", None, None, []),
            data.Transaction(data.new_metadata(".", 900), date1, FLAG, None,
                             "Previous day", None, None, []),
        ]

        for entry in entries:
            if isinstance(entry, data.Transaction):
                data.create_simple_posting(entry, 'Assets:Bank:Checking',
                                           '123.45', 'USD')

        return entries
    def extract(self, file, existing_entries=None):
        self.initialize(file)
        counter = itertools.count()
        new_entries = []
        config = self.config

        self.read_file(file)
        for ot in self.get_transactions():
            metadata = data.new_metadata(file.name, next(counter))
            # metadata['file_account'] = self.file_account(None)
            # metadata['type'] = ot.type # Optional metadata, useful for debugging #TODO

            # description fields:
            # - beancount: (payee, narration):  # payee is optional, narration is mandatory
            # - OFX: ot.payee tends to be the "main" description field, while ot.memo is optional

            # Build transaction entry
            entry = data.Transaction(metadata, ot.date.date(), self.FLAG,
                                     self.get_transaction_type_desc(ot), ot.payee,
                                     data.EMPTY_SET, data.EMPTY_SET, [])
            data.create_simple_posting(entry, config['main_account'], ot.amount, self.currency)

            # TODO: Commented out so smart_importer can fill this in
            # target_acct = self.get_target_acct(ot)
            # data.create_simple_posting(entry, target_acct, None, None)

            new_entries.append(entry)

        if self.includes_balances:
            new_entries += self.extract_balance(file, counter)

        return(new_entries)
示例#8
0
def add_splitdata(handler,
                  transaction,
                  taccount,
                  tvalue,
                  tcurrency,
                  account=None,
                  value=-0.5):
    saccount = _get_valid_account_list_or_string(
        handler, account, msg=f"Choose split account for {taccount}")
    echo_info_params("Split account will be {}", [saccount])
    fraction = get_valid_fraction(None,
                                  msg="Choose split fraction",
                                  default=value)
    svalue = tvalue * fraction
    trvalue = f"{round(tvalue, 2):.2f}"
    srvalue = f"{round(svalue, 2):.2f}"

    if svalue != 0:
        echo_info_params(
            f"{trvalue} {tcurrency} will be split into {saccount} with value "
            f"{srvalue} {tcurrency}")

        # Add line.
        create_simple_posting(transaction,
                              account=saccount,
                              number=svalue,
                              currency=tcurrency)
    else:
        echo_info("Zero value split ignored")
示例#9
0
def test_deserialise():
    postings = [
        {"account": "Assets:ETrade:Cash", "amount": "100 USD"},
        {"account": "Assets:ETrade:GLD"},
    ]
    json_txn = {
        "type": "Transaction",
        "date": "2017-12-12",
        "flag": "*",
        "payee": "Test3",
        "narration": "asdfasd #tag ^link",
        "meta": {},
        "postings": postings,
    }

    txn = Transaction(
        {},
        datetime.date(2017, 12, 12),
        "*",
        "Test3",
        "asdfasd",
        frozenset(["tag"]),
        frozenset(["link"]),
        [],
    )
    create_simple_posting(txn, "Assets:ETrade:Cash", "100", "USD")
    create_simple_posting(txn, "Assets:ETrade:GLD", None, None)
    assert deserialise(json_txn) == txn

    with pytest.raises(KeyError):
        deserialise({})

    with pytest.raises(FavaAPIException):
        deserialise({"type": "NoEntry"})
示例#10
0
def test_serialise(app):
    assert serialise(None) is None
    txn = Transaction({}, datetime.date(2017, 12, 12), '*', 'Test3', 'asdfasd',
                      frozenset(['tag']), frozenset(['link']), [])
    create_simple_posting(txn, 'Assets:ETrade:Cash', '100', 'USD')
    create_simple_posting(txn, 'Assets:ETrade:GLD', None, None)

    json_txn = {
        'date': '2017-12-12',
        'flag': '*',
        'meta': {},
        'narration': 'asdfasd #tag ^link',
        'payee': 'Test3',
        'type': 'Transaction',
        'postings': [
            {
                'account': 'Assets:ETrade:Cash',
                'amount': '100 USD',
            },
            {
                'account': 'Assets:ETrade:GLD',
                'amount': '',
            },
        ],
    }

    with app.test_request_context():
        serialised = loads(dumps(serialise(txn)))
    assert serialised == json_txn
示例#11
0
def test_deserialise():
    postings = [
        {
            'account': 'Assets:ETrade:Cash',
            'amount': '100 USD',
        },
        {
            'account': 'Assets:ETrade:GLD',
        },
    ]
    json_txn = {
        'type': 'Transaction',
        'date': '2017-12-12',
        'flag': '*',
        'payee': 'Test3',
        'narration': 'asdfasd #tag ^link',
        'meta': {},
        'postings': postings,
    }

    txn = Transaction({}, datetime.date(2017, 12, 12), '*', 'Test3', 'asdfasd',
                      frozenset(['tag']), frozenset(['link']), [])
    create_simple_posting(txn, 'Assets:ETrade:Cash', '100', 'USD')
    create_simple_posting(txn, 'Assets:ETrade:GLD', None, None)
    assert deserialise(json_txn) == txn

    with pytest.raises(KeyError):
        deserialise({})

    with pytest.raises(FavaAPIException):
        deserialise({'type': 'NoEntry'})
示例#12
0
def test_serialise(app):
    assert serialise(None) is None
    txn = Transaction({}, datetime.date(2017, 12, 12), '*', 'Test3', 'asdfasd',
                      frozenset(['tag']), frozenset(['link']), [])
    create_simple_posting(txn, 'Assets:ETrade:Cash', '100', 'USD')
    create_simple_posting(txn, 'Assets:ETrade:GLD', None, None)

    json_txn = {
        'date':
        '2017-12-12',
        'flag':
        '*',
        'meta': {},
        'narration':
        'asdfasd #tag ^link',
        'payee':
        'Test3',
        'type':
        'Transaction',
        'postings': [
            {
                'account': 'Assets:ETrade:Cash',
                'amount': '100 USD',
            },
            {
                'account': 'Assets:ETrade:GLD',
                'amount': '',
            },
        ],
    }

    with app.test_request_context():
        serialised = loads(dumps(serialise(txn)))
    assert serialised == json_txn
示例#13
0
def test_serialise(app):
    assert serialise(None) is None
    txn = Transaction({}, datetime.date(2017, 12, 12), '*', 'Test3', 'asdfasd',
                      frozenset(['tag']), frozenset(['link']), [])
    create_simple_posting(txn, 'Assets:ETrade:Cash', '100', 'USD')
    create_simple_posting(txn, 'Assets:ETrade:GLD', None, None)

    json_txn = {
        'type': 'Transaction',
        'date': '2017-12-12',
        'flag': '*',
        'payee': 'Test3',
        'narration': 'asdfasd #tag ^link',
        'meta': {},
    }

    with app.test_request_context():
        serialised = loads(dumps(serialise(txn)))

    for key, value in json_txn.items():
        assert serialised[key] == value or str(serialised[key]) == value

    assert serialised['postings'][0]['account'] == 'Assets:ETrade:Cash'
    assert serialised['postings'][0]['units'] == {
        'currency': 'USD',
        'number': 100,
    }
 def add_fee_postings(self, entry, ot):
     config = self.config
     if hasattr(ot, 'fees') or hasattr(ot, 'commission'):
         if getattr(ot, 'fees', 0) != 0:
             data.create_simple_posting(entry, config['fees'], ot.fees,
                                        self.currency)
         if getattr(ot, 'commission', 0) != 0:
             data.create_simple_posting(entry, config['fees'],
                                        ot.commission, self.currency)
示例#15
0
def add_linedata(
    handler,
    transaction,
    account=None,
    value=None,
    no_value=False,
    splits=None,
    do_split=False,
):
    """Validate/prompt user for linedata."""
    taccount = _get_valid_account_list_or_string(handler,
                                                 account,
                                                 allow_other=True,
                                                 msg="Choose account")
    echo_info_params("Account will be {}", [taccount])

    # Get posting value.
    if not no_value:
        # Use value defined in template if present.
        tvalue, tcurrency = get_valid_payment(value,
                                              msg="Enter value",
                                              force_once=True,
                                              allow_empty=True)
    else:
        # Valueless line.
        tvalue = None
        tcurrency = None

    if tvalue is not None:
        echo_info_params("Value will be {} {}", [f"{tvalue:.2f}", tcurrency])
    else:
        echo_info("Value will be empty")

    # Add line.
    create_simple_posting(transaction,
                          account=taccount,
                          number=tvalue,
                          currency=tcurrency)

    # Get splits.
    if not no_value and tvalue is not None and do_split:
        if splits is not None:
            # Splits defined by template.
            for _, split in enumerate(splits, start=1):
                add_splitdata(handler, transaction, taccount, tvalue,
                              tcurrency, **split)
        else:
            splitno = 1
            splitting = click.confirm("Add split?", default=False)

            while splitting:
                echo_info_params("Adding split {}...", [splitno])
                add_splitdata(handler, transaction, taccount, tvalue,
                              tcurrency)
                splitting = click.confirm("Add another split?", default=False)
                splitno += 1
示例#16
0
    def process_deposit(self, entry):
        txn = new_txn(entry)
        assert len(
            entry["giveto"]) == 1, "More than one giveto action in an entry"
        giveto = entry["giveto"][0]
        amount = to_decimal(giveto["give"])
        initial_balance = to_decimal(giveto['account_money'])
        account = self.get_member(giveto["account_id"], giveto["account_name"],
                                  txn.date, initial_balance)

        bcdata.create_simple_posting(txn, account, -amount, "EUR")
        bcdata.create_simple_posting(txn, "Assets:Cash:Bar", amount, "EUR")
        self.entries.append(txn)
示例#17
0
def test_serialise(app):
    assert serialise(None) is None
    txn = Transaction(
        {},
        datetime.date(2017, 12, 12),
        "*",
        "Test3",
        "asdfasd",
        frozenset(["tag"]),
        frozenset(["link"]),
        [],
    )
    create_simple_posting(txn, "Assets:ETrade:Cash", "100", "USD")
    create_simple_posting(txn, "Assets:ETrade:GLD", None, None)

    json_txn = {
        "date":
        "2017-12-12",
        "flag":
        "*",
        "meta": {},
        "narration":
        "asdfasd #tag ^link",
        "payee":
        "Test3",
        "type":
        "Transaction",
        "postings": [
            {
                "account": "Assets:ETrade:Cash",
                "amount": "100 USD"
            },
            {
                "account": "Assets:ETrade:GLD",
                "amount": ""
            },
        ],
    }

    with app.test_request_context():
        serialised = loads(dumps(serialise(txn)))
        assert serialised == json_txn

        txn = txn._replace(payee="")
        json_txn["payee"] = ""
        serialised = loads(dumps(serialise(txn)))
        assert serialised == json_txn

        txn = txn._replace(payee=None)
        serialised = loads(dumps(serialise(txn)))
        assert serialised == json_txn
示例#18
0
    def __init__(self,
                 member: Member,
                 amount: decimal.Decimal,
                 date: typing.Optional[datetime.datetime] = None):
        super(DepositTxn, self).__init__(title="%s deposited €%s" %
                                         (member.display_name, amount),
                                         date=date,
                                         meta={
                                             "type": "deposit",
                                         })

        self.primary_account = member
        bcdata.create_simple_posting(self.txn, member.account, -amount, "EUR")
        bcdata.create_simple_posting(self.txn, CASH_ACCT, amount, "EUR")
示例#19
0
    def parse(self):
        transactions = []
        d = self.soup
        table = d.find(lambda a:a.name == "table" and "商户名称" in a.text and a.find('table') == None)
        trs = table.select('tr')
        for x in range(2, len(trs)):
            tds = trs[x].select('td')
            time = datetime.strptime(tds[1].text.strip(), '%Y-%m-%d')
            description = tds[4].text.strip()
            price_array = tds[5].text.strip().split('/')
            price = price_array[0]
            currency = self.get_currency(price_array[1])
            print("Importing {} at {}".format(description, time))
            account = get_account_by_guess(description, '', time)
            flag = "*"
            amount = float(price.replace(',', ''))
            if not '支出' in tds[6].text:
                amount = -amount
            if account == "Expenses:Unknown":
                flag = "!"
            meta = {}
            meta = data.new_metadata(
                'beancount/core/testing.beancount',
                12345,
                meta
            )
            counterparty = description
            if '-' in description:
                counterparty = description.split('-')[0]
                description = '-'.join(description.split('-')[1:])
            else:
                description = ''
            entry = Transaction(
                meta,
                date(time.year, time.month, time.day),
                flag,
                counterparty,
                description,
                data.EMPTY_SET,
                data.EMPTY_SET, []
            )
            data.create_simple_posting(entry, account, price, currency)
            data.create_simple_posting(entry, Account工商, None, None)
            if not self.deduplicate.find_duplicate(entry, -amount, None, Account工商):
                transactions.append(entry)

        self.deduplicate.apply_beans()
        transactions.reverse()
        return transactions
示例#20
0
 def __init__(self,
              payer: Member,
              payee: Member,
              amount: decimal.Decimal,
              date: typing.Optional[datetime.datetime] = None):
     super(TransferTxn,
           self).__init__(title="%s gave %s a gift of €%s" %
                          (payer.display_name, payee.display_name, amount),
                          date=date,
                          meta={
                              "type": "transfer",
                          })
     self.primary_account = payer
     bcdata.create_simple_posting(self.txn, payer.account, amount, "EUR")
     bcdata.create_simple_posting(self.txn, payee.account, -amount, "EUR")
示例#21
0
 def test_transaction_has_conversion(self):
     entry = self.create_empty_transaction()
     posting = data.create_simple_posting(entry, 'Assets:Bank:Checking',
                                          '123.45', 'USD')
     posting = posting._replace(price=A('153.02 CAD'))
     entry.postings[0] = posting
     self.assertTrue(data.transaction_has_conversion(entry))
示例#22
0
 def test_create_simple_posting(self):
     entry = self.create_empty_transaction()
     posting = data.create_simple_posting(entry, 'Assets:Bank:Checking',
                                          '123.45', 'USD')
     self.assertTrue(isinstance(posting, data.Posting))
     self.assertTrue(entry.postings)
     self.assertEqual(entry.postings[0], posting)
示例#23
0
 def test_get_entry(self):
     entry = self.create_empty_transaction()
     posting = data.create_simple_posting(entry, 'Assets:Bank:Checking',
                                          '123.45', 'USD')
     self.assertEqual(entry, data.get_entry(entry))
     self.assertEqual(entry, data.get_entry(data.TxnPosting(entry,
                                                            posting)))
示例#24
0
def test_null_meta_posting():
    FILTER.set('any(some_meta:"1")')

    txn = Transaction(
        {},
        datetime.date(2017, 12, 12),
        "*",
        "",
        "",
        None,
        None,
        [],
    )
    # This will create a posting with meta set to `None`.
    create_simple_posting(txn, "Assets:ETrade:Cash", "100", "USD")
    assert txn.postings[0].meta is None
    assert len(FILTER.apply([txn])) == 0
示例#25
0
    def __init__(self,
                 buyer: Member,
                 products: typing.List[typing.Tuple[Product, int]],
                 date: typing.Optional[datetime.datetime]=None):
        total_cost = decimal.Decimal("0.00")
        total_count = 0
        for product, count in products:
            total_count += count
            total_cost += count * product.price

        super(BuyTxn, self).__init__(
            title="%s bought %d items for €%s" % (buyer.display_name, total_count, total_cost),
            date=date,
            meta={
                "type": "purchase",
            })
        self.buyer = buyer
        charge = decimal.Decimal("0.00")
        paybacks = collections.defaultdict(lambda: decimal.Decimal("0.00"))

        for product, qty in products:
            charge += product.price * qty
            if product.payback is not None:
                paybacks[product.payback.account] += \
                    product.payback.amount * qty
            bcdata.create_simple_posting(
                self.txn, "Assets:Inventory:Bar",
                -qty, product.currency)
            bcdata.create_simple_posting(
                self.txn, buyer.account,
                qty, product.currency
            )
        bcdata.create_simple_posting(
            self.txn, buyer.account,
            charge, "EUR"
        )
        for payee, amt in paybacks.items():
            bcdata.create_simple_posting(
                self.txn, payee,
                -amt, "EUR"
            )
            charge -= amt
        bcdata.create_simple_posting(
            self.txn, "Income:Bar",
            -charge, "EUR",
        )
    def parse(self):
        d = self.soup
        tables = d.select('#loopBand2>table>tr')
        currencies_count = int(len(tables) / 4)
        transactions = []
        for x in range(0, currencies_count):
            title = tables[x * 4]
            contents = tables[x * 4 + 3]
            currency = title.select('#fixBand29 td>table td')[1].text.strip()
            currency = self.get_currency(currency)
            bands = contents.select('#loopBand3>table>tr')
            for band in bands:
                tds = band.select(
                    'td>table>tr>td #fixBand9>table>tr>td>table>tr>td')
                time = self.get_date(tds[1].text.strip())
                description = tds[3].text.strip()
                price = tds[4].text.strip()
                print("Importing {} at {}".format(description, time))
                account = get_account_by_guess(description, '', time)
                flag = "*"
                amount = float(price.replace(',', ''))
                if account == "Unknown":
                    flag = "!"
                meta = {}
                meta = data.new_metadata(
                    'beancount/core/testing.beancount',
                    12345,
                    meta
                )
                entry = Transaction(
                    meta,
                    time,
                    flag,
                    description,
                    None,
                    data.EMPTY_SET,
                    data.EMPTY_SET, []
                )
                data.create_simple_posting(entry, account, price, currency)
                data.create_simple_posting(entry, Account民生, None, None)
                if not self.deduplicate.find_duplicate(entry, -amount, None, Account民生):
                    transactions.append(entry)

        self.deduplicate.apply_beans()
        return transactions
    def generate_transfer_entry(self, ot, file, counter):
        """ Cash or in-kind transfers. One of:
            [credit, debit, dep, transfer, income, dividends, capgains_lt, capgains_st, other]"""
        config = self.config
        metadata = data.new_metadata(file.name, next(counter))
        target_acct = self.get_target_acct(ot)
        date = getattr(ot, 'tradeDate', None)
        if not date:
            date = ot.date
        date = date.date()

        try:
            if ot.type in ['transfer']:
                units = ot.units
            elif ot.type in ['other', 'credit', 'debit', 'dep', 'cash']:
                units = ot.amount
            else:
                units = ot.total
        except AttributeError:
            print("Could not determine field for transaction amount")
            # import pdb; pdb.set_trace()

        if ot.type in [
                'income', 'dividends', 'capgains_lt', 'capgains_st', 'transfer'
        ] and (hasattr(ot, 'security') and ot.security):
            ticker, ticker_long_name = self.get_ticker_info(ot.security)
            description = f'[{ticker}] {ticker_long_name}'
            if ot.type in [
                    'income', 'dividends', 'capgains_st', 'capgains_lt'
            ]:  # no need to do this for transfers
                target_acct = self.commodity_leaf(
                    target_acct, ticker)  # book to Income:Dividends:HOOLI
        else:  # cash transfer
            description = ot.type
            ticker = self.currency

        # Build transaction entry
        entry = data.Transaction(metadata, date, self.FLAG, ot.memo,
                                 description, data.EMPTY_SET, data.EMPTY_SET,
                                 [])

        # Build postings
        if ot.type in ['income', 'dividends', 'capgains_st',
                       'capgains_lt']:  # cash
            data.create_simple_posting(entry, self.cash_account, ot.total,
                                       self.currency)
            data.create_simple_posting(entry, target_acct, -1 * ot.total,
                                       self.currency)
        else:
            main_acct = self.commodity_leaf(config['main_account'], ticker)
            data.create_simple_posting(entry, main_acct, units, ticker)
            data.create_simple_posting(entry, target_acct, -1 * units, ticker)
        return entry
示例#28
0
    def process_buy(self, entry):
        txn = new_txn(entry)
        amount = to_decimal(entry["products_totalprice"])
        if amount < 0:
            amount = -amount

        assert len(entry["takefrom"]) == 1, "More than one account in takefrom"

        takefrom = entry["takefrom"][0]
        initial_balance = to_decimal(takefrom["account_money"])
        account_name = self.get_member(takefrom["account_id"],
                                       takefrom["account_name"], txn.date,
                                       initial_balance)

        total_giveto_amount = to_decimal(0)
        if "giveto" in entry:
            for giveto in entry["giveto"]:
                giveto_account = self.get_member(
                    giveto["account_id"], giveto["account_name"], txn.date,
                    to_decimal(giveto["account_money"]))
                giveto_amount = to_decimal(giveto["account_money_give"])
                total_giveto_amount += giveto_amount
                bcdata.create_simple_posting(txn, giveto_account,
                                             -giveto_amount, "EUR")
        bcdata.create_simple_posting(txn, account_name, amount, "EUR")
        bcdata.create_simple_posting(txn, "Income:Bar",
                                     total_giveto_amount - amount, "EUR")

        products = {}
        for product in entry["products"]:
            product_currency = product_types_inverse[product["product_name"]]
            products[product_currency] = products.get(product_currency, 0) + 1

        assert sum(products.values()) == entry["products_amount"]
        for product_currency, qty in products.items():
            bcdata.create_simple_posting(txn, "Assets:Inventory:Bar", -qty,
                                         product_currency)
            bcdata.create_simple_posting(txn, account_name, qty,
                                         product_currency)

        # TODO: Report products purchased

        self.entries.append(txn)
示例#29
0
 def transfer_opening_balances(self):
     for account, balance in self.initial_balances.items():
         if balance is None:
             continue
         if balance == 0:
             continue
         txn = bcdata.Transaction(
             meta={},
             date=datetime.date(1970, 1, 1),
             flag="txn",
             payee="",
             narration="Initial balance transfer for " + account,
             tags=set(),
             links=set(),
             postings=[])
         bcdata.create_simple_posting(txn, account, -balance, "EUR")
         bcdata.create_simple_posting(txn, "Assets:InitialBalances",
                                      balance, "EUR")
         yield txn
示例#30
0
def json_to_entry(json_entry, valid_accounts):
    """Parse JSON to a Beancount entry."""
    # pylint: disable=not-callable
    date = util.date.parse_date(json_entry['date'])[0]
    if json_entry['type'] == 'transaction':
        narration, tags, links = extract_tags_links(json_entry['narration'])
        txn = data.Transaction(json_entry['metadata'], date,
                               json_entry['flag'], json_entry['payee'],
                               narration, tags, links, [])

        if not json_entry.get('postings'):
            raise FavaAPIException('Transaction contains no postings.')

        for posting in json_entry['postings']:
            if posting['account'] not in valid_accounts:
                raise FavaAPIException('Unknown account: {}.'.format(
                    posting['account']))
            data.create_simple_posting(txn, posting['account'],
                                       posting.get('number') or None,
                                       posting.get('currency'))

        return txn
    elif json_entry['type'] == 'balance':
        if json_entry['account'] not in valid_accounts:
            raise FavaAPIException('Unknown account: {}.'.format(
                json_entry['account']))
        number = D(json_entry['number'])
        amount = Amount(number, json_entry.get('currency'))

        return data.Balance(json_entry['metadata'], date,
                            json_entry['account'], amount, None, None)
    elif json_entry['type'] == 'note':
        if json_entry['account'] not in valid_accounts:
            raise FavaAPIException('Unknown account: {}.'.format(
                json_entry['account']))

        if '"' in json_entry['comment']:
            raise FavaAPIException('Note contains double-quotes (")')

        return data.Note(json_entry['metadata'], date, json_entry['account'],
                         json_entry['comment'])
    else:
        raise FavaAPIException('Unsupported entry type.')
示例#31
0
 def get_member(self, id, name, date, balance):
     #name = member_account(name)
     name = self.accounts_by_id[id]
     if id in self.accounts_by_id and self.accounts_by_id[id] != name:
         # Generate rename
         txn = bcdata.Transaction(meta={},
                                  date=date,
                                  flag="txn",
                                  payee="",
                                  narration="Rename %(from)s to %(to)s" % {
                                      "from": self.accounts_by_id[id],
                                      "to": name,
                                  },
                                  tags=set("rename"),
                                  links=set(),
                                  postings=[])
         bcdata.create_simple_posting(txn, self.accounts_by_id[id], balance,
                                      "EUR")
         bcdata.create_simple_posting(txn, name, -balance, "EUR")
         self.entries.append(txn)
         if self.accounts_by_id[id] not in DONT_CLOSE:
             self.entries.append(
                 bcdata.Close(
                     meta={},
                     date=date + datetime.timedelta(days=1),
                     account=self.accounts_by_id[id],
                 ))
         # Disable the balance assertion for the next day
         self.last_assertion[name] = date
         self.initial_balances[name] = None
         self.accounts_by_id[id] = name
     elif name not in self.initial_balances:
         self.initial_balances[name] = balance
         self.last_assertion[name] = date
         self.accounts_by_id[id] = name
     elif self.last_assertion.get(
             name, None) != date and name != "Assets:Cash:Bar":
         self.entries.append(
             bcdata.Balance({"iline": str(self.line)}, date, name,
                            bcdata.Amount(-balance, "EUR"), None, None))
         self.last_assertion[name] = date
     return name
示例#32
0
    def test_has_entry_account_component(self):
        entry = data.Transaction(data.new_metadata(".", 0),
                                 datetime.date.today(), FLAG, None,
                                 "Something", None, None, [])
        data.create_simple_posting(entry, 'Liabilities:US:CreditCard', '-50',
                                   'USD')
        data.create_simple_posting(entry, 'Expenses:Food:Restaurant', '50',
                                   'USD')

        has_component = data.has_entry_account_component
        self.assertTrue(has_component(entry, 'US'))
        self.assertFalse(has_component(entry, 'CA'))

        self.assertTrue(has_component(entry, 'CreditCard'))
        self.assertTrue(has_component(entry, 'Liabilities'))
        self.assertFalse(has_component(entry, 'Assets'))

        self.assertTrue(has_component(entry, 'Restaurant'))
        self.assertTrue(has_component(entry, 'Food'))
        self.assertTrue(has_component(entry, 'Expenses'))
        self.assertFalse(has_component(entry, 'Equity'))