def compute_payroll(wage_schedule, state_withholding, exemptions): """Return a 2013 payroll schedule as a Pandas DataFrame.""" month_integers = range(1, len(wage_schedule) + 1) pd = pandas.DataFrame(index=month_integers) pd['wages'] = wage_schedule compute = federal_monthly_withholding['MJ'].compute_tax_on pd['fedwh'] = [compute(amount, exemptions) for amount in wage_schedule] ss_excess = (pd['wages'].cumsum() - ss_limit).clip(zero) pd['ss_wages'] = (pd['wages'] - ss_excess).clip(zero) pd['ss'] = [cents(n) for n in ss_rate * pd['ss_wages']] pd['mc'] = [cents(n) for n in mc_rate * pd['wages']] pd['ss2'] = [cents(n) for n in ss_rate * 2 * pd['ss_wages']] pd['mc2'] = [cents(n) for n in mc_rate * 2 * pd['wages']] pd['statewh'] = [state_withholding(wage) for month, wage in pd['wages'].iteritems()] pd['localwh'] = [cents((wage - monthly_school_exemption * exemptions) * bluffton_school_rate) for wage in pd['wages']] pd['paycheck'] = (pd['wages'] - pd['fedwh'] - pd['ss'] - pd['mc'] - pd['statewh'] - pd['localwh']) return pd
def compute_payroll(wage_schedule, state_withholding, exemptions): """Return a 2013 payroll schedule as a Pandas DataFrame.""" month_integers = range(1, len(wage_schedule) + 1) pd = pandas.DataFrame(index=month_integers) pd['wages'] = wage_schedule compute = federal_monthly_withholding['MJ'].compute_tax_on pd['fedwh'] = [compute(amount, exemptions) for amount in wage_schedule] ss_excess = (pd['wages'].cumsum() - ss_limit).clip(zero) pd['ss_wages'] = (pd['wages'] - ss_excess).clip(zero) pd['ss'] = [cents(n) for n in ss_rate * pd['ss_wages']] pd['mc'] = [cents(n) for n in mc_rate * pd['wages']] pd['ss2'] = [cents(n) for n in ss_rate * 2 * pd['ss_wages']] pd['mc2'] = [cents(n) for n in mc_rate * 2 * pd['wages']] pd['statewh'] = [ state_withholding(wage) for month, wage in pd['wages'].iteritems() ] pd['localwh'] = [ cents((wage - monthly_school_exemption * exemptions) * bluffton_school_rate) for wage in pd['wages'] ] pd['paycheck'] = (pd['wages'] - pd['fedwh'] - pd['ss'] - pd['mc'] - pd['statewh'] - pd['localwh']) return pd
def compute(form): f = form f.line6 = f.line4 + f.line5 f.line7 = f.line3 - f.line6 f.line8 = cents(f.line7 * Decimal("0.006")) if f.all_wages_excluded_from_state_unemployment_tax: f.line9 = cents(f.line7 * Decimal(".054")) else: f.line9 = Decimal("0.00") f.line12 = f.line8 + f.line9 + f.line10 + f.line11 if f.line12 > f.line13: f.line14 = f.line12 - f.line13 f.line15 = zero else: f.line14 = zero f.line15 = f.line13 - f.line12 if f.line12 > cents("500.00"): f.line17 = f.line16a + f.line16b + f.line16c + f.line16d # Make this a warning? assert f.line12 == f.line17 else: f.line16a = f.line16b = f.line16c = f.line16d = "" f.line17 = zero
def compute(form): f = form f.line3 = cents(f.line2 * 75 / 1000) f.line4 = max(f.line1 - f.line3, zero) f.line9 = f.line5 + f.line6 + f.line7 + f.line8 f.line15 = f.line10 + f.line11 + f.line12 + f.line13 + f.line14 f.line19 = f.line16 + f.line17 + f.line18 f.line24 = f.line21 + f.line22 + f.line23 f.line26 = cents(f.line25 * 2 / 100) f.line27 = max(f.line24 - f.line26, zero) f.line29 = (f.line4 + f.line9 + f.line15 + f.line19 + f.line20 + f.line27 + f.line28)
def compute(form): f = form validate.year(f.year) validate.quarter(f.quarter) f.line4 = not (f.line5a1 or f.line5b1 or f.line5c1) social_security_rate = Decimal('0.104' if int(f.year) < 2013 else '0.124') medicare_rate = Decimal('0.029') additional_medicare_rate = Decimal('0.009') f.line5a2 = cents(f.line5a1 * social_security_rate) f.line5b2 = cents(f.line5b1 * social_security_rate) f.line5c2 = cents(f.line5c1 * medicare_rate) if form.form_version >= u'2014': f.line5d2 = cents(f.line5d1 * additional_medicare_rate) f.line5e = f.line5a2 + f.line5b2 + f.line5c2 + f.line5d2 f.line6 = f.line3 + f.line5e + f.line5f else: f.line5d = f.line5a2 + f.line5b2 + f.line5c2 f.line6 = f.line3 + f.line5d + getattr(f, 'line5e', zero) f.line10 = f.line6 + f.line7 + f.line8 + f.line9 if f.form_version == u'2017': f.line12 = f.line10 - f.line11 f.line16_total = f.line16_month1 + f.line16_month2 + f.line16_month3 if f.line12 > f.line13: f.line14 = f.line10 - f.line13 f.line15 = zero else: f.line14 = zero f.line15 = f.line13 - f.line10 elif f.form_version >= u'2014': f.line14_total = f.line14_month1 + f.line14_month2 + f.line14_month3 if f.line11 is None: f.line11 = f.line14_total if f.line10 > f.line11: f.line12 = f.line10 - f.line11 f.line13 = zero else: f.line12 = zero f.line13 = f.line11 - f.line10 else: f.line16_total = f.line16_month1 + f.line16_month2 + f.line16_month3 if f.line11 is None: f.line11 = f.line16_total f.line13 = f.line11 + f.line12a if f.line10 > f.line13: f.line14 = f.line10 - f.line13 f.line15 = zero else: f.line14 = zero f.line15 = f.line13 - f.line10
def process_form(f): subs = (f.Part_I.A, f.Part_I.B) for sub in subs: sub.profit = sub.rents - sub.expenses sub.tax = cents(sub.profit / 10) f.total_rents = sum(sub.rents for sub in subs) f.total_expenses = sum(sub.expenses for sub in subs) f.total_profit = sum(sub.profit for sub in subs) f.total_tax = sum(sub.tax for sub in subs)
def compute(form): f = form validate.year(f.year) validate.quarter(f.quarter) f.line4 = not (f.line5a1 or f.line5b1 or f.line5c1) social_security_rate = Decimal('0.104' if int(f.year) < 2013 else '0.124') medicare_rate = Decimal('0.029') additional_medicare_rate = Decimal('0.009') f.line5a2 = cents(f.line5a1 * social_security_rate) f.line5b2 = cents(f.line5b1 * social_security_rate) f.line5c2 = cents(f.line5c1 * medicare_rate) if form.form_version >= u'2014': f.line5d2 = cents(f.line5d1 * additional_medicare_rate) f.line5e = f.line5a2 + f.line5b2 + f.line5c2 + f.line5d2 f.line6 = f.line3 + f.line5e + f.line5f else: f.line5d = f.line5a2 + f.line5b2 + f.line5c2 f.line6 = f.line3 + f.line5d + getattr(f, 'line5e', zero) f.line10 = f.line6 + f.line7 + f.line8 + f.line9 if f.form_version >= u'2014': f.line14_total = f.line14_month1 + f.line14_month2 + f.line14_month3 if f.line11 is None: f.line11 = f.line14_total if f.line10 > f.line11: f.line12 = f.line10 - f.line11 f.line13 = zero else: f.line12 = zero f.line13 = f.line11 - f.line10 else: f.line16_total = f.line16_month1 + f.line16_month2 + f.line16_month3 if f.line11 is None: f.line11 = f.line16_total f.line13 = f.line11 + f.line12a if f.line10 > f.line13: f.line14 = f.line10 - f.line13 f.line15 = zero else: f.line14 = zero f.line15 = f.line13 - f.line10
def __init__(self, brackets, one_allowance=zero): """Return a Pandas tax schedule DataFrame. Each element of `brackets` should be a two-element tuple ``(over, rate)`` like ``(8926, 15)`` giving the base of the tax bracket in dollars ("if the amount is *over*...") and its tax rate as a percent. Each value in the tuple should either be a Decimal, or an integer or string that will be automatically converted to a Decimal. The value `one_allowance` should be the amount of any deduction that gets subtracted from income before the tax is computed. """ overs, rates = zip(*brackets) df = pd.DataFrame({'over': [cents(value) for value in overs]}) df['rate'] = [Decimal(rate) * percent for rate in rates] df['but_not_over'] = df['over'].shift(-1).fillna(infinity) self.brackets = df self.one_allowance = cents(one_allowance)
def compute(form): f = form f.line6 = f.line4 + f.line5 f.line7 = f.line3 - f.line6 f.line8 = cents(f.line7 * Decimal('0.006')) if f.all_wages_excluded_from_state_unemployment_tax: f.line9 = cents(f.line7 * Decimal('.054')) else: f.line9 = Decimal('0.00') f.line12 = f.line8 + f.line9 + f.line10 + f.line11 if f.line12 > f.line13: f.line14 = f.line12 - f.line13 f.line15 = zero else: f.line14 = zero f.line15 = f.line13 - f.line12 f.line17 = f.line16a + f.line16b + f.line16c + f.line16d
def compute(form): f = form f.line3 = (cents(f.line1) / cents(f.line2) ).quantize(Decimal('0.0001'), rounding=ROUND_HALF_UP) f.line7 = f.line3 f.line12a = sum(f['line', n, 'a'] for n in range(9, 12)) f.line12b = sum(f['line', n, 'b'] for n in range(9, 12)) f.line13 = cents(f.line12b * f.line7 / hundred) f.line14 = f.line12a + f.line13 f.line15 = max(zero, f.line8 - f.line14) f.line22a = sum(f['line', n, 'a'] for n in range(16, 22)) f.line22b = sum(f['line', n, 'b'] for n in range(16, 22)) f.line23 = cents(f.line22b * f.line7) f.line25 = f.line22a + f.line23 + f.line24 f.line38 = f.line36 - f.line37 f.line39 = cents(f.line38 * f.line7) f.line41 = cents(f.line39 * f.line40) f.line26 = min(f.line15, f.line25) f.line27 = f.line15 - f.line26 f.line29 = f.line41 f.line31 = f.line28 + f.line29 + f.line30 f.line32 = min(f.line27, f.line31) f.line33 = f.line14 + f.line26 + f.line32 # TODO: f.line34 should really involve casualty loss plus another form! f.line34 = zero f.line35 = f.line33 - f.line34 f.line42 = max(zero, f.line25 - f.line26) f.line43 = max(zero, f.line31 - f.line32)
def compute(form): f = form f.line3 = (cents(f.line1) / cents(f.line2)).quantize( Decimal('0.0001'), rounding=ROUND_HALF_UP) f.line7 = f.line3 f.line12a = sum(f['line', n, 'a'] for n in range(9, 12)) f.line12b = sum(f['line', n, 'b'] for n in range(9, 12)) f.line13 = cents(f.line12b * f.line7 / hundred) f.line14 = f.line12a + f.line13 f.line15 = max(zero, f.line8 - f.line14) f.line22a = sum(f['line', n, 'a'] for n in range(16, 22)) f.line22b = sum(f['line', n, 'b'] for n in range(16, 22)) f.line23 = cents(f.line22b * f.line7) f.line25 = f.line22a + f.line23 + f.line24 f.line38 = f.line36 - f.line37 f.line39 = cents(f.line38 * f.line7) f.line41 = cents(f.line39 * f.line40) f.line26 = min(f.line15, f.line25) f.line27 = f.line15 - f.line26 f.line29 = f.line41 f.line31 = f.line28 + f.line29 + f.line30 f.line32 = min(f.line27, f.line31) f.line33 = f.line14 + f.line26 + f.line32 # TODO: f.line34 should really involve casualty loss plus another form! f.line34 = zero f.line35 = f.line33 - f.line34 f.line42 = max(zero, f.line25 - f.line26) f.line43 = max(zero, f.line31 - f.line32)
def compute(form): f = form f.line3 = f.line1a + f.line1b + f.line2 f.line4 = cents(Decimal('.9235') * f.line3) if f.line4 < Decimal('400.00'): f.line5 = zero f.line6 = zero return if f.form_version == u'2014': ss_limit = Decimal('117000.00') combined_rate = Decimal('.153') maximum_ss = Decimal('14508.00') medicare_rate = Decimal('.029') else: ss_limit = Decimal('110100.00') combined_rate = Decimal('.133') maximum_ss = Decimal('11450.40') medicare_rate = Decimal('.029') if f.line4 <= ss_limit: f.line5 = combined_rate * f.line4 else: f.line5 = maximum_ss + medicare_rate * f.line4 f.line5 = cents(f.line5) if f.form_version >= u'2014': f.line6 = cents(f.line5 / 2) return # TODO: re-check the following if f.line5 <= Decimal('14643.30'): f.line6 = Decimal('.5751') * f.line5 else: f.line6 = Decimal('1100.00') + Decimal('.50') * f.line5 f.line6 = cents(f.line6)
def compute(form): f = form f.line3 = f.line1a + f.line1b + f.line2 if f.line3 >= zero: f.line4a = cents(Decimal('.9235') * f.line3) else: f.line4a = f.line3 f.line4c = f.line4a + f.line4b if f.line4c < Decimal('400.00'): f.line5b = zero f.line6 = zero f.line8d = zero f.line9 = zero f.line10 = zero f.line11 = zero f.line12 = zero f.line13 = zero return f.line5b = cents(Decimal('.9235') * f.line5a) if f.line5b < Decimal('100.00'): f.line5b = zero f.line6 = f.line4c + f.line5b line7 = cents('113700.00') if f.line8a >= line7: f.line8d = zero f.line9 = zero f.line10 = zero else: raise NotImplementedError() f.line11 = cents(f.line6 * Decimal('.029')) f.line12 = f.line10 + f.line11 f.line13 = cents(f.line12 * Decimal('0.5'))
from luca.forms.formlib import Form from luca.kit import cents, zzstr title = u"Form 1040 Schedule E: Supplemental Income and Loss" versions = u'2012', u'2013', u'2014' zero = cents(0) def defaults(form): f = form f.ssn = '' f.name = '' f.is_1099_required = False f.is_1099_filed = False f.Part_I = Form() f.Part_II = Form() for letter in 'ABC': setattr(f.Part_I, letter, Form()) sub = getattr(f.Part_I, letter) sub.address = '' sub.type = '' sub.fair_rental_days = 0 sub.personal_use_days = 0 sub.qjv = '' for i in range(3, 20): setattr(sub, 'line%d' % i, zero) for letter in 'ABCD': setattr(f.Part_II, letter, Form()) sub = getattr(f.Part_II, letter)
def test_cents_round_up(self): assert cents('1.235') == Decimal('1.24')
def run_yaml_file(terminal, path, statement_paths, show_balances, show_data, show_transactions, sort_by='date'): t = terminal screen_width = int(os.environ.get('COLUMNS', '0')) or t.width or 80 with open(path) as yaml_file: rule_tree = yaml.load(yaml_file, Loader=Loader) try: rule_tree_function = rules.compile_tree(rule_tree) except rules.ParseError as e: sys.stderr.write('ERROR: {}\n'.format(e)) sys.exit(1) balances = [] transactions = [] for path in statement_paths: if path.lower().endswith('.pdf'): text = extract_text_from_pdf_file(path) elif path.lower().endswith('.txt'): with open(path) as text_file: text = text_file.read().decode('utf-8') matching_importers = [importer for importer in importers if importer.does_this_match(text)] if len(matching_importers) == 0: raise RuntimeError('cannot find an importer for file: {}' .format(path)) elif len(matching_importers) > 1: raise RuntimeError('found too many importers for file: {}' .format(path)) else: importer = matching_importers[0] new_balances, new_transactions = importer(text) elif path.lower().endswith('.csv'): with open(path) as csv_file: new_balances, new_transactions = autocsv.importer(csv_file) else: raise ValueError('no idea what to do with file {!r}'.format(path)) balances.extend(new_balances) transactions.extend(new_transactions) output_lines = [] add = output_lines.append for line in verify_balances(balances, transactions, show_balances, t): add(line) new_splits = [] for tr in transactions: tr.set_full_text() category = rule_tree_function(tr) if category is not None and category.startswith('Split '): pieces = [piece.strip() for piece in category[6:].split(';')] full_amount = tr.amount for piece in pieces[:-1]: tr2 = copy(tr) #tr2.category, amount_str = piece.split() tr2.category, amount_str = piece.rsplit(None, 1) if amount_str.endswith('%'): percent = Decimal(amount_str[:-1]) tr2.amount = cents(full_amount * percent / 100) else: tr2.amount = Decimal(amount_str.replace(',', '')) new_splits.append(tr2) tr.amount -= tr2.amount category = pieces[-1] tr.category = category transactions.extend(new_splits) transactions = [tr for tr in transactions if tr.category is not None] if show_data: sio = StringIO() w = csv.writer(sio) w.writerow(['date', 'amount', 'category', 'account', 'description']) for t in transactions: w.writerow([ t.date, t.amount, t.category, t.account, t.description, ]) return sio.getvalue().splitlines() catdict = group_transactions_by_category(transactions) sumdict = sum_categories(transactions) categories = set(catdict) | set(sumdict) max_amount = max(abs(v) for v in sumdict.values()) amount_width = len('{:,}'.format(max_amount)) + 1 date_width = 10 gutter = 1 category_indent = ' ' * (amount_width + 1) description_indent = ' ' * (gutter + date_width + gutter) f = _make_formatter(terminal, amount_width) for category in sorted(categories, key=_category_key): csum = sumdict[category] depth = category.count('.') if not show_transactions and depth: name = category[category.rfind('.')+1:] else: name = category if show_transactions: name = t.bold(name) add('{}{} {}'.format(category_indent * depth, f(csum), name)) if not show_transactions: continue transaction_list = catdict.get(category, None) if not transaction_list: continue if sort_by == 'amount': transaction_list.sort(key=lambda t: -t.amount) elif sort_by == 'date': transaction_list.sort(key=lambda t: t.date) add('') max_amount2 = max(abs(tr.amount) for tr in transaction_list) amount_width2 = max(10, len('{:,}'.format(max_amount2)) + 1) f2 = _make_formatter(terminal, amount_width2) description_width = ( screen_width - # content from left to right: gutter - date_width - gutter - # (description goes here) gutter - amount_width2 - gutter) for tr in transaction_list: lines = wrap(tr.description, description_width) add(' {} {:<{}} {:>{}}'.format( tr.date, lines[0], description_width, f2(tr.amount), amount_width2)) for line in lines[1:]: add(description_indent + line) add('') return output_lines
def test_cents_round_down(self): assert cents('1.234') == Decimal('1.23')
from luca.forms.formlib import Form from luca.kit import cents, zstr title = u'Form 8949: Sales and Other Dispositions of Capital Assets' versions = '2011', '2012' zero = cents(0) def defaults(form): f = form f.name = '' f.ssn = '' build_example_row = { '2011': _build_example_row_2011, '2012': _build_example_row_2012 }[form.form_version] f.Part_I = Form() f.Part_I.box = 'A' f.Part_I.table = [ build_example_row(), build_example_row(), ] f.Part_II = Form() f.Part_II.box = 'A' f.Part_II.table = [ build_example_row(), build_example_row(), ]
def run_yaml_file(terminal, path, statement_paths, show_balances, show_data, show_transactions): t = terminal screen_width = int(os.environ.get('COLUMNS', '0')) or t.width or 80 with open(path) as yaml_file: rule_tree = yaml.load(yaml_file, Loader=Loader) try: rule_tree_function = rules.compile_tree(rule_tree) except rules.ParseError as e: sys.stderr.write('ERROR: {}\n'.format(e)) sys.exit(1) balances = [] transactions = [] for path in statement_paths: if path.lower().endswith('.pdf'): text = extract_text_from_pdf_file(path) elif path.lower().endswith('.txt'): with open(path) as text_file: text = text_file.read().decode('utf-8') else: raise ValueError('no idea what to do with file {!r}'.format(path)) matching_importers = [importer for importer in importers if importer.does_this_match(text)] if len(matching_importers) == 0: raise RuntimeError('cannot find an importer for file: {}' .format(path)) elif len(matching_importers) > 1: raise RuntimeError('found too many importers for file: {}' .format(path)) importer = matching_importers[0] new_balances, new_transactions = importer(text) balances.extend(new_balances) transactions.extend(new_transactions) output_lines = [] add = output_lines.append for line in verify_balances(balances, transactions, show_balances, t): add(line) new_splits = [] for tr in transactions: tr.set_full_text() category = rule_tree_function(tr) if category is not None and category.startswith('Split '): pieces = [piece.strip() for piece in category[6:].split(';')] for piece in pieces[:-1]: tr2 = copy(tr) #tr2.category, amount_str = piece.split() tr2.category, amount_str = piece.rsplit(None, 1) if amount_str.endswith('%'): percent = Decimal(amount_str[:-1]) tr2.amount = cents(tr.amount * percent / 100) else: tr2.amount = Decimal(amount_str.replace(',', '')) new_splits.append(tr2) tr.amount -= tr2.amount category = pieces[-1] tr.category = category transactions.extend(new_splits) transactions = [tr for tr in transactions if tr.category is not None] if show_data: sio = StringIO() w = csv.writer(sio) w.writerow(['date', 'amount', 'category', 'account', 'description']) for t in transactions: w.writerow([ t.date, t.amount, t.category, t.account, t.description, ]) return sio.getvalue().splitlines() catdict = group_transactions_by_category(transactions) sumdict = sum_categories(transactions) categories = set(catdict) | set(sumdict) max_amount = max(abs(v) for v in sumdict.values()) amount_width = len('{:,}'.format(max_amount)) + 1 date_width = 10 gutter = 1 category_indent = ' ' * (amount_width + 1) description_indent = ' ' * (gutter + date_width + gutter) f = _make_formatter(terminal, amount_width) for category in sorted(categories, key=_category_key): csum = sumdict[category] depth = category.count('.') if not show_transactions and depth: name = category[category.rfind('.')+1:] else: name = category if show_transactions: name = t.bold(name) add('{}{} {}'.format(category_indent * depth, f(csum), name)) if not show_transactions: continue transaction_list = catdict.get(category, None) if not transaction_list: continue add('') max_amount2 = max(abs(tr.amount) for tr in transaction_list) amount_width2 = max(10, len('{:,}'.format(max_amount2)) + 1) f2 = _make_formatter(terminal, amount_width2) description_width = ( screen_width - # content from left to right: gutter - date_width - gutter - # (description goes here) gutter - amount_width2 - gutter) for tr in transaction_list: lines = wrap(tr.description, description_width) add(' {} {:<{}} {:>{}}'.format( tr.date, lines[0], description_width, f2(tr.amount), amount_width2)) for line in lines[1:]: add(description_indent + line) add('') return output_lines