feat(schedule): рабочие часы склада и доставки в админке — открытие/закрытие, границы половин, отсечка «сегодня»; робот и ссылка клиента читают часы
This commit is contained in:
parent
794c3ce5e3
commit
11167516a3
|
|
@ -256,7 +256,42 @@ def load_work_days(conn, field="pickup_days"):
|
|||
return default
|
||||
|
||||
|
||||
def pickup_window(sms_sent_at, work_days=None, now=None):
|
||||
def load_work_hours(conn):
|
||||
"""Рабочие часы склада и доставки из «Графика работы» (админка).
|
||||
|
||||
Возвращает минуты от полуночи — сравнивать часы строками нельзя.
|
||||
Дефолты повторяют прежнее поведение кода: первая половина до 12:00,
|
||||
«сегодня» предлагалось весь день.
|
||||
"""
|
||||
defaults = {
|
||||
"pickup_half_end": 12 * 60,
|
||||
"pickup_same_day_cutoff": 24 * 60,
|
||||
"pickup_close": 18 * 60,
|
||||
"delivery_half_end": 15 * 60,
|
||||
"delivery_close": 21 * 60,
|
||||
}
|
||||
try:
|
||||
with conn.cursor(cursor_factory=RealDictCursor) as cur:
|
||||
cur.execute("""
|
||||
SELECT pickup_half_end, pickup_same_day_cutoff, pickup_close,
|
||||
delivery_half_end, delivery_close
|
||||
FROM business_schedule_settings WHERE id = 1
|
||||
""")
|
||||
row = cur.fetchone() or {}
|
||||
hours = {}
|
||||
for key, fallback in defaults.items():
|
||||
value = row.get(key)
|
||||
if value is None:
|
||||
hours[key] = fallback
|
||||
continue
|
||||
hours[key] = value.hour * 60 + value.minute
|
||||
return hours
|
||||
except Exception as exc: # noqa: BLE001 — часы не критичны, звоним по дефолту
|
||||
log.warning("Не удалось прочитать рабочие часы, беру значения по умолчанию: %s", exc)
|
||||
return dict(defaults)
|
||||
|
||||
|
||||
def pickup_window(sms_sent_at, work_days=None, now=None, cutoff_minutes=None):
|
||||
"""Даты, на которые предлагаем самовывоз.
|
||||
|
||||
Правило: забрать можно в день готовности (день попадания в базу — он же
|
||||
|
|
@ -267,18 +302,26 @@ def pickup_window(sms_sent_at, work_days=None, now=None):
|
|||
• готов вчера → сегодня, завтра (2)
|
||||
• готов позавчера → сегодня (1)
|
||||
• готов раньше → окно закрыто, звонить не о чем (0)
|
||||
• в пятницу готов → пятница и суббота (воскресенье закрыто)
|
||||
• пятница, готов сегодня → только пятница: сб и вс склад закрыт
|
||||
|
||||
cutoff_minutes — время, после которого «сегодня» уже не предлагаем
|
||||
(склад не успеет собрать заказ к выдаче, настройка админки).
|
||||
"""
|
||||
now = now or datetime.now(timezone.utc)
|
||||
work_days = work_days or [1, 2, 3, 4, 5]
|
||||
tz = timezone(timedelta(hours=3))
|
||||
today = now.astimezone(tz).date()
|
||||
local_now = now.astimezone(tz)
|
||||
today = local_now.date()
|
||||
ready = (sms_sent_at or now).astimezone(tz).date()
|
||||
last_allowed = ready + timedelta(days=2)
|
||||
if last_allowed < today:
|
||||
return []
|
||||
first_day = today
|
||||
# Поздно для сегодняшнего забора — окно начинается со следующего дня.
|
||||
if cutoff_minutes is not None and local_now.hour * 60 + local_now.minute >= cutoff_minutes:
|
||||
first_day = today + timedelta(days=1)
|
||||
dates = []
|
||||
day = today
|
||||
day = first_day
|
||||
while day <= last_allowed and len(dates) < 3:
|
||||
if day.isoweekday() in work_days:
|
||||
dates.append(day)
|
||||
|
|
@ -286,11 +329,13 @@ def pickup_window(sms_sent_at, work_days=None, now=None):
|
|||
return [d.isoformat() for d in dates]
|
||||
|
||||
|
||||
def is_today_before_noon(now=None):
|
||||
"""До 12:00 по Симферополю — первую половину дня ещё можно предлагать."""
|
||||
def is_first_half_available(now=None, half_end_minutes=None):
|
||||
"""До границы половины дня (настройка склада, по умолчанию 12:00)
|
||||
первую половину дня ещё можно предлагать клиенту."""
|
||||
now = now or datetime.now(timezone.utc)
|
||||
local = now.astimezone(timezone(timedelta(hours=3)))
|
||||
return local.hour < 12
|
||||
limit = 12 * 60 if half_end_minutes is None else half_end_minutes
|
||||
return local.hour * 60 + local.minute < limit
|
||||
|
||||
|
||||
def resolve_call_type(group):
|
||||
|
|
@ -334,6 +379,7 @@ def build_call_file(group, settings, attempt_no, is_test_call=False):
|
|||
pickup_days = pickup_window(
|
||||
group.get("sms_sent_at") or group.get("created_at"),
|
||||
settings.get("pickup_work_days"),
|
||||
cutoff_minutes=(settings.get("work_hours") or {}).get("pickup_same_day_cutoff"),
|
||||
)
|
||||
if not pickup_days:
|
||||
# Окно самовывоза закрыто (заказ готов более двух дней назад).
|
||||
|
|
@ -356,7 +402,7 @@ def build_call_file(group, settings, attempt_no, is_test_call=False):
|
|||
f"Setvar: PICKUP_DAY1={pickup_days[0] if len(pickup_days) > 0 else ''}",
|
||||
f"Setvar: PICKUP_DAY2={pickup_days[1] if len(pickup_days) > 1 else ''}",
|
||||
f"Setvar: PICKUP_DAY3={pickup_days[2] if len(pickup_days) > 2 else ''}",
|
||||
f"Setvar: PICKUP_DAY1_FIRST={1 if (pickup_days and is_today_before_noon()) else 0}",
|
||||
f"Setvar: PICKUP_DAY1_FIRST={1 if (pickup_days and is_first_half_available(now=None, half_end_minutes=settings.get('work_hours', {}).get('pickup_half_end'))) else 0}",
|
||||
f"Setvar: API_URL={API_URL}",
|
||||
f"Setvar: API_KEY={API_KEY}",
|
||||
f"Setvar: TEST_MODE={1 if test_mode else 0}",
|
||||
|
|
@ -575,8 +621,9 @@ def main():
|
|||
conn = get_db_conn()
|
||||
try:
|
||||
settings = load_settings(conn)
|
||||
# Рабочие дни для окна самовывоза (выходные робот не предлагает)
|
||||
# Рабочие дни и часы для окна самовывоза (выходные/закрытие склада робот не предлагает)
|
||||
settings["pickup_work_days"] = load_work_days(conn)
|
||||
settings["work_hours"] = load_work_hours(conn)
|
||||
|
||||
# Добор «не взял трубку»: заявки, по которым сеть так и не ответила
|
||||
# (истёкший call-файл, AGI не запускался). Только работа с БД.
|
||||
|
|
|
|||
|
|
@ -23,8 +23,41 @@ const DEFAULT_SCHEDULE = {
|
|||
deliveryDays: [1, 2, 3, 4, 5],
|
||||
pickupDays: [1, 2, 3, 4, 5],
|
||||
smsDays: [1, 2, 3, 4, 5],
|
||||
// Рабочие часы: склад (самовывоз) и доставка.
|
||||
pickupOpen: "09:00",
|
||||
pickupClose: "17:30",
|
||||
pickupHalfEnd: "13:00",
|
||||
pickupSameDayCutoff: "15:30",
|
||||
deliveryOpen: "09:00",
|
||||
deliveryClose: "21:00",
|
||||
deliveryHalfEnd: "15:00",
|
||||
};
|
||||
|
||||
const TIME_FIELDS = [
|
||||
"pickupOpen",
|
||||
"pickupClose",
|
||||
"pickupHalfEnd",
|
||||
"pickupSameDayCutoff",
|
||||
"deliveryOpen",
|
||||
"deliveryClose",
|
||||
"deliveryHalfEnd",
|
||||
];
|
||||
|
||||
const TimeField = ({ label, hint, value, onChange }) => (
|
||||
<label className="flex flex-col gap-1.5">
|
||||
<span className="text-xs font-semibold uppercase tracking-wider text-[var(--color-text-muted)]">
|
||||
{label}
|
||||
</span>
|
||||
<input
|
||||
type="time"
|
||||
value={value || ""}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
className="rounded-[14px] border-2 border-[var(--color-border)] bg-[var(--color-surface-strong)] px-3 py-2.5 text-sm font-semibold text-[var(--color-text)] outline-none transition focus:border-[var(--color-accent)]"
|
||||
/>
|
||||
{hint ? <span className="text-xs text-[var(--color-text-muted)]">{hint}</span> : null}
|
||||
</label>
|
||||
);
|
||||
|
||||
const DayPills = ({ selectedDays, onToggle, accentColor }) => {
|
||||
const toggle = (num) => {
|
||||
if (selectedDays.includes(num)) {
|
||||
|
|
@ -79,6 +112,13 @@ export const BusinessSchedulePanel = () => {
|
|||
deliveryDays: data.deliveryDays || DEFAULT_SCHEDULE.deliveryDays,
|
||||
pickupDays: data.pickupDays || DEFAULT_SCHEDULE.pickupDays,
|
||||
smsDays: data.smsDays || DEFAULT_SCHEDULE.smsDays,
|
||||
pickupOpen: data.pickupOpen || DEFAULT_SCHEDULE.pickupOpen,
|
||||
pickupClose: data.pickupClose || DEFAULT_SCHEDULE.pickupClose,
|
||||
pickupHalfEnd: data.pickupHalfEnd || DEFAULT_SCHEDULE.pickupHalfEnd,
|
||||
pickupSameDayCutoff: data.pickupSameDayCutoff || DEFAULT_SCHEDULE.pickupSameDayCutoff,
|
||||
deliveryOpen: data.deliveryOpen || DEFAULT_SCHEDULE.deliveryOpen,
|
||||
deliveryClose: data.deliveryClose || DEFAULT_SCHEDULE.deliveryClose,
|
||||
deliveryHalfEnd: data.deliveryHalfEnd || DEFAULT_SCHEDULE.deliveryHalfEnd,
|
||||
};
|
||||
setSchedule(loaded);
|
||||
setOriginal(loaded);
|
||||
|
|
@ -97,7 +137,8 @@ export const BusinessSchedulePanel = () => {
|
|||
const hasChanges =
|
||||
JSON.stringify(schedule.deliveryDays) !== JSON.stringify(original.deliveryDays) ||
|
||||
JSON.stringify(schedule.pickupDays) !== JSON.stringify(original.pickupDays) ||
|
||||
JSON.stringify(schedule.smsDays) !== JSON.stringify(original.smsDays);
|
||||
JSON.stringify(schedule.smsDays) !== JSON.stringify(original.smsDays) ||
|
||||
TIME_FIELDS.some((field) => schedule[field] !== original[field]);
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!hasChanges) return;
|
||||
|
|
@ -115,6 +156,13 @@ export const BusinessSchedulePanel = () => {
|
|||
p_delivery_days: schedule.deliveryDays,
|
||||
p_pickup_days: schedule.pickupDays,
|
||||
p_sms_days: schedule.smsDays,
|
||||
p_pickup_open: schedule.pickupOpen,
|
||||
p_pickup_close: schedule.pickupClose,
|
||||
p_pickup_half_end: schedule.pickupHalfEnd,
|
||||
p_pickup_same_day_cutoff: schedule.pickupSameDayCutoff,
|
||||
p_delivery_open: schedule.deliveryOpen,
|
||||
p_delivery_close: schedule.deliveryClose,
|
||||
p_delivery_half_end: schedule.deliveryHalfEnd,
|
||||
});
|
||||
if (rpcError) throw rpcError;
|
||||
if (data?.ok) {
|
||||
|
|
@ -183,6 +231,26 @@ export const BusinessSchedulePanel = () => {
|
|||
<p className="text-xs text-[var(--color-text-muted)]">
|
||||
Выбрано: {formatDays(schedule.deliveryDays) || "—"}
|
||||
</p>
|
||||
<div className="grid gap-4 sm:grid-cols-3">
|
||||
<TimeField
|
||||
label="Начало доставки"
|
||||
hint="с этого времени возим заказы"
|
||||
value={schedule.deliveryOpen}
|
||||
onChange={(v) => setSchedule((s) => ({ ...s, deliveryOpen: v }))}
|
||||
/>
|
||||
<TimeField
|
||||
label="Окончание доставки"
|
||||
hint="позже не доставляем"
|
||||
value={schedule.deliveryClose}
|
||||
onChange={(v) => setSchedule((s) => ({ ...s, deliveryClose: v }))}
|
||||
/>
|
||||
<TimeField
|
||||
label="Первая половина — до"
|
||||
hint="граница между половинами дня"
|
||||
value={schedule.deliveryHalfEnd}
|
||||
onChange={(v) => setSchedule((s) => ({ ...s, deliveryHalfEnd: v }))}
|
||||
/>
|
||||
</div>
|
||||
</Panel>
|
||||
|
||||
{/* Pickup days */}
|
||||
|
|
@ -206,6 +274,32 @@ export const BusinessSchedulePanel = () => {
|
|||
<p className="text-xs text-[var(--color-text-muted)]">
|
||||
Выбрано: {formatDays(schedule.pickupDays) || "—"}
|
||||
</p>
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<TimeField
|
||||
label="Открытие склада"
|
||||
hint="с этого времени выдаём заказы"
|
||||
value={schedule.pickupOpen}
|
||||
onChange={(v) => setSchedule((s) => ({ ...s, pickupOpen: v }))}
|
||||
/>
|
||||
<TimeField
|
||||
label="Закрытие склада"
|
||||
hint="позже заказы не выдаём"
|
||||
value={schedule.pickupClose}
|
||||
onChange={(v) => setSchedule((s) => ({ ...s, pickupClose: v }))}
|
||||
/>
|
||||
<TimeField
|
||||
label="Первая половина — до"
|
||||
hint="граница между половинами дня"
|
||||
value={schedule.pickupHalfEnd}
|
||||
onChange={(v) => setSchedule((s) => ({ ...s, pickupHalfEnd: v }))}
|
||||
/>
|
||||
<TimeField
|
||||
label="Сегодня предлагаем до"
|
||||
hint="позже робот и ссылка предложат только следующие дни — склад не успеет собрать заказ"
|
||||
value={schedule.pickupSameDayCutoff}
|
||||
onChange={(v) => setSchedule((s) => ({ ...s, pickupSameDayCutoff: v }))}
|
||||
/>
|
||||
</div>
|
||||
</Panel>
|
||||
|
||||
{/* SMS days */}
|
||||
|
|
@ -217,7 +311,7 @@ export const BusinessSchedulePanel = () => {
|
|||
SMS-приглашения
|
||||
</h2>
|
||||
<p className="text-xs text-[var(--color-text-muted)]">
|
||||
Дни, в которые отправляются SMS-приглашения клиентам. В нерабочие дни SMS не отправляются (проверка статусов работает круглосуточно).
|
||||
Дни, в которые отправляются SMS-приглашения клиентам.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,26 @@ const getCrimeaHour = (referenceDate = new Date()) => {
|
|||
);
|
||||
};
|
||||
|
||||
// Минуты от полуночи по Симферополю — чтобы сравнивать с настройками склада.
|
||||
const getCrimeaMinutes = (referenceDate = new Date()) => {
|
||||
const parts = new Intl.DateTimeFormat("ru-RU", {
|
||||
timeZone: DELIVERY_TIMEZONE,
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: false,
|
||||
}).formatToParts(referenceDate);
|
||||
const hour = parseInt(parts.find((p) => p.type === "hour")?.value || "0", 10);
|
||||
const minute = parseInt(parts.find((p) => p.type === "minute")?.value || "0", 10);
|
||||
return hour * 60 + minute;
|
||||
};
|
||||
|
||||
// "15:30" → 930
|
||||
const timeToMinutes = (value, fallback) => {
|
||||
const match = /^(\d{1,2}):(\d{2})$/.exec(String(value || "").trim());
|
||||
if (!match) return fallback;
|
||||
return Number(match[1]) * 60 + Number(match[2]);
|
||||
};
|
||||
|
||||
const isAllowedPickupDay = (dateKey, allowedDays = [1,2,3,4,5]) => {
|
||||
if (!dateKey) return false;
|
||||
const d = new Date(`${dateKey}T12:00:00Z`);
|
||||
|
|
@ -61,11 +81,20 @@ const getNextPickupWorkday = (dateKey, allowedDays = [1,2,3,4,5]) => {
|
|||
* максимум три дня включая сегодняшний. Отсчёт в списке — от фактического
|
||||
* сегодня: если заказ готов вчера, останутся только «Сегодня» и «Завтра».
|
||||
*/
|
||||
const getPickupSlots = (referenceDate = new Date(), pickupDays = [1,2,3,4,5], now = new Date()) => {
|
||||
const getPickupSlots = (
|
||||
referenceDate = new Date(),
|
||||
pickupDays = [1,2,3,4,5],
|
||||
now = new Date(),
|
||||
schedule = {},
|
||||
) => {
|
||||
const todayKey = getCrimeaTodayKey(now);
|
||||
const hour = getCrimeaHour(now);
|
||||
const readyKey = getCrimeaTodayKey(referenceDate);
|
||||
const lastAllowedKey = addDaysKey(readyKey, 2);
|
||||
// Часы склада из админки («График работы»): граница половины дня и отсечка
|
||||
// «сегодня». Дефолты — прежнее поведение (12:00 и «весь день»).
|
||||
const halfEndMinutes = timeToMinutes(schedule.halfEnd, 12 * 60);
|
||||
const cutoffMinutes = timeToMinutes(schedule.sameDayCutoff, 24 * 60);
|
||||
const nowMinutes = getCrimeaMinutes(now);
|
||||
// Дата попадает в окно: не раньше сегодня и не позже «готовность + 2 дня».
|
||||
const isInWindow = (dateKey) => Boolean(dateKey) && dateKey >= todayKey && dateKey <= lastAllowedKey;
|
||||
|
||||
|
|
@ -81,9 +110,14 @@ const getPickupSlots = (referenceDate = new Date(), pickupDays = [1,2,3,4,5], no
|
|||
});
|
||||
};
|
||||
|
||||
// Сегодня: первая половина — только до 12:00, вторая — в течение дня.
|
||||
if (isAllowedPickupDay(todayKey, pickupDays) && isInWindow(todayKey)) {
|
||||
if (hour < 12) addSlot(todayKey, "first", "Сегодня", "today");
|
||||
// Сегодня: первая половина — до границы половины дня, вторая — до отсечки
|
||||
// «сегодня» (после неё склад не успеет собрать заказ, поэтому сегодня не предлагаем).
|
||||
if (
|
||||
isAllowedPickupDay(todayKey, pickupDays)
|
||||
&& isInWindow(todayKey)
|
||||
&& nowMinutes < cutoffMinutes
|
||||
) {
|
||||
if (nowMinutes < halfEndMinutes) addSlot(todayKey, "first", "Сегодня", "today");
|
||||
addSlot(todayKey, "second", "Сегодня", "today");
|
||||
}
|
||||
|
||||
|
|
@ -146,8 +180,14 @@ export const PickupSlotsPicker = ({
|
|||
selectedSlotId,
|
||||
referenceDate = new Date(),
|
||||
pickupDays = [1, 2, 3, 4, 5],
|
||||
schedule = {},
|
||||
}) => {
|
||||
const slots = React.useMemo(() => getPickupSlots(referenceDate, pickupDays), [referenceDate, pickupDays]);
|
||||
const halfEnd = schedule?.halfEnd;
|
||||
const sameDayCutoff = schedule?.sameDayCutoff;
|
||||
const slots = React.useMemo(
|
||||
() => getPickupSlots(referenceDate, pickupDays, new Date(), { halfEnd, sameDayCutoff }),
|
||||
[referenceDate, pickupDays, halfEnd, sameDayCutoff],
|
||||
);
|
||||
|
||||
if (!slots.length) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -14,9 +14,8 @@ import { Skeleton } from "../components/UI/Loading";
|
|||
import { formatDeliveryDate } from "../components/client/deliveryDateFormatting";
|
||||
import {
|
||||
confirmDeliveryChoice,
|
||||
fetchBusinessSchedule,
|
||||
fetchDeliveryInvitation,
|
||||
fetchDeliveryWorkDays,
|
||||
fetchPickupWorkDays,
|
||||
} from "../services/deliveryInvitationApi";
|
||||
|
||||
const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL || "";
|
||||
|
|
@ -329,19 +328,21 @@ export const ClientDeliveryPage = () => {
|
|||
[token, invitation?.smsSentAt],
|
||||
);
|
||||
|
||||
// Рабочие дни — из графика компании, те же, что называет робот.
|
||||
// Рабочие дни и часы — из графика компании, те же, что использует робот.
|
||||
const [pickupWorkDays, setPickupWorkDays] = React.useState([1, 2, 3, 4, 5]);
|
||||
const [deliveryWorkDays, setDeliveryWorkDays] = React.useState(DEFAULT_DELIVERY_DAYS);
|
||||
const [pickupSchedule, setPickupSchedule] = React.useState({
|
||||
halfEnd: "13:00",
|
||||
sameDayCutoff: "15:30",
|
||||
});
|
||||
React.useEffect(() => {
|
||||
let cancelled = false;
|
||||
fetchDeliveryWorkDays()
|
||||
.then((days) => {
|
||||
if (!cancelled) setDeliveryWorkDays(days);
|
||||
})
|
||||
.catch(() => {});
|
||||
fetchPickupWorkDays()
|
||||
.then((days) => {
|
||||
if (!cancelled) setPickupWorkDays(days);
|
||||
fetchBusinessSchedule()
|
||||
.then((s) => {
|
||||
if (cancelled) return;
|
||||
setPickupWorkDays(s.pickupDays);
|
||||
setDeliveryWorkDays(s.deliveryDays);
|
||||
setPickupSchedule({ halfEnd: s.pickupHalfEnd, sameDayCutoff: s.pickupSameDayCutoff });
|
||||
})
|
||||
.catch(() => {});
|
||||
return () => {
|
||||
|
|
@ -687,6 +688,7 @@ export const ClientDeliveryPage = () => {
|
|||
selectedSlotId={selectedSlotId}
|
||||
referenceDate={referenceDate}
|
||||
pickupDays={pickupWorkDays}
|
||||
schedule={pickupSchedule}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ describe("ClientDeliveryPage helpers", () => {
|
|||
searchDate: "2026-04-14",
|
||||
}),
|
||||
).toEqual({
|
||||
deliveryType: "delivery",
|
||||
deliveryDate: "2026-04-16",
|
||||
deliveryTime: "После обеда",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -193,6 +193,13 @@ const SCHEDULE_FALLBACK = {
|
|||
deliveryDays: [1, 2, 3, 4, 5, 6],
|
||||
pickupDays: [1, 2, 3, 4, 5],
|
||||
smsDays: [1, 2, 3, 4, 5],
|
||||
pickupOpen: "09:00",
|
||||
pickupClose: "17:30",
|
||||
pickupHalfEnd: "13:00",
|
||||
pickupSameDayCutoff: "15:30",
|
||||
deliveryOpen: "09:00",
|
||||
deliveryClose: "21:00",
|
||||
deliveryHalfEnd: "15:00",
|
||||
};
|
||||
|
||||
const normalizeDays = (days, fallback) => {
|
||||
|
|
@ -213,6 +220,13 @@ export const fetchBusinessSchedule = async () => {
|
|||
deliveryDays: normalizeDays(data?.deliveryDays, SCHEDULE_FALLBACK.deliveryDays),
|
||||
pickupDays: normalizeDays(data?.pickupDays, SCHEDULE_FALLBACK.pickupDays),
|
||||
smsDays: normalizeDays(data?.smsDays, SCHEDULE_FALLBACK.smsDays),
|
||||
pickupOpen: data?.pickupOpen || SCHEDULE_FALLBACK.pickupOpen,
|
||||
pickupClose: data?.pickupClose || SCHEDULE_FALLBACK.pickupClose,
|
||||
pickupHalfEnd: data?.pickupHalfEnd || SCHEDULE_FALLBACK.pickupHalfEnd,
|
||||
pickupSameDayCutoff: data?.pickupSameDayCutoff || SCHEDULE_FALLBACK.pickupSameDayCutoff,
|
||||
deliveryOpen: data?.deliveryOpen || SCHEDULE_FALLBACK.deliveryOpen,
|
||||
deliveryClose: data?.deliveryClose || SCHEDULE_FALLBACK.deliveryClose,
|
||||
deliveryHalfEnd: data?.deliveryHalfEnd || SCHEDULE_FALLBACK.deliveryHalfEnd,
|
||||
};
|
||||
} catch (e) {
|
||||
console.warn("Не удалось получить график работы, беру значения по умолчанию", e);
|
||||
|
|
|
|||
Loading…
Reference in New Issue