ГлавнаяБлогРасчёт польских процентов за просрочку: пошаговое руководство
Python

Расчёт польских процентов за просрочку: пошаговое руководство

Узнайте, как правильно рассчитать проценты за просрочку платежа в Польше с учётом изменения ставок. Практический код на Python и примеры.

Al
Редакция Algolitalgolit.ru
8 мин чтения29 июля 2026 г.

Как рассчитать проценты за просрочку платежа в Польше

Если вы выставляете счёт польскому бизнесу, а платят с опозданием, вам полагаются законные проценты. Простая формула проценты = сумма × ставка × дни / 365 часто неверна. Причина — ставка меняется за время долга, и расчёт нужно разбивать на периоды.

Два режима, два разных механизма

В Польше существуют две отдельные ставки законных процентов. Их путают чаще всего.

Odsetki ustawowe za opóźnienie (Гражданский кодекс) применяются к обычным обязательствам. Ставка привязана к референсной ставке Национального банка Польши (NBP) и меняется каждый раз, когда центробанк изменяет ставку, с даты решения.

Odsetki za opóźnienie w transakcjach handlowych применяются к коммерческим сделкам B2B. Эта ставка фиксируется на полугодие: на 1 января для первого полугодия и на 1 июля для второго. Она не меняется внутри полугодия, даже если NBP изменит ставку в марте.

Для одной и той же задержки в 200 дней первый режим может потребовать пять сегментов, а второй — ровно два. Выбор неправильного режима меняет не только число, но и саму структуру расчёта.

Сегментация расчёта

Как только вы принимаете, что ставка — это функция от времени, а не константа, расчёт превращается в свёртку по интервалам. Вот реализация на Python:

from datetime import date, timedelta
from typing import List, Tuple

def days_between(d1: date, d2: date) -> int:
    """Количество дней между датами (d2 - d1)."""
    return (d2 - d1).days

def interest_for_periods(amount_grosze: int, periods: List[Tuple[date, date, float]]) -> int:
    """
    Расчёт процентов по сегментам.
    :param amount_grosze: сумма долга в грошах (целое число)
    :param periods: список кортежей (from, to, rate_percent)
    :return: сумма процентов в грошах
    """
    total = 0
    for from_date, to_date, rate_percent in periods:
        days = days_between(from_date, to_date)
        # Округление на каждом сегменте, работаем в целых грошах
        total += round((amount_grosze * rate_percent * days) / (365 * 100))
    return total

В этой небольшой функции три ключевых момента:

  • Деньги остаются в целых грошах. 0.1 + 0.2 != 0.3, а проценты — это сумма многих малых слагаемых. Накопление в числах с плавающей точкой приводит к расхождению в один грош с расчётом бухгалтера контрагента, что превращает обсуждение оплаты в спор.
  • Округление происходит на каждом сегменте, а не один раз в конце. Это модель решения, а не математическая необходимость: каждый сегмент — это отдельный юридически определённый период, поэтому естественно округлять его независимо. Какую бы конвенцию вы ни выбрали, делайте это осознанно и документируйте, потому что разница составит грош-два, и кто-нибудь спросит.
  • Подсчёт дней исключает одну из границ. Ошибка на один день при задержке в 400 дней — это не погрешность округления, а целый день процентов.

Построение списка периодов

Сама сегментация — это слияние окна задержки с временной шкалой ставок:

from datetime import date
from typing import List, Tuple

def split_by_rate_changes(
    from_date: date,
    to_date: date,
    rate_table: List[Tuple[date, float]]
) -> List[Tuple[date, date, float]]:
    """
    Разбивает интервал [from_date, to_date) на сегменты по изменениям ставок.
    :param from_date: дата начала долга
    :param to_date: дата окончания долга
    :param rate_table: список кортежей (effective_from, rate_percent), отсортированных по дате
    :return: список сегментов (from, to, rate_percent)
    """
    # Сортируем таблицу ставок по дате вступления в силу
    sorted_rates = sorted(rate_table, key=lambda x: x[0])
    periods = []
    for i, (eff_from, rate) in enumerate(sorted_rates):
        start = eff_from
        end = sorted_rates[i + 1][0] if i + 1 < len(sorted_rates) else to_date
        # Обрезаем по окну задержки
        seg_from = max(start, from_date)
        seg_to = min(end, to_date)
        if seg_from < seg_to:
            periods.append((seg_from, seg_to, rate))
    return periods

Для режима коммерческих сделок вы передаёте таблицу с двумя записями в год. Для режима Гражданского кодекса — каждое решение NBP. Одна и та же свёртка, разные входные данные. Разница режимов должна быть в данных, а не в логике управления.

Почему нельзя просто использовать текущую ставку

Потому что ответ будет неверным, причём в легко заметную сторону. В цикле снижения ставок применение сегодняшней ставки ко всей задержке занижает сумму, которую вам должны. В цикле повышения — завышает, что ещё хуже: теперь ваше требование содержит цифру, которую должник может оспорить.

Что стоит знать, если вы реализуете это

  • Таблицы ставок должны быть источником истины с датами вступления в силу. Они меняются без предупреждения по вашему графику.
  • Храните разбивку по сегментам, а не только итог. Когда кто-то оспаривает цифру, фраза «вот пять периодов и ставка в каждом» заканчивает разговор; одно число — нет.
  • Тестируйте на границах: задержка, начинающаяся в день изменения ставки, заканчивающаяся в этот день, и полностью внутри одного периода.

Практический вывод

Прямо сейчас возьмите свой последний случай просрочки и проверьте, не используете ли вы одну ставку для всего периода. Если да — пересчитайте с учётом сегментации. Вы можете использовать приведённый код как основу для своего инструмента или обратиться к готовому решению.

#польские проценты#расчёт процентов#просрочка платежа#Python
Al
Редакция Algolit

Пишем про алгоритмы, подготовку к собеседованиям и карьеру в IT — так, чтобы было понятно и полезно.

Хочешь закрепить знания на практике?

Решай задачи на Algolit — интерактивная платформа для обучения

Начать бесплатно →