Thanks for your interest. pit-38 is an open-source tax calculator people
rely on to file real PIT-38 declarations, so correctness matters more than
speed. Friendly code review is how we get there — first-time contributors
are very welcome.
Discussions and PR reviews happen in English or Polish — use whichever you're more comfortable with. The code and docs are primarily English so that international contributors can participate, but tax rules are Polish and forum links are usually Polish, so both languages coexist here naturally.
🇵🇱 Wersja polska niedługo — jeśli wolisz pisać po polsku, śmiało otwieraj issue / PR po polsku. Odpiszemy w Twoim języku.
- Quick start
- Prerequisites
- Development setup
- Running tests
- Linting and style
- Submitting a pull request
- Adding a new broker plugin
- Reporting tax-law bugs
- Code of Conduct
- Where to ask questions
git clone https://github.com/pbialon/pit-38
cd pit-38
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest tests/If that passes, you have a working dev environment. Skip to Submitting a pull request if you already know what you want to change, or read on for details.
- Python 3.10+ — the code uses
X | Yunion syntax and other 3.10 features. Earlier versions won't work. - git — for cloning and branching.
- pipx (optional) — only needed if you want to install
pit-38as a CLI tool; not required for development.
Use a virtualenv so the editable install and its dev dependencies don't pollute your system Python:
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"The [dev] extra installs pytest, flake8, and coverage (see
pyproject.toml). After install, pit38 is available on your PATH and
points at the checkout, so your edits are picked up without reinstalling.
Sanity check:
pit38 --helpThe full suite runs in seconds:
pytest tests/End-to-end tests, which exercise the CLI against real anonymized CSV
fixtures, live under tests/e2e/:
pytest tests/e2e/With coverage:
pytest tests/ --cov=pit38 --cov-branchTest conventions (worth matching when you add tests):
- Tests use
unittest.TestCase— not pytest fixtures. - Helpers live in
tests/utils.py— factory functions likebuy(),sell(),apple(),usd(),zl(), andStubExchanger(fixed 4.0 USD→PLN). Prefer these over hand-rolled objects. - If you change tax-calculation behaviour, update or add an E2E fixture that shows the change end-to-end.
Flake8 runs in CI with a minimal config — only syntax errors and undefined names fail the build (E9, F63, F7, F82). We deliberately keep the bar low here so style nitpicks don't stall contributions. Run it locally if you want:
flake8 pit38/ tests/ --select=E9,F63,F7,F82General code style:
- Follow patterns already present in the module you're editing. When in doubt, mirror the neighbouring file.
- No new abstractions unless the task requires them. Three similar lines beat a premature helper.
- Type hints are encouraged on public functions but not required.
-
Fork and branch. Branch names roughly follow the pattern
<type>/<issue-number>-<short-slug>:fix/33-revolut-csv-bomfeat/9-csv-loader-consolidationdocs/46-contributingrefactor/...,test/...,chore/...
-
Commit style. Terse, imperative mood, present tense. First line under ~72 chars. Include the issue number when closing one:
fix: Revolut CSV with BOM, lowercase headers, and unknown operations (#33)Longer rationale goes in the body. Look at
git logfor tone. -
Link to the issue. In the PR body, put
Closes #<n>so the issue auto-closes on merge. If there's no issue yet and the change is non-trivial, open one first — reviewers like having the "why" captured somewhere durable. -
Use the PR template.
.github/PULL_REQUEST_TEMPLATE.mdasks specifically about tax-correctness impact. Fill it in honestly — even "purely refactor, no tax impact" is a valid answer and saves reviewer time. -
Keep PRs focused. One issue per PR, ideally. If you find tangential bugs while working, file them as separate issues rather than bundling.
-
Expect review. For anything that touches tax output, expect a careful pass. Reviewers may ask for references to the ustawa article (the Polish income tax act) or ask you to add a regression fixture.
Broker plugins live under pit38/plugins/ and transform a broker-specific
CSV export into the standardized format that pit38 consumes. Each
plugin is self-contained. See
pit38/plugins/README.md for the plugin
architecture overview (PL/EN).
Reference implementations (read these first):
- Stocks —
pit38/plugins/stock/revolut/(most complete, handles BOM, unknown operations, dividends, fees, stock splits) - Crypto —
pit38/plugins/crypto/binance/
High-level recipe:
- Create
pit38/plugins/stock/<broker>/(orcrypto/<broker>/). - Implement an entry point (
__main__.pyor a CLI command) that accepts--input-pathand--output-path. - Parse the broker's export, emit rows in the standardized CSV shape
(see
pit38/data_sources/stock_loader/example_format.csvorpit38/data_sources/crypto_loader/example_format.csv). - Share number/currency parsing via
pit38.plugins.normalization(normalize_currency_layout,parse_amount) rather than re-rolling regex. Revolut and E*Trade both use it — new plugins should too. - Add the broker to the
pit38 importsubcommand group inpit38/cli.py. - Write unit tests for the row parser and at least one E2E test with
an anonymized sample CSV under
tests/e2e/fixtures/. Remove account numbers, names, and PII before committing. - Update the "Supported Brokers" table in both
README.mdandREADME.pl.md.
If you're unsure whether a format quirk belongs in shared normalization or plugin-local code, open a draft PR — easier to discuss over real code than in the abstract.
Before you start coding, please open a broker-support issue using the broker support template and attach a sanitized CSV sample. This lets us confirm we have reference data to validate against, and flags any format ambiguities before you invest time.
Bad tax math = wrong PIT filings, so tax-correctness bugs get special
treatment. If you suspect pit38 produces the wrong number:
- Open an issue immediately — don't wait until you have a fix. Other users may be affected.
- Include a minimal reproducible CSV (anonymized — remove PII before pasting). Five rows is usually enough.
- Cite the tax rule. If you can, point to the article of the ustawa (Ustawa o podatku dochodowym od osób fizycznych) or to docs/TAX_RULES.md section that you believe is being violated. A KIS interpretation or forum link works too.
- State expected vs actual output. "Expected tax of 123.45 PLN because X, got 678.90 PLN." Numbers matter here.
- Label it
tax-correctness. Maintainers will prioritize these above feature work.
If you fix a tax-law bug yourself, update docs/TAX_RULES.md /
docs/TAX_RULES.pl.md if the rule itself was under-documented, and
add a regression test with the reproducing fixture so it can't silently
come back.
This project follows the Contributor Covenant v2.1.
By participating, you agree to abide by its terms. Issues relating to
conduct can be raised privately via a GitHub issue marked confidential
or by emailing the maintainer listed in CODE_OF_CONDUCT.md.
- GitHub Discussions — tax-rule Q&A, broker format questions, general discussion. English and Polish both welcome.
- Issues — bug reports, feature requests, broker-support requests. Templates exist for each.
- PR comments — implementation-level questions during review.
Happy hacking, and thanks for helping make Polish tax filing less painful.