diff --git a/src/webparts/requestPortal/forms/RecruitingSelection/RecruitingSelectionForm.tsx b/src/webparts/requestPortal/forms/RecruitingSelection/RecruitingSelectionForm.tsx index b9e826d..08a0735 100644 --- a/src/webparts/requestPortal/forms/RecruitingSelection/RecruitingSelectionForm.tsx +++ b/src/webparts/requestPortal/forms/RecruitingSelection/RecruitingSelectionForm.tsx @@ -64,6 +64,7 @@ import { VacancySection } from './sections/VacancySection'; import { HealthTripsSection } from './sections/HealthTripsSection'; import { ConditionsSection } from './sections/ConditionsSection'; import { useStableCallback } from '../../shared/useStableCallback'; +import { submitDateOnOpen } from '../../shared/submitDate'; const RecruitingSelectionForm: React.FC = ({ requestType, @@ -105,7 +106,7 @@ const RecruitingSelectionForm: React.FC = ({ Promise.all([loadSelectionRequest(editId, editListId), getAttachments(editListId || dataListTitle(FORM_ID), editId)]) .then(([loaded, atts]) => { if (alive) { - setData(loaded.data); + setData({ ...loaded.data, SubmitDate: submitDateOnOpen(loaded.status, loaded.data.SubmitDate) }); setApproverChanges(loaded.approverChanges); setRequestStatus(loaded.status); setRequestCode(loaded.code || undefined); diff --git a/src/webparts/requestPortal/forms/RecruitingSelection/submit.ts b/src/webparts/requestPortal/forms/RecruitingSelection/submit.ts index 0faf9bf..526420c 100644 --- a/src/webparts/requestPortal/forms/RecruitingSelection/submit.ts +++ b/src/webparts/requestPortal/forms/RecruitingSelection/submit.ts @@ -3,6 +3,7 @@ import { ApprovalListService, IApproverToWrite, approvalServiceFor, writeRoute } import { ISaveItemResult, resolveDataList, saveRequestItem } from '../../shared/dataList'; import { IFieldChange, serializeFieldChanges } from '../../shared/fieldChanges/fieldChanges'; import { buildRequestTitle } from '../../shared/requestTitle'; +import { submitDateOnSave } from '../../shared/submitDate'; import { FORM_ID, IFormData } from './model'; import { APPROVER_CHANGES_FIELD } from './changeTracking'; import { cleanVacancyReasons, legacyDetailsText } from './vacancyReasons'; @@ -146,6 +147,7 @@ export async function saveSelectionRequest( ): Promise { const item = buildScalarBody(data); item.RequestStatus = status; + item.SubmitDate = submitDateOnSave(status, data.SubmitDate) || null; await applyPeople(item, data); const result = await saveRequestItem(FORM_ID, existingId, listId, requestTypeId, item); diff --git a/src/webparts/requestPortal/forms/Referral/ReferralForm.tsx b/src/webparts/requestPortal/forms/Referral/ReferralForm.tsx index 9c55fe6..db7d1e0 100644 --- a/src/webparts/requestPortal/forms/Referral/ReferralForm.tsx +++ b/src/webparts/requestPortal/forms/Referral/ReferralForm.tsx @@ -59,6 +59,7 @@ import { VacancySection } from './sections/VacancySection'; import { CandidateInfoSection } from './sections/CandidateInfoSection'; import { AssessmentSection } from './sections/AssessmentSection'; import { useStableCallback } from '../../shared/useStableCallback'; +import { submitDateOnOpen } from '../../shared/submitDate'; const ReferralForm: React.FC = ({ requestType, @@ -111,7 +112,7 @@ const ReferralForm: React.FC = ({ Promise.all([loadReferralRequest(editId, editListId), getAttachments(editListId || dataListTitle(FORM_ID), editId)]) .then(([loaded, atts]) => { if (alive) { - setData(loaded.data); + setData({ ...loaded.data, SubmitDate: submitDateOnOpen(loaded.status, loaded.data.SubmitDate) }); setRequestStatus(loaded.status); setRequestCode(loaded.code || undefined); if (loaded.code) notifyCode(loaded.code); @@ -141,7 +142,7 @@ const ReferralForm: React.FC = ({ setData((prev) => ({ ...prev, Referrer: prev.Referrer || user.email, - SubmitDate: prev.SubmitDate || todayIso() + SubmitDate: todayIso() })); approval .getProfileFields(user.loginName) diff --git a/src/webparts/requestPortal/forms/Referral/sections/ReferrerSection.tsx b/src/webparts/requestPortal/forms/Referral/sections/ReferrerSection.tsx index 76715ad..7fd3ee9 100644 --- a/src/webparts/requestPortal/forms/Referral/sections/ReferrerSection.tsx +++ b/src/webparts/requestPortal/forms/Referral/sections/ReferrerSection.tsx @@ -5,8 +5,9 @@ import { ISectionProps, REFERRER_PLACEHOLDER } from '../model'; /** Секция 1: кто рекомендует — дата подачи, сам рекомендатель и его контакты. */ export const ReferrerSection: React.FC = ({ data, set, context, onReferrerChange, printOrgIndex }) => ( + {/* Дата подачи — всегда сегодняшняя (ставит форма, при отправке — заново), руками не меняется. */} - set('SubmitDate', v)} /> + set('SubmitDate', v)} readOnly /> = ({ loadSkudExplanationRequest(editId, editListId) .then((loaded) => { if (!alive) return; - // Черновик/доработку подают заново — дата подачи становится сегодняшней; - // у отправленной заявки остаётся та, с которой её подали. - const submitDate = isEditableStatus(loaded.status) ? todayDateKey() : loaded.data.SubmitDate; - setData({ ...loaded.data, SubmitDate: submitDate }); + setData({ ...loaded.data, SubmitDate: submitDateOnOpen(loaded.status, loaded.data.SubmitDate) }); setRequestStatus(loaded.status); setRequestCode(loaded.code || undefined); if (loaded.code) notifyCode(loaded.code); diff --git a/src/webparts/requestPortal/forms/SkudExplanation/submit.ts b/src/webparts/requestPortal/forms/SkudExplanation/submit.ts index d442630..0d41187 100644 --- a/src/webparts/requestPortal/forms/SkudExplanation/submit.ts +++ b/src/webparts/requestPortal/forms/SkudExplanation/submit.ts @@ -1,6 +1,7 @@ import { ApprovalListService, IApproverToWrite, approvalServiceFor, writeRoute } from '../../shared/approval'; import { ISaveItemResult, saveRequestItem } from '../../shared/dataList'; import { ensureEmployee } from '../../shared/employee'; +import { submitDateOnSave } from '../../shared/submitDate'; import { FORM_ID, IFormData, OTHER_REASON } from './model'; import { formatMissedDaysSummary, formatShortDate } from './noteText'; import { todayDateKey } from './skud/skudDays'; @@ -19,7 +20,7 @@ function buildItem(data: IFormData, status: string, employeeId: number, employee return { Title: buildTitle(employeeName), RequestStatus: status, - SubmitDate: data.SubmitDate || null, + SubmitDate: submitDateOnSave(status, data.SubmitDate) || null, EmployeeId: employeeId, SkudEmployeeFio: data.EmployeeFio || null, Position: data.Position || null, diff --git a/src/webparts/requestPortal/shared/submitDate.test.ts b/src/webparts/requestPortal/shared/submitDate.test.ts new file mode 100644 index 0000000..a546741 --- /dev/null +++ b/src/webparts/requestPortal/shared/submitDate.test.ts @@ -0,0 +1,21 @@ +import { REQUEST_STATUS } from './approval/models/statuses'; +import { submitDateOnOpen, submitDateOnSave, todaySubmitDate } from './submitDate'; + +const OLD_DATE = '2026-09-01'; + +describe('submitDate', () => { + it('отправка на согласование — сегодня, даже если черновик сохранён раньше', () => { + expect(submitDateOnSave(REQUEST_STATUS.onApproval, OLD_DATE)).toBe(todaySubmitDate()); + expect(submitDateOnSave(REQUEST_STATUS.queued, OLD_DATE)).toBe(todaySubmitDate()); + }); + + it('черновик хранит дату с экрана', () => { + expect(submitDateOnSave(REQUEST_STATUS.draft, OLD_DATE)).toBe(OLD_DATE); + }); + + it('открыли черновик или доработку — сегодня, отправленную — как подали', () => { + expect(submitDateOnOpen(REQUEST_STATUS.draft, OLD_DATE)).toBe(todaySubmitDate()); + expect(submitDateOnOpen(REQUEST_STATUS.returned, OLD_DATE)).toBe(todaySubmitDate()); + expect(submitDateOnOpen(REQUEST_STATUS.onApproval, OLD_DATE)).toBe(OLD_DATE); + }); +}); diff --git a/src/webparts/requestPortal/shared/submitDate.ts b/src/webparts/requestPortal/shared/submitDate.ts new file mode 100644 index 0000000..d870a3e --- /dev/null +++ b/src/webparts/requestPortal/shared/submitDate.ts @@ -0,0 +1,30 @@ +import { REQUEST_STATUS, isEditableStatus } from './approval/models/statuses'; + +function pad2(n: number): string { + return n < 10 ? `0${n}` : `${n}`; +} + +/** Сегодняшняя дата по местному времени, YYYY-MM-DD (формат input type=date). */ +export function todaySubmitDate(): string { + const now = new Date(); + return `${now.getFullYear()}-${pad2(now.getMonth() + 1)}-${pad2(now.getDate())}`; +} + +/** + * «Дата подачи» для формы, открытой на редактирование. Черновик и доработку + * подают заново — на экране сегодняшняя дата, а не дата первого сохранения; + * у уже отправленной заявки остаётся та, с которой её подали. + */ +export function submitDateOnOpen(status: string, savedDate: string): string { + return isEditableStatus(status) ? todaySubmitDate() : savedDate; +} + +/** + * «Дата подачи» для записи в SharePoint из формы подачи. Любое сохранение кроме + * черновика — это отправка («На согласовании» или «Ждёт очереди», см. + * initialSubmitStatus): дата всегда сегодняшняя — форму могли открыть вчера, а + * черновик сохранить неделю назад. Черновик хранит то, что на экране. + */ +export function submitDateOnSave(status: string, currentDate: string): string { + return status === REQUEST_STATUS.draft ? currentDate : todaySubmitDate(); +}