getItemWhere("account_id = $account_id AND valid_to IS NULL"); } /** * Меняет ставку задним/будущим числом — не правит старую строку, а закрывает её * (`valid_to` = день перед новым `valid_from`) и открывает новую, чтобы не потерять историю * за уже прошедшие периоды (см. бюджет_текущий_план.md → Account_rates → «При изменении * ставки»). Атомарно — `Repository::transaction()` (на вложенном уровне — SAVEPOINT). * * @param int $account_id * @param float $rate_percent Новая ставка, % годовых * @param string $valid_from Дата, с которой действует новая ставка (Y-m-d) — не обязательно * «сегодня», банк мог прислать уведомление заранее/задним числом * @return void * @throws MyException Если $valid_from не позже даты начала текущей ставки — иначе закрытая * строка получила бы valid_to раньше собственного valid_from */ public function setRate(int $account_id, float $rate_percent, string $valid_from): void { $this->transaction(function () use ($account_id, $rate_percent, $valid_from) { $current = $this->getCurrentRate($account_id); if ($current) { if ($valid_from <= $current->valid_from) { throw new MyException( 'Дата новой ставки (:new) должна быть позже даты начала текущей (:current)', [':new' => $valid_from, ':current' => $current->valid_from] ); } $valid_to = (new DateTimeImmutable($valid_from))->modify('-1 day')->format('Y-m-d'); $this->update(['id' => $current->id, 'valid_to' => $valid_to]); } $this->create([ 'account_id' => $account_id, 'rate_percent' => $rate_percent, 'valid_from' => $valid_from, 'valid_to' => null, ]); }); } }