This commit is contained in:
@@ -36,7 +36,15 @@ import {
|
||||
IAttachmentInfo
|
||||
} from '../../shared/attachments/attachments.service';
|
||||
import { effectiveUserEmail } from '../../shared/impersonation';
|
||||
import { getEmployeeOrganizations, formatEmployeeOrganizations } from '../../shared/dictionary';
|
||||
import {
|
||||
getEmployeeOrganizations,
|
||||
formatEmployeeOrganizations,
|
||||
getNomenclature,
|
||||
INomenclatureRow
|
||||
} from '../../shared/dictionary';
|
||||
import { ILineItem } from '../../shared/ui';
|
||||
import { requestLinkUrl } from '../../shared/routing';
|
||||
import { writeDemandLinePortalLink } from '../../shared/dataverse/demandLine.service';
|
||||
import { friendlyErrorMessage } from '../../shared/diagnostics';
|
||||
import { IRequestFormProps } from '../../models/types';
|
||||
import {
|
||||
@@ -52,6 +60,23 @@ import { HeaderSection } from './sections/HeaderSection';
|
||||
import { ItemsSection } from './sections/ItemsSection';
|
||||
import { useStableCallback } from '../../shared/useStableCallback';
|
||||
|
||||
/**
|
||||
* Строка ответа номенклатуры → строка табличной части (name/код/ед.изм/кол-во).
|
||||
* nomenclatureId (row.id) сохраняем в позиции: по нему потом отличают строки из
|
||||
* справочника от дописанных пользователем вручную.
|
||||
* NB: «Инв. номер» в ответе номенклатуры отсутствует — оставляем пустым.
|
||||
*/
|
||||
function nomenclatureRowToLineItem(row: INomenclatureRow): ILineItem {
|
||||
return {
|
||||
name: row.name || row.fullName || '',
|
||||
invNumber: '',
|
||||
code: row.code || '',
|
||||
unit: row.unit || '',
|
||||
qty: row.quantity !== undefined && row.quantity !== null ? String(row.quantity) : '',
|
||||
nomenclatureId: row.id
|
||||
};
|
||||
}
|
||||
|
||||
const TmcTransferForm: React.FC<IRequestFormProps> = ({
|
||||
requestType,
|
||||
context,
|
||||
@@ -59,7 +84,10 @@ const TmcTransferForm: React.FC<IRequestFormProps> = ({
|
||||
onSaved,
|
||||
editId,
|
||||
editListId,
|
||||
onCodeLoaded
|
||||
onCodeLoaded,
|
||||
positionId,
|
||||
nomenclatureId,
|
||||
storeName
|
||||
}) => {
|
||||
const [data, setData] = useState<IFormData>(EMPTY_DATA);
|
||||
const [files, setFiles] = useState<File[]>([]);
|
||||
@@ -112,6 +140,38 @@ const TmcTransferForm: React.FC<IRequestFormProps> = ({
|
||||
};
|
||||
}, [editId, context]);
|
||||
|
||||
// Открытие по внешней ссылке (positionId + nomenclatureId в URL): запоминаем
|
||||
// строку-источник и предзаполняем таблицу остатками номенклатуры по складам.
|
||||
// Только для новой заявки — при редактировании позиции уже сохранены.
|
||||
useEffect(() => {
|
||||
if (editId || !nomenclatureId) {
|
||||
return;
|
||||
}
|
||||
// Строку-источник фиксируем сразу, независимо от ответа справочника: она
|
||||
// нужна при отправке для обратной записи ссылки в Dataverse. nomenclatureId
|
||||
// отдельно в шапке не храним — он едет в каждой позиции ItemsJson.
|
||||
setData((prev) => ({ ...prev, SourcePositionId: positionId || '' }));
|
||||
let alive = true;
|
||||
getNomenclature(context, nomenclatureId, storeName)
|
||||
.then((rows) => {
|
||||
if (!alive) {
|
||||
return;
|
||||
}
|
||||
const items = rows.map(nomenclatureRowToLineItem);
|
||||
if (items.length > 0) {
|
||||
setData((prev) => ({ ...prev, Items: items }));
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
if (alive) {
|
||||
setSubmitError(`Не удалось загрузить номенклатуру: ${friendlyErrorMessage(e)}`);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
alive = false;
|
||||
};
|
||||
}, [editId, nomenclatureId, positionId, storeName, context, setSubmitError]);
|
||||
|
||||
// Выбор другого заявителя подтягивает его ФИО (для печати) и организации (как
|
||||
// в Vacation.handleEmployeeChange, но источник тот же Employee/ByEmail — здесь
|
||||
// дополнительно берём ФИО, т.к. TMC хранит его отдельным полем AuthorName).
|
||||
@@ -290,6 +350,19 @@ const TmcTransferForm: React.FC<IRequestFormProps> = ({
|
||||
dataListName: writeListId ? dataListTitleForListId(FORM_ID, writeListId) : dataListTitle(FORM_ID),
|
||||
approvalListName: writeListId ? approvalListTitleForListId(FORM_ID, writeListId) : approvalListTitle(FORM_ID)
|
||||
});
|
||||
// Обратная связь во внешнюю систему: пишем ссылку на эту заявку в
|
||||
// строку-источник Dataverse (только если заявка открыта по внешней ссылке).
|
||||
// Не критично для самой заявки — она уже сохранена и отправлена, поэтому
|
||||
// сбой записи не роняем, а только логируем (см. флаг ниже для UX-решения).
|
||||
if (data.SourcePositionId) {
|
||||
const link = requestLinkUrl(FORM_ID, id, writeListId);
|
||||
try {
|
||||
await writeDemandLinePortalLink(context, data.SourcePositionId, link);
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('Не удалось записать ссылку в строку-источник (Dataverse):', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ export async function loadTmcTransferRequest(itemId: number, listId?: string): P
|
||||
'Reason',
|
||||
'ContactPerson',
|
||||
'ContactPhone',
|
||||
'SourcePositionId',
|
||||
'ItemsJson',
|
||||
'RequestStatus',
|
||||
'RequestUniqueCode'
|
||||
@@ -76,6 +77,7 @@ export async function loadTmcTransferRequest(itemId: number, listId?: string): P
|
||||
Reason: (item.Reason as string) || '',
|
||||
ContactPerson: (item.ContactPerson as string) || '',
|
||||
ContactPhone: (item.ContactPhone as string) || '',
|
||||
SourcePositionId: (item.SourcePositionId as string) || '',
|
||||
Items: parseItems(item.ItemsJson)
|
||||
};
|
||||
|
||||
|
||||
@@ -54,7 +54,16 @@ export interface IFormData {
|
||||
ContactPerson: string; // Исполнитель («Исп. …»), Ф.И.О.
|
||||
ContactPhone: string; // Телефон исполнителя
|
||||
|
||||
// Табличная часть — позиции ТМЦ (сериализуется в ItemsJson)
|
||||
/**
|
||||
* Строка-источник во внешней системе (Dataverse «Строка заявки», GUID). Одна
|
||||
* на всю заявку (не на позицию) — приходит в URL как positionId. После отправки
|
||||
* на согласование по нему пишем обратно ссылку на заявку. '' — заявка заведена
|
||||
* вручную в портале, без внешней ссылки.
|
||||
*/
|
||||
SourcePositionId: string;
|
||||
|
||||
// Табличная часть — позиции ТМЦ (сериализуется в ItemsJson). У строк из
|
||||
// справочника есть nomenclatureId (см. ILineItem), у дописанных вручную — нет.
|
||||
Items: ILineItem[];
|
||||
}
|
||||
|
||||
@@ -97,5 +106,6 @@ export const EMPTY_DATA: IFormData = {
|
||||
Reason: '',
|
||||
ContactPerson: '',
|
||||
ContactPhone: '',
|
||||
SourcePositionId: '',
|
||||
Items: []
|
||||
};
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
{ "internalName": "ContactPerson", "title": "Исполнитель (Ф.И.О.)", "type": "Text" },
|
||||
{ "internalName": "ContactPhone", "title": "Телефон исполнителя", "type": "Text" },
|
||||
|
||||
{ "internalName": "SourcePositionId", "title": "Id строки-источника (Dataverse)", "type": "Text" },
|
||||
|
||||
{ "internalName": "ItemsJson", "title": "Позиции ТМЦ (JSON)", "type": "Note" }
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user