dana-medical-site/bridge/bridge_1c.py

659 lines
33 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
1C:Enterprise HTTP Bridge for Dana Medical Website.
Translates REST API calls from Go backend to 1C MCP Toolkit /api/execute_code.
Runs in Docker container on 195.95.133.47 and talks to 1C at 10.240.70.70:6003.
"""
import sys
import json
import urllib.parse
from http.server import HTTPServer, BaseHTTPRequestHandler
from datetime import datetime, timezone
import requests
import psycopg2
MCP_URL = "http://10.240.70.70:6003/api/execute_code"
PG_HOST = "db"
PG_PORT = 5432
PG_USER = "dana"
PG_DB = "dana"
PG_PASS = "301ed23c81a8e2317b77f67340f571411ac16a5e5927e4bf17b076e01657f5c0"
def get_linked_doctor_ids():
try:
conn = psycopg2.connect(
host=PG_HOST, port=PG_PORT, user=PG_USER, password=PG_PASS, dbname=PG_DB, connect_timeout=3
)
cur = conn.cursor()
cur.execute("SELECT onec_id FROM onec_doctors WHERE doctor_id IS NOT NULL")
rows = cur.fetchall()
conn.close()
ids = [r[0] for r in rows if r[0]]
if ids:
return ids
except Exception as e:
print(f"[DB Warning] Could not fetch linked doctors from Postgres: {e}", flush=True)
# Fallback to known linked doctors
return [
"e27d7269-b659-11f1-8958-00155d46cd22",
"f07031a2-21a6-11f0-8712-00155d46cd22",
"a748a2aa-8771-11ed-840b-00155d46cd22",
"fce19e6d-3d52-11ec-adba-00155d46cd1d",
"137a17ba-0af6-11f0-86f5-00155d46cd22",
"a748a2a7-8771-11ed-840b-00155d46cd22",
]
AUTH_TOKEN = "747570bf-e7ab-461b-9d30-6f1df275a8df"
def call_1c(code: str):
try:
headers = {"Authorization": f"Bearer {AUTH_TOKEN}", "Content-Type": "application/json"}
resp = requests.post(MCP_URL, json={"code": code}, headers=headers, timeout=30)
if resp.status_code != 200:
return None, f"1C MCP returned HTTP {resp.status_code}: {resp.text[:200]}"
res_json = resp.json()
if not res_json.get("success", False):
return None, f"1C execution failed: {res_json.get('error', 'unknown error')}"
raw_data = res_json.get("data", "")
if isinstance(raw_data, str):
try:
parsed = json.loads(raw_data)
if isinstance(parsed, str) and (parsed.strip().startswith("{") or parsed.strip().startswith("[")):
try:
return json.loads(parsed), None
except Exception:
return parsed, None
return parsed, None
except Exception:
pass
return raw_data, None
except Exception as e:
return None, f"Network error connecting to 1C: {e}"
class BridgeHandler(BaseHTTPRequestHandler):
def log_message(self, format, *args):
sys.stderr.write("%s - - [%s] %s\n" % (self.client_address[0], self.log_date_time_string(), format % args))
def send_json(self, status_code: int, data: any):
body = json.dumps(data, ensure_ascii=False).encode("utf-8")
self.send_response(status_code)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def do_GET(self):
parsed = urllib.parse.urlparse(self.path)
path = parsed.path.rstrip("/")
query = urllib.parse.parse_qs(parsed.query)
if path == "/branches":
code = """
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Филиалы.Ссылка КАК Ссылка,
| Филиалы.Наименование КАК Наименование
|ИЗ
| Справочник.Филиалы КАК Филиалы
|ГДЕ
| Филиалы.ПометкаУдаления = ЛОЖЬ
| И (Филиалы.Наименование ПОДОБНО ""%Дана%"" ИЛИ Филиалы.Наименование ПОДОБНО ""%Ляхова 22%"")
| И НЕ Филиалы.Наименование ПОДОБНО ""%Абилити%""
| И НЕ Филиалы.Наименование ПОДОБНО ""%Целитель%""
|УПОРЯДОЧИТЬ ПО
| Филиалы.Наименование";
Таблица = Запрос.Выполнить().Выгрузить();
Массив = Новый Массив;
Для Каждого Стр Из Таблица Цикл
Элемент = Новый Структура;
Элемент.Вставить("id", НРег(XMLСтрока(Стр.Ссылка)));
Элемент.Вставить("name", Стр.Наименование);
Адрес = "г. Махачкала, ул. Ляхова";
Если Найти(ВРег(Стр.Наименование), "22") > 0 Тогда
Адрес = "г. Махачкала, ул. Ляхова, д. 22";
ИначеЕсли Найти(ВРег(Стр.Наименование), "7") > 0 Тогда
Адрес = "г. Махачкала, ул. Ляхова, д. 7";
ИначеЕсли Найти(ВРег(Стр.Наименование), "ХАСАВЮРТ") > 0 Тогда
Адрес = "г. Хасавюрт";
ИначеЕсли Найти(ВРег(Стр.Наименование), "ЦОМ") > 0 ИЛИ Найти(ВРег(Стр.Наименование), "ОСТЕО") > 0 Тогда
Адрес = "г. Махачкала, ул. Салаватова, д. 53";
КонецЕсли;
Элемент.Вставить("address", Адрес);
Элемент.Вставить("phone", "+7 (8722) 55-33-11");
Элемент.Вставить("active", Истина);
Массив.Добавить(Элемент);
КонецЦикла;
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, Массив);
Результат = ЗаписьJSON.Закрыть();
"""
data, err = call_1c(code)
if err:
self.send_json(502, {"code": "1c_error", "message": err})
else:
slots = data or []
while isinstance(slots, str):
try:
slots = json.loads(slots)
except Exception:
break
if not isinstance(slots, list):
slots = []
self.send_json(200, slots)
return
if path == "/doctors":
code = """
Запрос1С = Новый Структура("ПараметрыЗапроса", Новый Соответствие);
Ответ = ДМ_СайтРасписание.ПолучитьВрачей(Запрос1С);
Результат = Ответ.ПолучитьТелоКакСтроку();
"""
data, err = call_1c(code)
if err:
self.send_json(502, {"code": "1c_error", "message": err})
else:
doctors = data or []
while isinstance(doctors, str):
try:
doctors = json.loads(doctors)
except Exception:
break
if not isinstance(doctors, list):
doctors = []
for d in doctors:
if isinstance(d, dict):
try:
d["kids_from"] = int(d.get("kids_from", 0))
except (ValueError, TypeError):
d["kids_from"] = 0
self.send_json(200, doctors)
return
if path.startswith("/doctors/") and path.endswith("/services"):
parts = path.split("/")
doctor_id = parts[2]
code = f"""
ВрачСсылка = Справочники.Сотрудники.ПолучитьСсылку(Новый УникальныйИдентификатор("{doctor_id}"));
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 100
| Цены.Номенклатура.Ссылка КАК Ссылка,
| Цены.Номенклатура.Наименование КАК Наименование,
| Цены.Цена КАК Цена
|ИЗ
| РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ) КАК Цены
|ГДЕ
| Цены.Номенклатура.ПометкаУдаления = ЛОЖЬ
| И Цены.Цена > 0
| И (Цены.бит_Сотрудник = &Врач ИЛИ Цены.бит_Сотрудник = ЗНАЧЕНИЕ(Справочник.Сотрудники.ПустаяСсылка))
|УПОРЯДОЧИТЬ ПО
| Цены.Номенклатура.Наименование";
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("Врач", ВрачСсылка);
Таблица = Запрос.Выполнить().Выгрузить();
Массив = Новый Массив;
Длит = ВрачСсылка.ДлительностьПриема;
ДлитМин = ?(ТипЗнч(Длит) = Тип("Дата"), Час(Длит) * 60 + Минута(Длит), 30);
Если ДлитМин < 15 ИЛИ ДлитМин > 60 Тогда ДлитМин = 30; КонецЕсли;
Для Каждого Стр Из Таблица Цикл
Услуга = Новый Структура;
Услуга.Вставить("id", НРег(XMLСтрока(Стр.Ссылка)));
Услуга.Вставить("name", Стр.Наименование);
Услуга.Вставить("duration_min", ДлитМин);
Услуга.Вставить("price", Стр.Цена);
Массив.Добавить(Услуга);
КонецЦикла;
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, Массив);
Результат = ЗаписьJSON.Закрыть();
"""
data, err = call_1c(code)
if err:
self.send_json(502, {"code": "1c_error", "message": err})
else:
services = data or []
while isinstance(services, str):
try:
services = json.loads(services)
except Exception:
break
if not isinstance(services, list):
services = []
for s in services:
if isinstance(s, dict):
try:
s["duration_min"] = int(s.get("duration_min", 30))
except (ValueError, TypeError):
s["duration_min"] = 30
try:
s["price"] = float(s.get("price", 0.0))
except (ValueError, TypeError):
s["price"] = 0.0
self.send_json(200, services)
return
if path == "/schedule":
doctor_id = query.get("doctor_id", [""])[0]
from_str = query.get("from", [""])[0]
to_str = query.get("to", [""])[0]
branch_id = query.get("branch_id", [""])[0]
now = datetime.now()
dt_from = now
if from_str:
try:
dt_from = datetime.fromisoformat(from_str.replace("Z", "+00:00"))
except Exception:
pass
dt_to = dt_from
if to_str:
try:
dt_to = datetime.fromisoformat(to_str.replace("Z", "+00:00"))
except Exception:
pass
if dt_to <= dt_from:
from datetime import timedelta
dt_to = dt_from + timedelta(days=14)
# Determine target doctors
if doctor_id:
target_ids = [doctor_id]
else:
target_ids = get_linked_doctor_ids()
ids_1c_array = ", ".join([f'Новый УникальныйИдентификатор("{x}")' for x in target_ids])
if branch_id:
branch_clause = f'И Графики.Филиал = Справочники.Филиалы.ПолучитьСсылку(Новый УникальныйИдентификатор("{branch_id}"))'
else:
branch_clause = ""
code = f"""
МассивИД = Новый Массив;
"""
for x in target_ids:
code += f'МассивИД.Добавить(Новый УникальныйИдентификатор("{x}"));\n'
code += f"""
МассивСсылок = Новый Массив;
Для Каждого ИД Из МассивИД Цикл
Ссылка = Справочники.Сотрудники.ПолучитьСсылку(ИД);
МассивСсылок.Добавить(Ссылка);
КонецЦикла;
ДатаС = Дата({dt_from.year}, {dt_from.month}, {dt_from.day}, {dt_from.hour}, {dt_from.minute}, {dt_from.second});
ДатаПо = Дата({dt_to.year}, {dt_to.month}, {dt_to.day}, {dt_to.hour}, {dt_to.minute}, {dt_to.second});
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Графики.Сотрудник КАК Сотрудник,
| Графики.Сотрудник.ДлительностьПриема КАК ДлительностьПриема,
| Графики.Дата КАК Дата,
| Графики.ВремяНачала КАК ВремяНачала,
| Графики.ВремяОкончания КАК ВремяОкончания,
| Графики.Кабинет КАК Кабинет,
| Графики.Кабинет.Наименование КАК КабинетИмя,
| Графики.Филиал КАК Филиал
|ИЗ
| РегистрСведений.ГрафикиРаботы КАК Графики
|ГДЕ
| Графики.Дата МЕЖДУ &ДатаС И &ДатаПо
| И Графики.Сотрудник В (&МассивВрачей)
| И Графики.Сотрудник.ПометкаУдаления = ЛОЖЬ
| И (Графики.Филиал.Наименование ПОДОБНО ""%Дана%"" ИЛИ Графики.Филиал.Наименование ПОДОБНО ""%Ляхова 22%"")
| И НЕ Графики.Филиал.Наименование ПОДОБНО ""%Абилити%""
| И НЕ Графики.Филиал.Наименование ПОДОБНО ""%Целитель%""
| {branch_clause}";
Запрос.УстановитьПараметр("МассивВрачей", МассивСсылок);
Запрос.УстановитьПараметр("ДатаС", ДатаС);
Запрос.УстановитьПараметр("ДатаПо", ДатаПо);
Таблица = Запрос.Выполнить().Выгрузить();
// Получаем существующие заявки для блокировки занятых слотов
ЗапросЗаявок = Новый Запрос;
ЗапросЗаявок.Текст =
"ВЫБРАТЬ
| Заявка.Сотрудник КАК Сотрудник,
| Заявка.ДатаНачала КАК ДатаНачала,
| Заявка.ДатаОкончания КАК ДатаОкончания
|ИЗ
| Документ.Заявка КАК Заявка
|ГДЕ
| Заявка.ПометкаУдаления = ЛОЖЬ
| И (Заявка.Состояние.ТипСостояния <> &ТипОтменена ИЛИ Заявка.Состояние.ТипСостояния ЕСТЬ NULL)
| И Заявка.Сотрудник В (&МассивВрачей)
| И Заявка.ДатаНачала < &ДатаПо
| И Заявка.ДатаОкончания > &ДатаС";
ЗапросЗаявок.УстановитьПараметр("МассивВрачей", МассивСсылок);
ЗапросЗаявок.УстановитьПараметр("ДатаС", ДатаС);
ЗапросЗаявок.УстановитьПараметр("ДатаПо", ДатаПо);
ЗапросЗаявок.УстановитьПараметр("ТипОтменена", Перечисления.ТипыСостоянийЗаявок.Отменена);
ТаблицаЗаявок = ЗапросЗаявок.Выполнить().Выгрузить();
Слоты = Новый Массив;
ТекущееВремя = ТекущаяДата();
Для Каждого Стр Из Таблица Цикл
Длит = Стр.ДлительностьПриема;
ШагМинут = ?(ТипЗнч(Длит) = Тип("Дата"), Час(Длит) * 60 + Минута(Длит), 30);
Если ШагМинут < 15 ИЛИ ШагМинут > 60 Тогда ШагМинут = 30; КонецЕсли;
ШагСекунд = ШагМинут * 60;
ДатаСмены = НачалоДня(Стр.Дата);
НачалоСмены = ДатаСмены + (Стр.ВремяНачала - НачалоДня(Стр.ВремяНачала));
КонецСмены = ДатаСмены + (Стр.ВремяОкончания - НачалоДня(Стр.ВремяОкончания));
IDВрача = НРег(XMLСтрока(Стр.Сотрудник));
IDФилиала = НРег(XMLСтрока(Стр.Филиал));
ИмяКабинета = ?(ЗначениеЗаполнено(Стр.Кабинет), Стр.КабинетИмя, "");
Текущий = НачалоСмены;
Пока (Текущий + ШагСекунд) <= КонецСмены Цикл
СлотКонец = Текущий + ШагСекунд;
// Проверка на занятость заявками
Занят = Ложь;
Для Каждого СтрЗаявки Из ТаблицаЗаявок Цикл
Если СтрЗаявки.Сотрудник = Стр.Сотрудник И (СтрЗаявки.ДатаНачала < СлотКонец) И (СтрЗаявки.ДатаОкончания > Текущий) Тогда
Занят = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
Слот = Новый Структура;
Слот.Вставить("doctor_id", IDВрача);
Слот.Вставить("branch_id", IDФилиала);
Слот.Вставить("cabinet", ИмяКабинета);
Слот.Вставить("start", Формат(Текущий, "ДФ=yyyy-MM-ddTHH:mm:ssZ"));
Слот.Вставить("end", Формат(СлотКонец, "ДФ=yyyy-MM-ddTHH:mm:ssZ"));
Слот.Вставить("status", ?(Занят, "busy", "free"));
Слот.Вставить("slot_id", IDВрача + "_" + Формат(Текущий, "ДФ=yyyyMMddHHmmss"));
Слоты.Добавить(Слот);
Текущий = СлотКонец;
КонецЦикла;
КонецЦикла;
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, Слоты);
Результат = ЗаписьJSON.Закрыть();
"""
data, err = call_1c(code)
if err:
self.send_json(502, {"code": "1c_error", "message": err})
else:
self.send_json(200, data or [])
return
if path.startswith("/appointments/"):
appt_id = path.split("/")[2]
code = f"""
ДокСсылка = Документы.Заявка.ПолучитьСсылку(Новый УникальныйИдентификатор("{appt_id}"));
Ответ = Новый Структура;
Ответ.Вставить("appointment_id", "{appt_id}");
Ответ.Вставить("status", "booked");
Если ЗначениеЗаполнено(ДокСсылка) И Не ДокСсылка.Пустая() Тогда
Если ДокСсылка.ПометкаУдаления Тогда
Ответ.Вставить("status", "cancelled");
ИначеЕсли ДокСсылка.Состояние.ТипСостояния = Перечисления.ТипыСостоянийЗаявок.Отменена Тогда
Ответ.Вставить("status", "cancelled");
КонецЕсли;
Ответ.Вставить("start", Формат(ДокСсылка.ДатаНачала, "ДФ=yyyy-MM-ddTHH:mm:ssZ"));
Ответ.Вставить("end", Формат(ДокСсылка.ДатаОкончания, "ДФ=yyyy-MM-ddTHH:mm:ssZ"));
КонецЕсли;
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, Ответ);
Результат = ЗаписьJSON.Закрыть();
"""
data, err = call_1c(code)
if err:
self.send_json(502, {"code": "1c_error", "message": err})
else:
self.send_json(200, data or {"appointment_id": appt_id, "status": "booked"})
return
if path == "/changes":
self.send_json(200, {"version": 1, "doctor_ids": []})
return
self.send_json(404, {"code": "not_found", "message": "Unknown endpoint"})
def do_POST(self):
parsed = urllib.parse.urlparse(self.path)
path = parsed.path.rstrip("/")
content_len = int(self.headers.get("Content-Length", 0))
post_data = self.rfile.read(content_len) if content_len > 0 else b"{}"
try:
req_json = json.loads(post_data.decode("utf-8"))
except Exception:
self.send_json(400, {"code": "bad_request", "message": "Invalid JSON"})
return
if path == "/appointments":
doctor_id = req_json.get("doctor_id", "")
branch_id = req_json.get("branch_id", "")
patient_name = req_json.get("patient_name", "")
patient_phone = req_json.get("patient_phone", "")
comment = req_json.get("comment", "")
start_str = req_json.get("start", "")
if not start_str:
start_str = req_json.get("start_time", "")
if "patient" in req_json and isinstance(req_json["patient"], dict):
p = req_json["patient"]
if not patient_name:
patient_name = p.get("name") or f"{p.get('last_name','')} {p.get('first_name','')} {p.get('middle_name','')}".strip()
if not patient_phone:
patient_phone = p.get("phone", "")
if not comment:
comment = p.get("comment", "")
if not patient_name:
patient_name = "Пациент с сайта"
# Normalize phone digits and generate search patterns
digits = "".join([c for c in patient_phone if c.isdigit()])
if len(digits) >= 10:
tail = digits[-10:]
d = list(tail)
pattern_delims = f"%{d[0]}{d[1]}{d[2]}%{d[3]}{d[4]}{d[5]}%{d[6]}{d[7]}%{d[8]}{d[9]}%"
pattern_solid = f"%{tail}%"
else:
pattern_delims = f"%{digits}%"
pattern_solid = f"%{digits}%"
code = f"""
Попытка
ВрачСсылка = Справочники.Сотрудники.ПолучитьСсылку(Новый УникальныйИдентификатор("{doctor_id}"));
// 1. Поиск существующего клиента по номеру телефона и подмена на него
ЗапросКл = Новый Запрос;
ЗапросКл.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| КИ.Объект КАК Клиент
|ИЗ
| РегистрСведений.КонтактнаяИнформация КАК КИ
|ГДЕ
| КИ.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.Телефон)
| И КИ.Объект ССЫЛКА Справочник.Клиенты
| И КИ.Объект.ПометкаУдаления = ЛОЖЬ
| И (КИ.Представление ПОДОБНО &ШаблонРазд
| ИЛИ КИ.Представление ПОДОБНО &ШаблонСплош
| ИЛИ КИ.Поле3 ПОДОБНО &ШаблонРазд
| ИЛИ КИ.Поле3 ПОДОБНО &ШаблонСплош)";
ЗапросКл.УстановитьПараметр("ШаблонРазд", "{pattern_delims}");
ЗапросКл.УстановитьПараметр("ШаблонСплош", "{pattern_solid}");
ВыбКл = ЗапросКл.Выполнить().Выбрать();
КлиентСсылка = Неопределено;
Если ВыбКл.Следующий() И ЗначениеЗаполнено(ВыбКл.Клиент) Тогда
// Подменяем на найденного клиента из базы 1С
КлиентСсылка = ВыбКл.Клиент;
Иначе
// Клиент не найден -> создаем нового
ОбъектКл = Справочники.Клиенты.СоздатьЭлемент();
ОбъектКл.Наименование = "{patient_name}";
ОбъектКл.Комментарий = "Создан через сайт danamedical.ru";
ОбъектКл.Записать();
КлиентСсылка = ОбъектКл.Ссылка;
Попытка
ВидКИ = Неопределено;
Попытка
ВидКИ = Справочники.ВидыКонтактнойИнформации.ТелефонСотовый;
Исключение
ВидКИ = Справочники.ВидыКонтактнойИнформации.НайтиПоНаименованию("Телефон сотовый");
КонецПопытки;
МЗ = РегистрыСведений.КонтактнаяИнформация.СоздатьМенеджерЗаписи();
МЗ.Объект = КлиентСсылка;
МЗ.Тип = Перечисления.ТипыКонтактнойИнформации.Телефон;
Если ЗначениеЗаполнено(ВидКИ) Тогда
МЗ.Вид = ВидКИ;
КонецЕсли;
МЗ.Представление = "{patient_phone}";
МЗ.Поле3 = "{patient_phone}";
МЗ.Записать(Истина);
Исключение
КонецПопытки;
КонецЕсли;
// Время
ВремяНач = ДМ_СайтAPI.РазобратьISO8601("{start_str}");
Если Не ЗначениеЗаполнено(ВремяНач) Тогда
ВремяНач = ТекущаяДата();
КонецЕсли;
Длит = ВрачСсылка.ДлительностьПриема;
ШагМинут = ?(ТипЗнч(Длит) = Тип("Дата"), Час(Длит) * 60 + Минута(Длит), 30);
Если ШагМинут < 15 ИЛИ ШагМинут > 60 Тогда ШагМинут = 30; КонецЕсли;
// Определение филиала (строго клиника сети Дана)
ФилиалСсылка = Неопределено;
Если ЗначениеЗаполнено("{branch_id}") Тогда
Попытка
ФилиалКанд = Справочники.Филиалы.ПолучитьСсылку(Новый УникальныйИдентификатор("{branch_id}"));
Если ЗначениеЗаполнено(ФилиалКанд) И Не ФилиалКанд.Пустая() Тогда
ИмяФ = ВРег(ФилиалКанд.Наименование);
Если (Найти(ИмяФ, "ДАНА") > 0 ИЛИ Найти(ИмяФ, "ЛЯХОВА 22") > 0)
И Найти(ИмяФ, "АБИЛИТИ") = 0
И Найти(ИмяФ, "ЦЕЛИТЕЛЬ") = 0 Тогда
ФилиалСсылка = ФилиалКанд;
КонецЕсли;
КонецЕсли;
Исключение
КонецПопытки;
КонецЕсли;
Если Не ЗначениеЗаполнено(ФилиалСсылка) Тогда
ЗапросСмены = Новый Запрос;
ЗапросСмены.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Графики.Филиал КАК Филиал
|ИЗ
| РегистрСведений.ГрафикиРаботы КАК Графики
|ГДЕ
| Графики.Сотрудник = &Врач
| И Графики.Дата = НАЧАЛОПЕРИОДА(&ВремяНач, ДЕНЬ)
| И (Графики.Филиал.Наименование ПОДОБНО ""%Дана%"" ИЛИ Графики.Филиал.Наименование ПОДОБНО ""%Ляхова 22%"")
| И НЕ Графики.Филиал.Наименование ПОДОБНО ""%Абилити%""
| И НЕ Графики.Филиал.Наименование ПОДОБНО ""%Целитель%"" ";
ЗапросСмены.УстановитьПараметр("Врач", ВрачСсылка);
ЗапросСмены.УстановитьПараметр("ВремяНач", ВремяНач);
ВыбСмены = ЗапросСмены.Выполнить().Выбрать();
Если ВыбСмены.Следующий() Тогда
ФилиалСсылка = ВыбСмены.Филиал;
КонецЕсли;
КонецЕсли;
Если Не ЗначениеЗаполнено(ФилиалСсылка) Тогда
ЗапросФ = Новый Запрос(
"ВЫБРАТЬ ПЕРВЫЕ 1 Ссылка ИЗ Справочник.Филиалы КАК Филиалы
|ГДЕ Филиалы.ПометкаУдаления = ЛОЖЬ
| И (Филиалы.Наименование ПОДОБНО ""%Дана%"" ИЛИ Филиалы.Наименование ПОДОБНО ""%Ляхова 22%"")
| И НЕ Филиалы.Наименование ПОДОБНО ""%Абилити%""
| И НЕ Филиалы.Наименование ПОДОБНО ""%Целитель%"" ");
ВыбФ = ЗапросФ.Выполнить().Выбрать();
Если ВыбФ.Следующий() Тогда
ФилиалСсылка = ВыбФ.Ссылка;
КонецЕсли;
КонецЕсли;
// Создаем Заявку
Док = Документы.Заявка.СоздатьДокумент();
Док.Дата = ТекущаяДата();
Док.ДатаНачала = ВремяНач;
Док.ДатаОкончания = ВремяНач + ШагМинут * 60;
Док.ВремяНачала = Док.ДатаНачала;
Док.ВремяОкончания = Док.ДатаОкончания;
Док.Сотрудник = ВрачСсылка;
Док.Филиал = ФилиалСсылка;
Док.Клиент = КлиентСсылка;
Док.Состояние = Справочники.ВидыСостоянийЗаявок.Запланирована;
Док.Примечание = "Онлайн-запись: {comment}";
Ист = Справочники.ИсточникиИнформации.НайтиПоНаименованию("Сайт");
Если ЗначениеЗаполнено(Ист) Тогда
Попытка Док.БИТ_ИсточникИнформации = Ист; Исключение КонецПопытки;
КонецЕсли;
Док.Записать(РежимЗаписиДокумента.Запись);
ID = НРег(XMLСтрока(Док.Ссылка));
Ответ = Новый Структура;
Ответ.Вставить("appointment_id", ID);
Ответ.Вставить("status", "booked");
Ответ.Вставить("start", Формат(Док.ДатаНачала, "ДФ=yyyy-MM-ddTHH:mm:ssZ"));
Ответ.Вставить("end", Формат(Док.ДатаОкончания, "ДФ=yyyy-MM-ddTHH:mm:ssZ"));
Ответ.Вставить("branch_id", НРег(XMLСтрока(ФилиалСсылка)));
Ответ.Вставить("patient_id", НРег(XMLСтрока(КлиентСсылка)));
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, Ответ);
Результат = ЗаписьJSON.Закрыть();
Исключение
Результат = "ОШИБКА: " + ОписаниеОшибки();
КонецПопытки;
"""
data, err = call_1c(code)
if err or (isinstance(data, str) and data.startswith("ОШИБКА")):
self.send_json(500, {"code": "booking_failed", "message": err or data})
else:
self.send_json(201, data)
return
self.send_json(404, {"code": "not_found", "message": "Unknown endpoint"})
def run():
server_address = ("0.0.0.0", 8088)
httpd = HTTPServer(server_address, BridgeHandler)
print("Bridge server running on port 8088 with live Postgres link...", flush=True)
httpd.serve_forever()
if __name__ == "__main__":
run()