diff --git a/src/webparts/requestPortal/forms/Ticket1C/Ticket1CForm.tsx b/src/webparts/requestPortal/forms/Ticket1C/Ticket1CForm.tsx index be5f4e7..2859ab0 100644 --- a/src/webparts/requestPortal/forms/Ticket1C/Ticket1CForm.tsx +++ b/src/webparts/requestPortal/forms/Ticket1C/Ticket1CForm.tsx @@ -45,7 +45,7 @@ import { dataListTitle, dataListTitleForListId } from '../../shared/listTitles'; -import { FORM_ID, IFormData, EMPTY_DATA, SetField } from './model'; +import { FORM_ID, IFormData, EMPTY_DATA, SetField, hasOtherDatabase } from './model'; import { saveTicketRequest, ticket1cApprovalService } from './submit'; import { loadTicketRequest } from './load'; import { getApplicantProfile } from './services/profile.service'; @@ -222,17 +222,20 @@ const Ticket1CForm: React.FC = ({ ); const matrixEditor = useMemo(() => ({ ...matrix, rows: matrixRows }), [matrix, matrixRows]); - const isValid = !!( + const isOtherDatabaseMissing = hasOtherDatabase(data) && !data.DB1COther.trim(); + const areRequiredFilled = !!( data.TaskName.trim() && data.WorkType && data.Organization && data.DB1C.length > 0 && data.Question.trim() ); + const isValid = areRequiredFilled && !isOtherDatabaseMissing; const validate = (): string | undefined => { - if (!isValid) { + if (!areRequiredFilled) { return 'Заполните обязательные поля: наименование задачи, вид работ, организация, база 1С, вопрос.'; } + if (isOtherDatabaseMissing) return 'Выбрана база 1С «Другое» — укажите её название.'; if (requestTypeIdState.error) return `Не удалось определить тип заявки: ${requestTypeIdState.error}`; if (!requestTypeId) return 'Тип заявки ещё определяется — подождите немного и повторите.'; return undefined; diff --git a/src/webparts/requestPortal/forms/Ticket1C/Ticket1CView.tsx b/src/webparts/requestPortal/forms/Ticket1C/Ticket1CView.tsx index 6429698..a4aac1e 100644 --- a/src/webparts/requestPortal/forms/Ticket1C/Ticket1CView.tsx +++ b/src/webparts/requestPortal/forms/Ticket1C/Ticket1CView.tsx @@ -34,7 +34,7 @@ import { dataListTitle, dataListTitleForListId } from '../../shared/listTitles'; -import { FORM_ID, IFormData, EMPTY_DATA, SetField } from './model'; +import { FORM_ID, IFormData, EMPTY_DATA, SetField, hasOtherDatabase } from './model'; import { loadTicketRequest } from './load'; import { ticket1cApprovalService, getPlannerTaskId, setPlannerTaskId } from './submit'; import { PlannerTicketService } from './services/plannerTicket.service'; @@ -69,6 +69,7 @@ function buildPlannerNotes( lines.push(`Отдел: ${data.Department}`); lines.push(`Должность: ${data.Position}`); lines.push(`База 1С: ${data.DB1C.join(', ')}`); + if (hasOtherDatabase(data)) lines.push(`Другая база 1С: ${data.DB1COther}`); lines.push(`Виды работ: ${data.WorkType}`); lines.push(''); lines.push('Вопрос:'); diff --git a/src/webparts/requestPortal/forms/Ticket1C/load.ts b/src/webparts/requestPortal/forms/Ticket1C/load.ts index 394169c..9165336 100644 --- a/src/webparts/requestPortal/forms/Ticket1C/load.ts +++ b/src/webparts/requestPortal/forms/Ticket1C/load.ts @@ -5,7 +5,7 @@ import { FORM_ID, IFormData, EMPTY_DATA } from './model'; // TaskName пишется/читается через системную колонку Title (см. provisioning.json/titleColumnName). // DB1C — отдельно, это множественный выбор (см. toStringArray). const SCALAR_FIELDS: (keyof IFormData)[] = [ - 'WorkType', 'Organization', 'FIO', 'Email', 'Phone', 'Department', 'Position', 'Manager', 'Question', + 'WorkType', 'Organization', 'DB1COther', 'FIO', 'Email', 'Phone', 'Department', 'Position', 'Manager', 'Question', ]; /** diff --git a/src/webparts/requestPortal/forms/Ticket1C/model.ts b/src/webparts/requestPortal/forms/Ticket1C/model.ts index b3aae3b..988ad14 100644 --- a/src/webparts/requestPortal/forms/Ticket1C/model.ts +++ b/src/webparts/requestPortal/forms/Ticket1C/model.ts @@ -18,6 +18,8 @@ export interface IFormData { Organization: string; /** Базы 1С — множественный выбор (колонка MultiChoice), минимум одна. */ DB1C: string[]; + /** Название базы вручную — обязательно, только если в DB1C отмечено «Другое». */ + DB1COther: string; FIO: string; Email: string; Phone: string; @@ -32,6 +34,16 @@ export interface IFormData { Question: string; } +/** + * Вариант колонки «База 1С», при выборе которого заявитель вписывает базу сам + * (поле DB1COther). Должен дословно совпадать с вариантом в колонке SP. + */ +export const DB1C_OTHER_OPTION = 'Другое'; + +export function hasOtherDatabase(data: IFormData): boolean { + return data.DB1C.indexOf(DB1C_OTHER_OPTION) !== -1; +} + /** Обновление одного поля формы с сохранением типа значения. */ export type SetField = (key: K, value: IFormData[K]) => void; @@ -47,6 +59,7 @@ export const EMPTY_DATA: IFormData = { WorkType: '', Organization: '', DB1C: [], + DB1COther: '', FIO: '', Email: '', Phone: '', diff --git a/src/webparts/requestPortal/forms/Ticket1C/provisioning.json b/src/webparts/requestPortal/forms/Ticket1C/provisioning.json index 936392c..f25947a 100644 --- a/src/webparts/requestPortal/forms/Ticket1C/provisioning.json +++ b/src/webparts/requestPortal/forms/Ticket1C/provisioning.json @@ -39,8 +39,10 @@ "1С: Бухгалтерия ГеоТрек", "1С: Бухгалтерия Монтажник-Т", "1С: Бухгалтерия ОСН Беркут", - "1С: Бухгалтерия CT Global" + "1С: Бухгалтерия CT Global", + "Другое" ] }, + { "internalName": "DB1COther", "title": "База 1С (другое)", "type": "Text" }, { "internalName": "FIO", "title": "Ф.И.О.", "type": "Text" }, { "internalName": "Email", "title": "Email", "type": "Text" }, diff --git a/src/webparts/requestPortal/forms/Ticket1C/sections/RequestSection.tsx b/src/webparts/requestPortal/forms/Ticket1C/sections/RequestSection.tsx index bd5f117..805d44d 100644 --- a/src/webparts/requestPortal/forms/Ticket1C/sections/RequestSection.tsx +++ b/src/webparts/requestPortal/forms/Ticket1C/sections/RequestSection.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Alert, CollapseSection, FormField, MultiChoiceField, Select, TextArea, TextInput, ISelectOption } from '../../../shared/ui'; import { useFieldChoices, IFieldChoicesState } from '../../../shared/fieldChoices'; import { dataListTitle } from '../../../shared/listTitles'; -import { FORM_ID, ISectionProps } from '../model'; +import { DB1C_OTHER_OPTION, FORM_ID, ISectionProps, hasOtherDatabase } from '../model'; import { QUESTION_MAX, CHOICES_LOADING_PLACEHOLDER, @@ -55,14 +55,30 @@ export const RequestSection: React.FC = ({ data, set }) => { placeholder={choicesPlaceholder(organizations, '— выберите —')} /> - + {/* На всю ширину: чипы выбранных баз растят поле по высоте, и соседнее + поле в той же строке сетки съезжало бы относительно него. */} + set('DB1C', v)} + onChange={(v) => { + set('DB1C', v); + // Сняли «Другое» — вписанное название больше не относится к заявке. + if (v.indexOf(DB1C_OTHER_OPTION) === -1) set('DB1COther', ''); + }} options={databases.options} placeholder={choicesPlaceholder(databases, 'Выберите одну или несколько баз')} /> + {hasOtherDatabase(data) && ( + + set('DB1COther', v)} + placeholder="Укажите название базы" + /> + + )}