feat: комментарий после итога сметы в командировке

Необязательное поле «Комментарий» (колонка TripComment) после итога сметы;
в просмотре пустой комментарий не показывается.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Тимур Городецкий
2026-10-23 15:16:43 +05:00
parent 50a1ae2ce3
commit 63448e60aa
6 changed files with 18 additions and 2 deletions
@@ -102,6 +102,8 @@
привязкой, у «Другого расхода» — название, у суточных — кол-во МРП.
- Хранение: `TripEstimateJson` (статьи + total), читаемо — `TripEstimate` (позиция на
строку), итог числом — `TripEstimateTotal`. Просмотр/печать — таблица по статьям.
- **Комментарий** (с 10-23, `TripComment`, Note) — необязательное поле после итога сметы; в
просмотре пустой не показывается.
- **Обязательство** по авансовому отчёту (текст бланка) — флажок, без него не отправить.
- **Дата подачи** (`SubmitDate`, общий столбец сайта) — ставится сегодняшней, пока заявку
можно править (см. shared/submitDate.ts).
@@ -33,6 +33,7 @@ export async function loadBusinessTripRequest(itemId: number, listId?: string):
'TripRouteJson',
'TripTasksJson',
'TripEstimateJson',
'TripComment',
'TripObligationAccepted',
'RequestStatus',
'RequestUniqueCode',
@@ -53,6 +54,7 @@ export async function loadBusinessTripRequest(itemId: number, listId?: string):
Route: parseRoute(item.TripRouteJson),
Tasks: parseTasks(item.TripTasksJson),
Estimate: parseEstimate(item.TripEstimateJson),
TripComment: (item.TripComment as string) || '',
TripObligationAccepted: item.TripObligationAccepted === true
};
@@ -315,6 +315,8 @@ export interface IFormData {
/** Смета: основные статьи + добавленные (суммы вводятся вручную). */
Estimate: IEstimateArticle[];
/** Комментарий к заявке — после итога сметы, необязательный. */
TripComment: string;
/** Сотрудник принял обязательство по авансовому отчёту (см. OBLIGATION_TEXT). */
TripObligationAccepted: boolean;
@@ -397,6 +399,7 @@ export const EMPTY_DATA: IFormData = {
// Новая заявка сразу показывает одну пустую строку задачи — понятнее, что заполнять.
Tasks: [''],
Estimate: EMPTY_ESTIMATE,
TripComment: '',
TripObligationAccepted: false
};
@@ -28,6 +28,7 @@
{ "internalName": "TripEstimate", "title": "Смета на командировку", "type": "Note" },
{ "internalName": "TripEstimateJson", "title": "Смета на командировку (JSON)", "type": "Note" },
{ "internalName": "TripEstimateTotal", "title": "Итого по смете, тенге", "type": "Number" },
{ "internalName": "TripComment", "title": "Комментарий", "type": "Note" },
{ "internalName": "TripObligationAccepted", "title": "Обязательство по авансовому отчёту принято", "type": "Boolean" }
]
@@ -1,6 +1,6 @@
import * as React from 'react';
import styles from './EstimateSection.module.scss';
import { CollapseSection, useFormReadOnly } from '../../../shared/ui';
import { CollapseSection, FormField, TextArea, useFormReadOnly } from '../../../shared/ui';
import { ISectionProps } from '../model';
import { EstimateEditor } from '../components/estimate/EstimateEditor';
import { EstimateTable } from '../components/estimate/EstimateTable';
@@ -9,7 +9,8 @@ import { EstimateSummary } from '../components/estimate/EstimateSummary';
/**
* Смета на командировку. Заполнение — карточками статей с позициями, привязанными
* к точкам и участкам маршрута (EstimateEditor); просмотр и печать — таблицей.
* Итог с долями и разбивкой по объектам — в обоих режимах.
* Итог с долями и разбивкой по объектам — в обоих режимах. После итога —
* необязательный комментарий к заявке.
*/
export const EstimateSection: React.FC<ISectionProps> = ({ data, set }) => {
const readOnly = useFormReadOnly();
@@ -26,6 +27,12 @@ export const EstimateSection: React.FC<ISectionProps> = ({ data, set }) => {
<EstimateEditor data={data} set={set} />
)}
</div>
{/* Пустой комментарий в просмотре не показываем — пустое поле только мешает читать. */}
{(!readOnly || data.TripComment.trim()) && (
<FormField label="Комментарий" htmlFor="TripComment" full>
<TextArea id="TripComment" value={data.TripComment} onChange={(v) => set('TripComment', v)} rows={3} />
</FormField>
)}
</CollapseSection>
);
};
@@ -56,6 +56,7 @@ function buildItem(data: IFormData, status: string, employeeId: number, employee
TripEstimateJson: serializeEstimate(data.Estimate),
// Итог дублируется числовой колонкой — для отчётов и сортировки в SP.
TripEstimateTotal: estimateTotal(data.Estimate),
TripComment: data.TripComment.trim() || null,
TripObligationAccepted: data.TripObligationAccepted
};