refactor(ServiceRequest): CostsJson/CostTotal -> EstimateJson/EstimateTotal

Понятнее по смыслу колонки: это смета (Estimate), а не «costs» вообще.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
gorbenko.pavel
2026-09-01 11:26:57 +05:00
parent ec53e48064
commit c73ab17cbd
6 changed files with 11 additions and 11 deletions
@@ -12,8 +12,8 @@
## Особое поведение ## Особое поведение
- Подача за другого: заявитель — `PeopleField`, по умолчанию текущий пользователь, можно выбрать любого — маршрут резолвится на ВЫБРАННОГО (см. `handleRequesterChange`). - Подача за другого: заявитель — `PeopleField`, по умолчанию текущий пользователь, можно выбрать любого — маршрут резолвится на ВЫБРАННОГО (см. `handleRequesterChange`).
- Табличная часть «Приложения» (`Annexes`) — документы заявки (чертежи, ТЗ, ведомости объёмов) с чекбоксом «подтверждено»; не путать с блоком «Вложения» (реальные файлы) — это ОТДЕЛЬНЫЙ учёт, как в бумажной форме. - Табличная часть «Приложения» (`Annexes`) — документы заявки (чертежи, ТЗ, ведомости объёмов) с чекбоксом «подтверждено»; не путать с блоком «Вложения» (реальные файлы) — это ОТДЕЛЬНЫЙ учёт, как в бумажной форме.
- Табличная часть «Смета» (`Costs`) — список групп (разделов работ), внутри каждой свои позиции; название группы необязательно. Сумма по позиции, по группе и общий итог считаются на лету (`qty × rate`, см. `cost.ts`), не хранятся в структуре — правка количества/цены не может их рассинхронизировать. `CostTotal` дублируется числовой колонкой в SharePoint при сохранении — только для отчётов, форма её не читает обратно. - Табличная часть «Смета» (`Costs`) — список групп (разделов работ), внутри каждой свои позиции; название группы необязательно. Сумма по позиции, по группе и общий итог считаются на лету (`qty × rate`, см. `cost.ts`), не хранятся в структуре — правка количества/цены не может их рассинхронизировать. `EstimateTotal` дублируется числовой колонкой в SharePoint при сохранении — только для отчётов, форма её не читает обратно.
- Обе табличные части сериализуются в JSON-колонки (`AnnexesJson`, `CostsJson`) — тот же приём, что в `TmcTransfer` (`ItemsJson`). - Обе табличные части сериализуются в JSON-колонки (`AnnexesJson`, `EstimateJson`) — тот же приём, что в `TmcTransfer` (`ItemsJson`).
- Продолжительность работ под датами «начало/окончание» — производная величина (считается на лету), в SharePoint не пишется. - Продолжительность работ под датами «начало/окончание» — производная величина (считается на лету), в SharePoint не пишется.
- Фазы исполнения нет: `saveServiceRequest` пишет маршрут с пустым списком исполнителей. - Фазы исполнения нет: `saveServiceRequest` пишет маршрут с пустым списком исполнителей.
@@ -34,7 +34,7 @@ const SELECT_FIELDS = [
'EstimatedFinishDate', 'EstimatedFinishDate',
'GeneralRemarks', 'GeneralRemarks',
'AnnexesJson', 'AnnexesJson',
'CostsJson', 'EstimateJson',
'RequestStatus', 'RequestStatus',
'RequestUniqueCode' 'RequestUniqueCode'
]; ];
@@ -94,7 +94,7 @@ export async function loadServiceRequest(itemId: number, listId?: string): Promi
EstimatedFinishDate: toDateInput(item.EstimatedFinishDate), EstimatedFinishDate: toDateInput(item.EstimatedFinishDate),
GeneralRemarks: (item.GeneralRemarks as string) || '', GeneralRemarks: (item.GeneralRemarks as string) || '',
Annexes: parseRows<IAnnexRow>(item.AnnexesJson), Annexes: parseRows<IAnnexRow>(item.AnnexesJson),
Costs: parseRows<ICostGroup>(item.CostsJson) Costs: parseRows<ICostGroup>(item.EstimateJson)
}; };
return { return {
@@ -80,7 +80,7 @@ export const CONTRACT_ACTION_OPTIONS: ISelectOption[] = [
* Данные формы «Заявка на услуги» (бумажный аналог — Service Requisition Form * Данные формы «Заявка на услуги» (бумажный аналог — Service Requisition Form
* ПТО). Ключи = internalName колонок SP-списка «Заявка на услуги — данные». * ПТО). Ключи = internalName колонок SP-списка «Заявка на услуги — данные».
* Табличные части (Annexes / Costs) в колонки не разворачиваются, а * Табличные части (Annexes / Costs) в колонки не разворачиваются, а
* сериализуются в JSON-колонки AnnexesJson / CostsJson (см. submit.ts / load.ts). * сериализуются в JSON-колонки AnnexesJson / EstimateJson (см. submit.ts / load.ts).
*/ */
export interface IFormData { export interface IFormData {
// --- Заявитель --- // --- Заявитель ---
@@ -132,7 +132,7 @@ export interface IFormData {
// --- Табличные части --- // --- Табличные части ---
/** Приложения к заявке (сериализуются в AnnexesJson). */ /** Приложения к заявке (сериализуются в AnnexesJson). */
Annexes: IAnnexRow[]; Annexes: IAnnexRow[];
/** Сметная стоимость по группам (сериализуется в CostsJson). */ /** Сметная стоимость по группам (сериализуется в EstimateJson). */
Costs: ICostGroup[]; Costs: ICostGroup[];
} }
@@ -29,7 +29,7 @@
{ "internalName": "GeneralRemarks", "title": "Общие замечания", "type": "Note" }, { "internalName": "GeneralRemarks", "title": "Общие замечания", "type": "Note" },
{ "internalName": "AnnexesJson", "title": "Приложения (JSON)", "type": "Note" }, { "internalName": "AnnexesJson", "title": "Приложения (JSON)", "type": "Note" },
{ "internalName": "CostsJson", "title": "Сметная стоимость (JSON)", "type": "Note" }, { "internalName": "EstimateJson", "title": "Сметная стоимость (JSON)", "type": "Note" },
{ "internalName": "CostTotal", "title": "Итого без НДС", "type": "Number", "decimals": 2 } { "internalName": "EstimateTotal", "title": "Итого без НДС", "type": "Number", "decimals": 2 }
] ]
} }
@@ -116,7 +116,7 @@ const CostGroup: React.FC<IGroupProps> = ({
* Сметная стоимость услуг-работ по разделам (группам). Сумма по позиции, * Сметная стоимость услуг-работ по разделам (группам). Сумма по позиции,
* по группе и общий итог — все производные (qty × цена), считаются на лету * по группе и общий итог — все производные (qty × цена), считаются на лету
* и НЕ хранятся построчно (см. cost.ts): правка количества/цены не может * и НЕ хранятся построчно (см. cost.ts): правка количества/цены не может
* рассинхронизировать сумму. CostTotal в SharePoint дублируется отдельной * рассинхронизировать сумму. EstimateTotal в SharePoint дублируется отдельной
* числовой колонкой при сохранении (см. submit.ts) — только для отчётов, * числовой колонкой при сохранении (см. submit.ts) — только для отчётов,
* форма её сама не читает. * форма её сама не читает.
*/ */
@@ -33,12 +33,12 @@ function buildItem(data: IFormData, status: string): Record<string, unknown> {
}); });
item.AnnexesJson = data.Annexes.length > 0 ? JSON.stringify(data.Annexes) : null; item.AnnexesJson = data.Annexes.length > 0 ? JSON.stringify(data.Annexes) : null;
item.CostsJson = data.Costs.length > 0 ? JSON.stringify(data.Costs) : null; item.EstimateJson = data.Costs.length > 0 ? JSON.stringify(data.Costs) : null;
// Итог сметы дублируем числовой колонкой намеренно: в самой форме он считается // Итог сметы дублируем числовой колонкой намеренно: в самой форме он считается
// на лету (см. cost.ts), но представлениям SharePoint и отчётам нужна сумма // на лету (см. cost.ts), но представлениям SharePoint и отчётам нужна сумма
// без разбора JSON — иначе любой отчёт по заявкам пришлось бы писать кодом. // без разбора JSON — иначе любой отчёт по заявкам пришлось бы писать кодом.
const total = costTotal(data.Costs); const total = costTotal(data.Costs);
item.CostTotal = total > 0 ? total : null; item.EstimateTotal = total > 0 ? total : null;
return item; return item;
} }