Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

This project follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Fixed

- The `Sponsorship.annual_value` and `sponsorships()` docs no longer say an ended contract's annual value is zero. Ended contracts often keep a non-zero annual value, so filter on `end` to find running contracts.

Refs #11.

## [0.5.10] - 2026-10-10

### Fixed
Expand Down
3 changes: 2 additions & 1 deletion docs/guides/what-a-save-holds.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ only adaptability.

`managed_clubs()` is the club you run, and is empty between jobs. `finances()` is monthly figures
per club, in the save's own unit. `sponsorships()` includes historical contracts, so a row
does not necessarily describe current income. `stadiums()` holds capacities and ownership;
does not necessarily describe current income: an ended contract can keep a non-zero annual
value, so filter on `end` against the save's date. `stadiums()` holds capacities and ownership;
names are available for only some grounds.

Club data still has limits. The meaning of a zero corporate-facilities rating and some
Expand Down
5 changes: 3 additions & 2 deletions src/fmsave/_save.py
Original file line number Diff line number Diff line change
Expand Up @@ -1460,8 +1460,9 @@ def sponsorships(self) -> Table[Sponsorship]:

The clubs are the ones `finances()` keeps a series for, in the same order, and each
club's contracts keep the order the save stores them in. **The list keeps contracts that
have ended**, whose `annual_value` is zero, so a club's current sponsorship income is the
sum of `annual_value` over the rows whose `end` is after the save's date.
have ended**, and an ended contract's `annual_value` is often non-zero, so a club's
current sponsorship income is the sum of `annual_value` over the rows whose `end` is
after the save's date, not over every row.

Every `type` code is UNKNOWN: the save groups its sponsorships into about twenty kinds
and no displayed label has pinned one of those codes. Money is a whole number in the
Expand Down
9 changes: 6 additions & 3 deletions src/fmsave/models/finances.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,12 @@ class Sponsorship:
kind: What kind of sponsorship it is; every code is UNKNOWN (unconfirmed).
start: The day the contract starts.
end: The day it ends.
total_value: The contract's whole value, which is the annual value times its whole
years on about 19 contracts in 20.
annual_value: What it pays a year; zero on a contract that has ended.
total_value: The contract's whole value. On most running contracts it is the annual
value times the contract's whole years.
annual_value: What a running contract pays a year. A contract that has ended can
still carry a non-zero annual value, often equal to its total value, so neither a
zero nor a non-zero value says whether the contract is still running; compare
`end` with the save's date for that.
unknown: Numeric fields with no known meaning (unconfirmed).
"""

Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/career.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ class ExampleClub:
)
FINANCE_MONTH_COUNT = len(FINANCE_MONTH_VALUES)

# Sponsor contracts. A is running, B ended two years before the clock and so carries no annual
# Sponsor contracts. A is running, B ended two years before the clock and carries no annual
# value, and C is Example Athletic's one running contract.
SPONSOR_A = {
"sponsor_type": 4,
Expand Down
Loading