reconcile.helper: Add entry_point to avoid traceback
This commit is contained in:
		
							parent
							
								
									026f54bca1
								
							
						
					
					
						commit
						71bf8137a5
					
				
					 1 changed files with 115 additions and 105 deletions
				
			
		|  | @ -114,7 +114,7 @@ def reconciliation_report_path(account, end_date): | ||||||
|     return f'Financial/Controls/Reports-for-Treasurer/{end_date}_{account_name}_bank-reconciliation.csv' |     return f'Financial/Controls/Reports-for-Treasurer/{end_date}_{account_name}_bank-reconciliation.csv' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def parse_args(): | def parse_args(argv): | ||||||
|     parser = argparse.ArgumentParser(description='Reconciliation helper') |     parser = argparse.ArgumentParser(description='Reconciliation helper') | ||||||
|     parser.add_argument('--beancount-file', required=True) |     parser.add_argument('--beancount-file', required=True) | ||||||
|     parser.add_argument('--account', help='Full account name, e.g. "Liabilities:CreditCard:AMEX"', required=True) |     parser.add_argument('--account', help='Full account name, e.g. "Liabilities:CreditCard:AMEX"', required=True) | ||||||
|  | @ -126,7 +126,7 @@ def parse_args(): | ||||||
|     parser.add_argument('--cost-function', default='COST') |     parser.add_argument('--cost-function', default='COST') | ||||||
|     parser.add_argument('--grep-output-filename') |     parser.add_argument('--grep-output-filename') | ||||||
|     # parser.add_argument('--report-group-regex') |     # parser.add_argument('--report-group-regex') | ||||||
|     args = parser.parse_args() |     args = parser.parse_args(args=argv[1:]) | ||||||
|     if args.month or args.period: |     if args.month or args.period: | ||||||
|         if not (args.month and args.period): |         if not (args.month and args.period): | ||||||
|             parser.error('--month and --period must be used together') |             parser.error('--month and --period must be used together') | ||||||
|  | @ -140,108 +140,118 @@ def beancount_file_exists(path): | ||||||
|     return os.path.isfile(path) |     return os.path.isfile(path) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| args = parse_args() | def main(args): | ||||||
| if not beancount_file_exists(args.beancount_file): |     if not beancount_file_exists(args.beancount_file): | ||||||
|     sys.exit(f'Beancount file does not exist: {args.beancount_file}') |         sys.exit(f'Beancount file does not exist: {args.beancount_file}') | ||||||
| if args.month or args.period: |     if args.month or args.period: | ||||||
|     parsed_date = datetime.datetime.strptime(args.month, '%Y-%m').date() |         parsed_date = datetime.datetime.strptime(args.month, '%Y-%m').date() | ||||||
|     preDate = end_of_month(parsed_date - relativedelta(months=args.period)).isoformat() |         preDate = end_of_month(parsed_date - relativedelta(months=args.period)).isoformat() | ||||||
|     lastDateInPeriod = end_of_month(parsed_date).isoformat() |         lastDateInPeriod = end_of_month(parsed_date).isoformat() | ||||||
|     month = args.month |         month = args.month | ||||||
| else: |  | ||||||
|     preDate = args.prev_end_date |  | ||||||
|     lastDateInPeriod = args.cur_end_date.isoformat() |  | ||||||
|     month = args.cur_end_date.strftime('%Y-%m') |  | ||||||
| grep_output_file: typing.IO |  | ||||||
| if args.grep_output_filename: |  | ||||||
|     grep_output_file = open(args.grep_output_filename, 'w') |  | ||||||
| else: |  | ||||||
|     grep_output_file = tempfile.NamedTemporaryFile(prefix='bc-reconcile-grep-output_', mode='w', delete=False) |  | ||||||
| beancount_file = args.beancount_file |  | ||||||
| account = args.account |  | ||||||
| cost_function = args.cost_function |  | ||||||
| statement_match = args.statement_match if args.statement_match else month |  | ||||||
| 
 |  | ||||||
