diff --git a/src/webparts/ctFormsPortal/forms/ct/components/utils/criteria.test.ts b/src/webparts/ctFormsPortal/forms/ct/components/utils/criteria.test.ts index fcb6b0f..f6e7197 100644 --- a/src/webparts/ctFormsPortal/forms/ct/components/utils/criteria.test.ts +++ b/src/webparts/ctFormsPortal/forms/ct/components/utils/criteria.test.ts @@ -1,4 +1,4 @@ -import { buildCriteriaRecords, isPaymentTypeOption, restoreCriteria } from './criteria'; +import { buildCriteriaRecords, isPaymentTypeOption, plainTextFromSp, restoreCriteria } from './criteria'; import { Supplier } from '../types/domain'; const ARLAN_KZT: Supplier = { id: '263_277', spId: 263, kpFileSpId: 277, name: 'ТОО «Арлан Офис Снаб»', hasOffer: true }; @@ -96,3 +96,19 @@ describe('isPaymentTypeOption', () => { expect(isPaymentTypeOption('40% предоплата, 60% по аккредитиву')).toBe(false); }); }); + +describe('plainTextFromSp', () => { + // comment в SP «Критерии» — многострочное поле с форматированием: SharePoint заворачивает текст в HTML, + // и по ссылке своя строка показывалась как «
да
». + it('убирает обёртку ExternalClass и теги', () => { + expect(plainTextFromSp('
да
')).toBe('да'); + }); + + it('переносы строк сохраняет, HTML-сущности раскодирует', () => { + expect(plainTextFromSp('
12 мес
от "поставки" & монтажа
')).toBe('12 мес\nот "поставки" & монтажа'); + }); + + it('обычный текст не трогает', () => { + expect(plainTextFromSp('12 мес')).toBe('12 мес'); + }); +}); diff --git a/src/webparts/ctFormsPortal/forms/ct/components/utils/criteria.ts b/src/webparts/ctFormsPortal/forms/ct/components/utils/criteria.ts index 7277b17..73f3b3e 100644 --- a/src/webparts/ctFormsPortal/forms/ct/components/utils/criteria.ts +++ b/src/webparts/ctFormsPortal/forms/ct/components/utils/criteria.ts @@ -36,6 +36,26 @@ const EMPTY_ENTRY: CriteriaEntry = { paymentType: '', deliveryType: '', deadline const hasAnyValue = (e: CriteriaEntry): boolean => [e.paymentType, e.deliveryType, e.deadline, e.sertificates].some(v => (v ?? '').trim() !== ''); +const HTML_ENTITIES: Record = { + ' ': ' ', + '&': '&', + '<': '<', + '>': '>', + '"': '"', + ''': "'", +}; + +// Текст из многострочного поля SP с форматированием (comment в «Критерии»): SharePoint хранит его как HTML +// в обёртке
. Оставляем чистый текст, переносы строк — как есть. +export function plainTextFromSp(value: string): string { + if (!value.includes('<')) return value; + return value + .replace(//gi, '\n') + .replace(/<[^>]+>/g, '') + .replace(/&(nbsp|amp|lt|gt|quot|#39);/g, entity => HTML_ENTITIES[entity] ?? entity) + .trim(); +} + export function isPaymentTypeOption(value: string): boolean { return PAYMENT_TYPE_OPTIONS.includes(value.trim()); } diff --git a/src/webparts/ctFormsPortal/forms/ct/services/CtService.ts b/src/webparts/ctFormsPortal/forms/ct/services/CtService.ts index fcfeb12..f58406b 100644 --- a/src/webparts/ctFormsPortal/forms/ct/services/CtService.ts +++ b/src/webparts/ctFormsPortal/forms/ct/services/CtService.ts @@ -29,7 +29,7 @@ import { keepLatestOfferRecords, parseOfferItemIndex, } from "../../../shared/offers/offerRecords"; -import { CriteriaRecord } from "../components/utils/criteria"; +import { CriteriaRecord, plainTextFromSp } from "../components/utils/criteria"; import { NoOfferSupplierEntry, NoOfferSupplierRecord, @@ -680,7 +680,8 @@ export class CtService { return ((data.value ?? []) as Array>).map((item): CriteriaRecord => { const base = { id: Number(item.Id), supplierSpId: Number(item.supplierId) }; const label = String(item.criteriaLabel ?? "").trim(); - if (label) return { ...base, criteriaLabel: label, value: String(item.comment ?? "") }; + // comment — многострочное поле с форматированием: SharePoint отдаёт его в HTML-обёртке. + if (label) return { ...base, criteriaLabel: label, value: plainTextFromSp(String(item.comment ?? "")) }; return { ...base, paymentType: String(item.paymentType ?? ""),