Skip to content

feat(mgr): тип Дата для extra fields - #623

Open
Ibochkarev wants to merge 1 commit into
betafrom
feat/issue-612-extra-field-date
Open

feat(mgr): тип Дата для extra fields#623
Ibochkarev wants to merge 1 commit into
betafrom
feat/issue-612-extra-field-date

Conversation

@Ibochkarev

Copy link
Copy Markdown
Member

Описание

В каталоге extra fields не было типа Дата, хотя в Опциях он уже есть. Добавлен datefield в ExtraFieldsManager с дефолтами колонки DATE, рендер через DatePicker и хранение значения в родителе как YYYY-MM-DD (без UTC-сдвига и без обрезки времени у нативных datetime полей заказа).

Тип изменений

  • Исправление бага (non-breaking change)
  • Новая функциональность (non-breaking change)
  • Breaking change
  • Рефакторинг
  • Документация
  • Другое:

Связанные Issues

Closes #612

Как это было протестировано?

cd vueManager
npx eslint src/components/DynamicField.vue src/components/ExtraFieldsManager.vue \
  src/utils/structuredExtraField.js src/utils/structuredExtraField.test.js
# exit 0

npx vitest run src/utils/structuredExtraField.test.js src/utils/formatLocalDateYmd.test.js
# exit 0 — 8 tests

npm run build
# exit 0
  • Ручное тестирование
  • Автоматические тесты
  • Тестирование на разных версиях PHP/MODX

Конфигурация тестирования:

  • MiniShop3: feat/issue-612-extra-field-date
  • MODX: —
  • PHP: —

Чеклист

  • Код соответствует стилю проекта
  • Комментарии в сложных местах (Date внутри DynamicField, parent = YMD)
  • Не ломает существующую функциональность
  • Лексиконы ru+en (ms3_vue_xtype_datefield, ms3_vue_dbtype_date)
  • PHPStan — PHP не менялся
  • ESLint по затронутым путям
  • CHANGELOG — не трогали (релиз)

Дополнительные заметки

Gate A

AC Code Test
Тип Дата в пикере extra field yes n/a UI
Дефолты dbtype/phptype при выборе yes n/a
Save/load без UTC-сдвига yes Vitest parse/serialize
Lexicons ru+en yes n/a
Не ломать order datetime yes ревью: сериализатор не на всех полях

Review: thermo BLOCK (Date в parent + общий serialize) исправлен: Date только в DynamicField, parent получает YYYY-MM-DD. Soft: phptype=datetime при dbtype=date — как дефолт UI; в phptypeOptions нет отдельного date.

Routing: plan=cursor-grok-4.6-high-fast, make=composer-2.5-fast, review=gpt-5.6-sol-medium (Opus slug недоступен в Task), thermo=cursor-grok-4.6-high-fast, simplify=composer-2.5-fast