| QUERIES = { |  | ||||||
|     f"00: CLEARED BAL ENDING DAY BEFORE {preDate}": |  | ||||||
|     # $CONLEDGER -V -C -e "$preDate" bal "/$acct/" |  | ||||||
|     f"""SELECT sum({cost_function}(position)) AS aa WHERE account = "{account}" |  | ||||||
|         AND date < {preDate} AND META('bank-statement') != NULL""", |  | ||||||
| 
 |  | ||||||
|     f"01: ALL TRANSACTION BAL ENDING DAY BEFORE {preDate}": |  | ||||||
|     # $CONLEDGER -V -e "$preDate" bal "/$acct/" |  | ||||||
|     f"""SELECT sum({cost_function}(position)) AS aa WHERE account = "{account}" |  | ||||||
|         AND date < {preDate}""", |  | ||||||
| 
 |  | ||||||
|     f"02: ALL TRANSACTION BAL, ending {lastDateInPeriod}": |  | ||||||
|     # $CONLEDGER -V -e "$date" bal "/$acct/" |  | ||||||
|     f"""SELECT sum({cost_function}(position)) AS aa WHERE account = "{account}" |  | ||||||
|     AND date <= {lastDateInPeriod}""", |  | ||||||
| 
 |  | ||||||
|     f"03: UNCLEARED TRANSACTIONS, ending {lastDateInPeriod}": |  | ||||||
|     f"""SELECT date, {cost_function}(position) as amt, ANY_META('check-id') as chknum, narration, payee, ENTRY_META('code') as code |  | ||||||
|     WHERE account = "{account}" |  | ||||||
|     AND date <= {lastDateInPeriod} AND META('bank-statement') = NULL |  | ||||||
|     ORDER BY date, payee, narration""", |  | ||||||
| 
 |  | ||||||
|     "04: UNCLEARED TRANSACTION FILE, SUITABLE FOR GREP": |  | ||||||
|     # $CONLEDGER -w -F "%(filename):%(beg_line): %(date) %(code) %(payee) %(amount)\n" --sort d -U -e "$date" reg "/$acct/" > "$TMPDIR/unreconciled-lines" |  | ||||||
|     f"""SELECT  ENTRY_META('filename') as file, META('lineno') as line, date, |  | ||||||
|     {cost_function}(position) as amt, ANY_META('check-id') as chknum, narration, payee, ANY_META("entity") as entity, ENTRY_META('code') as c |  | ||||||
|     WHERE account = "{account}" |  | ||||||
|     AND date <= {lastDateInPeriod} AND META('bank-statement') = NULL |  | ||||||
|     ORDER BY date, payee, narration""", |  | ||||||
| 
 |  | ||||||
|     f"05: CLEARED BALANCE ending {lastDateInPeriod}": |  | ||||||
|     # $CONLEDGER -V -C -e "$date" bal "/$acct/" |  | ||||||
|     f"""SELECT sum({cost_function}(position)) AS aa WHERE account = "{account}" |  | ||||||
|     AND date <= {lastDateInPeriod} AND META('bank-statement') != NULL""", |  | ||||||
| 
 |  | ||||||
|     f"06: CLEARED SUBTRACTIONS on {month}'s statement": |  | ||||||
|     # $CONLEDGER -V -C --limit "a > 0 and tag(\"Statement\") =~ /$statementSearchString/" bal "/$acct/" |  | ||||||
|     f"""SELECT  sum(number({cost_function}(position))) AS aa |  | ||||||
|     WHERE account = "{account}" |  | ||||||
|     and META("bank-statement") ~  "{statement_match}" and number({cost_function}(position)) < 0""", |  | ||||||
| 
 |  | ||||||
|     f"07: CLEARED ADDITIONS on {month}'s statement": |  | ||||||
|     # $CONLEDGER -V -C --limit "a < 0 and tag(\"Statement\") =~ /$statementSearchString/" bal "/$acct/" |  | ||||||
|     f"""SELECT  sum(number({cost_function}(position))) AS aa |  | ||||||
|     WHERE account = "{account}" |  | ||||||
|     and META("bank-statement") ~  "{statement_match}" and number({cost_function}(position)) > 0""", |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| # Run Beancount queries. |  | ||||||
| print(f"START RECONCILIATION FOR {account} ENDING {lastDateInPeriod} (previous end date {preDate})") |  | ||||||
| entries, _, options = loader.load_file(beancount_file) |  | ||||||
| uncleared_rows = []  # Hack to capture results of query 03. |  | ||||||
| cleared_balance = decimal.Decimal('0') |  | ||||||
| all_trans_balance = decimal.Decimal('0') |  | ||||||
| for desc, query in QUERIES.items(): |  | ||||||
|     rtypes, rrows = run_query(entries, options, query, numberify=True) |  | ||||||
|     if not rrows: |  | ||||||
|         print(f'{desc:<55} {"N/A":>11}') |  | ||||||
|     elif desc.startswith('04'): |  | ||||||
|         homedir = os.getenv('HOME', '') |  | ||||||
|         print(f'{desc}\n   See {grep_output_file.name}') |  | ||||||
|         grep_rows = [format_record_for_grep(row, homedir) for row in rrows] |  | ||||||
|         print(tabulate(grep_rows), file=grep_output_file) |  | ||||||
|     elif len(rrows) == 1 and isinstance(rrows[0][0], decimal.Decimal): |  | ||||||
|         result = rrows[0][0] |  | ||||||
|         print(f'{desc:<55} {result:11,.2f}') |  | ||||||
|         if desc.startswith('02'): |  | ||||||
|             all_trans_balance = result |  | ||||||
|         if desc.startswith('05'): |  | ||||||
|             cleared_balance = result |  | ||||||
|     else: |     else: | ||||||
|         headers = [c[0].capitalize() for c in rtypes] |         preDate = args.prev_end_date | ||||||
|         if desc.startswith('03'): |         lastDateInPeriod = args.cur_end_date.isoformat() | ||||||
|             uncleared_rows = rrows |         month = args.cur_end_date.strftime('%Y-%m') | ||||||
|         print(desc) |     grep_output_file: typing.IO | ||||||
|         print(textwrap.indent(tabulate(rrows, headers=headers), '    ')) |     if args.grep_output_filename: | ||||||
|  |         grep_output_file = open(args.grep_output_filename, 'w') | ||||||
|  |     else: | ||||||
|  |         grep_output_file = tempfile.NamedTemporaryFile(prefix='bc-reconcile-grep-output_', mode='w', delete=False) | ||||||
|  |     beancount_file = args.beancount_file | ||||||
|  |     account = args.account | ||||||
|  |     cost_function = args.cost_function | ||||||
|  |     statement_match = args.statement_match if args.statement_match else month | ||||||
| 
 | 
 | ||||||
| uncleared = [(r[0], r[2], r[4] or r[3], r[1]) for r in uncleared_rows] |     QUERIES = { | ||||||
| report_path = os.path.join(os.getenv('CONSERVANCY_REPOSITORY', ''), reconciliation_report_path(account, lastDateInPeriod)) |         f"00: CLEARED BAL ENDING DAY BEFORE {preDate}": | ||||||
| # TODO: Make the directory if it doesn't exist. |         # $CONLEDGER -V -C -e "$preDate" bal "/$acct/" | ||||||
| with open(report_path, 'w') as f: |         f"""SELECT sum({cost_function}(position)) AS aa WHERE account = "{account}" | ||||||
|     f.write(reconciliation_report(account, lastDateInPeriod, cleared_balance, uncleared, '1900-01-01', all_trans_balance, [])) |             AND date < {preDate} AND META('bank-statement') != NULL""", | ||||||
| print(f'Wrote reconciliation report: {report_path}.') | 
 | ||||||
|  |         f"01: ALL TRANSACTION BAL ENDING DAY BEFORE {preDate}": | ||||||
|  |         # $CONLEDGER -V -e "$preDate" bal "/$acct/" | ||||||
|  |         f"""SELECT sum({cost_function}(position)) AS aa WHERE account = "{account}" | ||||||
|  |             AND date < {preDate}""", | ||||||
|  | 
 | ||||||
|  |         f"02: ALL TRANSACTION BAL, ending {lastDateInPeriod}": | ||||||
|  |         # $CONLEDGER -V -e "$date" bal "/$acct/" | ||||||
|  |         f"""SELECT sum({cost_function}(position)) AS aa WHERE account = "{account}" | ||||||
|  |         AND date <= {lastDateInPeriod}""", | ||||||
|  | 
 | ||||||
|  |         f"03: UNCLEARED TRANSACTIONS, ending {lastDateInPeriod}": | ||||||
|  |         f"""SELECT date, {cost_function}(position) as amt, ANY_META('check-id') as chknum, narration, payee, ENTRY_META('code') as code | ||||||
|  |         WHERE account = "{account}" | ||||||
|  |         AND date <= {lastDateInPeriod} AND META('bank-statement') = NULL | ||||||
|  |         ORDER BY date, payee, narration""", | ||||||
|  | 
 | ||||||
|  |         "04: UNCLEARED TRANSACTION FILE, SUITABLE FOR GREP": | ||||||
|  |         # $CONLEDGER -w -F "%(filename):%(beg_line): %(date) %(code) %(payee) %(amount)\n" --sort d -U -e "$date" reg "/$acct/" > "$TMPDIR/unreconciled-lines" | ||||||
|  |         f"""SELECT  ENTRY_META('filename') as file, META('lineno') as line, date, | ||||||
|  |         {cost_function}(position) as amt, ANY_META('check-id') as chknum, narration, payee, ANY_META("entity") as entity, ENTRY_META('code') as c | ||||||
|  |         WHERE account = "{account}" | ||||||
|  |         AND date <= {lastDateInPeriod} AND META('bank-statement') = NULL | ||||||
|  |         ORDER BY date, payee, narration""", | ||||||
|  | 
 | ||||||
|  |         f"05: CLEARED BALANCE ending {lastDateInPeriod}": | ||||||
|  |         # $CONLEDGER -V -C -e "$date" bal "/$acct/" | ||||||
|  |         f"""SELECT sum({cost_function}(position)) AS aa WHERE account = "{account}" | ||||||
|  |         AND date <= {lastDateInPeriod} AND META('bank-statement') != NULL""", | ||||||
|  | 
 | ||||||
|  |         f"06: CLEARED SUBTRACTIONS on {month}'s statement": | ||||||
|  |         # $CONLEDGER -V -C --limit "a > 0 and tag(\"Statement\") =~ /$statementSearchString/" bal "/$acct/" | ||||||
|  |         f"""SELECT  sum(number({cost_function}(position))) AS aa | ||||||
|  |         WHERE account = "{account}" | ||||||
|  |         and META("bank-statement") ~  "{statement_match}" and number({cost_function}(position)) < 0""", | ||||||
|  | 
 | ||||||
|  |         f"07: CLEARED ADDITIONS on {month}'s statement": | ||||||
|  |         # $CONLEDGER -V -C --limit "a < 0 and tag(\"Statement\") =~ /$statementSearchString/" bal "/$acct/" | ||||||
|  |         f"""SELECT  sum(number({cost_function}(position))) AS aa | ||||||
|  |         WHERE account = "{account}" | ||||||
|  |         and META("bank-statement") ~  "{statement_match}" and number({cost_function}(position)) > 0""", | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     # Run Beancount queries. | ||||||
|  |     print(f"START RECONCILIATION FOR {account} ENDING {lastDateInPeriod} (previous end date {preDate})") | ||||||
|  |     entries, _, options = loader.load_file(beancount_file) | ||||||
|  |     uncleared_rows = []  # Hack to capture results of query 03. | ||||||
|  |     cleared_balance = decimal.Decimal('0') | ||||||
|  |     all_trans_balance = decimal.Decimal('0') | ||||||
|  |     for desc, query in QUERIES.items(): | ||||||
|  |         rtypes, rrows = run_query(entries, options, query, numberify=True) | ||||||
|  |         if not rrows: | ||||||
|  |             print(f'{desc:<55} {"N/A":>11}') | ||||||
|  |         elif desc.startswith('04'): | ||||||
|  |             homedir = os.getenv('HOME', '') | ||||||
|  |             print(f'{desc}\n   See {grep_output_file.name}') | ||||||
|  |             grep_rows = [format_record_for_grep(row, homedir) for row in rrows] | ||||||
|  |             print(tabulate(grep_rows), file=grep_output_file) | ||||||
|  |         elif len(rrows) == 1 and isinstance(rrows[0][0], decimal.Decimal): | ||||||
|  |             result = rrows[0][0] | ||||||
|  |             print(f'{desc:<55} {result:11,.2f}') | ||||||
|  |             if desc.startswith('02'): | ||||||
|  |                 all_trans_balance = result | ||||||
|  |             if desc.startswith('05'): | ||||||
|  |                 cleared_balance = result | ||||||
|  |         else: | ||||||
|  |             headers = [c[0].capitalize() for c in rtypes] | ||||||
|  |             if desc.startswith('03'): | ||||||
|  |                 uncleared_rows = rrows | ||||||
|  |             print(desc) | ||||||
|  |             print(textwrap.indent(tabulate(rrows, headers=headers), '    ')) | ||||||
|  | 
 | ||||||
|  |     uncleared = [(r[0], r[2], r[4] or r[3], r[1]) for r in uncleared_rows] | ||||||
|  |     report_path = os.path.join(os.getenv('CONSERVANCY_REPOSITORY', ''), reconciliation_report_path(account, lastDateInPeriod)) | ||||||
|  |     # TODO: Make the directory if it doesn't exist. | ||||||
|  |     with open(report_path, 'w') as f: | ||||||
|  |         f.write(reconciliation_report(account, lastDateInPeriod, cleared_balance, uncleared, '1900-01-01', all_trans_balance, [])) | ||||||
|  |     print(f'Wrote reconciliation report: {report_path}.') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     args = parse_args(sys.argv) | ||||||
|  |     main(args) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def entry_point(): | ||||||
|  |     args = parse_args(sys.argv) | ||||||
|  |     main(args) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue