diff --git a/src/services/orderGroupViews.js b/src/services/orderGroupViews.js index c8f3ba4..1347765 100644 --- a/src/services/orderGroupViews.js +++ b/src/services/orderGroupViews.js @@ -323,6 +323,24 @@ export const getSmsFailureReason = (log) => { const text = log.trim(); const onlyCode = text.match(/^(\d{3})$/)?.[1]; if (onlyCode && SMS_ERROR_REASONS[onlyCode]) return SMS_ERROR_REASONS[onlyCode]; + // Технические строки из планировщика вида «Not delivered after 2 attempts + // (code=106)» переводим в человеческие: код объясняем словами. + const embedded = text.match(/code\s*=\s*(\d{3})/i)?.[1]; + if (embedded && SMS_ERROR_REASONS[embedded]) { + return `${SMS_ERROR_REASONS[embedded]} (код ${embedded})`; + } + // «Не доставлено после N попыток» без кода — говорим так, как есть. + const attempts = text.match(/after\s+(\d+)\s+attempts?/i); + if (/not delivered|delivery failed|failed after/i.test(text)) { + return attempts + ? `клиент не получил сообщение за ${attempts[1]} попытки — телефон недоступен или оператор не доставил` + : "клиент не получил сообщение — телефон недоступен или оператор не доставил"; + } + if (/limit exceeded/i.test(text)) { + return embedded && SMS_ERROR_REASONS[embedded] + ? `${SMS_ERROR_REASONS[embedded]} (код ${embedded})` + : "сработал лимит отправки — SMS-сервис временно блокирует рассылку"; + } return text.slice(0, 120) || null; } const code = smsErrorCode(log); diff --git a/src/services/orderGroupViews.smsReasons.test.js b/src/services/orderGroupViews.smsReasons.test.js index ccdb640..4bc7920 100644 --- a/src/services/orderGroupViews.smsReasons.test.js +++ b/src/services/orderGroupViews.smsReasons.test.js @@ -55,6 +55,29 @@ describe("isSmsFundsError", () => { }); }); +describe("технические строки из планировщика переводятся в человеческие", () => { + it("«Second SMS failed after 2 attempts (code=106)» — код объяснён словами", () => { + const reason = getSmsFailureReason("Second SMS failed after 2 attempts (code=106)"); + expect(reason).toContain("сбой на телефоне абонента"); + expect(reason).toContain("код 106"); + }); + + it("«Not delivered after 2 attempts» без кода — говорим прямо", () => { + const reason = getSmsFailureReason("Not delivered after 2 attempts"); + expect(reason).toContain("не получил сообщение"); + expect(reason).toContain("2 попытки"); + }); + + it("«Second SMS limit exceeded (code=232)» — лимит словами", () => { + const reason = getSmsFailureReason("Second SMS limit exceeded (code=232)"); + expect(reason).toContain("лимит одинаковых сообщений в сутки"); + }); + + it("одна цифра 201 с переводом строки по-прежнему расшифровывается", () => { + expect(getSmsFailureReason("201\n")).toBe("на счёте SMS-сервиса закончились средства"); + }); +}); + describe("подсветка SMS в таблице остаётся прежней", () => { it("без отправленных SMS — «не отправлено»", () => { expect(getSmsProblemTone({})).toBe("not_sent");