feat: предзаполнение «Куда» в ТМЦ, разморозка 1С, readOnly «Отдел»
Tests / frontend-test (push) Successful in 1m16s

- СЗ на перемещение ТМЦ: поле «Куда» предзаполняется проектом-получателем
  по строке-источнику Dataverse (cree9_demandline -> lzk position -> LZK ->
  project id), см. shared/dataverse/lzkPosition.service.ts
- Заявка 1С досрочно разморожена для всех пользователей (UNFROZEN_FORMS
  в SubmitActions), остальные формы остаются замороженными
- Поле «Отдел» в заявке 1С сделано read-only

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Тимур Городецкий
2026-09-23 11:07:54 +05:00
parent f6dcf349e9
commit 98226e4753
6 changed files with 173 additions and 4 deletions
@@ -406,6 +406,7 @@ const Ticket1CForm: React.FC<IRequestFormProps> = ({
<SubmitActions
submit={submit}
isValid={isValid}
formId="ticket1c"
readOnly={readOnly}
onCancel={onCancel}
onSaveDraft={handleSaveDraft}
@@ -14,7 +14,7 @@ export const ContactSection: React.FC<ISectionProps> = ({ data, set, context })
<PhoneInput id="Phone" value={data.Phone} onChange={(v) => set('Phone', v)} />
</FormField>
<FormField label="Отдел" htmlFor="Department">
<TextInput id="Department" value={data.Department} onChange={(v) => set('Department', v)} />
<TextInput id="Department" value={data.Department} onChange={(v) => set('Department', v)} readOnly />
</FormField>
<FormField label="Должность" htmlFor="Position">
<TextInput id="Position" value={data.Position} onChange={(v) => set('Position', v)} />
@@ -40,11 +40,13 @@ import {
getEmployeeOrganizations,
formatEmployeeOrganizations,
getNomenclature,
getProjects,
INomenclatureRow
} from '../../shared/dictionary';
import { ILineItem } from '../../shared/ui';
import { requestLinkUrl } from '../../shared/routing';
import { writeDemandLinePortalLink } from '../../shared/dataverse/demandLine.service';
import { resolveLzkProjectId } from '../../shared/dataverse/lzkPosition.service';
import { friendlyErrorMessage } from '../../shared/diagnostics';
import { IRequestFormProps } from '../../models/types';
import {
@@ -172,6 +174,46 @@ const TmcTransferForm: React.FC<IRequestFormProps> = ({
};
}, [editId, nomenclatureId, positionId, storeName, context, setSubmitError]);
// Проект-получатель («Куда») по той же внешней ссылке: строка-источник в
// Dataverse хранит ID позиции ЛЗК, у которой есть родительская запись LZK
// с проектом (см. shared/dataverse/lzkPosition.service). Необязательное
// удобство — на ошибке просто не предзаполняем поле, пользователь выберет
// проект вручную.
useEffect(() => {
if (editId || !positionId) {
return;
}
let alive = true;
resolveLzkProjectId(context, positionId)
.then((projectOid) => {
if (!alive || !projectOid) {
return undefined;
}
return getProjects(context).then((projects) => {
// GUID в Dataverse и в /Project могут отличаться регистром — сравниваем без него.
const project = projects.find((p) => p.oid.toLowerCase() === projectOid.toLowerCase());
if (!project) {
// eslint-disable-next-line no-console
console.warn(`Проект ${projectOid} из LZK не найден в справочнике /Project — «Куда» не предзаполнено.`);
return;
}
if (alive) {
// prev.TargetProjectId уже могло быть выбрано пользователем вручную,
// пока запрос летал — не затираем его.
setData((prev) => (prev.TargetProjectId ? prev : { ...prev, TargetProject: project.name, TargetProjectId: project.oid }));
}
});
})
.catch((e) => {
// Необязательное удобство — форму не роняем, но причину оставляем в консоли.
// eslint-disable-next-line no-console
console.warn('Не удалось определить проект «Куда» по строке-источнику (Dataverse):', e);
});
return () => {
alive = false;
};
}, [editId, positionId, context]);
// Выбор другого заявителя подтягивает его ФИО (для печати) и организации (как
// в Vacation.handleEmployeeChange, но источник тот же Employee/ByEmail — здесь
// дополнительно берём ФИО, т.к. TMC хранит его отдельным полем AuthorName).