feat(requests): добавлена клиентская валидация формы создания заявления
- RecipientPicker.validate() проверяет наличие получателей/отделов - RecipientPicker.setError()/clearError() для визуальной индикации ошибок - Валидация в requestCrudHandler для create и edit - Проверка: автор не может быть получателем - Автоматическая очистка ошибки при изменении получателей - Удалено временное логирование из сериализатора
This commit is contained in:
parent
7d15ad1a4f
commit
20bd4daade
@ -245,8 +245,9 @@ class RequestWriteSerializer(serializers.ModelSerializer):
|
||||
def to_internal_value(self, data: Mapping[str, Any]) -> dict[str, Any]:
|
||||
"""Предвалидационная очистка входа.
|
||||
|
||||
Для обычного пользователя удаляет `employee`, `status`, `approver` до полевой валидации,
|
||||
чтобы не получить 400 из-за, например, несуществующего PK в `employee`.
|
||||
Для обычного пользователя удаляет `employee`, `status`, `approver`
|
||||
до полевой валидации, чтобы не получить 400 из-за, например,
|
||||
несуществующего PK в `employee`.
|
||||
|
||||
Args:
|
||||
data (Mapping[str, Any]): Входные данные запроса.
|
||||
@ -260,6 +261,7 @@ class RequestWriteSerializer(serializers.ModelSerializer):
|
||||
data.pop("employee", None)
|
||||
data.pop("status", None)
|
||||
data.pop("approver", None)
|
||||
|
||||
return super().to_internal_value(data)
|
||||
|
||||
def validate(self, attrs):
|
||||
@ -316,7 +318,8 @@ class RequestWriteSerializer(serializers.ModelSerializer):
|
||||
def create(self, validated_data: dict[str, Any]) -> Request:
|
||||
"""Создание заявки.
|
||||
|
||||
Обычным пользователям проставляет текущего пользователя; статус берётся из default модели.
|
||||
Обычным пользователям проставляет текущего пользователя;
|
||||
статус берётся из default модели.
|
||||
|
||||
Args:
|
||||
validated_data (dict[str, Any]): Провалидированные данные.
|
||||
|
||||
@ -491,6 +491,9 @@ export class RecipientPicker {
|
||||
}
|
||||
|
||||
triggerChange() {
|
||||
// Очищаем ошибку при любом изменении
|
||||
this.clearError();
|
||||
|
||||
if (this.options.onChange) {
|
||||
this.options.onChange(this.getValues());
|
||||
}
|
||||
@ -505,6 +508,49 @@ export class RecipientPicker {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет, заполнены ли получатели
|
||||
* @returns {boolean} true если есть получатели или отделы
|
||||
*/
|
||||
validate() {
|
||||
const hasDepartments = this.state.departments.length > 0;
|
||||
const hasRecipients = this.state.recipients.length > 0;
|
||||
const isValid = hasDepartments || hasRecipients;
|
||||
|
||||
if (!isValid) {
|
||||
this.setError('Укажите хотя бы одного получателя или отдел');
|
||||
} else {
|
||||
this.clearError();
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает сообщение об ошибке
|
||||
* @param {string} message - Текст ошибки
|
||||
*/
|
||||
setError(message) {
|
||||
this.clearError();
|
||||
const errorDiv = document.createElement('div');
|
||||
errorDiv.className = 'invalid-feedback d-block';
|
||||
errorDiv.textContent = message;
|
||||
errorDiv.id = 'recipientPickerError';
|
||||
this.container.appendChild(errorDiv);
|
||||
this.container.classList.add('is-invalid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Убирает сообщение об ошибке
|
||||
*/
|
||||
clearError() {
|
||||
const errorDiv = this.container.querySelector('#recipientPickerError');
|
||||
if (errorDiv) {
|
||||
errorDiv.remove();
|
||||
}
|
||||
this.container.classList.remove('is-invalid');
|
||||
}
|
||||
|
||||
setValues(data) {
|
||||
if (data.department_ids) {
|
||||
this.state.departments = Array.isArray(data.department_ids)
|
||||
|
||||
@ -65,6 +65,23 @@ export function initRequestCrudHandler(options) {
|
||||
|
||||
// Добавляем данные получателей из RecipientPicker
|
||||
if (createRecipientPicker) {
|
||||
// Валидация: для обычной отправки должны быть получатели или отделы
|
||||
if (saveAs === 'submit') {
|
||||
if (!createRecipientPicker.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Проверка: автор не может быть получателем
|
||||
const recipients = createRecipientPicker.getValues();
|
||||
if (recipients.recipient_ids && window.currentUserId) {
|
||||
if (recipients.recipient_ids.includes(window.currentUserId)) {
|
||||
alert('Вы не можете отправить заявление самому себе');
|
||||
createRecipientPicker.setError('Вы не можете быть получателем');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const recipients = createRecipientPicker.getValues();
|
||||
|
||||
// Добавляем department_ids
|
||||
@ -146,6 +163,23 @@ export function initRequestCrudHandler(options) {
|
||||
|
||||
// Добавляем данные получателей из RecipientPicker
|
||||
if (editRecipientPicker) {
|
||||
// Валидация: для обычной отправки должны быть получатели или отделы
|
||||
if (saveAs === 'submit') {
|
||||
if (!editRecipientPicker.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Проверка: автор не может быть получателем
|
||||
const recipients = editRecipientPicker.getValues();
|
||||
if (recipients.recipient_ids && window.currentUserId) {
|
||||
if (recipients.recipient_ids.includes(window.currentUserId)) {
|
||||
alert('Вы не можете отправить заявление самому себе');
|
||||
editRecipientPicker.setError('Вы не можете быть получателем');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const recipients = editRecipientPicker.getValues();
|
||||
|
||||
// Добавляем department_ids
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user