Expose datefield in ExtraFieldsManager with DATE column defaults and keep
YYYY-MM-DD in parent state so saves do not truncate native order datetime.
@Ibochkarev
Ibochkarev requested a review from biz87 August 21, 2026 03:26
AgelxNash pushed a commit to AgelxNash/MiniShop3 that referenced this pull request Sep 6, 2026
@AgelxNash AgelxNash mentioned this pull request Sep 6, 2026
16 tasks
AgelxNash pushed a commit to AgelxNash/MiniShop3 that referenced this pull request Sep 6, 2026
Conflict resolution: PR 640 supersedes merged modx-pro#621 rework (same author, same
intent) — took PR side for 36 files; manually preserved modx-pro#631 useConfirm grids,
modx-pro#623 datefield dialog styles, modx-pro#643 gallery bits, modx-pro#605 order entry; ProductData
sections rebuilt on groupProductDataSections (keeps modx-pro#611/modx-pro#620 sort_order) under
PR 640 Panel layout.
@AgelxNash

Copy link
Copy Markdown

Этот PR включён в тестовую интеграционную сборку всех открытых PR MiniShop3: AgelxNash/MiniShop3, ветка integration/open-prs-20260906 (28/28 открытых).

Сборка нужна, чтобы проверить совместимость взаимозависимых серий PR до их мержа — при последовательном слиянии они конфликтуют друг с другом. Это не ревью и не конкурирующий PR: авторство сохранено (1 PR = 1 коммит с исходным автором), ветка пересобирается по мере обновления PR.

Как вошёл в сборку: Слился чисто. (При последующем #640 вручную сохранены его XTYPE_DB_DEFAULTS с DATEFIELD и глобальные стили диалога edit-field.)

@AgelxNash

Copy link
Copy Markdown

Спасибо за PR! Пожелание: скорейшего ревью и мержа 👍 Удачи!

@biz87

biz87 commented Sep 7, 2026

Copy link
Copy Markdown
Member

Смёрджил с актуальной beta (после #646, который правит тот же ExtraFieldsManager.vue) — конфликт один и механический: обе стороны независимо вставили свой блок объявлений после isKeyValueField, у тебя XTYPE_DB_DEFAULTS, в beta SQL_IDENTIFIER_PATTERN/isValidFieldKey. Логика не пересекается, разрешается union'ом. Лексиконы vue.inc.php слились автоматически, дублей нет, ru и en синхронны.

После мержа всё зелёное: smoke 92/92, PHPUnit 288 тестов / 719 assertions, ESLint по четырём изменённым файлам чисто, vitest 48/48 (включая твои 5 в structuredExtraField.test.js), build проходит.

Сама реализация со стороны фронта аккуратная — хранение как YYYY-MM-DD без UTC-сдвига сделано правильно (formatLocalDateYmd берёт локальные компоненты, а не toISOString()), нативные datetime-поля заказа не задеты, существующие extra-поля других типов не ломаются.

Но есть блокирующий баг в самом базовом сценарии.

Сохранение товара падает, если необязательное поле «Дата» оставлено пустым

Цепочка, прослежена по коду:

  1. vueManager/src/components/DynamicField.vue:108-112 — скрытый input для datefield рендерится всегда, со значением formatLocalDateYmd(datePickerValue) ?? ''. Дата не выбрана — в форму уходит пустая строка, а не отсутствие поля и не null. Форма товара это обычная MODX resource-edit форма, все hidden-инпуты сериализуются при каждом сохранении.

  2. ProductDataPayloadTrait::assignProductDataFields() передаёт значение в $productData->fromArray($fields, '', true, true) — с $rawValues = true.

  3. ProductDataService::prepareObject() (строки ~128-144) нормализует только числа и булев:

// Cast numeric/boolean fields (incl. extra fields) so '' does not break MySQL decimals/ints
match ($phptype) {
    'float'   => $productData->set($key, $isEmpty ? 0.0 : (float)$value),
    'integer' => $productData->set($key, $isEmpty ? 0 : (int)$value),
    'boolean' => $productData->set($key, $isEmpty ? false : (bool)$value),
    default   => null,
};

Комментарий здесь прямым текстом описывает ровно эту проблему — «чтобы '' не ломала MySQL». Для дат ветки просто нет, а [DATEFIELD_XTYPE] в ExtraFieldsManager.vue:167 по умолчанию получает phptype: 'datetime', так что значение проходит насквозь.

  1. xPDOObject::_setRaw() для phptype из date/datetime/timestamp с не-integer dbtype проваливается в default: и присваивает '' без валидации, помечая поле dirty.

  2. При save() в DATE-колонку уходит пустая строка. Под STRICT_TRANS_TABLES (дефолт MySQL 8 и MariaDB) это ERROR 1292: Incorrect date value: '' — проверено прямым INSERT на dev-базе.

То есть создать необязательное поле «Дата» и не заполнить его — это ошибка сохранения товара. Для optional-поля это не край, а основной случай.

Второй эффект того же корня: уже заполненную дату нельзя очистить. xPDOObject::set() на пустой строке не находит валидный timestamp, поле не помечается dirty, значение в БД остаётся прежним — без ошибки и без уведомления пользователя.

В описании PR ты пометил phptype: 'datetime' как «soft»-проблему. Она не soft: именно она уводит значение в ветку, где пустая строка не обрабатывается. Замена на phptype: 'date' сама по себе не поможет — в _setRaw() ветка case 'date': проваливается в тот же default:.

Где чинить: нормализация нужна до fromArray()/set() — пустое значение для date-типов должно превращаться в null, по аналогии с уже существующими ветками для float/integer/boolean в том же match. Это PHP-сторона, которая в PR не затронута вовсе (в чеклисте отмечено «PHPStan — PHP не менялся»).

Заодно, раз будешь трогать этот путь

structuredExtraField.js, parseDateFieldValue('0000-00-00') возвращает мусорную дату около 1899 года вместо null — MySQL zero-date не распознаётся как «нет значения». Пикер покажет случайную дату вместо пустого поля.

parseDateFieldValue('0000-00-00') → Date 1899-11-29
parseDateFieldValue(null)         → null   (ок)
parseDateFieldValue('')           → null   (ок)

Тестов на null / '' / '0000-00-00' в structuredExtraField.test.js сейчас нет — покрыт только happy path на валидной строке.

Ограничение проверки

Я разбирал конкретно путь msProductData — это основной сценарий и он же первый в твоём тест-плане. Остальные классы из classOptions (msOrder, msVendor, msCustomer и другие) по отдельности не проверял: там может быть другой процессор сохранения без rawValues = true. Но раз корень в дефолтном phptype и отсутствии нормализации пустых дат, риск скорее системный — стоит пройтись и по ним.

Само API — формат хранения, лексиконы на двух языках, разграничение datefield и остальных xtype в DynamicField — сделано хорошо, поэтому и жалко возвращать. Готов пересмотреть сразу после фикса нормализации.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] В типах полей extra field отсутствует тип поля Дата

3 participants