Compare commits
3 Commits
7e9848e83d
...
4f292dde3d
| Author | SHA1 | Date | |
|---|---|---|---|
| 4f292dde3d | |||
| a05a336275 | |||
| ff7d1f26a7 |
@@ -22,6 +22,13 @@
|
||||
color: #000 !important;
|
||||
border-color: #ccc !important;
|
||||
}
|
||||
|
||||
// У выбранного человека в PeoplePicker (Fluent) есть кнопка «×». В окне печати
|
||||
// svg-иконки скрыты, а стили Fluent туда не копируются — остаётся пустая
|
||||
// системная кнопка-квадратик под ФИО. На печати удалять некого — скрываем.
|
||||
.rp-print-root .ms-PickerItem-removeButton {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,15 @@
|
||||
color: $navy-900;
|
||||
}
|
||||
|
||||
// Только для окна печати (PrintButton печатает с media print — там блок виден).
|
||||
.printOnly {
|
||||
margin-top: $sp-6;
|
||||
|
||||
@media screen {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@@ -7,11 +7,11 @@ import {
|
||||
ApproverService,
|
||||
DecisionPanel,
|
||||
StatusBadge,
|
||||
ApprovalMatrix,
|
||||
ApprovalHistory,
|
||||
notifyActiveStep,
|
||||
useRequestTypeId
|
||||
} from '../../shared/approval';
|
||||
import type { ISiteUser } from '../../shared/approval';
|
||||
import { friendlyErrorMessage } from '../../shared/diagnostics';
|
||||
import {
|
||||
approvalListTitle,
|
||||
@@ -28,6 +28,7 @@ import { EmployeeSection } from './sections/EmployeeSection';
|
||||
import { DaysSection } from './sections/DaysSection';
|
||||
import { ExplanationSection } from './sections/ExplanationSection';
|
||||
import { NoteDocument } from './components/NoteDocument';
|
||||
import { SkudRouteTables } from './components/SkudRouteTables';
|
||||
|
||||
interface ISkudExplanationViewProps {
|
||||
context: WebPartContext;
|
||||
@@ -44,7 +45,8 @@ const NOOP_SET: SetField = () => undefined;
|
||||
|
||||
/**
|
||||
* Объяснительная для согласующего: данные заявки (только чтение) + документ
|
||||
* записки. На печать идёт только документ (rp-print-root) — как бумажный бланк.
|
||||
* записки. На печать идёт только rp-print-root: документ как бумажный бланк и
|
||||
* под ним маршрут согласования/исполнения вместо подписи.
|
||||
*/
|
||||
const SkudExplanationView: React.FC<ISkudExplanationViewProps> = ({ context, itemId, formKey, listId, onCodeLoaded }) => {
|
||||
const notifyCode = useStableCallback(onCodeLoaded);
|
||||
@@ -119,6 +121,7 @@ const SkudExplanationView: React.FC<ISkudExplanationViewProps> = ({ context, ite
|
||||
};
|
||||
|
||||
const sectionProps = { data, set: NOOP_SET, context, printOrgIndex };
|
||||
const searchUsers = (query: string): Promise<ISiteUser[]> => approval.searchUsers(query);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -150,21 +153,15 @@ const SkudExplanationView: React.FC<ISkudExplanationViewProps> = ({ context, ite
|
||||
<OrgLetterhead value={data.Organization} selectedIndex={printOrgIndex} />
|
||||
{/* Created — запасной вариант для заявок, сохранённых до появления «Даты подачи». */}
|
||||
<NoteDocument data={data} noteDate={data.SubmitDate || created} orgIndex={printOrgIndex} />
|
||||
{/* На экране маршрут уже есть под карточкой — копия нужна только в окне печати. */}
|
||||
<div className={styles.printOnly}>
|
||||
<SkudRouteTables approvers={approvers} executors={executors} search={searchUsers} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FormCard>
|
||||
|
||||
<ApprovalMatrix editor={approvers} search={(q) => approval.searchUsers(q)} />
|
||||
|
||||
{!executors.loading && executors.rows.length > 0 && (
|
||||
<ApprovalMatrix
|
||||
editor={executors}
|
||||
search={(q) => approval.searchUsers(q)}
|
||||
title="Исполнение"
|
||||
roleLabel="Исполнитель"
|
||||
emptyText="Исполнители не назначены."
|
||||
/>
|
||||
)}
|
||||
<SkudRouteTables approvers={approvers} executors={executors} search={searchUsers} />
|
||||
|
||||
<div className="rp-no-print">
|
||||
<ApprovalHistory approval={approval} dataListRef={dataListRef} requestId={itemId} reloadKey={decisionSeq} />
|
||||
|
||||
+2
-13
@@ -4,9 +4,10 @@
|
||||
// красная строка — как в образце объяснительной отдела кадров.
|
||||
$doc-font: 'Times New Roman', Times, serif;
|
||||
$paragraph-indent: 2.5em;
|
||||
$sign-line-width: 120px;
|
||||
|
||||
.document {
|
||||
// border-box: иначе padding и рамка прибавляются к 100% и документ вылезает за карточку.
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: $sp-4;
|
||||
border: 1px solid $gray-200;
|
||||
@@ -57,15 +58,3 @@ $sign-line-width: 120px;
|
||||
margin-top: $sp-8;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.signature {
|
||||
display: inline-flex;
|
||||
align-items: flex-end;
|
||||
gap: $sp-2;
|
||||
}
|
||||
|
||||
.signLine {
|
||||
display: inline-block;
|
||||
width: $sign-line-width;
|
||||
border-bottom: 1px solid $gray-900;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ function companyOf(organization: string, index: number): string {
|
||||
|
||||
/**
|
||||
* Объяснительная записка в виде бумажного документа (как бланк отдела кадров):
|
||||
* шапка справа, заголовок, текст, дата и подпись. Предпросмотр в форме и
|
||||
* шапка справа, заголовок, текст, дата и ФИО. Предпросмотр в форме и
|
||||
* печатная форма в просмотре — один и тот же компонент.
|
||||
*/
|
||||
export const NoteDocument: React.FC<INoteDocumentProps> = ({ data, noteDate, orgIndex = 0 }) => {
|
||||
@@ -42,12 +42,10 @@ export const NoteDocument: React.FC<INoteDocumentProps> = ({ data, noteDate, org
|
||||
</p>
|
||||
))}
|
||||
|
||||
{/* Без линии для подписи: подписью служит маршрут согласования, который печатается под запиской. */}
|
||||
<div className={styles.footer}>
|
||||
<span>{formatShortDate(noteDate)} г.</span>
|
||||
<span className={styles.signature}>
|
||||
<span className={styles.signLine} />
|
||||
{name}
|
||||
</span>
|
||||
<span>{name}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import * as React from 'react';
|
||||
import { ApprovalMatrix } from '../../../shared/approval';
|
||||
import type { ISiteUser, IUseMatrixEditor } from '../../../shared/approval';
|
||||
|
||||
interface ISkudRouteTablesProps {
|
||||
approvers: IUseMatrixEditor;
|
||||
executors: IUseMatrixEditor;
|
||||
search: (query: string) => Promise<ISiteUser[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Маршрут согласования + исполнение (если исполнители есть). Рисуется дважды:
|
||||
* на экране под карточкой и внутри печатного блока под запиской — на печати
|
||||
* таблица маршрута заменяет подпись сотрудника.
|
||||
*/
|
||||
export const SkudRouteTables: React.FC<ISkudRouteTablesProps> = ({ approvers, executors, search }) => {
|
||||
const hasExecutors = !executors.loading && executors.rows.length > 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ApprovalMatrix editor={approvers} search={search} />
|
||||
{hasExecutors && (
|
||||
<ApprovalMatrix
|
||||
editor={executors}
|
||||
search={search}
|
||||
title="Исполнение"
|
||||
roleLabel="Исполнитель"
|
||||
emptyText="Исполнители не назначены."
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -85,8 +85,7 @@ export interface ISkudReason {
|
||||
/** Варианты причины. Меняя value — менять и choices колонки SkudReason в provisioning.json. */
|
||||
export const SKUD_REASONS: ISkudReason[] = [
|
||||
{ value: 'Невнимательность', phrase: 'невнимательности' },
|
||||
{ value: 'Забыл(а) пропуск', phrase: 'того, что я забыл(а) пропуск' },
|
||||
{ value: 'Неисправность турникета или считывателя', phrase: 'неисправности турникета или считывателя' },
|
||||
{ value: 'Неисправность СКУД', phrase: 'неисправности СКУД' },
|
||||
{ value: OTHER_REASON, phrase: '' }
|
||||
];
|
||||
|
||||
|
||||
@@ -43,7 +43,21 @@ describe('noteText', () => {
|
||||
ReasonDetails: 'был на объекте',
|
||||
PresenceConfirmed: false
|
||||
});
|
||||
expect(second).toBe('Отсутствие отметки в системе СКУД в указанную дату произошло по следующей причине: был на объекте');
|
||||
expect(second).toBe('Отсутствие отметки в системе СКУД в указанную дату произошло по следующей причине: был на объекте.');
|
||||
});
|
||||
|
||||
it('«Другое»: свою точку сотрудника не дублирует, фраза о подтверждении не прилипает', () => {
|
||||
const [, second] = buildNoteParagraphs({
|
||||
...DATA,
|
||||
MissedDays: [DATA.MissedDays[0]],
|
||||
Reason: OTHER_REASON,
|
||||
ReasonDetails: 'был на объекте.',
|
||||
PresenceConfirmed: true
|
||||
});
|
||||
expect(second).toBe(
|
||||
'Отсутствие отметки в системе СКУД в указанную дату произошло по следующей причине: был на объекте. ' +
|
||||
'Факт моего нахождения на рабочем месте и исполнения трудовых обязанностей в течение рабочего дня подтверждаю.'
|
||||
);
|
||||
});
|
||||
|
||||
it('сводка дней для колонки SP', () => {
|
||||
|
||||
@@ -62,9 +62,14 @@ export function formatMissedDaysSummary(days: IMissedDay[]): string {
|
||||
return days.map((d) => `${formatShortDate(d.date)} (${formatMissedDetails(d)})`).join('; ');
|
||||
}
|
||||
|
||||
/** Текст сотрудника — законченное предложение: без точки следующая фраза записки к нему прилипает. */
|
||||
function withFinalPunctuation(text: string): string {
|
||||
return /[.!?…]$/.test(text) ? text : `${text}.`;
|
||||
}
|
||||
|
||||
function reasonSentence(data: IFormData, dateWord: string): string {
|
||||
if (data.Reason === OTHER_REASON) {
|
||||
const details = data.ReasonDetails.trim() || BLANK;
|
||||
const details = withFinalPunctuation(data.ReasonDetails.trim() || BLANK);
|
||||
return `Отсутствие отметки в системе СКУД в ${dateWord} произошло по следующей причине: ${details}`;
|
||||
}
|
||||
const reason = SKUD_REASONS.find((r) => r.value === data.Reason);
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
{ "internalName": "Organization", "title": "Организация", "type": "Note" },
|
||||
{ "internalName": "SkudMissedDates", "title": "Дни без отметок", "type": "Note" },
|
||||
{ "internalName": "SkudMissedDaysJson", "title": "Дни без отметок (JSON)", "type": "Note" },
|
||||
{ "internalName": "SkudReason", "title": "Причина", "type": "Choice", "choices": ["Невнимательность", "Забыл(а) пропуск", "Неисправность турникета или считывателя", "Другое"] },
|
||||
{ "internalName": "SkudReason", "title": "Причина", "type": "Choice", "choices": ["Невнимательность", "Неисправность СКУД", "Другое"] },
|
||||
{ "internalName": "SkudReasonDetails", "title": "Причина (подробно)", "type": "Note" },
|
||||
{ "internalName": "SkudPresenceConfirmed", "title": "Присутствие подтверждено", "type": "Boolean" }
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user