First comment; the code
   withdrawal_amount % 2 != 0

checks for "evenness" in the sense of odd (1, 3, 5) vs even (2, 4, 6)
numbers.   This doesn't seem like what you'd want, since if your fee were
$5 and you withdraw $100, it would count it one way, but if the fee were $2
it would count it the other.  Only you can come up with the best heuristic
for yourself (based on your typical withdrawal amounts and typical fees)
but you might want to revisit this one.


as for not appending the posting, it's a bit hard to figure out from your
two code snippets since I can't tell where the 2nd code snippet lives in
relation to the 1st.  But I suspect that in the 2nd snippet, the "txn"
you're adding the postings to is not the one you expect.  If the 2nd
snippet actually lives in the context of the
                if "ATM" in description:
condition in the first snippet, then you're adding to "txn" before you
create the txn for this input.  That means you're probably adding it to the
txn from the previous iteration, which is still visible and accessible
(thanks, Python).

On Mon, Feb 26, 2024 at 7:11 AM CDT <docs...@gmail.com> wrote:

> I don't like to carry much cash when I travel for business, so from time
> to time I use my ATM card at a machine when I'm away and it charges a fee
> that is included in the total.
>
> ie - I get $500 cash but the full amount shows as one transaction of
> $507.99 (the fee isn't separated)
>
> I am trying to figure out a way to have this separated out when I use an
> Importer so that it would show up as $500 USD in cash and 7.99 USD in fees
>
> I have a working CSV import for my PNC transactions, but I can't seem to
> figure out how to include this.
>
> Here's the code I use at them moment (without the fees)...
>
> from beancount.ingest.importer import ImporterProtocol
> from beancount.core import data
> from beancount.core import amount
> from beancount.core.number import D
> from dateutil.parser import parse
> import datetime
> import csv
> import re
> import logging
> import warnings
> from beancount.core import flags
>
> class PNCChecking(ImporterProtocol):
>     def __init__(self, account, currency="USD"):
>         self.account = account
>         self.currency = currency
>         super().__init__()
>
>     def identify(self, file):
>         # Check if the file name contains "pnc checking" and ends with
> ".csv"
>         if "pnc checking" in file.name.lower() and file
> .name.lower().endswith('.csv'):
>             return True
>         else:
>             return False
>
>     def file_account(self, file):
>         return self.account
>
>     def extract(self, file):
>         entries = []
>
>         with open(file.name) as infile:
>             reader = csv.reader(infile)
>             next(reader, None)  # Skip the header row
>
>             for index, row in enumerate(reader):
>                 if len(row) < 5:  # Checking if the row has at least 5
> elements
>                     logging.warning("Row %d does not have enough
> elements. Skipping.", index)
>                     continue
>
>                 meta = data.new_metadata(file.name, index)  # Beancount
> meta
>                 date = datetime.datetime.strptime(row[0], "%m/%d/%Y").date()
>  # Parse the date of the transaction
>
>                 # Extracting relevant information from the description to
> determine payee and other_account
>                 description = row[1]  # Assuming description is in the
> second column
>                 payee = ""  # Placeholder, modify this based on the
> description
>                 other_account = "Expenses"  # Placeholder, modify this
> based on the description
>
>                 # Check if the description contains "CARD PMT"
>                 if "CARD PMT" in description:
>                     payee = "PNC Cash Rewards"
>                     other_account = "Liabilities:PNC:CashRewards"
>
>                 # Check if this is an ATM withdrawal
>                 # and then subtract their ATM Fee and put that in
> separate expense
>
>                 if "ATM" in description:
>                     other_account = "Assets:Cash"
>
>
>                 # Determining the credit/debit type based on withdrawals
> and deposits
>                 withdrawal = row[2]  # Withdrawals amount
>                 deposit = row[3]  # Deposits amount
>
>                 if withdrawal:
>                     units = -amount.Amount(D(withdrawal.replace('$', '')),
> self.currency)  # Using withdrawal amount
>                 elif deposit:
>                     units = amount.Amount(D(deposit.replace('$', '')),
> self.currency)  # Using deposit amount
>                 else:
>                     logging.warning("Invalid row: %s", row)
>                     continue
>
>                 account = self.account  # Modify this according to your
> data
>
>                 txn = data.Transaction(
>                     meta,
>                     date,
>                     None,  # No specific flag
>                     payee,
>                     description,
>                     data.EMPTY_SET,
>                     data.EMPTY_SET,
>                     [
>                         data.Posting(
>                             account, units, None, None, None, None
>                         ),
>                         data.Posting(
>                             other_account, -units, None, None, None, None
>                         ),
>                     ],
>                 )
>
>                 entries.append(txn)
>
>         return entries
>
> This works well and I get this kind of output...
>
> 2019-03-01 None "ATM WITHDRAWAL"
>   Assets:PNC:Checking  -407.99 USD
>   Assets:Cash          407.99 USD
>
> In the section where the ATM transactions are figured out through the
> descriptor I added this...
>
> # Check if this is an ATM withdrawal
> # and then subtract their ATM Fee and put that in separate expense
>
>                 if "ATM" in description:
>                     other_account = "Assets:Cash"
>
> # Calculate the ATM fee
>                     atm_fee_amount = D("0.00")  # Initialize ATM fee
> amount
>                     withdrawal_amount = abs(units.number)  # Absolute
> value of the withdrawal amount
>
>                     # Check if the withdrawal amount is greater than an
> even number
>                     if withdrawal_amount % 2 != 0:  # If it's not an even
> number
>                         atm_fee_amount = D(withdrawal_amount - (
> withdrawal_amount // 2) * 2)  # Calculate the fee as the remainder after
> division by 2
>
>                     # Adjust the ATM fee amount
>                     atm_fee_amount = -atm_fee_amount
>
>                     # Convert the ATM fee amount to a beancount.Amount
> object
>                     atm_fee = amount.Amount(atm_fee_amount, self.currency)
>                     # Create a posting for the ATM fee
>                     atm_fee_posting = data.Posting(
>                         "Expenses:BankFees:ATM", atm_fee_amount, None,
> None, None, None
>                     )
>                     # Adjust the units for the main account posting
>                     main_account_units = units + atm_fee_amount
>                     # Add the adjusted main account posting
>                     main_account_posting = data.Posting(
>                         account, main_account_units, None, None, None,
> None
>                     )
>                     txn.postings.append(main_account_posting)
>                     # Add the ATM fee posting to the transaction
>                     txn.postings.append(atm_fee_posting)
>
>  But I can't seem to get it to append properly to the postings.
>
> I'd like the output to look like this...
>
> 2019-03-01 None "ATM WITHDRAWAL"
>   Assets:PNC:Checking             -407.99 USD
>   Assets:Cash                               400.00 USD
>   Expenses:BankFees:ATM        -7.99 USD
>
> Any help would be appreciated.
>
> I'll probably figure it out eventually, but if anyone has done this
> already I would love to hear how you did it.  So far I have been sort of
> Frankensteining pieces from Beancount examples I've found so I my
> understanding of everything is not great yet.
>
>
> --
> You received this message because you are subscribed to the Google Groups
> "Beancount" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to beancount+unsubscr...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/beancount/6225321a-b905-4b23-ac5d-6eeb44289122n%40googlegroups.com
> <https://groups.google.com/d/msgid/beancount/6225321a-b905-4b23-ac5d-6eeb44289122n%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
>

-- 
You received this message because you are subscribed to the Google Groups 
"Beancount" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to beancount+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/beancount/CAFXPr0t%3DZAr%2B5JexNr%3Ds%3DXW5qUQdztTPNN47Yghz5opbMsPS5A%40mail.gmail.com.

Reply via email to