# 0018. Price tiers are columns on the storage

- **Status:** Accepted
- **Date:** 2025-07-08

## Context

A storage unit has a different monthly price depending on how long the customer commits: one, three, six or
twelve months. The office edits these per unit, and the site shows "from X Ft".

## Decision

**Four unsigned integer columns on `storage`**: `price1M`, `price3M`, `price6M`, `price12M`, in net forint,
no decimals. No price table, no price history, no per-customer pricing.

`StorageService::minPrice()` picks the lowest **non-zero** of the four for the "from" display. A zero means
"not offered at that term", not "free".

The quote and the contract copy the numbers they need into their own columns —
`ProposalRequestItem` and `Rent` each carry `firstMonthPriceNet`, `monthlyPriceNet`, `lastMonthPriceNet`,
`depositPriceNet` (and the item also `totalPriceNet` and `discountPercentage`).

## Alternatives

- **A `storage_price` table** keyed by storage and term. Correct if the terms were variable; they are four
  fixed options, and every read would be a join.
- **A price list per storage type**, overridden per unit. More flexible than the business needs — pricing is
  genuinely per unit here, because two units of the same type on different floors are priced differently.
- **Decimal prices.** Forint has no minor unit in practice; integers avoid rounding entirely.

## Consequences

- **Reading a price is a column read.** The storage list renders prices without a join.
- **Adding a fifth term is a migration**, on the storage, on the item and on the rent.
- **No price history.** Changing a unit's price does not affect existing quotes or contracts, because those
  copied the numbers at the time — which is the behaviour that matters commercially. But there is no record
  of what a unit used to cost.
- **Zero is overloaded.** It means "not offered", and only `minPrice()` knows that; a raw read of `price12M`
  cannot distinguish an unset price from a free one.
- **Prices are net.** Gross is computed through `TaxRate` when a `Payment` is created, and both net and gross
  are then stored on the payment — so a later tax-rate change does not retroactively alter issued amounts.
