fix: дата подачи — день отправки, а не сохранения черновика
В подборе и реферале черновик хранил дату первого сохранения, и через несколько дней заявка уходила на согласование со старой датой подачи. Теперь при отправке дата подачи всегда сегодняшняя, а открытый черновик сразу показывает сегодня (как уже было в объяснительной по СКУД). В реферале дату больше нельзя поправить руками. Общая логика — shared/submitDate. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -64,6 +64,7 @@ import { VacancySection } from './sections/VacancySection';
|
|||||||
import { HealthTripsSection } from './sections/HealthTripsSection';
|
import { HealthTripsSection } from './sections/HealthTripsSection';
|
||||||
import { ConditionsSection } from './sections/ConditionsSection';
|
import { ConditionsSection } from './sections/ConditionsSection';
|
||||||
import { useStableCallback } from '../../shared/useStableCallback';
|
import { useStableCallback } from '../../shared/useStableCallback';
|
||||||
|
import { submitDateOnOpen } from '../../shared/submitDate';
|
||||||
|
|
||||||
const RecruitingSelectionForm: React.FC<IRequestFormProps> = ({
|
const RecruitingSelectionForm: React.FC<IRequestFormProps> = ({
|
||||||
requestType,
|
requestType,
|
||||||
@@ -105,7 +106,7 @@ const RecruitingSelectionForm: React.FC<IRequestFormProps> = ({
|
|||||||
Promise.all([loadSelectionRequest(editId, editListId), getAttachments(editListId || dataListTitle(FORM_ID), editId)])
|
Promise.all([loadSelectionRequest(editId, editListId), getAttachments(editListId || dataListTitle(FORM_ID), editId)])
|
||||||
.then(([loaded, atts]) => {
|
.then(([loaded, atts]) => {
|
||||||
if (alive) {
|
if (alive) {
|
||||||
setData(loaded.data);
|
setData({ ...loaded.data, SubmitDate: submitDateOnOpen(loaded.status, loaded.data.SubmitDate) });
|
||||||
setApproverChanges(loaded.approverChanges);
|
setApproverChanges(loaded.approverChanges);
|
||||||
setRequestStatus(loaded.status);
|
setRequestStatus(loaded.status);
|
||||||
setRequestCode(loaded.code || undefined);
|
setRequestCode(loaded.code || undefined);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { ApprovalListService, IApproverToWrite, approvalServiceFor, writeRoute }
|
|||||||
import { ISaveItemResult, resolveDataList, saveRequestItem } from '../../shared/dataList';
|
import { ISaveItemResult, resolveDataList, saveRequestItem } from '../../shared/dataList';
|
||||||
import { IFieldChange, serializeFieldChanges } from '../../shared/fieldChanges/fieldChanges';
|
import { IFieldChange, serializeFieldChanges } from '../../shared/fieldChanges/fieldChanges';
|
||||||
import { buildRequestTitle } from '../../shared/requestTitle';
|
import { buildRequestTitle } from '../../shared/requestTitle';
|
||||||
|
import { submitDateOnSave } from '../../shared/submitDate';
|
||||||
import { FORM_ID, IFormData } from './model';
|
import { FORM_ID, IFormData } from './model';
|
||||||
import { APPROVER_CHANGES_FIELD } from './changeTracking';
|
import { APPROVER_CHANGES_FIELD } from './changeTracking';
|
||||||
import { cleanVacancyReasons, legacyDetailsText } from './vacancyReasons';
|
import { cleanVacancyReasons, legacyDetailsText } from './vacancyReasons';
|
||||||
@@ -146,6 +147,7 @@ export async function saveSelectionRequest(
|
|||||||
): Promise<ISaveResult> {
|
): Promise<ISaveResult> {
|
||||||
const item = buildScalarBody(data);
|
const item = buildScalarBody(data);
|
||||||
item.RequestStatus = status;
|
item.RequestStatus = status;
|
||||||
|
item.SubmitDate = submitDateOnSave(status, data.SubmitDate) || null;
|
||||||
await applyPeople(item, data);
|
await applyPeople(item, data);
|
||||||
|
|
||||||
const result = await saveRequestItem(FORM_ID, existingId, listId, requestTypeId, item);
|
const result = await saveRequestItem(FORM_ID, existingId, listId, requestTypeId, item);
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ import { VacancySection } from './sections/VacancySection';
|
|||||||
import { CandidateInfoSection } from './sections/CandidateInfoSection';
|
import { CandidateInfoSection } from './sections/CandidateInfoSection';
|
||||||
import { AssessmentSection } from './sections/AssessmentSection';
|
import { AssessmentSection } from './sections/AssessmentSection';
|
||||||
import { useStableCallback } from '../../shared/useStableCallback';
|
import { useStableCallback } from '../../shared/useStableCallback';
|
||||||
|
import { submitDateOnOpen } from '../../shared/submitDate';
|
||||||
|
|
||||||
const ReferralForm: React.FC<IRequestFormProps> = ({
|
const ReferralForm: React.FC<IRequestFormProps> = ({
|
||||||
requestType,
|
requestType,
|
||||||
@@ -111,7 +112,7 @@ const ReferralForm: React.FC<IRequestFormProps> = ({
|
|||||||
Promise.all([loadReferralRequest(editId, editListId), getAttachments(editListId || dataListTitle(FORM_ID), editId)])
|
Promise.all([loadReferralRequest(editId, editListId), getAttachments(editListId || dataListTitle(FORM_ID), editId)])
|
||||||
.then(([loaded, atts]) => {
|
.then(([loaded, atts]) => {
|
||||||
if (alive) {
|
if (alive) {
|
||||||
setData(loaded.data);
|
setData({ ...loaded.data, SubmitDate: submitDateOnOpen(loaded.status, loaded.data.SubmitDate) });
|
||||||
setRequestStatus(loaded.status);
|
setRequestStatus(loaded.status);
|
||||||
setRequestCode(loaded.code || undefined);
|
setRequestCode(loaded.code || undefined);
|
||||||
if (loaded.code) notifyCode(loaded.code);
|
if (loaded.code) notifyCode(loaded.code);
|
||||||
@@ -141,7 +142,7 @@ const ReferralForm: React.FC<IRequestFormProps> = ({
|
|||||||
setData((prev) => ({
|
setData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
Referrer: prev.Referrer || user.email,
|
Referrer: prev.Referrer || user.email,
|
||||||
SubmitDate: prev.SubmitDate || todayIso()
|
SubmitDate: todayIso()
|
||||||
}));
|
}));
|
||||||
approval
|
approval
|
||||||
.getProfileFields(user.loginName)
|
.getProfileFields(user.loginName)
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ import { ISectionProps, REFERRER_PLACEHOLDER } from '../model';
|
|||||||
/** Секция 1: кто рекомендует — дата подачи, сам рекомендатель и его контакты. */
|
/** Секция 1: кто рекомендует — дата подачи, сам рекомендатель и его контакты. */
|
||||||
export const ReferrerSection: React.FC<ISectionProps> = ({ data, set, context, onReferrerChange, printOrgIndex }) => (
|
export const ReferrerSection: React.FC<ISectionProps> = ({ data, set, context, onReferrerChange, printOrgIndex }) => (
|
||||||
<CollapseSection title="1. Рекомендатель" icon="people">
|
<CollapseSection title="1. Рекомендатель" icon="people">
|
||||||
|
{/* Дата подачи — всегда сегодняшняя (ставит форма, при отправке — заново), руками не меняется. */}
|
||||||
<FormField label="1.1 Дата подачи" htmlFor="SubmitDate" required>
|
<FormField label="1.1 Дата подачи" htmlFor="SubmitDate" required>
|
||||||
<TextInput id="SubmitDate" type="date" value={data.SubmitDate} onChange={(v) => set('SubmitDate', v)} />
|
<TextInput id="SubmitDate" type="date" value={data.SubmitDate} onChange={(v) => set('SubmitDate', v)} readOnly />
|
||||||
</FormField>
|
</FormField>
|
||||||
<FormField label="1.2 Ф.И.О. рекомендателя" htmlFor="Referrer" required full>
|
<FormField label="1.2 Ф.И.О. рекомендателя" htmlFor="Referrer" required full>
|
||||||
<PeopleField
|
<PeopleField
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ApprovalListService, IApproverToWrite, approvalServiceFor, writeRoute } from '../../shared/approval';
|
import { ApprovalListService, IApproverToWrite, approvalServiceFor, writeRoute } from '../../shared/approval';
|
||||||
import { ISaveItemResult, saveRequestItem } from '../../shared/dataList';
|
import { ISaveItemResult, saveRequestItem } from '../../shared/dataList';
|
||||||
import { ensureEmployee } from '../../shared/employee';
|
import { ensureEmployee } from '../../shared/employee';
|
||||||
|
import { submitDateOnSave } from '../../shared/submitDate';
|
||||||
import { FORM_ID, IFormData, OTHER_VALUE } from './model';
|
import { FORM_ID, IFormData, OTHER_VALUE } from './model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,7 +29,7 @@ function buildItem(data: IFormData, status: string, referrerId: number): Record<
|
|||||||
return {
|
return {
|
||||||
Title: buildTitle(data.CandidateName, data.VacancyTitle),
|
Title: buildTitle(data.CandidateName, data.VacancyTitle),
|
||||||
RequestStatus: status,
|
RequestStatus: status,
|
||||||
SubmitDate: data.SubmitDate || null,
|
SubmitDate: submitDateOnSave(status, data.SubmitDate) || null,
|
||||||
ReferrerId: referrerId,
|
ReferrerId: referrerId,
|
||||||
ReferrerPosition: data.ReferrerPosition || null,
|
ReferrerPosition: data.ReferrerPosition || null,
|
||||||
ReferrerDepartment: data.ReferrerDepartment || null,
|
ReferrerDepartment: data.ReferrerDepartment || null,
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import {
|
|||||||
dataListTitleForListId
|
dataListTitleForListId
|
||||||
} from '../../shared/listTitles';
|
} from '../../shared/listTitles';
|
||||||
import { useStableCallback } from '../../shared/useStableCallback';
|
import { useStableCallback } from '../../shared/useStableCallback';
|
||||||
|
import { submitDateOnOpen } from '../../shared/submitDate';
|
||||||
import { FORM_ID, FORM_TITLE, IFormData, EMPTY_DATA, SetField } from './model';
|
import { FORM_ID, FORM_TITLE, IFormData, EMPTY_DATA, SetField } from './model';
|
||||||
import { validateSkudExplanation } from './validation';
|
import { validateSkudExplanation } from './validation';
|
||||||
import { saveSkudExplanationRequest, skudExplanationApprovalService } from './submit';
|
import { saveSkudExplanationRequest, skudExplanationApprovalService } from './submit';
|
||||||
@@ -83,10 +84,7 @@ const SkudExplanationForm: React.FC<IRequestFormProps> = ({
|
|||||||
loadSkudExplanationRequest(editId, editListId)
|
loadSkudExplanationRequest(editId, editListId)
|
||||||
.then((loaded) => {
|
.then((loaded) => {
|
||||||
if (!alive) return;
|
if (!alive) return;
|
||||||
// Черновик/доработку подают заново — дата подачи становится сегодняшней;
|
setData({ ...loaded.data, SubmitDate: submitDateOnOpen(loaded.status, loaded.data.SubmitDate) });
|
||||||
// у отправленной заявки остаётся та, с которой её подали.
|
|
||||||
const submitDate = isEditableStatus(loaded.status) ? todayDateKey() : loaded.data.SubmitDate;
|
|
||||||
setData({ ...loaded.data, SubmitDate: submitDate });
|
|
||||||
setRequestStatus(loaded.status);
|
setRequestStatus(loaded.status);
|
||||||
setRequestCode(loaded.code || undefined);
|
setRequestCode(loaded.code || undefined);
|
||||||
if (loaded.code) notifyCode(loaded.code);
|
if (loaded.code) notifyCode(loaded.code);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ApprovalListService, IApproverToWrite, approvalServiceFor, writeRoute } from '../../shared/approval';
|
import { ApprovalListService, IApproverToWrite, approvalServiceFor, writeRoute } from '../../shared/approval';
|
||||||
import { ISaveItemResult, saveRequestItem } from '../../shared/dataList';
|
import { ISaveItemResult, saveRequestItem } from '../../shared/dataList';
|
||||||
import { ensureEmployee } from '../../shared/employee';
|
import { ensureEmployee } from '../../shared/employee';
|
||||||
|
import { submitDateOnSave } from '../../shared/submitDate';
|
||||||
import { FORM_ID, IFormData, OTHER_REASON } from './model';
|
import { FORM_ID, IFormData, OTHER_REASON } from './model';
|
||||||
import { formatMissedDaysSummary, formatShortDate } from './noteText';
|
import { formatMissedDaysSummary, formatShortDate } from './noteText';
|
||||||
import { todayDateKey } from './skud/skudDays';
|
import { todayDateKey } from './skud/skudDays';
|
||||||
@@ -19,7 +20,7 @@ function buildItem(data: IFormData, status: string, employeeId: number, employee
|
|||||||
return {
|
return {
|
||||||
Title: buildTitle(employeeName),
|
Title: buildTitle(employeeName),
|
||||||
RequestStatus: status,
|
RequestStatus: status,
|
||||||
SubmitDate: data.SubmitDate || null,
|
SubmitDate: submitDateOnSave(status, data.SubmitDate) || null,
|
||||||
EmployeeId: employeeId,
|
EmployeeId: employeeId,
|
||||||
SkudEmployeeFio: data.EmployeeFio || null,
|
SkudEmployeeFio: data.EmployeeFio || null,
|
||||||
Position: data.Position || null,
|
Position: data.Position || null,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user