Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz> Reviewed-on: http://git.tnsu.kz/timur.gorodetskiy/request-portal/pulls/23
This commit was merged in pull request #23.
This commit is contained in:
@@ -13,7 +13,9 @@ import {
|
||||
useRequestSubmit
|
||||
} from '../../shared/ui';
|
||||
import {
|
||||
composeRemoveRowRule,
|
||||
ApprovalMatrix,
|
||||
ExecutorsMatrix,
|
||||
ApprovalHistory,
|
||||
StatusBadge,
|
||||
useMatrixEditor,
|
||||
@@ -24,8 +26,13 @@ import {
|
||||
REQUEST_STATUS,
|
||||
isEditableStatus,
|
||||
IApproverToWrite,
|
||||
unassignedStepMessage
|
||||
unassignedStepMessage,
|
||||
useFormExecutors,
|
||||
approvalPhaseRows,
|
||||
executorsToWrite,
|
||||
initialSubmitStatus
|
||||
} from '../../shared/approval';
|
||||
import { effectiveUserEmail } from '../../shared/impersonation';
|
||||
import { IRequestFormProps } from '../../models/types';
|
||||
import { getEmployeeOrganizations, formatEmployeeOrganizations } from '../../shared/dictionary';
|
||||
import { friendlyErrorMessage } from '../../shared/diagnostics';
|
||||
@@ -169,7 +176,7 @@ const ConcreteRequestForm: React.FC<IRequestFormProps> = ({
|
||||
const loadMatrix = useCallback(
|
||||
() =>
|
||||
editId
|
||||
? approval.getMatrixForRequest(editId)
|
||||
? approval.getMatrixForRequest(editId).then(approvalPhaseRows)
|
||||
: getPlannedMatrix(context, requestTypeId as number, data.SiteChiefEmail, approval, 'Approval', null, undefined, projectId),
|
||||
[context, approval, editId, data.SiteChiefEmail, requestTypeId, projectId]
|
||||
);
|
||||
@@ -181,6 +188,18 @@ const ConcreteRequestForm: React.FC<IRequestFormProps> = ({
|
||||
(login) => approval.getJobTitle(login)
|
||||
);
|
||||
|
||||
// Исполнители — из маршрута админки (заявитель их не правит); те же параметры
|
||||
// резолва, что и у согласующих.
|
||||
const executors = useFormExecutors({
|
||||
readOnly,
|
||||
context,
|
||||
approval,
|
||||
requestTypeId,
|
||||
userEmail: data.SiteChiefEmail,
|
||||
editId,
|
||||
projectId
|
||||
});
|
||||
|
||||
const hasItems = data.Items.some((i) => i.mixClass.trim().length > 0 && i.volume.trim().length > 0);
|
||||
const isValid = !!(data.RequestDate && data.SiteChiefEmail && data.Project.trim() && hasItems);
|
||||
const validate = (): string | undefined => {
|
||||
@@ -222,6 +241,7 @@ const ConcreteRequestForm: React.FC<IRequestFormProps> = ({
|
||||
rtId,
|
||||
data,
|
||||
toApprovers(matrix.rows),
|
||||
executorsToWrite(executors.rows),
|
||||
status,
|
||||
writeListId
|
||||
);
|
||||
@@ -236,7 +256,7 @@ const ConcreteRequestForm: React.FC<IRequestFormProps> = ({
|
||||
// Отправка на согласование — уведомляем первый активный шаг (поток оповещения).
|
||||
// Имена списков — той же партиции, куда фактически записана заявка (writeListId),
|
||||
// не текущей: поток ищет строку маршрута именно в НЕЙ.
|
||||
if (status === REQUEST_STATUS.onApproval) {
|
||||
if (status === REQUEST_STATUS.onApproval || status === REQUEST_STATUS.execution) {
|
||||
await notifyActiveStep(context, approval, id, {
|
||||
requestTypeId: rtId,
|
||||
dataListName: writeListId ? dataListTitleForListId(FORM_ID, writeListId) : dataListTitle(FORM_ID),
|
||||
@@ -258,11 +278,21 @@ const ConcreteRequestForm: React.FC<IRequestFormProps> = ({
|
||||
(r) => `Шаг ${r.order} маршрута согласования («${r.position || 'без должности'}») без назначенного человека — выберите согласующего или удалите шаг.`
|
||||
);
|
||||
if (unassignedError) return unassignedError;
|
||||
const unassignedExecutorError = unassignedStepMessage(
|
||||
executors.rows,
|
||||
(r) => `Шаг ${r.order} исполнения («${r.position || 'без должности'}») без назначенного человека — обратитесь к администратору маршрута.`
|
||||
);
|
||||
if (unassignedExecutorError) return unassignedExecutorError;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const handleSaveDraft = (): Promise<void> => submit.run('draft', save(REQUEST_STATUS.draft), validate);
|
||||
const handleSubmit = (): Promise<void> => submit.run('submit', save(REQUEST_STATUS.onApproval), validateSubmit);
|
||||
// Без исполнителей — прежний статус «На согласовании»; с ними — как у рефералки
|
||||
// (initialSubmitStatus учитывает маршрут без согласующих).
|
||||
const submitStatus = executors.rows.length > 0
|
||||
? initialSubmitStatus(matrix.rows.length, executors.rows.length)
|
||||
: REQUEST_STATUS.onApproval;
|
||||
const handleSubmit = (): Promise<void> => submit.run('submit', save(submitStatus), validateSubmit);
|
||||
|
||||
const sectionProps = {
|
||||
data,
|
||||
@@ -323,11 +353,14 @@ const ConcreteRequestForm: React.FC<IRequestFormProps> = ({
|
||||
|
||||
<ApprovalMatrix
|
||||
editor={matrix}
|
||||
canRemoveRow={composeRemoveRowRule(effectiveUserEmail(context))}
|
||||
search={(q) => approval.searchUsers(q)}
|
||||
waiting={!matrixReady}
|
||||
waitingText="Укажите начальника участка и проект, чтобы построить маршрут согласования."
|
||||
/>
|
||||
|
||||
<ExecutorsMatrix editor={executors} search={(q) => approval.searchUsers(q)} />
|
||||
|
||||
{/* Автору на доработке важнее всего видеть, почему вернули или
|
||||
отклонили. Только у уже существующей заявки: у новой истории нет. */}
|
||||
{editId && (
|
||||
|
||||
@@ -8,10 +8,13 @@ import {
|
||||
DecisionPanel,
|
||||
StatusBadge,
|
||||
ApprovalMatrix,
|
||||
ExecutorsMatrix,
|
||||
ApprovalHistory,
|
||||
useMatrixEditor,
|
||||
notifyActiveStep,
|
||||
useRequestTypeId
|
||||
useRequestTypeId,
|
||||
useViewExecutors,
|
||||
approvalPhaseRows
|
||||
} from '../../shared/approval';
|
||||
import { friendlyErrorMessage } from '../../shared/diagnostics';
|
||||
import {
|
||||
@@ -71,10 +74,11 @@ const ConcreteRequestView: React.FC<IConcreteRequestViewProps> = ({ context, ite
|
||||
// Матрица маршрута из фактических строк заявки (read-only). decisionSeq в
|
||||
// reloadKey: после решения строки на сервере меняются — перечитываем матрицу.
|
||||
const [decisionSeq, setDecisionSeq] = useState<number>(0);
|
||||
const loadApprovers = useCallback(() => approval.getMatrixForRequest(itemId), [approval, itemId]);
|
||||
const loadApprovers = useCallback(() => approval.getMatrixForRequest(itemId).then(approvalPhaseRows), [approval, itemId]);
|
||||
const approvers = useMatrixEditor(loadApprovers, `a-${itemId}-${decisionSeq}`, false, true, (l) =>
|
||||
approval.getJobTitle(l)
|
||||
);
|
||||
const executors = useViewExecutors(context, approval, itemId, decisionSeq);
|
||||
|
||||
useEffect(() => {
|
||||
let alive = true;
|
||||
@@ -153,6 +157,7 @@ const ConcreteRequestView: React.FC<IConcreteRequestViewProps> = ({ context, ite
|
||||
</FormCard>
|
||||
|
||||
<ApprovalMatrix editor={approvers} search={(q) => approval.searchUsers(q)} />
|
||||
<ExecutorsMatrix editor={executors} search={(q) => approval.searchUsers(q)} />
|
||||
</div>
|
||||
|
||||
{/* Панель решения — ВНЕ печати и FormModeProvider (комментарий редактируемый). */}
|
||||
|
||||
@@ -47,7 +47,7 @@ export type ISaveResult = ISaveItemResult;
|
||||
/**
|
||||
* Сохранить заявку на бетон: содержимое + маршрут согласования.
|
||||
* existingId задан → обновляем ту же запись (черновик/доработка), иначе создаём новую.
|
||||
* Фазы исполнения нет — writeRoute вызывается с пустым списком исполнителей.
|
||||
* Исполнители (фаза «Исполнение») приходят из маршрута админки и пишутся тем же writeRoute.
|
||||
* Возвращает Id заявки (+ код при создании).
|
||||
*
|
||||
* listId — партиция ОБНОВЛЯЕМОЙ заявки (см. loadConcreteRequest). Имеет смысл
|
||||
@@ -59,12 +59,13 @@ export async function saveConcreteRequest(
|
||||
requestTypeId: number,
|
||||
data: IFormData,
|
||||
approvers: IApproverToWrite[],
|
||||
executors: IApproverToWrite[],
|
||||
status: string,
|
||||
listId?: string
|
||||
): Promise<ISaveResult> {
|
||||
const item = buildItem(data, status);
|
||||
|
||||
const result = await saveRequestItem(FORM_ID, existingId, listId, requestTypeId, item);
|
||||
await writeRoute(concreteRequestApprovalService(listId), result.id, approvers, [], status);
|
||||
await writeRoute(concreteRequestApprovalService(listId), result.id, approvers, executors, status);
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user