diff --git a/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveForm.tsx b/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveForm.tsx index c46091c..c896389 100644 --- a/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveForm.tsx +++ b/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveForm.tsx @@ -47,7 +47,7 @@ import { dataListTitleForListId } from '../../shared/listTitles'; import { FORM_ID, IFormData, EMPTY_DATA, SetField, NOTICE_TITLE, NOTICE_ITEMS } from './model'; -import { organizationRouteFieldValues } from '../../shared/dictionary/organizationRouteFields'; +import { organizationProjectRouteFieldValues } from '../../shared/dictionary/organizationRouteFields'; import { formIssues } from './validation'; import { saveUnpaidLeaveRequest, unpaidLeaveApprovalService } from './submit'; import { loadUnpaidLeaveRequest } from './load'; @@ -164,10 +164,14 @@ const UnpaidLeaveForm: React.FC = ({ // пользователя) — так работает подача за другого. У заявки на редактировании // маршрут читается из уже записанных строк, чтобы не затереть правки инициатора. // - // Кадровый сотрудник и исполнитель зависят от организации сотрудника (условия - // «Organization содержит …» в админке) — см. shared/dictionary/organizationRouteFields.ts. - // Организация приходит позже выбора сотрудника, поэтому она в ключе перезагрузки. - const routeFieldValues = useMemo(() => organizationRouteFieldValues(data.Organization), [data.Organization]); + // Кадровый сотрудник и исполнитель зависят от организации сотрудника и проекта + // (условия «Organization содержит …», «Project содержит …» в админке) — см. + // shared/dictionary/organizationRouteFields.ts. Организация приходит позже + // выбора сотрудника, поэтому она в ключе перезагрузки. + const routeFieldValues = useMemo( + () => organizationProjectRouteFieldValues(data.Organization, data.Project), + [data.Organization, data.Project] + ); const routeOrgKey = routeFieldValues.map((f) => f.value).join('|'); const loadMatrix = useCallback( () => diff --git a/src/webparts/requestPortal/forms/UnpaidLeave/model.ts b/src/webparts/requestPortal/forms/UnpaidLeave/model.ts index baca43f..31f08a2 100644 --- a/src/webparts/requestPortal/forms/UnpaidLeave/model.ts +++ b/src/webparts/requestPortal/forms/UnpaidLeave/model.ts @@ -47,7 +47,7 @@ export interface IFormData { Department: string; /** Id отдела из справочника; '' — если введён вручную без выбора из списка. */ DepartmentId: string; - /** Проект (справочник /Project, см. shared/dictionary/ProjectPicker) — необязательное, справочное. */ + /** Проект (справочник /Project, см. shared/dictionary/ProjectPicker) — обязательный, от него зависит кадровик в маршруте. */ Project: string; /** oid проекта ('' — если введён вручную без выбора из списка). */ ProjectId: string; diff --git a/src/webparts/requestPortal/forms/UnpaidLeave/sections/RequestSection.tsx b/src/webparts/requestPortal/forms/UnpaidLeave/sections/RequestSection.tsx index 83919d8..416b1c7 100644 --- a/src/webparts/requestPortal/forms/UnpaidLeave/sections/RequestSection.tsx +++ b/src/webparts/requestPortal/forms/UnpaidLeave/sections/RequestSection.tsx @@ -39,7 +39,7 @@ export const RequestSection: React.FC = ({ data, set, context, on }} /> - + = [ data.Employee ? undefined : { field: 'Employee', label: 'Сотрудник' }, data.Position.trim() ? undefined : { field: 'Position', label: 'Должность' }, + projectIssue(data), startDateIssue(data), daysCountIssue(data), data.ReasonType diff --git a/src/webparts/requestPortal/forms/Vacation/VacationForm.tsx b/src/webparts/requestPortal/forms/Vacation/VacationForm.tsx index d317554..d2d425f 100644 --- a/src/webparts/requestPortal/forms/Vacation/VacationForm.tsx +++ b/src/webparts/requestPortal/forms/Vacation/VacationForm.tsx @@ -48,7 +48,7 @@ import { } from '../../shared/listTitles'; import { FORM_ID, IFormData, EMPTY_DATA, SetField, NOTICE_TITLE, NOTICE_ITEMS } from './model'; import { formIssues } from './validation'; -import { vacationRouteFieldValues } from './routeFields'; +import { organizationProjectRouteFieldValues } from '../../shared/dictionary/organizationRouteFields'; import { saveVacationRequest, vacationApprovalService } from './submit'; import { loadVacationRequest } from './load'; import { RequestSection } from './sections/RequestSection'; @@ -176,11 +176,12 @@ const VacationForm: React.FC = ({ // // Кадровый сотрудник и исполнитель в маршруте зависят от организации // сотрудника и проекта (условия «Organization содержит …», «Project - // содержит …» в админке) — см. routeFields.ts. Организация приходит позже - // выбора сотрудника (Employee/ByEmail), поэтому она в ключе перезагрузки: - // маршрут пересчитается, когда придёт, а не останется рассчитанным без условий. + // содержит …» в админке) — см. shared/dictionary/organizationRouteFields.ts. + // Организация приходит позже выбора сотрудника (Employee/ByEmail), поэтому + // она в ключе перезагрузки: маршрут пересчитается, когда придёт, а не + // останется рассчитанным без условий. const routeFieldValues = useMemo( - () => vacationRouteFieldValues({ Organization: data.Organization, Project: data.Project }), + () => organizationProjectRouteFieldValues(data.Organization, data.Project), [data.Organization, data.Project] ); const routeOrgKey = routeFieldValues.map((f) => f.value).join('|'); diff --git a/src/webparts/requestPortal/forms/Vacation/routeFields.ts b/src/webparts/requestPortal/forms/Vacation/routeFields.ts deleted file mode 100644 index 0bdfd04..0000000 --- a/src/webparts/requestPortal/forms/Vacation/routeFields.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { IResolveFieldValue } from '../../shared/approval/models/IApprovalRouteApi'; -import { organizationRouteFieldValues } from '../../shared/dictionary/organizationRouteFields'; -import { IFormData } from './model'; - -/** Поля заявки, от которых зависит маршрут отпуска (условия шагов в админке маршрута #9). */ -export type VacationRouteInput = Pick; - -/** - * Значения полей для условных шагов маршрута (fieldValues при resolve). - * - * Кадровик на шаге 1 зависит и от компании, и от проекта: в офисе — кадровик - * компании («Organization содержит TANSU И Project содержит Офис TANSU»), на - * объекте — кадровик объекта («Project содержит Топар»). Проект — название из - * справочника /Project, как его показывает поле формы. - */ -export function vacationRouteFieldValues(input: VacationRouteInput): IResolveFieldValue[] { - const fieldValues = organizationRouteFieldValues(input.Organization); - const project = input.Project.trim(); - return project ? [...fieldValues, { fieldName: 'Project', value: project }] : fieldValues; -} diff --git a/src/webparts/requestPortal/shared/dictionary/organizationRouteFields.test.ts b/src/webparts/requestPortal/shared/dictionary/organizationRouteFields.test.ts index 21c352e..84a40a2 100644 --- a/src/webparts/requestPortal/shared/dictionary/organizationRouteFields.test.ts +++ b/src/webparts/requestPortal/shared/dictionary/organizationRouteFields.test.ts @@ -1,4 +1,4 @@ -import { organizationRouteFieldValues } from './organizationRouteFields'; +import { organizationProjectRouteFieldValues, organizationRouteFieldValues } from './organizationRouteFields'; describe('organizationRouteFieldValues', () => { it('одна организация — её название', () => { @@ -28,3 +28,18 @@ describe('organizationRouteFieldValues', () => { expect(organizationRouteFieldValues('')).toEqual([]); }); }); + +describe('organizationProjectRouteFieldValues', () => { + it('организация и проект — две записи', () => { + expect(organizationProjectRouteFieldValues('ТОО TANSU Construction — Инженер', ' Офис TANSU ')).toEqual([ + { fieldName: 'Organization', value: 'ТОО TANSU Construction' }, + { fieldName: 'Project', value: 'Офис TANSU' }, + ]); + }); + + it('проект не выбран — только организация', () => { + expect(organizationProjectRouteFieldValues('ТОО АСТЕЛ-К — Экономист', '')).toEqual([ + { fieldName: 'Organization', value: 'ТОО АСТЕЛ-К' }, + ]); + }); +}); diff --git a/src/webparts/requestPortal/shared/dictionary/organizationRouteFields.ts b/src/webparts/requestPortal/shared/dictionary/organizationRouteFields.ts index 7159f89..56b599b 100644 --- a/src/webparts/requestPortal/shared/dictionary/organizationRouteFields.ts +++ b/src/webparts/requestPortal/shared/dictionary/organizationRouteFields.ts @@ -22,3 +22,16 @@ export function organizationRouteFieldValues(organization: string): IResolveFiel const names = companies.filter((name, i) => companies.indexOf(name) === i).sort().join(' '); return names ? [{ fieldName: 'Organization', value: names }] : []; } + +/** + * Организация + проект — для форм, где кадровик зависит и от места работы + * (отпуск, отпуск без содержания): в офисе — кадровик компании («Organization + * содержит TANSU И Project содержит Офис TANSU»), на объекте — кадровик + * объекта («Project содержит …»). Проект — название из справочника /Project, + * как его показывает поле формы. + */ +export function organizationProjectRouteFieldValues(organization: string, project: string): IResolveFieldValue[] { + const fieldValues = organizationRouteFieldValues(organization); + const projectName = project.trim(); + return projectName ? [...fieldValues, { fieldName: 'Project', value: projectName }] : fieldValues; +}