Metadata-Version: 2.4
Name: epnexess
Version: 1.1.3
Summary: Track subscriptions, bills and income across personal and company accounts - offline, multi-currency, with tax taken off what comes in.
Author: EPNExess author
License: # PolyForm Noncommercial License 1.0.0
        
        <https://polyformproject.org/licenses/noncommercial/1.0.0>
        
        ## Acceptance
        
        In order to get any license under these terms, you must agree to them as both strict obligations and conditions to all your licenses.
        
        ## Copyright License
        
        The licensor grants you a copyright license for the software to do everything you might do with the software that would otherwise infringe the licensor's copyright in it for any permitted purpose.  However, you may only distribute the software according to [Distribution License](#distribution-license) and make changes or new works based on the software according to [Changes and New Works License](#changes-and-new-works-license).
        
        ## Distribution License
        
        The licensor grants you an additional copyright license to distribute copies of the software.  Your license to distribute covers distributing the software with changes and new works permitted by [Changes and New Works License](#changes-and-new-works-license).
        
        ## Notices
        
        You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms or the URL for them above, as well as copies of any plain-text lines beginning with `Required Notice:` that the licensor provided with the software.  For example:
        
        > Required Notice: Copyright Yoyodyne, Inc. (http://example.com)
        
        ## Changes and New Works License
        
        The licensor grants you an additional copyright license to make changes and new works based on the software for any permitted purpose.
        
        ## Patent License
        
        The licensor grants you a patent license for the software that covers patent claims the licensor can license, or becomes able to license, that you would infringe by using the software.
        
        ## Noncommercial Purposes
        
        Any noncommercial purpose is a permitted purpose.
        
        ## Personal Uses
        
        Personal use for research, experiment, and testing for the benefit of public knowledge, personal study, private entertainment, hobby projects, amateur pursuits, or religious observance, without any anticipated commercial application, is use for a permitted purpose.
        
        ## Noncommercial Organizations
        
        Use by any charitable organization, educational institution, public research organization, public safety or health organization, environmental protection organization, or government institution is use for a permitted purpose regardless of the source of funding or obligations resulting from the funding.
        
        ## Fair Use
        
        You may have "fair use" rights for the software under the law. These terms do not limit them.
        
        ## No Other Rights
        
        These terms do not allow you to sublicense or transfer any of your licenses to anyone else, or prevent the licensor from granting licenses to anyone else.  These terms do not imply any other licenses.
        
        ## Patent Defense
        
        If you make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company.
        
        ## Violations
        
        The first time you are notified in writing that you have violated any of these terms, or done anything with the software not covered by your licenses, your licenses can nonetheless continue if you come into full compliance with these terms, and take practical steps to correct past violations, within 32 days of receiving notice.  Otherwise, all your licenses end immediately.
        
        ## No Liability
        
        ***As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.***
        
        ## Definitions
        
        The **licensor** is the individual or entity offering these terms, and the **software** is the software the licensor makes available under these terms.
        
        **You** refers to the individual or entity agreeing to these terms.
        
        **Your company** is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization.  **Control** means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise.  Control can be direct or indirect.
        
        **Your licenses** are all the licenses granted to you for the software under these terms.
        
        **Use** means anything you do with the software requiring one of your licenses.
        
Project-URL: Homepage, https://github.com/YOUR-USERNAME/epnexess
Project-URL: Changelog, https://github.com/YOUR-USERNAME/epnexess/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/YOUR-USERNAME/epnexess/issues
Keywords: subscriptions,finance,budget,tracker,offline,tkinter,desktop,recurring-payments,invoices
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: X11 Applications
Classifier: Environment :: Win32 (MS Windows)
Classifier: Environment :: MacOS X
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: Free for non-commercial use
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Office/Business :: Financial :: Accounting
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

<p align="center">
  <img src="epnexess/assets/logo-128.png" width="96" alt="EPNExess">
</p>

<h1 align="center">EPNExess</h1>

<p align="center">
  Track subscriptions, bills and income across personal and company accounts —
  offline, in any currency, with tax taken off what comes in.
</p>

<p align="center">
  <img alt="licence" src="https://img.shields.io/badge/free%20for%20noncommercial%20use-PolyForm%20NC-0F837B">
  <img alt="python" src="https://img.shields.io/badge/python-3.10%2B-0F837B">
  <img alt="dependencies" src="https://img.shields.io/badge/dependencies-none-0F837B">
  <img alt="platforms" src="https://img.shields.io/badge/windows%20%C2%B7%20macos%20%C2%B7%20linux-0F837B">
</p>

<p align="center">
  <b>Install:</b>
  <a href="INSTALL.md#windows">Windows</a> ·
  <a href="INSTALL.md#macos">macOS</a> ·
  <a href="INSTALL.md#linux">Linux</a>
  &nbsp;&nbsp;|&nbsp;&nbsp;
  <a href="../../releases">Download the app</a>
  &nbsp;&nbsp;|&nbsp;&nbsp;
  <b>No Python, no database, no account needed</b>
</p>

![Dashboard](screenshots/dashboard.png)
*The dashboard: headline rates, this period's actuals, the payment runway with
income below the line, and per-account cover — each card on its own time window.*

## Why

Subscription trackers assume one person, one currency, and money only going out.
That falls apart the moment you run a company alongside your own finances: two
sets of accounts, invoices that arrive gross and are worth much less after tax,
and one question that actually matters — **will the company account have enough in
it on the 14th, and if not, how much do I move across?**

EPNExess answers that. It runs entirely on your machine: no account, no server,
no network calls, no telemetry. Your data is one SQLite file you can copy, back up
or delete. It needs nothing outside the Python standard library.

## Get it

**Just want to use it?** Download it from the [Releases page](../../releases) and
double-click — nothing else to install. Step-by-step instructions with pictures
of what to click, for each system, are in **[INSTALL.md](INSTALL.md)**:
[Windows](INSTALL.md#windows) · [macOS](INSTALL.md#macos) · [Linux](INSTALL.md#linux).

**With Python installed** (3.10+ with Tk — the python.org installers include it):

```
pip install epnexess     # or: pipx install epnexess
epnexess
```

On recent Linux distributions and Homebrew Python, `pip` refuses to install
outside a virtual environment; use `pipx` there. [INSTALL.md](INSTALL.md) has
the details per system.

**Without Python** — download the Windows or macOS build from
[Releases](../../releases) and run it. Both are unsigned, so the first launch
shows a warning: on Windows click *More info* → *Run anyway*; on macOS
right-click the app → *Open* once.

**From a clone**:

```
git clone https://github.com/Antikatoptis-Pareidolia/Epnexess.git
cd Epnexess && python3 epnexess.py
```

**macOS.** Yes, it runs. Install Python from [python.org](https://www.python.org/downloads/macos/)
— those builds bundle a current Tk. Then:

```
python3 epnexess.py
```

Avoid the `/usr/bin/python3` that ships with macOS: it is pinned to an old Tk 8.5
that renders the interface poorly. If you use Homebrew, `brew install python-tk`
gives the same result. Check what you have with:

```
python3 -c "import tkinter; print(tkinter.TkVersion)"
```

8.6 or higher is what you want. To launch it from Finder, save a file called
`EPNExess.command` next to the app containing
`cd "$(dirname "$0")" && python3 epnexess.py`, then `chmod +x EPNExess.command`.

**Windows** — the python.org installer includes Tk.
**Debian / Ubuntu** — `sudo apt install python3-tk` if you get `ModuleNotFoundError: tkinter`.
**Fedora** — `sudo dnf install python3-tkinter`.

To see it populated before entering your own data:

```
python3 epnexess.py --demo
```

Add `--db /tmp/demo.db` to keep the sample data out of your real file.

## The screens

**Dashboard** — an account filter across the top, then four headline figures,
then this month, then the runway, then the detail.

*The account filter* is a checkbox per account in the header row, with
subaccounts nested under their group, all ticked by
default. Untick one and everything belonging to it — its expenses, its income, its
place on the runway, its row in account cover — disappears from every figure on
the screen, so you can look at just the personal side, or just one company. The
subtitle tells you what you are looking at. It resets to everything on each
launch, so you can never leave yourself with a misleadingly small total.

*The period cards* — this month, upcoming payments, account cover and where it
goes — each carry their own window selector: this or last month, this or last
quarter, this year, the next 30 or 90 days, or a custom range you type. They are
independent, so you can read last month's spending beside the next 90 days of
cover. Each figure splits into what has already settled and what is still to
come, and only entries dated inside the window count.

*Payment runway* is a timeline you can set to 14, 30, 45, 60 or 90 days, or to the
rest of the month or quarter, using the dropdown in the card's top right. Each day
is a column: money going out rises above the line, coloured by the account it
leaves from, and income drops below the line in green. Hover any bar for the
detail. A heavy week is visible as a shape before you read a single number.

Below that: upcoming movements — each row leads with its due date, then the name,
the account it leaves from and a **Mark paid** / **Received** button — account
cover, and where the money goes by category.

Miss a payment and the card grows an **Overdue** tab, red, with the count and
the sum owed — a weekly bill three weeks late shows as three charges, each at the
date it was due, and they stay in every total until you settle or pause them.
Paying settles the oldest first, and the **Needs attention** card jumps straight
there when clicked.

Marked something paid by mistake? Payment history on the Currencies & data screen
has **Mark as unpaid**: the payment is removed, the balance goes back, and the
charge returns to the schedule as due.

**Subscriptions** — every recurring entry, income included. Sortable and
searchable; filter by expenses, income, one-time or paused. Rows turn amber inside
seven days, red when overdue, green for income, grey when paused. Double-click to edit.

![Subscriptions](screenshots/subscriptions.png)
*Every recurring entry in one sortable table — expenses and income, net of tax,
with one-time entries kept out of the monthly rate.*

**Accounts** — personal and company accounts side by side, each showing what
leaves, what arrives, and what is left afterwards.

*Subaccounts.* An account can sit under another: the three banks your personal
money actually lives in, or the cards a company spends from. A group's figures
roll up everything beneath it, converting currencies as needed, so you can read
the company as one number — and tick **Break down by subaccount** on the cover
card to see exactly which card needs how much by when. Upcoming payments name the
subaccount too. Nesting stops at one level on purpose; deeper trees stop being
readable at a glance.

![Accounts](screenshots/accounts.png)
*Accounts and subaccounts: the three banks personal money actually sits in,
rolled up, with what is left after everything clears.*

**Currencies & data** — base currency, exchange rates, payment history, transfers
between accounts, CSV export.

![Currencies & data](screenshots/currencies.png)
*Hand-entered exchange rates (nothing is fetched), settled history with undo, and
money moved between accounts.*

*Payment history* takes a period: pick a month, a quarter, a year, or everything.
Only periods your data actually spans are offered, and the line beneath totals
what went out, what came in, and the net for that period.

## How it works

**Expenses and income.** Every entry is one or the other. Income replenishes an
account on the same weekly, monthly, quarterly or yearly rhythm as a bill, and
feeds into account cover so you can see whether the salary lands before the rent
leaves. Add one with **Add income** in the header or on the Subscriptions screen.

**Repeats, or not.** Each entry has a *Repeats* checkbox, ticked by default: once
the date passes, it rolls to the same day in the next period. Untick it and the
entry happens exactly once — it still appears on the runway, in this month's
figures and in account cover, but it is left out of the monthly and yearly rates,
because a one-off laptop purchase is not a monthly cost. Marking a one-off as paid
archives it instead of rolling it forward.

**Billing cycles.** Weekly, monthly, quarterly, yearly. Every recurring amount is
also shown as a monthly equivalent, so a €420 yearly insurance appears as €35 a
month and can be compared with everything else. Weekly items use 52/12 weeks per month.

**Billing dates use an anchor day.** Something billed on the 31st passes through
February as the 28th and then returns to the 31st, instead of drifting earlier
every year.

**Mark paid** logs the movement and rolls the date forward one period, so the
schedule stays accurate without you editing dates. Overdue items stay visible
until you deal with them.

**Pause instead of delete.** A paused entry keeps its history and drops out of
every total.

**Base currency and rates.** Every total is converted into your base currency.
Rates are entered by hand and never fetched online, so the app works offline and
your figures do not shift between sessions. Changing the base currency
recalculates all stored rates around the new base; the amounts on your entries are
never touched.

**Tax on income.** Company invoices rarely arrive whole. Each income entry takes
up to two tax rates that apply one after the other, so 16% profit tax followed by
16% dividend tax leaves 70.56% of the invoice, not 68%. Enter any percentage you
like; the presets are only shortcuts. Every total — the runway, this month,
account cover, the monthly rate — uses what you keep, while the gross figure stays
on the entry so you can still see what was invoiced. Picking a company account
suggests a rate; you can clear it.

**Account cover.** Give an account a balance and the dashboard works out
`balance + income − payments` over the next 30 days, showing what is left
afterwards and flagging any account that would go under.

**Topping up an account.** Every account in the cover card has a button: **Cover
it** when it is short, **Top up** otherwise. It opens with the exact shortfall
filled in, lets you pick which account to draw from — personal accounts are listed
first — and previews both balances before and after. Confirming moves the money,
converting between currencies if the two accounts differ, and both accounts'
"left after" figures update immediately. Every move is recorded under Currencies
& data.

**Balances follow what you mark.** Marking something paid or received adjusts the
account balance as well as rolling the date forward. This keeps "left after"
stable: the payment leaves the balance and leaves the upcoming total at the same
time, so the figure only moves when something real changes. If you prefer to type
balances straight from your bank, just overwrite them on the Accounts screen.

## Keyboard

| | |
|---|---|
| `Ctrl` `N` | Add subscription |
| `Ctrl` `I` | Add income |
| `Ctrl` `F` | Search |
| `Ctrl` `1`–`4` | Jump to a screen |
| `F5` | Refresh |
| `Esc` | Close a dialog |

On macOS use `Control`, not `Command`.

## Your data

One SQLite file, by default at `~/.epnexess/epnexess.db`. The exact path is shown
at the bottom of the Currencies & data screen. If you ran the earlier version, its
file is moved across automatically the first time you launch.

To back up, copy that file. To keep several sets of books, run with `--db` and a
different path. **Export to CSV** writes every entry with its type, recurrence and
monthly equivalent.

## Contributing

Bug reports and pull requests are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md)
for the ground rules, the main one being that the core stays offline and
dependency-free. Planned work lives in the
[issues](../../issues); [CHANGELOG.md](CHANGELOG.md) records what has landed.

```bash
python3 tests/test_core.py               # logic, no display needed
xvfb-run -a python3 tests/test_ui.py     # interface; drop xvfb-run off Linux
```

## Files

| File | What it holds |
|---|---|
| `epnexess/` | The package: `app.py` (window, sidebar), `core.py`, `views.py`, `widgets.py`, `theme.py`, `assets/` |
| `epnexess.py` | Thin launcher so a clone runs without installing |
| `tests/` | Two suites: core logic, and the interface |
| `tools/` | Maintainer scripts: icon regeneration. Not needed to run the app |

## Licence

[PolyForm Noncommercial 1.0.0](LICENSE): use it, copy it, change it and share it
freely for any noncommercial purpose. Using it to make money — selling it,
bundling it into a paid product or service — needs the author's written
permission first; open an issue or get in touch. Contributions are welcomed
under the same terms.

## Changing the look

Colours live at the top of `theme.py` as plain hex values: a very light blue page,
white cards, soft navy ink, the logo's teal as the accent, and its yellow for
anything due soon. Green means money arriving; red means overdue or short. To go
back to a blue accent, set `ACCENT = "#3D8FD6"`.

Other things worth knowing:

- `DashboardView.HORIZON` in `views.py` sets the runway length (45 days).
- Account cover uses a 30-day window; search `account_outlook(30` in `views.py`.
- Fonts are resolved from a preference list in `theme.py` (`_SANS_STACK`,
  `_MONO_STACK`) and fall back to whatever your system has. Money is always set in
  a monospaced face so figures line up in a column.
- Icons are drawn as vectors on a canvas in `theme.py`, so there are no image
  files to manage. `draw_icon` takes a name and a box.
