feat(oid): импорт работ из заявки на услуги на шаге 1
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+112
@@ -0,0 +1,112 @@
|
||||
@import '../common/theme';
|
||||
|
||||
.state {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 16px 0;
|
||||
color: $muted;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.searchBox {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.hint {
|
||||
margin-bottom: 8px;
|
||||
font-size: 12px;
|
||||
color: $muted;
|
||||
}
|
||||
|
||||
.list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
width: 100%;
|
||||
max-height: 50vh;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid $border-strong;
|
||||
border-radius: 4px;
|
||||
background: $surface;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 8px 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: $hover;
|
||||
}
|
||||
}
|
||||
|
||||
.itemMain {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.itemCode {
|
||||
font-weight: 600;
|
||||
color: $text;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.itemActivity {
|
||||
color: $text;
|
||||
font-size: 13px;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.itemMeta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
font-size: 12px;
|
||||
color: $muted;
|
||||
}
|
||||
|
||||
// ─── Статус-пилюля ───────────────────────────────────────────────────────────
|
||||
.pill {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pillOk {
|
||||
color: $ok;
|
||||
background: $ok-bg;
|
||||
}
|
||||
|
||||
.pillAccent {
|
||||
color: $accent-dark;
|
||||
background: $accent-bg;
|
||||
}
|
||||
|
||||
.pillWarn {
|
||||
color: $warn-fg;
|
||||
background: $warn-bg;
|
||||
}
|
||||
|
||||
.pillDanger {
|
||||
color: $danger;
|
||||
background: $danger-bg;
|
||||
}
|
||||
|
||||
.pillMuted {
|
||||
color: $muted;
|
||||
background: $head-bg;
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
import * as React from "react";
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
import { Dialog, TextInput, Spinner, Alert } from "@tnsu/ui-kit";
|
||||
import type { ServiceRequest } from "../types/domain";
|
||||
import { OidService } from "../../services/OidService";
|
||||
import styles from "./RequestPickerDialog.module.scss";
|
||||
|
||||
interface Props {
|
||||
service: OidService;
|
||||
onPick: (request: ServiceRequest) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
// Сколько последних заявок показывать без поиска (список может быть очень большим).
|
||||
const LATEST_LIMIT = 5;
|
||||
|
||||
function positionsCount(request: ServiceRequest): number {
|
||||
return request.groups.reduce((sum, g) => sum + g.positions.length, 0);
|
||||
}
|
||||
|
||||
function statusPillClass(status: string): string {
|
||||
switch (status) {
|
||||
case "Согласовано":
|
||||
return styles.pillOk;
|
||||
case "Выполнение":
|
||||
return styles.pillAccent;
|
||||
case "На согласовании":
|
||||
case "Возвращена на доработку":
|
||||
return styles.pillWarn;
|
||||
case "Отклонено":
|
||||
return styles.pillDanger;
|
||||
default:
|
||||
return styles.pillMuted; // Черновик, Закрыта, прочее
|
||||
}
|
||||
}
|
||||
|
||||
// Диалог выбора заявки на услуги (Шаг 1 ОИД): поиск + список (по умолчанию 5 последних).
|
||||
// Выбор заявки заполняет форму работами из неё.
|
||||
export const RequestPickerDialog: React.FC<Props> = ({ service, onPick, onClose }) => {
|
||||
const [requests, setRequests] = useState<ServiceRequest[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [query, setQuery] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
setIsLoading(true);
|
||||
service
|
||||
.fetchServiceRequests()
|
||||
.then((list) => { if (!cancelled) setRequests(list); })
|
||||
.catch((e) => { if (!cancelled) setError((e as Error).message || "Ошибка загрузки заявок"); })
|
||||
.finally(() => { if (!cancelled) setIsLoading(false); });
|
||||
return () => { cancelled = true; };
|
||||
}, [service]);
|
||||
|
||||
const isSearching = query.trim().length > 0;
|
||||
const filtered = useMemo(() => {
|
||||
const q = query.trim().toLowerCase();
|
||||
if (!q) return requests.slice(0, LATEST_LIMIT);
|
||||
return requests.filter(
|
||||
(r) =>
|
||||
r.code.toLowerCase().includes(q) ||
|
||||
r.activityName.toLowerCase().includes(q) ||
|
||||
r.projectName.toLowerCase().includes(q),
|
||||
);
|
||||
}, [requests, query]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open
|
||||
title="Выбор заявки на услуги"
|
||||
subText="Выберите заявку — её проект, работы и позиции заполнят форму."
|
||||
onDismiss={onClose}
|
||||
width="lg"
|
||||
>
|
||||
{isLoading && (
|
||||
<div className={styles.state}>
|
||||
<Spinner size="small" /> Загрузка заявок…
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isLoading && error && <Alert variant="err">{error}</Alert>}
|
||||
|
||||
{!isLoading && !error && requests.length === 0 && (
|
||||
<div className={styles.state}>Заявки не найдены</div>
|
||||
)}
|
||||
|
||||
{!isLoading && !error && requests.length > 0 && (
|
||||
<>
|
||||
<div className={styles.searchBox}>
|
||||
<TextInput
|
||||
value={query}
|
||||
onChange={setQuery}
|
||||
placeholder="Поиск по коду, наименованию работ или проекту…"
|
||||
ariaLabel="Поиск заявки"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{!isSearching && requests.length > LATEST_LIMIT && (
|
||||
<div className={styles.hint}>
|
||||
Показаны {LATEST_LIMIT} последних — используйте поиск, чтобы найти остальные
|
||||
</div>
|
||||
)}
|
||||
|
||||
{filtered.length === 0 ? (
|
||||
<div className={styles.state}>Ничего не найдено</div>
|
||||
) : (
|
||||
<ul className={styles.list}>
|
||||
{filtered.map((r) => (
|
||||
<li key={r.id} className={styles.item} onClick={() => onPick(r)}>
|
||||
<div className={styles.itemMain}>
|
||||
<span className={styles.itemCode}>{r.code || "б/н"}</span>
|
||||
<span className={styles.itemActivity}>{r.activityName || "—"}</span>
|
||||
</div>
|
||||
<div className={styles.itemMeta}>
|
||||
<span className={`${styles.pill} ${statusPillClass(r.status)}`}>
|
||||
{r.status || "—"}
|
||||
</span>
|
||||
<span>{r.projectName || "—"}</span>
|
||||
<span>позиций {positionsCount(r)}</span>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
+28
@@ -6,6 +6,34 @@
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.importRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.importHint {
|
||||
font-size: 13px;
|
||||
color: $muted;
|
||||
}
|
||||
|
||||
.sourceRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.sourceLink {
|
||||
font-size: 13px;
|
||||
color: $text;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.workNameRow {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
+43
-1
@@ -4,6 +4,7 @@ import type { OIDFormData, FormStep } from "../types/domain";
|
||||
import { OidService } from "../../services/OidService";
|
||||
import { useStep1WorksInput } from "./useStep1WorksInput";
|
||||
import { ComboField } from "./ComboField";
|
||||
import { RequestPickerDialog } from "./RequestPickerDialog";
|
||||
import styles from "./Step1WorksInput.module.scss";
|
||||
|
||||
interface Props {
|
||||
@@ -17,11 +18,52 @@ interface Props {
|
||||
// Шаг 1 «Ввод работ» (ручной ввод групп работ, не список потребностей — своя доменка ОИД).
|
||||
// Логика — в useStep1WorksInput; здесь только разметка.
|
||||
export const Step1WorksInput: React.FC<Props> = (props) => {
|
||||
const { isReadOnly = false } = props;
|
||||
const { isReadOnly = false, service } = props;
|
||||
const a = useStep1WorksInput(props);
|
||||
const [showPicker, setShowPicker] = React.useState(false);
|
||||
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
{!isReadOnly && (
|
||||
<div className={styles.importRow}>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => setShowPicker(true)}
|
||||
disabled={a.isSaving || a.isCreatingOid}
|
||||
>
|
||||
Выбрать из заявки
|
||||
</Button>
|
||||
<span className={styles.importHint}>
|
||||
Заполнить работы из заявки на услуги (или введите вручную ниже)
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{a.sourceRequest?.url && (
|
||||
<div className={styles.sourceRow}>
|
||||
<span className={styles.importHint}>Источник:</span>
|
||||
<a
|
||||
className={styles.sourceLink}
|
||||
href={a.sourceRequest.url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Заявка {a.sourceRequest.code || ""} ↗
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showPicker && (
|
||||
<RequestPickerDialog
|
||||
service={service}
|
||||
onClose={() => setShowPicker(false)}
|
||||
onPick={(req) => {
|
||||
a.applyRequest(req);
|
||||
setShowPicker(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className={styles.workNameRow}>
|
||||
<FormField label="Проект" required>
|
||||
<ComboField
|
||||
|
||||
+47
-2
@@ -1,6 +1,6 @@
|
||||
import * as React from "react";
|
||||
import { useState, useCallback, useRef, useEffect } from "react";
|
||||
import type { OIDFormData, PositionGroup, OIDPosition, FormStep } from "../types/domain";
|
||||
import { useState, useCallback, useRef, useEffect, useMemo } from "react";
|
||||
import type { OIDFormData, PositionGroup, OIDPosition, ServiceRequest, FormStep } from "../types/domain";
|
||||
import { OidService } from "../../services/OidService";
|
||||
|
||||
export interface ComboOption {
|
||||
@@ -254,6 +254,36 @@ export function useStep1WorksInput(args: UseStep1WorksInputArgs) {
|
||||
[formData.oid, onSave, service],
|
||||
);
|
||||
|
||||
// Заполнить форму данными выбранной заявки (проект, наименование работ, группы/позиции).
|
||||
// Ручной ввод не трогаем — это отдельное действие «Выбрать из заявки». Позиции получают
|
||||
// свежие id и requestCode/requestUrl из заявки (чтобы записались в «Позиции СТ ОИД»).
|
||||
const applyRequest = useCallback(
|
||||
(req: ServiceRequest): void => {
|
||||
dirtyRef.current = true;
|
||||
setProject(req.projectName);
|
||||
setProjectOptions((prev) =>
|
||||
prev.some((o) => o.key === req.projectName)
|
||||
? prev
|
||||
: [{ key: req.projectName, text: req.projectName }, ...prev],
|
||||
);
|
||||
setWorkName(req.activityName);
|
||||
|
||||
const groupsFromReq: PositionGroup[] = req.groups.map((g) => ({
|
||||
name: g.name,
|
||||
positions: g.positions.map((p) => ({ ...p, id: genId(), spId: undefined })),
|
||||
}));
|
||||
setGroups(groupsFromReq.length > 0 ? groupsFromReq : [makeEmptyGroup()]);
|
||||
|
||||
const oid = formData.oid;
|
||||
if (oid) {
|
||||
onSave({ oid: { ...oid, project: req.projectName } });
|
||||
service.updateOidProject(oid.id, req.projectName).catch(() => undefined);
|
||||
}
|
||||
markDirtyAndSync();
|
||||
},
|
||||
[formData.oid, onSave, service, markDirtyAndSync],
|
||||
);
|
||||
|
||||
const updateGroupName = useCallback((gIdx: number, name: string): void => {
|
||||
setGroups((prev) =>
|
||||
prev.map((g, i) =>
|
||||
@@ -328,6 +358,19 @@ export function useStep1WorksInput(args: UseStep1WorksInputArgs) {
|
||||
markDirtyAndSync();
|
||||
}, []);
|
||||
|
||||
// Заявка-источник (для ссылки на форме): берём из первой позиции, где проставлены
|
||||
// requestCode/requestUrl — работает и после импорта, и после перезагрузки из SP.
|
||||
const sourceRequest = useMemo(() => {
|
||||
for (const g of groups) {
|
||||
for (const p of g.positions) {
|
||||
if (p.requestCode || p.requestUrl) {
|
||||
return { code: p.requestCode ?? "", url: p.requestUrl ?? "" };
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}, [groups]);
|
||||
|
||||
const validate = (): boolean => {
|
||||
if (!project.trim()) {
|
||||
setValidationError("Выберите проект");
|
||||
@@ -434,10 +477,12 @@ export function useStep1WorksInput(args: UseStep1WorksInputArgs) {
|
||||
saveError,
|
||||
validationError,
|
||||
unitOptions,
|
||||
sourceRequest,
|
||||
dirtyRef,
|
||||
loadProjects,
|
||||
handleWorkNameBlur,
|
||||
handleProjectChange,
|
||||
applyRequest,
|
||||
updateGroupName,
|
||||
updatePosition,
|
||||
addGroup,
|
||||
|
||||
@@ -30,6 +30,22 @@ export interface OIDPosition {
|
||||
quantity: number;
|
||||
priceSmeta?: number;
|
||||
summaSmeta?: number;
|
||||
requestCode?: string; // номер заявки-источника (SP-поле RequestCode)
|
||||
requestUrl?: string; // ссылка на заявку в SP (SP-поле RequestUrl)
|
||||
}
|
||||
|
||||
// Заявка на услуги из SP-списка «Заявка на услуги 1 — данные» (/sites/requests).
|
||||
// Источник для импорта работ на Шаге 1 ОИД.
|
||||
export interface ServiceRequest {
|
||||
id: number;
|
||||
code: string; // RequestUniqueCode
|
||||
status: string; // RequestStatus
|
||||
projectName: string; // ProjectName
|
||||
activityName: string; // ActivityName
|
||||
requestDate: string; // RequestDate
|
||||
requesterName: string; // RequesterName
|
||||
url: string; // ссылка на форму просмотра элемента заявки
|
||||
groups: PositionGroup[]; // распарсено из WorkVolumesJson (позиции уже с requestCode/requestUrl)
|
||||
}
|
||||
|
||||
export interface Supplier {
|
||||
|
||||
@@ -7,6 +7,7 @@ import type {
|
||||
OIDStatus,
|
||||
OIDPosition,
|
||||
PositionGroup,
|
||||
ServiceRequest,
|
||||
Supplier,
|
||||
SupplierOffer,
|
||||
SupplierCriteria,
|
||||
@@ -18,6 +19,13 @@ import type {
|
||||
|
||||
const SITE_URL = "https://tnsukz.sharepoint.com/sites/sed";
|
||||
|
||||
// Список заявок на услуги (источник импорта работ на Шаге 1). Отдельный сайт, тот же тенант.
|
||||
const REQUESTS_SITE_URL = "https://tnsukz.sharepoint.com/sites/requests";
|
||||
const REQUESTS_LIST_TITLE = "Заявка на услуги 1 — данные";
|
||||
// Ссылка на заявку: страница портала заявок, rpid = Id элемента списка.
|
||||
const REQUEST_LINK_BASE =
|
||||
"https://tnsukz.sharepoint.com/sites/requests/SitePages/Портал-Заявок.aspx?rpmode=approve&rptype=wh-service-request&rpid=";
|
||||
|
||||
// "Единицы измерения" lives on the construction site collection (same tenant).
|
||||
const UNITS_SITE_URL = "https://tnsukz.sharepoint.com/sites/stroy";
|
||||
const UNITS_LIST_TITLE = "Единицы измерения";
|
||||
@@ -73,6 +81,63 @@ function listUrl(title: string): string {
|
||||
return `${SITE_URL}/_api/web/lists/getbytitle('${encodeURIComponent(title)}')`;
|
||||
}
|
||||
|
||||
function reqGenId(): string {
|
||||
return Math.random().toString(36).slice(2) + Date.now().toString(36);
|
||||
}
|
||||
|
||||
// qty/rate в JSON заявки — строки, бывают с пробелами и запятой ("2000 ", "5 000,5").
|
||||
function reqToNum(v: unknown): number {
|
||||
const n = parseFloat(String(v ?? "").trim().replace(/\s/g, "").replace(",", "."));
|
||||
return isNaN(n) ? 0 : n;
|
||||
}
|
||||
|
||||
interface WorkVolumeItem {
|
||||
description?: string;
|
||||
unit?: string;
|
||||
qty?: string | number;
|
||||
rate?: string | number;
|
||||
}
|
||||
interface WorkVolumeGroup {
|
||||
name?: string;
|
||||
items?: WorkVolumeItem[];
|
||||
}
|
||||
|
||||
// Парсит WorkVolumesJson заявки в группы/позиции формы ОИД.
|
||||
// Формат: {"groups":[{"name":"...","items":[{"description","unit","qty","rate"}]}],"total":N}
|
||||
function parseWorkVolumes(raw: string, requestCode: string, requestUrl: string): PositionGroup[] {
|
||||
if (!raw || !raw.trim()) return [];
|
||||
let data: { groups?: WorkVolumeGroup[] };
|
||||
try {
|
||||
data = JSON.parse(raw);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
const groups = Array.isArray(data.groups) ? data.groups : [];
|
||||
return groups.map((g) => {
|
||||
const name = String(g.name ?? "").trim();
|
||||
const items = Array.isArray(g.items) ? g.items : [];
|
||||
return {
|
||||
name,
|
||||
positions: items.map((it): OIDPosition => {
|
||||
const quantity = reqToNum(it.qty);
|
||||
const price = reqToNum(it.rate);
|
||||
return {
|
||||
id: reqGenId(),
|
||||
parentGroup: name,
|
||||
position: String(it.description ?? "").trim(),
|
||||
positionType: "Услуга",
|
||||
unit: String(it.unit ?? "").trim(),
|
||||
quantity,
|
||||
priceSmeta: price || undefined,
|
||||
summaSmeta: price && quantity ? price * quantity : undefined,
|
||||
requestCode,
|
||||
requestUrl,
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export class OidService {
|
||||
private readonly context: WebPartContext;
|
||||
private readonly baseUrl: string;
|
||||
@@ -245,6 +310,48 @@ export class OidService {
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Заявки на услуги (импорт работ на Шаге 1) ──────────────────────────────
|
||||
|
||||
// Читает заявки из списка «Заявка на услуги 1 — данные» (/sites/requests).
|
||||
// Грузим все заявки (последние сверху); поиск по коду/наименованию — на клиенте.
|
||||
async fetchServiceRequests(search?: string): Promise<ServiceRequest[]> {
|
||||
const url =
|
||||
`${REQUESTS_SITE_URL}/_api/web/lists/getbytitle('${encodeURIComponent(REQUESTS_LIST_TITLE)}')/items` +
|
||||
`?$select=Id,RequestUniqueCode,RequestStatus,ProjectName,ProjectId,ActivityName,RequestDate,RequesterName,WorkVolumesJson` +
|
||||
`&$orderby=RequestDate%20desc` +
|
||||
`&$top=200`;
|
||||
|
||||
const res = await this.sp.get(url, SPHttpClient.configurations.v1);
|
||||
if (!res.ok) throw new Error(`Ошибка загрузки заявок: ${res.status}`);
|
||||
const data = await res.json();
|
||||
const items = (data.value as Array<Record<string, unknown>>) || [];
|
||||
|
||||
const mapped: ServiceRequest[] = items.map((it) => {
|
||||
const id = it.Id as number;
|
||||
const code = (it.RequestUniqueCode as string) || "";
|
||||
const requestUrl = `${REQUEST_LINK_BASE}${id}`;
|
||||
return {
|
||||
id,
|
||||
code,
|
||||
status: (it.RequestStatus as string) || "",
|
||||
projectName: (it.ProjectName as string) || "",
|
||||
activityName: (it.ActivityName as string) || "",
|
||||
requestDate: (it.RequestDate as string) || "",
|
||||
requesterName: (it.RequesterName as string) || "",
|
||||
url: requestUrl,
|
||||
groups: parseWorkVolumes((it.WorkVolumesJson as string) || "", code, requestUrl),
|
||||
};
|
||||
});
|
||||
|
||||
const q = (search ?? "").trim().toLowerCase();
|
||||
if (!q) return mapped;
|
||||
return mapped.filter(
|
||||
(r) =>
|
||||
r.code.toLowerCase().includes(q) ||
|
||||
r.activityName.toLowerCase().includes(q),
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Positions ──────────────────────────────────────────────────────────────
|
||||
|
||||
async createOIDPositions(
|
||||
@@ -265,6 +372,8 @@ export class OidService {
|
||||
};
|
||||
if (pos.priceSmeta !== undefined) body.priceSmeta = pos.priceSmeta;
|
||||
if (pos.summaSmeta !== undefined) body.summaSmeta = pos.summaSmeta;
|
||||
if (pos.requestCode) body.RequestCode = pos.requestCode;
|
||||
if (pos.requestUrl) body.RequestUrl = pos.requestUrl;
|
||||
|
||||
const res = await this.sp.post(
|
||||
`${listUrl(LISTS.OID_POSITIONS)}/items`,
|
||||
@@ -308,6 +417,8 @@ export class OidService {
|
||||
quantity: pos.quantity,
|
||||
priceSmeta: pos.priceSmeta ?? null,
|
||||
summaSmeta: pos.summaSmeta ?? null,
|
||||
RequestCode: pos.requestCode ?? null,
|
||||
RequestUrl: pos.requestUrl ?? null,
|
||||
};
|
||||
|
||||
if (pos.spId && existingIds.has(pos.spId)) {
|
||||
@@ -376,7 +487,7 @@ export class OidService {
|
||||
const url =
|
||||
`${listUrl(LISTS.OID_POSITIONS)}/items` +
|
||||
`?$filter=CTId eq ${oidId}` +
|
||||
`&$select=Id,parentGroup,position,positionType,unit,quantity,priceSmeta,summaSmeta` +
|
||||
`&$select=Id,parentGroup,position,positionType,unit,quantity,priceSmeta,summaSmeta,RequestCode,RequestUrl` +
|
||||
`&$top=500`;
|
||||
const res = await this.sp.get(url, SPHttpClient.configurations.v1);
|
||||
const data = await res.json();
|
||||
@@ -392,6 +503,8 @@ export class OidService {
|
||||
quantity: (item.quantity as number) || 0,
|
||||
priceSmeta: item.priceSmeta as number | undefined,
|
||||
summaSmeta: item.summaSmeta as number | undefined,
|
||||
requestCode: (item.RequestCode as string) || undefined,
|
||||
requestUrl: (item.RequestUrl as string) || undefined,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user