Регулярная доставка: раз в месяц или каждые 30 дней

Клиент оформил регулярную доставку 31 января. В карточке написано «каждый месяц», а программа прибавляет тридцать дней. Следующий заказ попадает на март, февраль оказывается пропущен. Другой вариант: система переносит февральскую дату на 28-е, а затем незаметно начинает доставлять каждое 28-е число. В обоих случаях нужно уточнить смысл расписания до подключения автоматического создания заказов.
Ниже — модель для разработки сайта магазина, например с регулярной доставкой товаров для животных или расходных материалов. Это не рекомендация количества корма, порядка списания оплаты или условий договора подписки. Все даты учебные. В статье проектируется календарь и связь его отдельных доставок с заказами, а не автоматическая оплата и не гарантия наличия товара.
Сформулируйте обещание календаря обычными словами
Сначала выберите, что означает повтор: через фиксированное число календарных дней, в определённый день месяца или по другому согласованному правилу. «Раз в месяц» не заменяет полного описания, если стартовая дата может отсутствовать в следующем месяце. Покупателю нужно понять результат, а разработчику — получить однозначные ожидаемые даты.
Для учебного месячного плана установим правило: исходное число 31 сохраняется как опорное; если в очередном месяце такого числа нет, используется последний день этого месяца. В следующем месяце снова проверяется исходное 31-е. Это выбранная бизнес-модель примера, не универсальное поведение календарных библиотек и не норматив для всех подписок.
Для другого учебного плана зададим интервал тридцать календарных дней от каждой плановой даты. Здесь расчёт ведётся по календарным датам без обещания определённого числа часов между доставками. Если сервису важны время суток и часовой пояс, они требуют отдельного описания. Не подменяйте выбор даты незаметным прибавлением секунд в браузере.
Сравните два плана на границе месяца
Начальная дата обоих примеров — 31 января 2027 года. В месячном плане следующий день доставки равен 28 февраля, затем 31 марта. В плане с тридцатью днями после 31 января получится 2 марта, а затем 1 апреля. Различие появляется в правилах, ещё до учёта наличия, курьера и выходных дней.
| Состояние учебного плана | Месячный, опорное число 31 | Интервал 30 календарных дней | Что проверить |
|---|---|---|---|
| Начало | 31 января 2027 | 31 января 2027 | Одинаковая стартовая дата |
| Следующая доставка | 28 февраля 2027 | 2 марта 2027 | Короткий месяц меняет результат |
| Доставка после неё | 31 марта 2027 | 1 апреля 2027 | Месячный план возвращается к опоре |
| Ошибочный перенос опоры | 28 марта 2027 | Не относится к правилу | Не принять февральское исключение за новый якорь |
Даты полезно показать клиенту до подтверждения плана. Краткая надпись «ежемесячно» менее понятна, чем несколько ближайших конкретных отправок с объяснением исключения. После изменения параметров предпросмотр должен пересчитаться по новой версии. Календарь на экране и сохранённое расписание обязаны относиться к одному набору условий.
Год входит в исходные данные
Не сохраняйте февральское исключение как постоянное число 28. В високосном 2028 году последний день февраля — 29-е. Для того же месячного правила с опорой 31 следующий день после январской отправки будет 29 февраля, а для интервала 30 дней от 31 января — 1 марта. Это ещё одна пара ожидаемых результатов, которую стоит включить в проверку.
В интерфейсе примеров показывайте год, чтобы сотрудник мог повторить расчёт. Короткая подпись «конец февраля» подходит для объяснения правила, но не заменяет конкретную дату созданного вхождения. Если программа копирует прошлогодний список вместо вычисления согласованного календаря, такой тест обнаружит несоответствие до появления заказов.
Обмен с календарём требует проверки смысла
Если программа экспортирует расписание в календарный формат, нельзя предполагать, что любая запись повторения реализует выбранное исключение короткого месяца. RFC 5545 для правил повторения iCalendar отдельно указывает пропуск недопустимых дат. Из этого не следует автоматический перенос на последний день месяца. Собственную модель с таким переносом нужно корректно представить при обмене и проверить на получателе.
Для технического задания достаточно указать ожидаемую последовательность и примеры исключений. Разработчик выбирает подходящее представление и объясняет его границы. Не принимайте интеграцию только по успешному открытию файла календаря. Важно сверить конкретные даты, особенно февраль и переход к следующему месяцу, иначе внешняя программа может показывать другое расписание.
Если внешний календарь предназначен только для просмотра, это тоже нужно обозначить. Изменение события там не обязано изменять заказ магазина автоматически. Если обратная синхронизация предусмотрена, отдельно определяют допустимые действия и разрешение конфликтов. Сам факт кнопки «добавить в календарь» не устанавливает двустороннюю связь.
Пропустить одну доставку и сдвинуть серию — разные действия
В учебном месячном плане клиент пропускает 28 февраля. Если это исключение одной отправки, мартовская дата остаётся 31 марта. Если же он меняет правило на 15-е число, это уже изменение будущего расписания. Две команды требуют разных подписей и разных сохранённых событий, даже если обе убирают ближайший заказ из списка.
Перенос одной доставки также не должен автоматически менять опорное число. Например, согласованная мартовская отправка переносится с 31 марта на 29 марта, а апрель рассчитывается по прежнему месячному правилу. Если бизнес хочет сдвигать всю серию, предложите отдельный явный выбор. Не заставляйте сотрудника угадывать по фразе клиента «в этот раз пораньше», что произошло с остальными месяцами.
Сохраните связь исключения с конкретным плановым вхождением. Если опираться только на изменённую дату, повторное формирование может создать прежний заказ заново. Система должна знать, что февральская отправка была осознанно пропущена, а не просто отсутствует из-за сбоя. Иначе техническое восстановление очереди отменит принятое решение пользователя.

