macrs 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
macrs-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SybilGambleyyu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
macrs-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,138 @@
1
+ Metadata-Version: 2.4
2
+ Name: macrs
3
+ Version: 0.1.0
4
+ Summary: IRS Publication 946 MACRS depreciation schedules with decimal precision and an audit trail
5
+ Author: SybilGambleyyu
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/SybilGambleyyu/macrs
8
+ Project-URL: Documentation, https://github.com/SybilGambleyyu/macrs#readme
9
+ Project-URL: Repository, https://github.com/SybilGambleyyu/macrs
10
+ Project-URL: Issues, https://github.com/SybilGambleyyu/macrs/issues
11
+ Project-URL: IRS Pub 946, https://www.irs.gov/publications/p946
12
+ Keywords: macrs,depreciation,tax,irs,publication-946,fixed-assets,accounting,section-179,bonus-depreciation,schedule
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Financial and Insurance Industry
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Office/Business :: Financial :: Accounting
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest>=7.0; extra == "dev"
28
+ Requires-Dist: build; extra == "dev"
29
+ Requires-Dist: twine; extra == "dev"
30
+ Dynamic: license-file
31
+
32
+ # macrs
33
+
34
+ **IRS Publication 946 MACRS depreciation schedules** — decimal-precise, table-driven, with an audit trail.
35
+
36
+ Tax accountants, bookkeepers, and controllers still rebuild MACRS schedules in spreadsheets every year. Year-1 rates, mid-quarter vs half-year, and the residual last year are easy to get wrong. `macrs` encodes the IRS percentage tables from [Publication 946](https://www.irs.gov/publications/p946) and produces a full recovery schedule you can export, review, and reconcile.
37
+
38
+ > **Not tax advice.** Recovery period, convention, §179, and bonus depreciation eligibility depend on current law and facts. Always verify against the current IRS Publication 946 and your tax professional before filing.
39
+
40
+ ## Install
41
+
42
+ ```bash
43
+ pip install macrs
44
+ ```
45
+
46
+ Requires Python 3.10+.
47
+
48
+ ## Quick start
49
+
50
+ ```python
51
+ from macrs import AssetInput, build_schedule, schedule_for_class
52
+
53
+ # $10,000 office furniture, 7-year GDS, half-year (Pub 946 Table A-1)
54
+ sched = build_schedule(
55
+ AssetInput(cost="10000", recovery_years=7, convention="half_year",
56
+ placed_in_service_year=2024)
57
+ )
58
+ print(sched.lines[0].depreciation) # 1429.00 (14.29%)
59
+ print(sched.total_macrs_depreciation) # 10000.00
60
+ for reason in sched.reasons:
61
+ print(reason)
62
+ ```
63
+
64
+ Named asset classes for common lookups:
65
+
66
+ ```python
67
+ sched = schedule_for_class("12000", "computers", placed_in_service_year=2025)
68
+ # 5-year GDS half-year → year 1 = 2400.00
69
+ ```
70
+
71
+ ## CLI
72
+
73
+ ```bash
74
+ # 7-year property, half-year
75
+ macrs schedule --cost 10000 --years 7 --year 2024 -v
76
+
77
+ # Computers shortcut with §179 and 60% bonus
78
+ macrs schedule --cost 8000 --class computers \
79
+ --section-179 1000 --bonus 60 --format json
80
+
81
+ # Mid-quarter test (>40% of personal property in Q4?)
82
+ macrs mq-test --q1 5000 --q2 3000 --q3 2000 --q4 12000
83
+
84
+ # List class shortcuts
85
+ macrs classes
86
+ ```
87
+
88
+ ## What it covers
89
+
90
+ | Feature | Detail |
91
+ |---------|--------|
92
+ | GDS personal property | 3, 5, 7, 10, 15, 20-year — Table A-1 half-year |
93
+ | Mid-quarter | Tables A-2…A-5 (Q1–Q4) |
94
+ | Residential rental | 27.5-year mid-month — Table A-6 |
95
+ | Nonresidential real | 39-year mid-month — Table A-7a |
96
+ | §179 | Reduces basis before MACRS |
97
+ | Bonus depreciation | % of remaining basis after §179 |
98
+ | Business-use % | Scales qualified cost |
99
+ | Mid-quarter test | >40% Q4 personal property helper |
100
+ | Audit trail | `reasons` list cites table and steps |
101
+ | Export | text / CSV / JSON |
102
+
103
+ **Not yet covered** (by design for v0.1): ADS full straight-line tables for every period, short tax years, listed-property luxury auto limits, disposition / like-kind exchange, general asset accounts, and state decoupling. Open an issue if one of these is blocking you.
104
+
105
+ ## Rates source
106
+
107
+ Percentage rates are taken from **IRS Publication 946 (2025), Appendix A**. The IRS publishes these tables for public use. This package does not modify them; it applies them with `decimal.Decimal` math and absorbs the final-year residual so remaining basis hits `$0.00`.
108
+
109
+ - Tables: https://www.irs.gov/publications/p946
110
+ - PDF: https://www.irs.gov/pub/irs-pdf/p946.pdf
111
+
112
+ ## Example: mid-quarter vs half-year
113
+
114
+ ```python
115
+ from macrs import mid_quarter_required, AssetInput, build_schedule
116
+
117
+ required, ratio, why = mid_quarter_required([2000, 1500, 1000, 8000])
118
+ print(required, ratio) # True, ~0.64
119
+
120
+ # If required, build each personal-property asset with mid_quarter:
121
+ a = AssetInput(cost="8000", recovery_years=5, convention="mid_quarter",
122
+ placed_in_service_month=11) # Q4
123
+ print(build_schedule(a).lines[0].depreciation) # 5% year-1 rate → 400.00
124
+ ```
125
+
126
+ ## Demo
127
+
128
+ Browser calculator: https://sybilgambleyyu.github.io/demo/macrs.html
129
+
130
+ ## License
131
+
132
+ MIT. IRS tables are public government data; the software encoding is MIT-licensed.
133
+
134
+ ## Related tools
135
+
136
+ - [cashapply](https://github.com/SybilGambleyyu/cashapply) — cash application / AR matching
137
+ - [courtdays](https://github.com/SybilGambleyyu/courtdays) — FRCP legal deadlines
138
+ - [loadcomb](https://github.com/SybilGambleyyu/loadcomb) — ASCE 7 load combinations
macrs-0.1.0/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # macrs
2
+
3
+ **IRS Publication 946 MACRS depreciation schedules** — decimal-precise, table-driven, with an audit trail.
4
+
5
+ Tax accountants, bookkeepers, and controllers still rebuild MACRS schedules in spreadsheets every year. Year-1 rates, mid-quarter vs half-year, and the residual last year are easy to get wrong. `macrs` encodes the IRS percentage tables from [Publication 946](https://www.irs.gov/publications/p946) and produces a full recovery schedule you can export, review, and reconcile.
6
+
7
+ > **Not tax advice.** Recovery period, convention, §179, and bonus depreciation eligibility depend on current law and facts. Always verify against the current IRS Publication 946 and your tax professional before filing.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pip install macrs
13
+ ```
14
+
15
+ Requires Python 3.10+.
16
+
17
+ ## Quick start
18
+
19
+ ```python
20
+ from macrs import AssetInput, build_schedule, schedule_for_class
21
+
22
+ # $10,000 office furniture, 7-year GDS, half-year (Pub 946 Table A-1)
23
+ sched = build_schedule(
24
+ AssetInput(cost="10000", recovery_years=7, convention="half_year",
25
+ placed_in_service_year=2024)
26
+ )
27
+ print(sched.lines[0].depreciation) # 1429.00 (14.29%)
28
+ print(sched.total_macrs_depreciation) # 10000.00
29
+ for reason in sched.reasons:
30
+ print(reason)
31
+ ```
32
+
33
+ Named asset classes for common lookups:
34
+
35
+ ```python
36
+ sched = schedule_for_class("12000", "computers", placed_in_service_year=2025)
37
+ # 5-year GDS half-year → year 1 = 2400.00
38
+ ```
39
+
40
+ ## CLI
41
+
42
+ ```bash
43
+ # 7-year property, half-year
44
+ macrs schedule --cost 10000 --years 7 --year 2024 -v
45
+
46
+ # Computers shortcut with §179 and 60% bonus
47
+ macrs schedule --cost 8000 --class computers \
48
+ --section-179 1000 --bonus 60 --format json
49
+
50
+ # Mid-quarter test (>40% of personal property in Q4?)
51
+ macrs mq-test --q1 5000 --q2 3000 --q3 2000 --q4 12000
52
+
53
+ # List class shortcuts
54
+ macrs classes
55
+ ```
56
+
57
+ ## What it covers
58
+
59
+ | Feature | Detail |
60
+ |---------|--------|
61
+ | GDS personal property | 3, 5, 7, 10, 15, 20-year — Table A-1 half-year |
62
+ | Mid-quarter | Tables A-2…A-5 (Q1–Q4) |
63
+ | Residential rental | 27.5-year mid-month — Table A-6 |
64
+ | Nonresidential real | 39-year mid-month — Table A-7a |
65
+ | §179 | Reduces basis before MACRS |
66
+ | Bonus depreciation | % of remaining basis after §179 |
67
+ | Business-use % | Scales qualified cost |
68
+ | Mid-quarter test | >40% Q4 personal property helper |
69
+ | Audit trail | `reasons` list cites table and steps |
70
+ | Export | text / CSV / JSON |
71
+
72
+ **Not yet covered** (by design for v0.1): ADS full straight-line tables for every period, short tax years, listed-property luxury auto limits, disposition / like-kind exchange, general asset accounts, and state decoupling. Open an issue if one of these is blocking you.
73
+
74
+ ## Rates source
75
+
76
+ Percentage rates are taken from **IRS Publication 946 (2025), Appendix A**. The IRS publishes these tables for public use. This package does not modify them; it applies them with `decimal.Decimal` math and absorbs the final-year residual so remaining basis hits `$0.00`.
77
+
78
+ - Tables: https://www.irs.gov/publications/p946
79
+ - PDF: https://www.irs.gov/pub/irs-pdf/p946.pdf
80
+
81
+ ## Example: mid-quarter vs half-year
82
+
83
+ ```python
84
+ from macrs import mid_quarter_required, AssetInput, build_schedule
85
+
86
+ required, ratio, why = mid_quarter_required([2000, 1500, 1000, 8000])
87
+ print(required, ratio) # True, ~0.64
88
+
89
+ # If required, build each personal-property asset with mid_quarter:
90
+ a = AssetInput(cost="8000", recovery_years=5, convention="mid_quarter",
91
+ placed_in_service_month=11) # Q4
92
+ print(build_schedule(a).lines[0].depreciation) # 5% year-1 rate → 400.00
93
+ ```
94
+
95
+ ## Demo
96
+
97
+ Browser calculator: https://sybilgambleyyu.github.io/demo/macrs.html
98
+
99
+ ## License
100
+
101
+ MIT. IRS tables are public government data; the software encoding is MIT-licensed.
102
+
103
+ ## Related tools
104
+
105
+ - [cashapply](https://github.com/SybilGambleyyu/cashapply) — cash application / AR matching
106
+ - [courtdays](https://github.com/SybilGambleyyu/courtdays) — FRCP legal deadlines
107
+ - [loadcomb](https://github.com/SybilGambleyyu/loadcomb) — ASCE 7 load combinations
@@ -0,0 +1,59 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "macrs"
7
+ version = "0.1.0"
8
+ description = "IRS Publication 946 MACRS depreciation schedules with decimal precision and an audit trail"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.10"
13
+ authors = [
14
+ { name = "SybilGambleyyu" },
15
+ ]
16
+ keywords = [
17
+ "macrs",
18
+ "depreciation",
19
+ "tax",
20
+ "irs",
21
+ "publication-946",
22
+ "fixed-assets",
23
+ "accounting",
24
+ "section-179",
25
+ "bonus-depreciation",
26
+ "schedule",
27
+ ]
28
+ classifiers = [
29
+ "Development Status :: 4 - Beta",
30
+ "Intended Audience :: Financial and Insurance Industry",
31
+ "Intended Audience :: Developers",
32
+ "Programming Language :: Python :: 3",
33
+ "Programming Language :: Python :: 3.10",
34
+ "Programming Language :: Python :: 3.11",
35
+ "Programming Language :: Python :: 3.12",
36
+ "Programming Language :: Python :: 3.13",
37
+ "Topic :: Office/Business :: Financial :: Accounting",
38
+ "Topic :: Software Development :: Libraries :: Python Modules",
39
+ ]
40
+ dependencies = []
41
+
42
+ [project.optional-dependencies]
43
+ dev = ["pytest>=7.0", "build", "twine"]
44
+
45
+ [project.scripts]
46
+ macrs = "macrs.cli:main"
47
+
48
+ [project.urls]
49
+ Homepage = "https://github.com/SybilGambleyyu/macrs"
50
+ Documentation = "https://github.com/SybilGambleyyu/macrs#readme"
51
+ Repository = "https://github.com/SybilGambleyyu/macrs"
52
+ Issues = "https://github.com/SybilGambleyyu/macrs/issues"
53
+ "IRS Pub 946" = "https://www.irs.gov/publications/p946"
54
+
55
+ [tool.setuptools.packages.find]
56
+ where = ["src"]
57
+
58
+ [tool.pytest.ini_options]
59
+ testpaths = ["tests"]
macrs-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,40 @@
1
+ """macrs — IRS Publication 946 MACRS depreciation schedules.
2
+
3
+ Deterministic, decimal-precise depreciation schedules for US tax depreciation
4
+ under the Modified Accelerated Cost Recovery System. Rates come from IRS
5
+ Publication 946 Appendix A tables.
6
+
7
+ Not tax advice. Verify recovery period, convention, section 179, and bonus
8
+ depreciation eligibility against current law and the current Publication 946
9
+ before filing.
10
+ """
11
+
12
+ from .models import AssetInput, DepreciationSchedule, ScheduleLine, money
13
+ from .schedule import (
14
+ asset_from_class,
15
+ build_schedule,
16
+ lookup_asset_class,
17
+ mid_quarter_required,
18
+ month_to_quarter,
19
+ schedule_for_class,
20
+ )
21
+ from .tables import ASSET_CLASSES, TABLE_SOURCE, TABLE_URL
22
+
23
+ __version__ = "0.1.0"
24
+
25
+ __all__ = [
26
+ "ASSET_CLASSES",
27
+ "AssetInput",
28
+ "DepreciationSchedule",
29
+ "ScheduleLine",
30
+ "TABLE_SOURCE",
31
+ "TABLE_URL",
32
+ "asset_from_class",
33
+ "build_schedule",
34
+ "lookup_asset_class",
35
+ "mid_quarter_required",
36
+ "money",
37
+ "month_to_quarter",
38
+ "schedule_for_class",
39
+ "__version__",
40
+ ]
@@ -0,0 +1,284 @@
1
+ """Command-line interface for MACRS depreciation schedules."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import csv
7
+ import json
8
+ import sys
9
+ from decimal import Decimal
10
+ from typing import Optional, Sequence, TextIO
11
+
12
+ from . import __version__
13
+ from .models import AssetInput, money
14
+ from .schedule import (
15
+ build_schedule,
16
+ lookup_asset_class,
17
+ mid_quarter_required,
18
+ schedule_for_class,
19
+ )
20
+ from .tables import ASSET_CLASSES, GDS_PERSONAL_PERIODS, TABLE_SOURCE, TABLE_URL
21
+
22
+
23
+ def _print_schedule(sched, out: Optional[TextIO] = None, *, verbose: bool = False) -> None:
24
+ if out is None:
25
+ out = sys.stdout
26
+ a = sched.asset
27
+ print(f"# MACRS depreciation schedule", file=out)
28
+ if a.label:
29
+ print(f"# Asset: {a.label}", file=out)
30
+ if a.description:
31
+ print(f"# Class: {a.description}", file=out)
32
+ print(f"# Cost: {a.cost}", file=out)
33
+ print(f"# Recovery: {a.recovery_years}-year Convention: {sched.convention}", file=out)
34
+ print(f"# Table: {sched.table_id}", file=out)
35
+ print(f"# Source: {sched.table_source}", file=out)
36
+ print(f"# §179: {sched.section_179_deduction} Bonus: {sched.bonus_deduction}", file=out)
37
+ print(f"# MACRS basis: {sched.macrs_basis}", file=out)
38
+ print(f"# Year-1 total (§179+bonus+MACRS): {sched.total_first_year}", file=out)
39
+ print(file=out)
40
+ headers = [
41
+ "year",
42
+ "calendar",
43
+ "rate_%",
44
+ "depreciation",
45
+ "cumulative",
46
+ "remaining",
47
+ ]
48
+ print("\t".join(headers), file=out)
49
+ for line in sched.lines:
50
+ print(
51
+ "\t".join(
52
+ [
53
+ str(line.recovery_year),
54
+ str(line.calendar_year or ""),
55
+ str(line.rate_percent),
56
+ str(line.depreciation),
57
+ str(line.cumulative_depreciation),
58
+ str(line.remaining_basis),
59
+ ]
60
+ ),
61
+ file=out,
62
+ )
63
+ print(file=out)
64
+ print(f"# Total MACRS depreciation: {sched.total_macrs_depreciation}", file=out)
65
+ if sched.warnings:
66
+ print("# Warnings:", file=out)
67
+ for w in sched.warnings:
68
+ print(f"# - {w}", file=out)
69
+ if verbose:
70
+ print("# Audit trail:", file=out)
71
+ for r in sched.reasons:
72
+ print(f"# - {r}", file=out)
73
+ print(
74
+ "# Disclaimer: Not tax advice. Verify against current IRS Publication 946.",
75
+ file=out,
76
+ )
77
+
78
+
79
+ def cmd_schedule(args: argparse.Namespace) -> int:
80
+ if args.asset_class:
81
+ sched = schedule_for_class(
82
+ args.cost,
83
+ args.asset_class,
84
+ convention=args.convention,
85
+ placed_in_service_month=args.month,
86
+ placed_in_service_year=args.year,
87
+ section_179=args.section_179,
88
+ bonus_percent=args.bonus,
89
+ business_use_percent=args.business_use,
90
+ label=args.label,
91
+ )
92
+ else:
93
+ asset = AssetInput(
94
+ cost=money(args.cost),
95
+ recovery_years=args.years,
96
+ convention=args.convention,
97
+ placed_in_service_month=args.month,
98
+ placed_in_service_year=args.year,
99
+ quarter=args.quarter,
100
+ section_179=money(args.section_179),
101
+ bonus_percent=Decimal(str(args.bonus)),
102
+ business_use_percent=Decimal(str(args.business_use)),
103
+ label=args.label,
104
+ )
105
+ sched = build_schedule(asset)
106
+
107
+ if args.format == "json":
108
+ payload = {
109
+ "cost": str(sched.asset.cost),
110
+ "recovery_years": sched.asset.recovery_years,
111
+ "convention": sched.convention,
112
+ "table_id": sched.table_id,
113
+ "table_source": sched.table_source,
114
+ "section_179": str(sched.section_179_deduction),
115
+ "bonus": str(sched.bonus_deduction),
116
+ "macrs_basis": str(sched.macrs_basis),
117
+ "total_first_year": str(sched.total_first_year),
118
+ "total_macrs": str(sched.total_macrs_depreciation),
119
+ "lines": sched.to_rows(),
120
+ "reasons": sched.reasons if args.verbose else [],
121
+ "warnings": sched.warnings,
122
+ "disclaimer": "Not tax advice. Verify against current IRS Publication 946.",
123
+ }
124
+ json.dump(payload, sys.stdout, indent=2)
125
+ print()
126
+ elif args.format == "csv":
127
+ w = csv.DictWriter(
128
+ sys.stdout,
129
+ fieldnames=[
130
+ "recovery_year",
131
+ "calendar_year",
132
+ "rate_percent",
133
+ "depreciation",
134
+ "cumulative_depreciation",
135
+ "remaining_basis",
136
+ "notes",
137
+ ],
138
+ )
139
+ w.writeheader()
140
+ for row in sched.to_rows():
141
+ w.writerow(row)
142
+ else:
143
+ _print_schedule(sched, verbose=args.verbose)
144
+ return 0
145
+
146
+
147
+ def cmd_classes(_: argparse.Namespace) -> int:
148
+ print(f"# Common asset classes (from {TABLE_SOURCE})")
149
+ print("key\tgds_years\tads_years\tkind\tdescription")
150
+ for key, meta in sorted(ASSET_CLASSES.items()):
151
+ print(
152
+ f"{key}\t{meta['gds_years']}\t{meta['ads_years']}\t"
153
+ f"{meta['kind']}\t{meta['description']}"
154
+ )
155
+ return 0
156
+
157
+
158
+ def cmd_mq_test(args: argparse.Namespace) -> int:
159
+ qs = [args.q1, args.q2, args.q3, args.q4]
160
+ required, ratio, reasons = mid_quarter_required(qs)
161
+ if args.format == "json":
162
+ json.dump(
163
+ {
164
+ "mid_quarter_required": required,
165
+ "q4_ratio": str(ratio),
166
+ "quarters": [str(money(q)) for q in qs],
167
+ "reasons": reasons,
168
+ },
169
+ sys.stdout,
170
+ indent=2,
171
+ )
172
+ print()
173
+ else:
174
+ print(f"Mid-quarter required: {required}")
175
+ print(f"Q4 ratio: {ratio * 100}%")
176
+ for r in reasons:
177
+ print(f" - {r}")
178
+ return 0
179
+
180
+
181
+ def cmd_rates(args: argparse.Namespace) -> int:
182
+ from .tables import personal_gds_rates
183
+
184
+ rates = personal_gds_rates(
185
+ args.years,
186
+ convention=args.convention,
187
+ quarter=args.quarter,
188
+ )
189
+ for i, r in enumerate(rates, 1):
190
+ print(f"{i}\t{r}")
191
+ return 0
192
+
193
+
194
+ def build_parser() -> argparse.ArgumentParser:
195
+ p = argparse.ArgumentParser(
196
+ prog="macrs",
197
+ description=(
198
+ "MACRS depreciation schedules from IRS Publication 946 percentage tables. "
199
+ "Not tax advice."
200
+ ),
201
+ )
202
+ p.add_argument("--version", action="version", version=f"macrs {__version__}")
203
+ sub = p.add_subparsers(dest="command", required=True)
204
+
205
+ s = sub.add_parser("schedule", help="Build a depreciation schedule")
206
+ s.add_argument("--cost", type=str, required=True, help="Asset cost / basis")
207
+ s.add_argument(
208
+ "--years",
209
+ type=int,
210
+ default=5,
211
+ help="Recovery period: 3,5,7,10,15,20 (personal); 27 residential; 39 nonresidential",
212
+ )
213
+ s.add_argument(
214
+ "--convention",
215
+ default="half_year",
216
+ choices=["half_year", "mid_quarter", "mid_month"],
217
+ help="Depreciation convention",
218
+ )
219
+ s.add_argument("--month", type=int, default=1, help="Month placed in service (1-12)")
220
+ s.add_argument("--year", type=int, default=None, help="Calendar year placed in service")
221
+ s.add_argument("--quarter", type=int, default=None, help="Quarter 1-4 (mid_quarter)")
222
+ s.add_argument("--section-179", type=str, default="0", dest="section_179")
223
+ s.add_argument("--bonus", type=str, default="0", help="Bonus depreciation percent 0-100")
224
+ s.add_argument(
225
+ "--business-use",
226
+ type=str,
227
+ default="100",
228
+ dest="business_use",
229
+ help="Business-use percent 0-100",
230
+ )
231
+ s.add_argument(
232
+ "--class",
233
+ dest="asset_class",
234
+ default=None,
235
+ help=f"Named asset class: {', '.join(sorted(ASSET_CLASSES))}",
236
+ )
237
+ s.add_argument("--label", default=None)
238
+ s.add_argument(
239
+ "--format",
240
+ choices=["text", "json", "csv"],
241
+ default="text",
242
+ )
243
+ s.add_argument("-v", "--verbose", action="store_true", help="Show audit trail")
244
+ s.set_defaults(func=cmd_schedule)
245
+
246
+ c = sub.add_parser("classes", help="List common asset class shortcuts")
247
+ c.set_defaults(func=cmd_classes)
248
+
249
+ m = sub.add_parser(
250
+ "mq-test",
251
+ help="Apply the >40%% fourth-quarter mid-quarter convention test",
252
+ )
253
+ m.add_argument("--q1", type=str, required=True)
254
+ m.add_argument("--q2", type=str, required=True)
255
+ m.add_argument("--q3", type=str, required=True)
256
+ m.add_argument("--q4", type=str, required=True)
257
+ m.add_argument("--format", choices=["text", "json"], default="text")
258
+ m.set_defaults(func=cmd_mq_test)
259
+
260
+ r = sub.add_parser("rates", help="Print raw IRS percentage rates for a class/convention")
261
+ r.add_argument("--years", type=int, required=True, choices=list(GDS_PERSONAL_PERIODS))
262
+ r.add_argument(
263
+ "--convention",
264
+ default="half_year",
265
+ choices=["half_year", "mid_quarter"],
266
+ )
267
+ r.add_argument("--quarter", type=int, default=None)
268
+ r.set_defaults(func=cmd_rates)
269
+
270
+ return p
271
+
272
+
273
+ def main(argv: Optional[Sequence[str]] = None) -> int:
274
+ parser = build_parser()
275
+ args = parser.parse_args(argv)
276
+ try:
277
+ return args.func(args)
278
+ except (ValueError, KeyError) as e:
279
+ print(f"error: {e}", file=sys.stderr)
280
+ return 2
281
+
282
+
283
+ if __name__ == "__main__":
284
+ raise SystemExit(main())