fix(autocall): «ручное управление» — это НЕ согласованный заказ; кнопку автодозвона по нему больше не блокирует (ошибка из прошлой правки)

This commit is contained in:
Hermes 2026-09-17 12:04:00 +02:00
parent 512aab9ff2
commit 775a72ea29
4 changed files with 56 additions and 8 deletions

View File

@ -593,8 +593,11 @@ def process_queue(conn, settings):
# Заказ уже согласован (клиент выбрал дату или менеджер поставил её сам) —
# роботу звонить не о чем, иначе он предложит дни по заказу, где всё решено.
# Заявку НЕ закрываем (правило владельца) — просто не обрабатываем.
agreed = (str(q.get("notification_status") or "") in ("confirmed", "manual_required", "opt_out")
or q.get("delivery_date") is not None)
# ВАЖНО: 'manual_required' («нужен менеджер») сюда не относится — это как раз
# заказ, где клиент дату НЕ выбрал, звонок робота по нему допустим.
agreed = (str(q.get("notification_status") or "") == "confirmed"
or q.get("delivery_date") is not None
or q.get("autocall_opt_out") is True)
if agreed and not is_test:
log.info(
f"Queue item {q['queue_id']} (group {group_id}): заказ уже согласован "

View File

@ -7,7 +7,7 @@ import React, { useState, useEffect, useCallback, useMemo } from "react";
import { Badge } from "../UI/Badge";
import { supabase } from "../../supabaseClient";
import { resolveAutocallStatus, AUTOCALL_STATUS_META } from "../../services/orderGroupViews";
import { describeCallProcess } from "../../services/autocallProcess";
import { describeCallProcess, isOrderAgreed } from "../../services/autocallProcess";
import { isAutocallPossible } from "../../services/autocallAvailability";
const OUTCOME_META = {
@ -161,11 +161,10 @@ export const AutocallStatusCard = ({ order, userRole }) => {
}, [order?.id, order?.delivery_type, order?.created_at]);
const autocallBlocked = availability ? !availability.possible : false;
// Заказ уже согласован — дату выбрал клиент (в том числе в звонке) или поставил менеджер
const orderAgreed = Boolean(order?.delivery_date || order?.deliveryDate)
|| ["confirmed", "manual_required", "opt_out"].includes(
String(order?.notification_status || order?.notificationStatus || ""),
);
// Заказ уже согласован — дату выбрал клиент (в том числе в звонке) или поставил менеджер.
// 'manual_required' («нужен менеджер») сюда НЕ входит: там дату как раз не выбрали,
// и звонок робота допустим (см. isOrderAgreed и её тесты).
const orderAgreed = isOrderAgreed(order);
const process = useMemo(
() => describeCallProcess({

View File

@ -185,3 +185,19 @@ export const describeCallProcess = ({ queueItem, callAfter, settings = {}, now =
hint: `Робот звонит только в рабочее время: ${padHour(startHour)}–${padHour(endHour)}, дни — ${workDays.join(", ")} — ${requested}`,
};
};
/**
* Согласован ли уже заказ (дату выбрал клиент или поставил менеджер).
*
* Нужно карточке, чтобы не предлагать автодозвон по заказу, где всё решено.
* ВАЖНО: `manual_required` («нужен менеджер») — это НЕ согласованный заказ:
* дату клиент как раз не выбрал, и звонок робота по нему допустим.
* Отдельно стоит флаг «не звонить» (`autocall_opt_out`) — он про запрет звонков,
* а не про согласование, и обрабатывается своим состоянием.
*/
export const isOrderAgreed = (order) => {
if (!order) return false;
const date = order.delivery_date ?? order.deliveryDate;
const status = String(order.notification_status ?? order.notificationStatus ?? "");
return Boolean(date) || status === "confirmed";
};

View File

@ -6,6 +6,7 @@
import { describe, it, expect } from "vitest";
import {
describeCallProcess,
isOrderAgreed,
isCallTimeNow,
moscowNow,
nextCallDayLabel,
@ -160,3 +161,32 @@ describe("describeCallProcess — что видит логист", () => {
expect(res.title).not.toContain("уже согласован");
});
});
describe("isOrderAgreed — когда автодозвон предлагать нельзя", () => {
it("дата выбрана — согласован", () => {
expect(isOrderAgreed({ delivery_date: "2026-09-18", notification_status: "pickup" })).toBe(true);
});
it("статус «согласовано» — согласован", () => {
expect(isOrderAgreed({ notification_status: "confirmed" })).toBe(true);
expect(isOrderAgreed({ notificationStatus: "confirmed" })).toBe(true);
});
it("«ручное управление» — НЕ согласован: клиент дату не выбрал", () => {
expect(isOrderAgreed({ notification_status: "manual_required", delivery_date: null })).toBe(false);
});
it("«первое приглашение отправлено» — НЕ согласован", () => {
expect(isOrderAgreed({ notification_status: "first_sms_sent" })).toBe(false);
});
it("«не звонить» само по себе не делает заказ согласованным", () => {
expect(isOrderAgreed({ notification_status: "first_sms_sent", autocall_opt_out: true })).toBe(false);
});
it("пустой заказ — не согласован", () => {
expect(isOrderAgreed(null)).toBe(false);
expect(isOrderAgreed(undefined)).toBe(false);
expect(isOrderAgreed({})).toBe(false);
});
});