Архивация списков #9
@@ -1,21 +1,15 @@
|
||||
import { getSP } from '../../shared/pnp/pnpjs';
|
||||
import { ApprovalListService, IApproverToWrite, writeRoute } from '../../shared/approval';
|
||||
import { buildRequestUniqueCode } from '../../shared/requestCode';
|
||||
import { approvalListTitle, approvalListTitleForListId, dataListTitle } from '../../shared/listTitles';
|
||||
import { ApprovalListService, IApproverToWrite, approvalServiceFor, writeRoute } from '../../shared/approval';
|
||||
import { ISaveItemResult, resolveDataList, saveRequestItem } from '../../shared/dataList';
|
||||
import { ensureEmployee } from '../../shared/employee';
|
||||
import { FORM_ID, IFormData } from './model';
|
||||
|
||||
/**
|
||||
* Экземпляр сервиса списка согласования этой формы.
|
||||
*
|
||||
* listId — партиция КОНКРЕТНОЙ заявки (см. shared/approval/listPartitions.ts).
|
||||
* Не задан — текущая партиция, как раньше. Список согласования ищется только по
|
||||
* имени (нет прямой ссылки на него, в отличие от списка данных), поэтому для
|
||||
* архивной партиции имя приходится резолвить через реестр — если реестр не
|
||||
* знает про такой listId (устаревшая ссылка), резолвер бросит понятную ошибку,
|
||||
* а не тихо откатится на текущую партицию.
|
||||
* Экземпляр сервиса списка согласования этой формы — тонкая обёртка над
|
||||
* approvalServiceFor (см. shared/approval/approvalServiceFactory.ts), под
|
||||
* именем формы для читаемости вызывающего кода.
|
||||
*/
|
||||
export function clearanceApprovalService(listId?: string): ApprovalListService {
|
||||
return new ApprovalListService(listId ? approvalListTitleForListId(FORM_ID, listId) : approvalListTitle(FORM_ID));
|
||||
return approvalServiceFor(FORM_ID, listId);
|
||||
}
|
||||
|
||||
/** Отметки, которые проставляет не инициатор, а профильный согласующий (см. approverRights.ts). */
|
||||
@@ -47,16 +41,10 @@ export async function updateApproverFields(
|
||||
Number.isFinite(amount) && fields.TrainingAmount.trim() !== '' ? amount : null;
|
||||
}
|
||||
if (Object.keys(item).length === 0) return;
|
||||
const list = listId ? getSP().web.getList(listId) : getSP().web.lists.getByTitle(dataListTitle(FORM_ID));
|
||||
const list = resolveDataList(FORM_ID, listId);
|
||||
await list.items.getById(id).update(item);
|
||||
}
|
||||
|
||||
/** email / claims-логин → пользователь сайта (Id для Person-поля + Title для заголовка). */
|
||||
async function ensureEmployee(login: string): Promise<{ Id: number; Title: string }> {
|
||||
const full = login.indexOf('|') >= 0 ? login : `i:0#.f|membership|${login}`;
|
||||
return getSP().web.ensureUser(full);
|
||||
}
|
||||
|
||||
function pad2(n: number): string {
|
||||
return n < 10 ? `0${n}` : `${n}`;
|
||||
}
|
||||
@@ -83,11 +71,7 @@ function buildItem(data: IFormData, status: string, employeeId: number, employee
|
||||
};
|
||||
}
|
||||
|
||||
export interface ISaveResult {
|
||||
id: number;
|
||||
/** Задан только при создании новой заявки (существующий код не меняется). */
|
||||
code?: string;
|
||||
}
|
||||
export type ISaveResult = ISaveItemResult;
|
||||
|
||||
/**
|
||||
* Сохранить обходной лист: содержимое + маршрут (согласование + исполнение).
|
||||
@@ -111,23 +95,10 @@ export async function saveClearanceRequest(
|
||||
status: string,
|
||||
listId?: string
|
||||
): Promise<ISaveResult> {
|
||||
const list = listId ? getSP().web.getList(listId) : getSP().web.lists.getByTitle(dataListTitle(FORM_ID));
|
||||
const employee = await ensureEmployee(data.Employee);
|
||||
const item = buildItem(data, status, employee.Id, employee.Title || data.Employee);
|
||||
|
||||
let id: number;
|
||||
let code: string | undefined;
|
||||
if (existingId) {
|
||||
await list.items.getById(existingId).update(item);
|
||||
id = existingId;
|
||||
} else {
|
||||
const added = await list.items.add(item);
|
||||
id = (added as { Id: number }).Id;
|
||||
// Код известен только после создания элемента (нужен его Id).
|
||||
code = buildRequestUniqueCode(requestTypeId, id, new Date());
|
||||
await list.items.getById(id).update({ RequestUniqueCode: code });
|
||||
}
|
||||
|
||||
await writeRoute(clearanceApprovalService(listId), id, approvers, executors, status);
|
||||
return { id, code };
|
||||
const result = await saveRequestItem(FORM_ID, existingId, listId, requestTypeId, item);
|
||||
await writeRoute(clearanceApprovalService(listId), result.id, approvers, executors, status);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,24 +1,17 @@
|
||||
import { getSP } from '../../shared/pnp/pnpjs';
|
||||
import { ApprovalListService, IApproverToWrite, writeRoute } from '../../shared/approval';
|
||||
import { buildRequestUniqueCode } from '../../shared/requestCode';
|
||||
import { approvalListTitle, approvalListTitleForListId, dataListTitle } from '../../shared/listTitles';
|
||||
import { ApprovalListService, IApproverToWrite, approvalServiceFor, writeRoute } from '../../shared/approval';
|
||||
import { ISaveItemResult, saveRequestItem } from '../../shared/dataList';
|
||||
import { FORM_ID, IFormData } from './model';
|
||||
|
||||
// Числовые поля: пустая строка → null, иначе Number().
|
||||
const NUMBER_FIELDS: (keyof IFormData)[] = ['PpeHeight', 'PpeShoeSize', 'SalaryProbation', 'SalaryAfterProbation'];
|
||||
|
||||
/**
|
||||
* Экземпляр сервиса списка согласования этой формы.
|
||||
*
|
||||
* listId — партиция КОНКРЕТНОЙ заявки (см. shared/approval/listPartitions.ts).
|
||||
* Не задан — текущая партиция, как раньше. Список согласования ищется только по
|
||||
* имени (нет прямой ссылки на него, в отличие от списка данных), поэтому для
|
||||
* архивной партиции имя приходится резолвить через реестр — если реестр не
|
||||
* знает про такой listId (устаревшая ссылка), резолвер бросит понятную ошибку,
|
||||
* а не тихо откатится на текущую партицию.
|
||||
* Экземпляр сервиса списка согласования этой формы — тонкая обёртка над
|
||||
* approvalServiceFor (см. shared/approval/approvalServiceFactory.ts), под
|
||||
* именем формы для читаемости вызывающего кода.
|
||||
*/
|
||||
export function hireApprovalService(listId?: string): ApprovalListService {
|
||||
return new ApprovalListService(listId ? approvalListTitleForListId(FORM_ID, listId) : approvalListTitle(FORM_ID));
|
||||
return approvalServiceFor(FORM_ID, listId);
|
||||
}
|
||||
|
||||
/** Осмысленный заголовок элемента списка (системная колонка Title). */
|
||||
@@ -42,11 +35,7 @@ function buildItem(data: IFormData, status: string): Record<string, unknown> {
|
||||
return item;
|
||||
}
|
||||
|
||||
export interface ISaveResult {
|
||||
id: number;
|
||||
/** Задан только при создании новой заявки (существующий код не меняется). */
|
||||
code?: string;
|
||||
}
|
||||
export type ISaveResult = ISaveItemResult;
|
||||
|
||||
/**
|
||||
* Сохранить заявку «на прием»: контент + маршрут (согласующие + исполнители) со статусом.
|
||||
@@ -72,22 +61,9 @@ export async function saveHireRequest(
|
||||
status: string,
|
||||
listId?: string
|
||||
): Promise<ISaveResult> {
|
||||
const list = listId ? getSP().web.getList(listId) : getSP().web.lists.getByTitle(dataListTitle(FORM_ID));
|
||||
const item = buildItem(data, status);
|
||||
|
||||
let id: number;
|
||||
let code: string | undefined;
|
||||
if (existingId) {
|
||||
await list.items.getById(existingId).update(item);
|
||||
id = existingId;
|
||||
} else {
|
||||
const added = await list.items.add(item);
|
||||
id = (added as { Id: number }).Id;
|
||||
// Код известен только после создания элемента (нужен его Id).
|
||||
code = buildRequestUniqueCode(requestTypeId, id, new Date());
|
||||
await list.items.getById(id).update({ RequestUniqueCode: code });
|
||||
}
|
||||
|
||||
await writeRoute(hireApprovalService(listId), id, approvers, executors, status);
|
||||
return { id, code };
|
||||
const result = await saveRequestItem(FORM_ID, existingId, listId, requestTypeId, item);
|
||||
await writeRoute(hireApprovalService(listId), result.id, approvers, executors, status);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { getSP } from '../../shared/pnp/pnpjs';
|
||||
import { ApprovalListService, IApproverToWrite, writeRoute } from '../../shared/approval';
|
||||
import { buildRequestUniqueCode } from '../../shared/requestCode';
|
||||
import { approvalListTitle, approvalListTitleForListId, dataListTitle } from '../../shared/listTitles';
|
||||
import { ApprovalListService, IApproverToWrite, approvalServiceFor, writeRoute } from '../../shared/approval';
|
||||
import { ISaveItemResult, saveRequestItem } from '../../shared/dataList';
|
||||
import { FORM_ID, IFormData } from './model';
|
||||
|
||||
// Поля-люди (один человек): логин → ensureUser → Id в колонку <Field>Id.
|
||||
@@ -12,17 +11,12 @@ const MULTI_PERSON_FIELDS: (keyof IFormData)[] = ['HiringDecisionMaker'];
|
||||
const NUMBER_FIELDS: (keyof IFormData)[] = ['OpenVacanciesCount', 'RateProbation', 'RateAfterProbation'];
|
||||
|
||||
/**
|
||||
* Экземпляр сервиса списка согласования этой формы.
|
||||
*
|
||||
* listId — партиция КОНКРЕТНОЙ заявки (см. shared/approval/listPartitions.ts).
|
||||
* Не задан — текущая партиция, как раньше. Список согласования ищется только по
|
||||
* имени (нет прямой ссылки на него, в отличие от списка данных), поэтому для
|
||||
* архивной партиции имя приходится резолвить через реестр — если реестр не
|
||||
* знает про такой listId (устаревшая ссылка), резолвер бросит понятную ошибку,
|
||||
* а не тихо откатится на текущую партицию.
|
||||
* Экземпляр сервиса списка согласования этой формы — тонкая обёртка над
|
||||
* approvalServiceFor (см. shared/approval/approvalServiceFactory.ts), под
|
||||
* именем формы для читаемости вызывающего кода.
|
||||
*/
|
||||
export function selectionApprovalService(listId?: string): ApprovalListService {
|
||||
return new ApprovalListService(listId ? approvalListTitleForListId(FORM_ID, listId) : approvalListTitle(FORM_ID));
|
||||
return approvalServiceFor(FORM_ID, listId);
|
||||
}
|
||||
|
||||
/** Логин → числовой Id пользователя на текущем сайте. */
|
||||
@@ -86,11 +80,7 @@ async function applyPeople(item: Record<string, unknown>, data: IFormData): Prom
|
||||
}
|
||||
}
|
||||
|
||||
export interface ISaveResult {
|
||||
id: number;
|
||||
/** Задан только при создании новой заявки (существующий код не меняется). */
|
||||
code?: string;
|
||||
}
|
||||
export type ISaveResult = ISaveItemResult;
|
||||
|
||||
/**
|
||||
* Сохранить заявку «на подбор»: содержимое + маршрут согласования.
|
||||
@@ -114,24 +104,11 @@ export async function saveSelectionRequest(
|
||||
status: string,
|
||||
listId?: string
|
||||
): Promise<ISaveResult> {
|
||||
const list = listId ? getSP().web.getList(listId) : getSP().web.lists.getByTitle(dataListTitle(FORM_ID));
|
||||
const item = buildScalarBody(data);
|
||||
item.RequestStatus = status;
|
||||
await applyPeople(item, data);
|
||||
|
||||
let id: number;
|
||||
let code: string | undefined;
|
||||
if (existingId) {
|
||||
await list.items.getById(existingId).update(item);
|
||||
id = existingId;
|
||||
} else {
|
||||
const added = await list.items.add(item);
|
||||
id = (added as { Id: number }).Id;
|
||||
// Код известен только после создания элемента (нужен его Id).
|
||||
code = buildRequestUniqueCode(requestTypeId, id, new Date());
|
||||
await list.items.getById(id).update({ RequestUniqueCode: code });
|
||||
}
|
||||
|
||||
await writeRoute(selectionApprovalService(listId), id, approvers, [], status);
|
||||
return { id, code };
|
||||
const result = await saveRequestItem(FORM_ID, existingId, listId, requestTypeId, item);
|
||||
await writeRoute(selectionApprovalService(listId), result.id, approvers, [], status);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { getSP } from '../../shared/pnp/pnpjs';
|
||||
import { ApprovalListService, IApproverToWrite, writeRoute } from '../../shared/approval';
|
||||
import { buildRequestUniqueCode } from '../../shared/requestCode';
|
||||
import { approvalListTitle, approvalListTitleForListId, dataListTitle } from '../../shared/listTitles';
|
||||
import { ApprovalListService, IApproverToWrite, approvalServiceFor, writeRoute } from '../../shared/approval';
|
||||
import { ISaveItemResult, resolveDataList, saveRequestItem } from '../../shared/dataList';
|
||||
import { FORM_ID, IFormData } from './model';
|
||||
|
||||
// Префикс уникального кода заявки — буквенная метка «1С» вместо числового id
|
||||
@@ -10,17 +8,12 @@ import { FORM_ID, IFormData } from './model';
|
||||
export const TICKET1C_CODE_PREFIX = '1С';
|
||||
|
||||
/**
|
||||
* Экземпляр сервиса списка согласования этой формы.
|
||||
*
|
||||
* listId — партиция КОНКРЕТНОЙ заявки (см. shared/approval/listPartitions.ts).
|
||||
* Не задан — текущая партиция, как раньше. Список согласования ищется только по
|
||||
* имени (нет прямой ссылки на него, в отличие от списка данных), поэтому для
|
||||
* архивной партиции имя приходится резолвить через реестр — если реестр не
|
||||
* знает про такой listId (устаревшая ссылка), резолвер бросит понятную ошибку,
|
||||
* а не тихо откатится на текущую партицию.
|
||||
* Экземпляр сервиса списка согласования этой формы — тонкая обёртка над
|
||||
* approvalServiceFor (см. shared/approval/approvalServiceFactory.ts), под
|
||||
* именем формы для читаемости вызывающего кода.
|
||||
*/
|
||||
export function ticket1cApprovalService(listId?: string): ApprovalListService {
|
||||
return new ApprovalListService(listId ? approvalListTitleForListId(FORM_ID, listId) : approvalListTitle(FORM_ID));
|
||||
return approvalServiceFor(FORM_ID, listId);
|
||||
}
|
||||
|
||||
/** Поля элемента списка данных из модели формы. TaskName пишется в системную колонку Title. */
|
||||
@@ -36,11 +29,7 @@ function buildItem(data: IFormData, status: string): Record<string, unknown> {
|
||||
return item;
|
||||
}
|
||||
|
||||
export interface ISaveResult {
|
||||
id: number;
|
||||
/** Задан только при создании новой заявки (существующий код не меняется). */
|
||||
code?: string;
|
||||
}
|
||||
export type ISaveResult = ISaveItemResult;
|
||||
|
||||
/**
|
||||
* Сохранить заявку в отдел 1С: содержимое + маршрут согласования.
|
||||
@@ -63,24 +52,11 @@ export async function saveTicketRequest(
|
||||
status: string,
|
||||
listId?: string
|
||||
): Promise<ISaveResult> {
|
||||
const list = listId ? getSP().web.getList(listId) : getSP().web.lists.getByTitle(dataListTitle(FORM_ID));
|
||||
const item = buildItem(data, status);
|
||||
|
||||
let id: number;
|
||||
let code: string | undefined;
|
||||
if (existingId) {
|
||||
await list.items.getById(existingId).update(item);
|
||||
id = existingId;
|
||||
} else {
|
||||
const added = await list.items.add(item);
|
||||
id = (added as { Id: number }).Id;
|
||||
// Код известен только после создания элемента (нужен его Id).
|
||||
code = buildRequestUniqueCode(TICKET1C_CODE_PREFIX, id, new Date());
|
||||
await list.items.getById(id).update({ RequestUniqueCode: code });
|
||||
}
|
||||
|
||||
await writeRoute(ticket1cApprovalService(listId), id, approvers, [], status);
|
||||
return { id, code };
|
||||
const result = await saveRequestItem(FORM_ID, existingId, listId, TICKET1C_CODE_PREFIX, item);
|
||||
await writeRoute(ticket1cApprovalService(listId), result.id, approvers, [], status);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,12 +65,12 @@ export async function saveTicketRequest(
|
||||
* решение согласующего применяется из нескольких вкладок/сессий одновременно.
|
||||
*/
|
||||
export async function getPlannerTaskId(id: number, listId?: string): Promise<string | undefined> {
|
||||
const list = listId ? getSP().web.getList(listId) : getSP().web.lists.getByTitle(dataListTitle(FORM_ID));
|
||||
const list = resolveDataList(FORM_ID, listId);
|
||||
const item = (await list.items.getById(id).select('PlannerTaskId')()) as { PlannerTaskId?: string };
|
||||
return item.PlannerTaskId || undefined;
|
||||
}
|
||||
|
||||
export async function setPlannerTaskId(id: number, taskId: string, listId?: string): Promise<void> {
|
||||
const list = listId ? getSP().web.getList(listId) : getSP().web.lists.getByTitle(dataListTitle(FORM_ID));
|
||||
const list = resolveDataList(FORM_ID, listId);
|
||||
await list.items.getById(id).update({ PlannerTaskId: taskId });
|
||||
}
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
import { getSP } from '../../shared/pnp/pnpjs';
|
||||
import { ApprovalListService, IApproverToWrite, writeRoute } from '../../shared/approval';
|
||||
import { buildRequestUniqueCode } from '../../shared/requestCode';
|
||||
import { ApprovalListService, IApproverToWrite, approvalServiceFor, writeRoute } from '../../shared/approval';
|
||||
import { ISaveItemResult, saveRequestItem } from '../../shared/dataList';
|
||||
import { ILineItem } from '../../shared/ui';
|
||||
import { approvalListTitle, approvalListTitleForListId, dataListTitle } from '../../shared/listTitles';
|
||||
import { FORM_ID, IFormData } from './model';
|
||||
|
||||
/**
|
||||
* Экземпляр сервиса списка согласования этой формы.
|
||||
*
|
||||
* listId — партиция КОНКРЕТНОЙ заявки (см. shared/approval/listPartitions.ts).
|
||||
* Не задан — текущая партиция, как раньше. Список согласования ищется только по
|
||||
* имени (нет прямой ссылки на него, в отличие от списка данных), поэтому для
|
||||
* архивной партиции имя приходится резолвить через реестр — если реестр не
|
||||
* знает про такой listId (устаревшая ссылка), резолвер бросит понятную ошибку,
|
||||
* а не тихо откатится на текущую партицию.
|
||||
* Экземпляр сервиса списка согласования этой формы — тонкая обёртка над
|
||||
* approvalServiceFor (см. shared/approval/approvalServiceFactory.ts), под
|
||||
* именем формы для читаемости вызывающего кода.
|
||||
*/
|
||||
export function tmcTransferApprovalService(listId?: string): ApprovalListService {
|
||||
return new ApprovalListService(listId ? approvalListTitleForListId(FORM_ID, listId) : approvalListTitle(FORM_ID));
|
||||
return approvalServiceFor(FORM_ID, listId);
|
||||
}
|
||||
|
||||
/** Осмысленный заголовок элемента (системная колонка Title). */
|
||||
@@ -43,11 +36,7 @@ function buildItem(data: IFormData, status: string): Record<string, unknown> {
|
||||
return item;
|
||||
}
|
||||
|
||||
export interface ISaveResult {
|
||||
id: number;
|
||||
/** Задан только при создании новой заявки (существующий код не меняется). */
|
||||
code?: string;
|
||||
}
|
||||
export type ISaveResult = ISaveItemResult;
|
||||
|
||||
/**
|
||||
* Сохранить заявку «СЗ на перемещение ТМЦ»: контент + маршрут (согласующие + исполнители).
|
||||
@@ -69,22 +58,9 @@ export async function saveTmcTransferRequest(
|
||||
status: string,
|
||||
listId?: string
|
||||
): Promise<ISaveResult> {
|
||||
const list = listId ? getSP().web.getList(listId) : getSP().web.lists.getByTitle(dataListTitle(FORM_ID));
|
||||
const item = buildItem(data, status);
|
||||
|
||||
let id: number;
|
||||
let code: string | undefined;
|
||||
if (existingId) {
|
||||
await list.items.getById(existingId).update(item);
|
||||
id = existingId;
|
||||
} else {
|
||||
const added = await list.items.add(item);
|
||||
id = (added as { Id: number }).Id;
|
||||
// Код известен только после создания элемента (нужен его Id).
|
||||
code = buildRequestUniqueCode(requestTypeId, id, new Date());
|
||||
await list.items.getById(id).update({ RequestUniqueCode: code });
|
||||
}
|
||||
|
||||
await writeRoute(tmcTransferApprovalService(listId), id, approvers, executors, status);
|
||||
return { id, code };
|
||||
const result = await saveRequestItem(FORM_ID, existingId, listId, requestTypeId, item);
|
||||
await writeRoute(tmcTransferApprovalService(listId), result.id, approvers, executors, status);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,27 +1,15 @@
|
||||
import { getSP } from '../../shared/pnp/pnpjs';
|
||||
import { ApprovalListService, IApproverToWrite, writeRoute } from '../../shared/approval';
|
||||
import { buildRequestUniqueCode } from '../../shared/requestCode';
|
||||
import { approvalListTitle, approvalListTitleForListId, dataListTitle } from '../../shared/listTitles';
|
||||
import { ApprovalListService, IApproverToWrite, approvalServiceFor, writeRoute } from '../../shared/approval';
|
||||
import { ISaveItemResult, saveRequestItem } from '../../shared/dataList';
|
||||
import { ensureEmployee } from '../../shared/employee';
|
||||
import { FORM_ID, IFormData } from './model';
|
||||
|
||||
/**
|
||||
* Экземпляр сервиса списка согласования этой формы.
|
||||
*
|
||||
* listId — партиция КОНКРЕТНОЙ заявки (см. shared/approval/listPartitions.ts).
|
||||
* Не задан — текущая партиция, как раньше. Список согласования ищется только по
|
||||
* имени (нет прямой ссылки на него, в отличие от списка данных), поэтому для
|
||||
* архивной партиции имя приходится резолвить через реестр — если реестр не
|
||||
* знает про такой listId (устаревшая ссылка), резолвер бросит понятную ошибку,
|
||||
* а не тихо откатится на текущую партицию.
|
||||
* Экземпляр сервиса списка согласования этой формы — тонкая обёртка над
|
||||
* approvalServiceFor (см. shared/approval/approvalServiceFactory.ts), под
|
||||
* именем формы для читаемости вызывающего кода.
|
||||
*/
|
||||
export function unpaidLeaveApprovalService(listId?: string): ApprovalListService {
|
||||
return new ApprovalListService(listId ? approvalListTitleForListId(FORM_ID, listId) : approvalListTitle(FORM_ID));
|
||||
}
|
||||
|
||||
/** email / claims-логин → пользователь сайта (Id для Person-поля + Title для заголовка). */
|
||||
async function ensureUser(login: string): Promise<{ Id: number; Title: string }> {
|
||||
const full = login.indexOf('|') >= 0 ? login : `i:0#.f|membership|${login}`;
|
||||
return getSP().web.ensureUser(full);
|
||||
return approvalServiceFor(FORM_ID, listId);
|
||||
}
|
||||
|
||||
function pad2(n: number): string {
|
||||
@@ -64,11 +52,7 @@ function buildItem(
|
||||
};
|
||||
}
|
||||
|
||||
export interface ISaveResult {
|
||||
id: number;
|
||||
/** Задан только при создании новой заявки (существующий код не меняется). */
|
||||
code?: string;
|
||||
}
|
||||
export type ISaveResult = ISaveItemResult;
|
||||
|
||||
/**
|
||||
* Сохранить заявку без содержания: содержимое + маршрут согласования.
|
||||
@@ -89,25 +73,12 @@ export async function saveUnpaidLeaveRequest(
|
||||
status: string,
|
||||
listId?: string
|
||||
): Promise<ISaveResult> {
|
||||
const list = listId ? getSP().web.getList(listId) : getSP().web.lists.getByTitle(dataListTitle(FORM_ID));
|
||||
const employee = await ensureUser(data.Employee);
|
||||
const employee = await ensureEmployee(data.Employee);
|
||||
// Замещающий необязателен: резолвим только если выбран.
|
||||
const substituteId = data.Substitute ? (await ensureUser(data.Substitute)).Id : undefined;
|
||||
const substituteId = data.Substitute ? (await ensureEmployee(data.Substitute)).Id : undefined;
|
||||
const item = buildItem(data, status, employee.Id, employee.Title || data.Employee, substituteId);
|
||||
|
||||
let id: number;
|
||||
let code: string | undefined;
|
||||
if (existingId) {
|
||||
await list.items.getById(existingId).update(item);
|
||||
id = existingId;
|
||||
} else {
|
||||
const added = await list.items.add(item);
|
||||
id = (added as { Id: number }).Id;
|
||||
// Код известен только после создания элемента (нужен его Id).
|
||||
code = buildRequestUniqueCode(requestTypeId, id, new Date());
|
||||
await list.items.getById(id).update({ RequestUniqueCode: code });
|
||||
}
|
||||
|
||||
await writeRoute(unpaidLeaveApprovalService(listId), id, approvers, [], status);
|
||||
return { id, code };
|
||||
const result = await saveRequestItem(FORM_ID, existingId, listId, requestTypeId, item);
|
||||
await writeRoute(unpaidLeaveApprovalService(listId), result.id, approvers, [], status);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,27 +1,15 @@
|
||||
import { getSP } from '../../shared/pnp/pnpjs';
|
||||
import { ApprovalListService, IApproverToWrite, writeRoute } from '../../shared/approval';
|
||||
import { buildRequestUniqueCode } from '../../shared/requestCode';
|
||||
import { approvalListTitle, approvalListTitleForListId, dataListTitle } from '../../shared/listTitles';
|
||||
import { ApprovalListService, IApproverToWrite, approvalServiceFor, writeRoute } from '../../shared/approval';
|
||||
import { ISaveItemResult, saveRequestItem } from '../../shared/dataList';
|
||||
import { ensureEmployee } from '../../shared/employee';
|
||||
import { FORM_ID, IFormData } from './model';
|
||||
|
||||
/**
|
||||
* Экземпляр сервиса списка согласования этой формы.
|
||||
*
|
||||
* listId — партиция КОНКРЕТНОЙ заявки (см. shared/approval/listPartitions.ts).
|
||||
* Не задан — текущая партиция, как раньше. Список согласования ищется только по
|
||||
* имени (нет прямой ссылки на него, в отличие от списка данных), поэтому для
|
||||
* архивной партиции имя приходится резолвить через реестр — если реестр не
|
||||
* знает про такой listId (устаревшая ссылка), резолвер бросит понятную ошибку,
|
||||
* а не тихо откатится на текущую партицию.
|
||||
* Экземпляр сервиса списка согласования этой формы — тонкая обёртка над
|
||||
* approvalServiceFor (см. shared/approval/approvalServiceFactory.ts), под
|
||||
* именем формы для читаемости вызывающего кода.
|
||||
*/
|
||||
export function vacationApprovalService(listId?: string): ApprovalListService {
|
||||
return new ApprovalListService(listId ? approvalListTitleForListId(FORM_ID, listId) : approvalListTitle(FORM_ID));
|
||||
}
|
||||
|
||||
/** email / claims-логин → пользователь сайта (Id для Person-поля + Title для заголовка). */
|
||||
async function ensureEmployee(login: string): Promise<{ Id: number; Title: string }> {
|
||||
const full = login.indexOf('|') >= 0 ? login : `i:0#.f|membership|${login}`;
|
||||
return getSP().web.ensureUser(full);
|
||||
return approvalServiceFor(FORM_ID, listId);
|
||||
}
|
||||
|
||||
function pad2(n: number): string {
|
||||
@@ -55,11 +43,7 @@ function buildItem(data: IFormData, status: string, employeeId: number, employee
|
||||
};
|
||||
}
|
||||
|
||||
export interface ISaveResult {
|
||||
id: number;
|
||||
/** Задан только при создании новой заявки (существующий код не меняется). */
|
||||
code?: string;
|
||||
}
|
||||
export type ISaveResult = ISaveItemResult;
|
||||
|
||||
/**
|
||||
* Сохранить заявку на отпуск: содержимое + маршрут согласования.
|
||||
@@ -83,23 +67,10 @@ export async function saveVacationRequest(
|
||||
status: string,
|
||||
listId?: string
|
||||
): Promise<ISaveResult> {
|
||||
const list = listId ? getSP().web.getList(listId) : getSP().web.lists.getByTitle(dataListTitle(FORM_ID));
|
||||
const employee = await ensureEmployee(data.Employee);
|
||||
const item = buildItem(data, status, employee.Id, employee.Title || data.Employee);
|
||||
|
||||
let id: number;
|
||||
let code: string | undefined;
|
||||
if (existingId) {
|
||||
await list.items.getById(existingId).update(item);
|
||||
id = existingId;
|
||||
} else {
|
||||
const added = await list.items.add(item);
|
||||
id = (added as { Id: number }).Id;
|
||||
// Код известен только после создания элемента (нужен его Id).
|
||||
code = buildRequestUniqueCode(requestTypeId, id, new Date());
|
||||
await list.items.getById(id).update({ RequestUniqueCode: code });
|
||||
}
|
||||
|
||||
await writeRoute(vacationApprovalService(listId), id, approvers, [], status);
|
||||
return { id, code };
|
||||
const result = await saveRequestItem(FORM_ID, existingId, listId, requestTypeId, item);
|
||||
await writeRoute(vacationApprovalService(listId), result.id, approvers, [], status);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,14 @@
|
||||
import { getSP } from '../../shared/pnp/pnpjs';
|
||||
import { ApprovalListService, IApproverToWrite, writeRoute } from '../../shared/approval';
|
||||
import { buildRequestUniqueCode } from '../../shared/requestCode';
|
||||
import { approvalListTitle, approvalListTitleForListId, dataListTitle } from '../../shared/listTitles';
|
||||
import { ApprovalListService, IApproverToWrite, approvalServiceFor, writeRoute } from '../../shared/approval';
|
||||
import { ISaveItemResult, resolveDataList, saveRequestItem } from '../../shared/dataList';
|
||||
import { FORM_ID, IFormData } from './model';
|
||||
|
||||
/**
|
||||
* Экземпляр сервиса списка согласования этой формы.
|
||||
*
|
||||
* listId — партиция КОНКРЕТНОЙ заявки (см. shared/approval/listPartitions.ts).
|
||||
* Не задан — текущая партиция, как раньше. Список согласования ищется только по
|
||||
* имени (нет прямой ссылки на него, в отличие от списка данных), поэтому для
|
||||
* архивной партиции имя приходится резолвить через реестр — если реестр не
|
||||
* знает про такой listId (устаревшая ссылка), резолвер бросит понятную ошибку,
|
||||
* а не тихо откатится на текущую партицию.
|
||||
* Экземпляр сервиса списка согласования этой формы — тонкая обёртка над
|
||||
* approvalServiceFor (см. shared/approval/approvalServiceFactory.ts), под
|
||||
* именем формы для читаемости вызывающего кода.
|
||||
*/
|
||||
export function workplaceApprovalService(listId?: string): ApprovalListService {
|
||||
return new ApprovalListService(listId ? approvalListTitleForListId(FORM_ID, listId) : approvalListTitle(FORM_ID));
|
||||
return approvalServiceFor(FORM_ID, listId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,7 +23,7 @@ export async function updateReviewFields(
|
||||
fields: { Software: string; AccessRights: string },
|
||||
listId?: string
|
||||
): Promise<void> {
|
||||
const list = listId ? getSP().web.getList(listId) : getSP().web.lists.getByTitle(dataListTitle(FORM_ID));
|
||||
const list = resolveDataList(FORM_ID, listId);
|
||||
await list.items.getById(id).update({
|
||||
Software: fields.Software === '' ? null : fields.Software,
|
||||
AccessRights: fields.AccessRights === '' ? null : fields.AccessRights,
|
||||
@@ -62,11 +55,7 @@ function buildItem(data: IFormData, status: string, sourceHireId?: number): Reco
|
||||
return item;
|
||||
}
|
||||
|
||||
export interface ISaveResult {
|
||||
id: number;
|
||||
/** Задан только при создании новой заявки (существующий код не меняется). */
|
||||
code?: string;
|
||||
}
|
||||
export type ISaveResult = ISaveItemResult;
|
||||
|
||||
/**
|
||||
* Сохранить заявку «орг. рабочего места»: контент + маршрут (согласующие + исполнители).
|
||||
@@ -92,22 +81,9 @@ export async function saveWorkplaceRequest(
|
||||
sourceHireId?: number,
|
||||
listId?: string
|
||||
): Promise<ISaveResult> {
|
||||
const list = listId ? getSP().web.getList(listId) : getSP().web.lists.getByTitle(dataListTitle(FORM_ID));
|
||||
const item = buildItem(data, status, sourceHireId);
|
||||
|
||||
let id: number;
|
||||
let code: string | undefined;
|
||||
if (existingId) {
|
||||
await list.items.getById(existingId).update(item);
|
||||
id = existingId;
|
||||
} else {
|
||||
const added = await list.items.add(item);
|
||||
id = (added as { Id: number }).Id;
|
||||
// Код известен только после создания элемента (нужен его Id).
|
||||
code = buildRequestUniqueCode(requestTypeId, id, new Date());
|
||||
await list.items.getById(id).update({ RequestUniqueCode: code });
|
||||
}
|
||||
|
||||
await writeRoute(workplaceApprovalService(listId), id, approvers, executors, status);
|
||||
return { id, code };
|
||||
const result = await saveRequestItem(FORM_ID, existingId, listId, requestTypeId, item);
|
||||
await writeRoute(workplaceApprovalService(listId), result.id, approvers, executors, status);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { RequestTypeId } from '../../models/types';
|
||||
import { approvalListTitle, approvalListTitleForListId } from '../listTitles';
|
||||
import { ApprovalListService } from './approvalList.service';
|
||||
|
||||
/**
|
||||
* Экземпляр сервиса списка согласования формы.
|
||||
*
|
||||
* listId — партиция КОНКРЕТНОЙ заявки (см. shared/approval/listPartitions.ts).
|
||||
* Не задан — текущая партиция, как раньше. Список согласования ищется только по
|
||||
* имени (нет прямой ссылки на него, в отличие от списка данных), поэтому для
|
||||
* архивной партиции имя приходится резолвить через реестр — если реестр не
|
||||
* знает про такой listId (устаревшая ссылка), резолвер бросит понятную ошибку,
|
||||
* а не тихо откатится на текущую партицию.
|
||||
*
|
||||
* Раньше каждая форма объявляла свою «xApprovalService» с телом-копией этой
|
||||
* функции (8 одинаковых реализаций) — оставлены только тонкие обёртки под
|
||||
* именем формы (см. submit.ts каждой формы), чтобы вызывающий код не терял
|
||||
* читаемость («vacationApprovalService» вместо безликого параметра formId).
|
||||
*/
|
||||
export function approvalServiceFor(formId: RequestTypeId, listId?: string): ApprovalListService {
|
||||
return new ApprovalListService(
|
||||
listId ? approvalListTitleForListId(formId, listId) : approvalListTitle(formId)
|
||||
);
|
||||
}
|
||||
@@ -42,6 +42,7 @@ export { useRequestTypeId } from './useRequestTypeId';
|
||||
export type { IRequestTypeIdState } from './useRequestTypeId';
|
||||
export { ApprovalListService } from './approvalList.service';
|
||||
export type { IApproverToWrite } from './approvalList.service';
|
||||
export { approvalServiceFor } from './approvalServiceFactory';
|
||||
export { ApproverService } from './approverDecision.service';
|
||||
export type { ApproverDecision, IPendingRow } from './approverDecision.service';
|
||||
export { getPlannedMatrix, writeApprovers, writeRoute } from './approvalSubmission.service';
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import '@pnp/sp/webs';
|
||||
import '@pnp/sp/lists';
|
||||
import '@pnp/sp/items';
|
||||
import { IList } from '@pnp/sp/lists';
|
||||
import { getSP } from './pnp/pnpjs';
|
||||
import { buildRequestUniqueCode } from './requestCode';
|
||||
import { dataListTitle } from './listTitles';
|
||||
import { RequestTypeId } from '../models/types';
|
||||
|
||||
/**
|
||||
* Список данных, в который фактически пишем: listId (конкретная партиция —
|
||||
* либо ОБНОВЛЯЕМОЙ заявки, либо только что резолвленная текущая, см.
|
||||
* getCurrentListPartition) или, если он не задан, список по конвенции имён
|
||||
* (см. dataListTitle) — тот же откат, что действовал до партиционирования.
|
||||
*
|
||||
* Раньше эта строка (`listId ? getSP().web.getList(listId) : ...`) была
|
||||
* скопирована в save/update-функции каждой формы и в submit.ts отдельных
|
||||
* форм (Ticket1C — Planner, Clearance — updateApproverFields) — 8+ мест с
|
||||
* одинаковым кодом. Расхождение здесь означало бы, что часть операций тихо
|
||||
* пишет не в ту партицию.
|
||||
*/
|
||||
export function resolveDataList(formId: RequestTypeId, listId?: string): IList {
|
||||
return listId ? getSP().web.getList(listId) : getSP().web.lists.getByTitle(dataListTitle(formId));
|
||||
}
|
||||
|
||||
export interface ISaveItemResult {
|
||||
id: number;
|
||||
/** Задан только при создании новой заявки (существующий код не меняется). */
|
||||
code?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Общее ядро сохранения элемента списка данных: резолв списка, создание/обновление,
|
||||
* генерация уникального кода заявки. Вынесено из saveXRequest каждой формы — сам
|
||||
* набор полей (buildItem) и работа с маршрутом (writeRoute) остаются у формы,
|
||||
* форма отличается только ими.
|
||||
*
|
||||
* existingId задан → обновляем ту же запись, иначе создаём новую.
|
||||
* codePrefix — обычно числовой RequestTypeId, но может быть строкой-меткой
|
||||
* (см. buildRequestUniqueCode, TICKET1C_CODE_PREFIX).
|
||||
*/
|
||||
export async function saveRequestItem(
|
||||
formId: RequestTypeId,
|
||||
existingId: number | undefined,
|
||||
listId: string | undefined,
|
||||
codePrefix: number | string,
|
||||
item: Record<string, unknown>
|
||||
): Promise<ISaveItemResult> {
|
||||
const list = resolveDataList(formId, listId);
|
||||
|
||||
if (existingId) {
|
||||
await list.items.getById(existingId).update(item);
|
||||
return { id: existingId };
|
||||
}
|
||||
|
||||
const added = await list.items.add(item);
|
||||
const id = (added as { Id: number }).Id;
|
||||
// Код известен только после создания элемента (нужен его Id).
|
||||
const code = buildRequestUniqueCode(codePrefix, id, new Date());
|
||||
await list.items.getById(id).update({ RequestUniqueCode: code });
|
||||
return { id, code };
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import '@pnp/sp/webs';
|
||||
import '@pnp/sp/site-users/web';
|
||||
import { getSP } from './pnp/pnpjs';
|
||||
|
||||
/**
|
||||
* email / claims-логин → пользователь сайта (Id для Person-поля + Title для заголовка).
|
||||
* Было скопировано в submit.ts нескольких форм с сотрудником-инициатором
|
||||
* (Vacation, Clearance) — вынесено сюда, чтобы не разойтись при правке.
|
||||
*/
|
||||
export async function ensureEmployee(login: string): Promise<{ Id: number; Title: string }> {
|
||||
const full = login.indexOf('|') >= 0 ? login : `i:0#.f|membership|${login}`;
|
||||
return getSP().web.ensureUser(full);
|
||||
}
|
||||
Reference in New Issue
Block a user