feat: ручной ввод базы 1С при выборе варианта «Другое»
Если нужной базы нет в списке, заявитель отмечает «Другое» и вписывает название в обязательное поле. Текст хранится в отдельной колонке DB1COther, показывается в просмотре и попадает в карточку Planner. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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<IRequestFormProps> = ({
|
||||
);
|
||||
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;
|
||||
|
||||
@@ -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('Вопрос:');
|
||||
|
||||
@@ -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',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 = <K extends keyof IFormData>(key: K, value: IFormData[K]) => void;
|
||||
|
||||
@@ -47,6 +59,7 @@ export const EMPTY_DATA: IFormData = {
|
||||
WorkType: '',
|
||||
Organization: '',
|
||||
DB1C: [],
|
||||
DB1COther: '',
|
||||
FIO: '',
|
||||
Email: '',
|
||||
Phone: '',
|
||||
|
||||
@@ -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" },
|
||||
|
||||
@@ -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<ISectionProps> = ({ data, set }) => {
|
||||
placeholder={choicesPlaceholder(organizations, '— выберите —')}
|
||||
/>
|
||||
</FormField>
|
||||
<FormField label="База 1С" fieldKey="DB1C" required>
|
||||
{/* На всю ширину: чипы выбранных баз растят поле по высоте, и соседнее
|
||||
поле в той же строке сетки съезжало бы относительно него. */}
|
||||
<FormField label="База 1С" fieldKey="DB1C" required full>
|
||||
<MultiChoiceField
|
||||
value={data.DB1C}
|
||||
onChange={(v) => set('DB1C', v)}
|
||||
onChange={(v) => {
|
||||
set('DB1C', v);
|
||||
// Сняли «Другое» — вписанное название больше не относится к заявке.
|
||||
if (v.indexOf(DB1C_OTHER_OPTION) === -1) set('DB1COther', '');
|
||||
}}
|
||||
options={databases.options}
|
||||
placeholder={choicesPlaceholder(databases, 'Выберите одну или несколько баз')}
|
||||
/>
|
||||
</FormField>
|
||||
{hasOtherDatabase(data) && (
|
||||
<FormField label="Другая база 1С" htmlFor="DB1COther" required full>
|
||||
<TextInput
|
||||
id="DB1COther"
|
||||
value={data.DB1COther}
|
||||
onChange={(v) => set('DB1COther', v)}
|
||||
placeholder="Укажите название базы"
|
||||
/>
|
||||
</FormField>
|
||||
)}
|
||||
<FormField label={`Вопрос (${data.Question.length}/${QUESTION_MAX})`} htmlFor="Question" required full>
|
||||
<TextArea
|
||||
id="Question"
|
||||
|
||||
Reference in New Issue
Block a user