Плановая дата не равна выполненной доставке
Курьер мог передать заказ позже предусмотренного дня. Нужно определить, от чего рассчитывается следующая отправка: от исходного календаря или от фактического исполнения. В учебном месячном плане исходная опора сохраняется. Задержка одной доставки сама по себе не переносит все следующие. Если выбран другой процесс, это самостоятельное правило, которое требуется показать в условиях расписания.
В карточке полезно различать план, согласованное изменение и факт. Тогда менеджер сможет объяснить, почему мартовская передача состоялась 1 апреля, а следующий плановый заказ всё ещё назначен на конец апреля. Один столбец «дата доставки», который переписывается после каждого события, теряет исходное обещание и мешает разбирать изменения.
Не считайте сформированный заказ фактически исполненным. Планирование, подтверждение наличия, сборка и передача относятся к разным этапам выбранного процесса. Эта статья не задаёт обязательный полный набор статусов, но требует не выдавать создание строки в системе за завершённую доставку. Сводный отчёт должен указывать, какие именно события он считает.
Повторный запуск планировщика не создаёт вторую отправку
Программа может пересчитать ближайший период после перезапуска или повторной попытки. Для уже обработанного планового вхождения нужен устойчивый способ установить, создан ли его заказ. Совпадение адреса клиента и даты недостаточно: у одного человека могут существовать разные регулярные планы или дополнительные разовые покупки.
В предлагаемой модели заказ связывается с идентификатором плана и конкретным вхождением. Повтор обработки того же основания не должен создавать второй экземпляр. Новый самостоятельный план или разовая покупка имеют другое основание. Это помогает избежать противоположной ошибки, когда защита от дублей удаляет законный дополнительный заказ из-за совпадения товара.
Общий порядок повторной обработки разобран в статье о запросе после обрыва сети. Для регулярных доставок важно дополнить его сохранёнными исключениями: пропущенная пользователем дата не становится необработанной задачей только потому, что по ней нет заказа. Причина отсутствия должна быть понятна планировщику и сотруднику.
Изменение адреса и состава требует границы применения
Клиент может поменять набор товаров или адрес, когда ближайший заказ уже находится в работе. Определите, затрагивает ли изменение только будущие ещё не созданные отправки или также требует пересмотра текущего заказа. Не переписывайте подтверждённое задание курьеру незаметно вместе с настройками подписки. У текущей операции должен быть собственный согласованный процесс изменения.
Для просмотра полезно показать ближайшую отправку отдельно от параметров следующих. Тогда клиент видит, что именно он редактирует. Сохранённая история должна объяснять, по какой версии состава создан каждый заказ. При этом интерфейс не обязан показывать технические идентификаторы покупателю — достаточно ясной даты действия и понятного состава.
При приёмке проверьте январское начало, короткий февраль, возвращение к исходному числу, пропуск, перенос одной даты и повторное формирование периода. Затем измените состав после создания ближайшего заказа и сверяйте оба результата. Рабочая первая версия сохраняет смысл календаря и исключений. Она не должна обещать, что регулярная строка в интерфейсе сама гарантирует наличие, оплату или исполнение каждой будущей доставки.
Материалы для проверки
Если нужна реализация
Состав работ зависит от текущего проекта и подтверждённой причины. Можно обсудить диагностику или разработку подходящего решения.
Вопросы и ответы
Раз в месяц означает каждые 30 дней?
Нет. В учебном начале 31 января 2027 года следующие даты различаются: месячный план даёт 28 февраля, а интервал тридцать дней — 2 марта.
Короткий месяц всегда должен переносить отправку на последнее число?
Это выбранное правило примера. Его нужно согласовать отдельно; оно не является универсальным поведением календарей или любого магазина.
Пропуск одной доставки должен сдвинуть все следующие?
В описанной модели это исключение конкретного вхождения, а будущая опора сохраняется. Для изменения всей серии нужна отдельная явная команда.
Почему отсутствие заказа не означает, что его нужно создать заново?
Доставка могла быть сознательно пропущена. Планировщик должен различать такое исключение, уже созданный заказ и ещё не обработанное вхождение.