/** * @file smsProblemSummary.js * @description Складывает неудачные отправки SMS в одну понятную причину. * * В таблице видно только «SMS не отправлено», а причина в логе — код сервиса. * Здесь считаем: сколько сообщений, сколько заказов, за какой период и что * именно случилось чаще всего (например, что на счёте закончились средства). */ import { getSmsFailureReason } from "./orderGroupViews"; /** * @param {Array} rows строки sms_campaign_log со статусами ошибок * @param {Array} visibleIds id заказов, которые сейчас видны в таблице * @returns {null|{reason, messages, groups, affectedInTable, hasFundsReason, firstAt, lastAt}} */ export const summarizeSmsFailures = (rows = [], visibleIds = []) => { const list = Array.isArray(rows) ? rows.filter(Boolean) : []; if (!list.length) return null; const reasons = new Map(); const lastByGroup = new Map(); let firstAt = null; let lastAt = null; for (const row of list) { const reason = getSmsFailureReason(row) || "причина не указана"; reasons.set(reason, (reasons.get(reason) || 0) + 1); if (row.order_group_id && !lastByGroup.has(row.order_group_id)) { lastByGroup.set(row.order_group_id, row); } if (row.created_at) { if (!firstAt || row.created_at < firstAt) firstAt = row.created_at; if (!lastAt || row.created_at > lastAt) lastAt = row.created_at; } } const [reason, topMessages] = [...reasons.entries()].sort((a, b) => b[1] - a[1])[0]; const visible = new Set(visibleIds); return { reason, messages: list.length, topMessages, groups: lastByGroup.size, affectedInTable: [...lastByGroup.keys()].filter((id) => visible.has(id)).length, hasVisibleRows: visible.size > 0, hasFundsReason: /закончились средства/.test(reason), firstAt, lastAt, }; }; /** «сообщение / сообщения / сообщений», «заказ / заказа / заказов». */ export const plural = (n, one, few, many) => { const mod10 = n % 10; const mod100 = n % 100; if (mod10 === 1 && mod100 !== 11) return one; if (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)) return few; return many; };