From f9e5d73868d8b45a7463fc3cb3cb06845cf6af0d Mon Sep 17 00:00:00 2001 From: SERTY Date: Mon, 21 Jul 2025 23:52:53 +0300 Subject: [PATCH] added --- .gitignore | 3 +- database.py | 3 +- files/00105707796831.json | 23 ----- files/00106126844935.json | 23 ----- files/00106202123911.json | 23 ----- files/00106302050149.json | 23 ----- files/00106309062146.json | 23 ----- files/00106309239401.json | 23 ----- files/00106309300916.json | 23 ----- files/00106309330871.json | 23 ----- files/00106905442664.json | 23 ----- files/00108100739722.json | 23 ----- files/00108129540393.json | 23 ----- files/00108722684571.json | 23 ----- files/00108729580581.json | 16 ---- files/00109522991414.json | 23 ----- files/00109529077045.json | 16 ---- files/2024-04-03 copy.json | 14 --- files/2024-04-03.json | 14 --- files/date (2).json | 14 --- sd_api.py | 2 +- sync_logic.py | 183 +++++++++++++++++++++++++++---------- 22 files changed, 140 insertions(+), 424 deletions(-) delete mode 100644 files/00105707796831.json delete mode 100644 files/00106126844935.json delete mode 100644 files/00106202123911.json delete mode 100644 files/00106302050149.json delete mode 100644 files/00106309062146.json delete mode 100644 files/00106309239401.json delete mode 100644 files/00106309300916.json delete mode 100644 files/00106309330871.json delete mode 100644 files/00106905442664.json delete mode 100644 files/00108100739722.json delete mode 100644 files/00108129540393.json delete mode 100644 files/00108722684571.json delete mode 100644 files/00108729580581.json delete mode 100644 files/00109522991414.json delete mode 100644 files/00109529077045.json delete mode 100644 files/2024-04-03 copy.json delete mode 100644 files/2024-04-03.json delete mode 100644 files/date (2).json diff --git a/.gitignore b/.gitignore index 956ece1..26db683 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ docker-compose.yml *.db __* -*.json \ No newline at end of file +*.json +files/* \ No newline at end of file diff --git a/database.py b/database.py index b4624b0..110e8e4 100644 --- a/database.py +++ b/database.py @@ -115,7 +115,8 @@ class DatabaseManager: uuid TEXT PRIMARY KEY, owner_uuid TEXT, clean_anydesk_id TEXT, - clean_teamviewer_id TEXT + clean_teamviewer_id TEXT, + lastModifiedDate TEXT )""") # Новая таблица для кэширования UUID справочников diff --git a/files/00105707796831.json b/files/00105707796831.json deleted file mode 100644 index 16c1fe4..0000000 --- a/files/00105707796831.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "modelName": "АТОЛ 25Ф", - "serialNumber": "00105707796831", - "RNM": "0004421585034085", - "organizationName": "ОБЩЕСТВО С ОГРАНИЧЕННОЙ ОТВЕТСТВЕННОСТЬЮ \"МЕГА СЕРВИС\"", - "fn_serial": "9287440301117169", - "datetime_reg": "2021-06-17 17:01:00", - "dateTime_end": "2024-07-01 00:00:00", - "ofdName": "АО <КАЛУГА АСТРАЛ>", - "bootVersion": "3.0.8319", - "ffdVersion": "105", - "INN": "7730255999", - "attribute_excise": "False", - "attribute_marked": "Не поддерживается в текущей версии драйвера", - "fnExecution": "Не поддерживается в текущей версии драйвера", - "hostname": "13CASH08", - "url_rms": "https://aom-himki.iiko.it:443/resto", - "teamviever_id": "1050831481", - "anydesk_id": "334171076", - "total_space_sys": "69.48 Gb", - "free_space_sys": "45.86 Gb", - "current_time": "2024-05-08 20:42:24" -} \ No newline at end of file diff --git a/files/00106126844935.json b/files/00106126844935.json deleted file mode 100644 index 9a2fefb..0000000 --- a/files/00106126844935.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "modelName": "АТОЛ 30Ф", - "serialNumber": "00106126844935", - "RNM": "0007195642054348", - "organizationName": "ИП Зайцев Егор Владимирович", - "fn_serial": "7281440500463082", - "datetime_reg": "2023-04-24 21:05:00", - "dateTime_end": "2024-06-07 00:00:00", - "ofdName": "ООО Такском", - "bootVersion": "5.8.100", - "ffdVersion": "120", - "INN": "110120364802", - "attribute_excise": "True", - "attribute_marked": "True", - "fnExecution": "ФН-1.2 исполнение Ин15-3 ", - "hostname": "AVE-SHAVE-GK", - "url_rms": "https://ave-shawe.iiko.it:443/resto", - "teamviever_id": "None", - "anydesk_id": "391033027", - "total_space_sys": "58.13 Gb", - "free_space_sys": "27.29 Gb", - "current_time": "2024-05-09 14:15:56" -} \ No newline at end of file diff --git a/files/00106202123911.json b/files/00106202123911.json deleted file mode 100644 index ad4cd95..0000000 --- a/files/00106202123911.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "modelName": "АТОЛ 55Ф", - "serialNumber": "00106202123911", - "RNM": "0002593555046068", - "organizationName": "ООО \"ТЕАТРАЛЬНАЯ\"", - "fn_serial": "7284440500258734", - "datetime_reg": "2023-06-30 14:08:00", - "dateTime_end": "2024-08-13 00:00:00", - "ofdName": "сбис", - "bootVersion": "5.8.100", - "ffdVersion": "120", - "INN": "6827024224 ", - "attribute_excise": "True", - "attribute_marked": "True", - "fnExecution": "ФН-1.2 исполнение Ав15-3 ", - "hostname": "User-PC80", - "url_rms": "https://chainik-cloud.iiko.it:443/resto", - "teamviever_id": "593526432", - "anydesk_id": "1919438899", - "total_space_sys": "111.25 Gb", - "free_space_sys": "73.01 Gb", - "current_time": "2024-05-07 18:20:55" -} \ No newline at end of file diff --git a/files/00106302050149.json b/files/00106302050149.json deleted file mode 100644 index 3260d64..0000000 --- a/files/00106302050149.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "modelName": "АТОЛ FPrint-22ПТК", - "serialNumber": "00106302050149", - "RNM": "0000839196015549", - "organizationName": "ИП АВАНЕСОВА НАТАЛЬЯ НОРДЕЕВНА", - "fn_serial": "7281440501031166", - "datetime_reg": "2023-05-28 10:18:00", - "dateTime_end": "2024-07-11 00:00:00", - "ofdName": "ООО \"Такском\"", - "bootVersion": "5.8.100", - "ffdVersion": "120", - "INN": "507901506303", - "attribute_excise": "True", - "attribute_marked": "True", - "fnExecution": "ФН-1.2 исполнение Ин15-3 ", - "hostname": "RASSKAZOVKA_BAGET", - "url_rms": "http://88.99.60.29:8187/resto", - "teamviever_id": "1145765525", - "anydesk_id": "952794502", - "total_space_sys": "59.62 Gb", - "free_space_sys": "7.44 Gb", - "current_time": "2024-05-06 02:02:23" -} \ No newline at end of file diff --git a/files/00106309062146.json b/files/00106309062146.json deleted file mode 100644 index 5be453f..0000000 --- a/files/00106309062146.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "modelName": "АТОЛ FPrint-22ПТК", - "serialNumber": "00106309062146", - "RNM": "0005464060042782", - "organizationName": "ИП ИЛЬЕНКО МАКСИМ МИХАЙЛОВИЧ", - "fn_serial": "9287440301158465", - "datetime_reg": "2021-05-21 16:35:00", - "dateTime_end": "2024-06-04 00:00:00", - "ofdName": "ООО \"Эвотор ОФД\"", - "bootVersion": "5.8.100", - "ffdVersion": "105", - "INN": "501300190904", - "attribute_excise": "False", - "attribute_marked": "Не поддерживается в текущей версии драйвера", - "fnExecution": "Не поддерживается в текущей версии драйвера", - "hostname": "Kaldis_ST-8", - "url_rms": "https://kaldis-st-8.iiko.it:443/resto", - "teamviever_id": "130045574", - "anydesk_id": "856695376", - "total_space_sys": "59.62 Gb", - "free_space_sys": "26.18 Gb", - "current_time": "2024-05-09 01:11:30" -} \ No newline at end of file diff --git a/files/00106309239401.json b/files/00106309239401.json deleted file mode 100644 index a95bdde..0000000 --- a/files/00106309239401.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "modelName": "АТОЛ FPrint-22ПТК", - "serialNumber": "00106309239401", - "RNM": "0005463930014863", - "organizationName": "ИП ИЛЬЕНКО МАКСИМ МИХАЙЛОВИЧ", - "fn_serial": "9287440301158440", - "datetime_reg": "2021-05-21 15:58:00", - "dateTime_end": "2024-06-04 00:00:00", - "ofdName": "ООО \"Эвотор ОФД\"", - "bootVersion": "5.8.100", - "ffdVersion": "105", - "INN": "501300190904", - "attribute_excise": "False", - "attribute_marked": "False", - "fnExecution": "", - "hostname": "Kaldis_ST-7", - "url_rms": "https://kaldis-st-7.iiko.it:443/resto", - "teamviever_id": "130042147", - "anydesk_id": "595633117", - "total_space_sys": "59.62 Gb", - "free_space_sys": "20.22 Gb", - "current_time": "2024-05-06 01:41:41" -} \ No newline at end of file diff --git a/files/00106309300916.json b/files/00106309300916.json deleted file mode 100644 index 4c334cb..0000000 --- a/files/00106309300916.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "modelName": "АТОЛ FPrint-22ПТК", - "serialNumber": "00106309300916", - "RNM": "0006138955056715", - "organizationName": "ИП ИЛЬЕНКО МАРИЯ ВЛАДИМИРОВНА", - "fn_serial": "7281440701572965", - "datetime_reg": "2024-04-23 21:49:00", - "dateTime_end": "2025-06-07 00:00:00", - "ofdName": "ООО \"Эвотор ОФД\"", - "bootVersion": "5.8.100", - "ffdVersion": "120", - "INN": "504005415507", - "attribute_excise": "True", - "attribute_marked": "True", - "fnExecution": "ФН-1.2 исполнение Ин15-3 ", - "hostname": "POS-Lianozovo", - "url_rms": "https://kaldis-lianozovo.iiko.it:443/resto", - "teamviever_id": "1493401222", - "anydesk_id": "668050369", - "total_space_sys": "59.14 Gb", - "free_space_sys": "7.49 Gb", - "current_time": "2024-05-09 23:16:42" -} \ No newline at end of file diff --git a/files/00106309330871.json b/files/00106309330871.json deleted file mode 100644 index 54e304e..0000000 --- a/files/00106309330871.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "modelName": "АТОЛ FPrint-22ПТК", - "serialNumber": "00106309330871", - "RNM": "0002792971023790", - "organizationName": "ООО \"ФинЦентр\"", - "fn_serial": "9961440300055208", - "datetime_reg": "2023-02-23 09:04:00", - "dateTime_end": "2026-03-09 00:00:00", - "ofdName": "АО <Калуга Астрал>", - "bootVersion": "5.8.100", - "ffdVersion": "120", - "INN": "7743884031 ", - "attribute_excise": "False", - "attribute_marked": "True", - "fnExecution": "ФН-1.1М исполнение Ин36-1М ", - "hostname": "POS-W10", - "url_rms": "https://press-bar-moskva-m-servis.iiko.it:443/resto", - "teamviever_id": "1231759409", - "anydesk_id": "407482388", - "total_space_sys": "59.62 Gb", - "free_space_sys": "28.14 Gb", - "current_time": "2024-05-06 12:25:56" -} \ No newline at end of file diff --git a/files/00106905442664.json b/files/00106905442664.json deleted file mode 100644 index ce29ba2..0000000 --- a/files/00106905442664.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "modelName": "АТОЛ 77Ф", - "serialNumber": "00106905442664", - "RNM": "0001326280045003", - "organizationName": "АВАНЕСОВА НАТАЛЬЯ НОРДЕЕВНА", - "fn_serial": "7380440700519749", - "datetime_reg": "2024-04-05 14:58:00", - "dateTime_end": "2025-05-20 00:00:00", - "ofdName": "ООО <Такском>", - "bootVersion": "5.8.100", - "ffdVersion": "120", - "INN": "507901506303", - "attribute_excise": "True", - "attribute_marked": "True", - "fnExecution": "ФН-1.2 исполнение Ин15-4 ", - "hostname": "DESKTOP-IUSVUSE", - "url_rms": "http://88.99.60.29:8187/resto", - "teamviever_id": "1284989181", - "anydesk_id": "None", - "total_space_sys": "59.04 Gb", - "free_space_sys": "18.64 Gb", - "current_time": "2024-05-06 02:08:13" -} \ No newline at end of file diff --git a/files/00108100739722.json b/files/00108100739722.json deleted file mode 100644 index 6dbc216..0000000 --- a/files/00108100739722.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "modelName": "АТОЛ 20Ф", - "serialNumber": "00108100739722", - "RNM": "0001781789038839", - "organizationName": "ИП Пилин Игорь Андреевич", - "fn_serial": "9961440300916272", - "datetime_reg": "2022-08-18 08:36:00", - "dateTime_end": "2025-09-01 00:00:00", - "ofdName": "АО \"ЭСК\"", - "bootVersion": "3.0.4253", - "ffdVersion": "105", - "INN": "344309628497", - "attribute_excise": "False", - "attribute_marked": "False", - "fnExecution": "", - "hostname": "WIN-DMS6GB0U6TO", - "url_rms": "https://3-sushi-mira.iiko.it:443/resto", - "teamviever_id": "1285328750", - "anydesk_id": "998297587", - "total_space_sys": "111.38 Gb", - "free_space_sys": "63.19 Gb", - "current_time": "2024-05-09 07:08:03" -} \ No newline at end of file diff --git a/files/00108129540393.json b/files/00108129540393.json deleted file mode 100644 index 12688b3..0000000 --- a/files/00108129540393.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "modelName": "АТОЛ 20Ф", - "serialNumber": "00108129540393", - "RNM": "0007978093023550", - "organizationName": "ИП Ким Вячеслав", - "fn_serial": "7380440700292820", - "datetime_reg": "2024-04-11 15:18:00", - "dateTime_end": "2025-05-26 00:00:00", - "ofdName": "ООО ПЕТЕР-СЕРВИС Спецтехнологии", - "bootVersion": "5.8.100", - "ffdVersion": "120", - "INN": "650125159002", - "attribute_excise": "True", - "attribute_marked": "True", - "fnExecution": "ФН-1.2 исполнение Ин15-4 ", - "hostname": "KASSA2-KHABAR", - "url_rms": "https://mirine-brosko-habarovsk.iiko.it:443/resto", - "teamviever_id": "None", - "anydesk_id": "1063125576", - "total_space_sys": "59.09 Gb", - "free_space_sys": "34.89 Gb", - "current_time": "2024-05-07 12:27:50" -} \ No newline at end of file diff --git a/files/00108722684571.json b/files/00108722684571.json deleted file mode 100644 index ffc0b46..0000000 --- a/files/00108722684571.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "modelName": "АТОЛ 27Ф", - "serialNumber": "00108722684571", - "RNM": "0005975228045176", - "organizationName": "Индивидуальный предприниматель Аванесова Наталья Нордеевна", - "fn_serial": "7380440700402306", - "datetime_reg": "2024-04-05 14:43:00", - "dateTime_end": "2025-05-20 00:00:00", - "ofdName": "ООО Такском", - "bootVersion": "5.10.0", - "ffdVersion": "120", - "INN": "507901506303", - "attribute_excise": "True", - "attribute_marked": "True", - "fnExecution": "ФН-1.2 исполнение Ин15-4 ", - "hostname": "Burkina-kofe-NEW", - "url_rms": "http://88.99.60.29:8187/resto", - "teamviever_id": "650019532", - "anydesk_id": "1562660748", - "total_space_sys": "59.62 Gb", - "free_space_sys": "34.64 Gb", - "current_time": "2024-05-06 02:16:47" -} \ No newline at end of file diff --git a/files/00108729580581.json b/files/00108729580581.json deleted file mode 100644 index 55082de..0000000 --- a/files/00108729580581.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "modelName": "АТОЛ 27Ф", - "serialNumber": "00108729580581", - "RNM": "0007037066025713", - "organizationName": "ИП ДАВЛАТОВ МИРЗОШО РАХМАТШОЕВИЧ", - "fn_serial": "7281440500179066", - "datetime_reg": "2023-02-16 10:39:00", - "dateTime_end": "2024-05-31 00:00:00", - "ofdName": "ООО Эвотор ОФД", - "bootVersion": "5.7.13", - "ffdVersion": "105", - "INN": "670602568722", - "fnExecution": "", - "attribute_podakciz": "False", - "attribute_marked": "False" -} \ No newline at end of file diff --git a/files/00109522991414.json b/files/00109522991414.json deleted file mode 100644 index c697158..0000000 --- a/files/00109522991414.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "modelName": "АТОЛ 22 v2 Ф", - "serialNumber": "00109522991414", - "RNM": "0007882610017388", - "organizationName": "ИП СОКЛАКОВА АННА СЕРГЕЕВНА", - "fn_serial": "7380440700100735", - "datetime_reg": "2024-02-29 13:10:00", - "dateTime_end": "2025-04-14 00:00:00", - "ofdName": "ООО Такском", - "bootVersion": "5.8.17", - "ffdVersion": "120", - "INN": "771315163893", - "attribute_excise": "True", - "attribute_marked": "True", - "fnExecution": "ФН-1.2 исполнение Ин15-4 ", - "hostname": "VESTERDAM", - "url_rms": "https://pokolenie-kofe-vesterdam.iiko.it:443/resto", - "teamviever_id": "743932094", - "anydesk_id": "369655451", - "total_space_sys": "58.13 Gb", - "free_space_sys": "21.30 Gb", - "current_time": "2024-05-09 10:38:44" -} \ No newline at end of file diff --git a/files/00109529077045.json b/files/00109529077045.json deleted file mode 100644 index 63ed033..0000000 --- a/files/00109529077045.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "modelName": "АТОЛ 22 v2 Ф", - "serialNumber": "00109529077045", - "RNM": "0006899083013508", - "organizationName": "ООО \"ГАВАНА\"", - "fn_serial": "7281440701652919", - "datetime_reg": "2024-03-20 11:43:00", - "dateTime_end": "2025-07-03 00:00:00", - "ofdName": "ООО Эвотор ОФД", - "bootVersion": "5.8.20", - "ffdVersion": "105", - "INN": "5904388065 ", - "fnExecution": "None", - "attribute_podakciz": "True", - "attribute_marked": "False" -} \ No newline at end of file diff --git a/files/2024-04-03 copy.json b/files/2024-04-03 copy.json deleted file mode 100644 index 827af6d..0000000 --- a/files/2024-04-03 copy.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "modelName": "АТОЛ 22 v2 Ф", - "serialNumber": "00109525422090", - "RNM": "0000000004454545", - "organizationName": "ООО \"Предприятие\"", - "fn_serial": "7380440700425457", - "datetime_reg": "2024-04-02 03:35:00", - "dateTime_end": "2079-12-31 00:00:00", - "ofdName": "ООО \"Эвотор ОФД\"", - "bootVersion": "5.8.100", - "ffdVersion": "120", - "fnExecution": "Эмулятор ФН с поддержкой ФФД 1.2 ", - "INN": "1111222233 " -} diff --git a/files/2024-04-03.json b/files/2024-04-03.json deleted file mode 100644 index 7fe4b05..0000000 --- a/files/2024-04-03.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "modelName": "АТОЛ 22 v2 Ф", - "serialNumber": "00109525422090", - "RNM": "0000000001032218", - "organizationName": "ООО \"Предприятие\"", - "fn_serial": "7380440700425457", - "datetime_reg": "2024-04-02 03:35:00", - "dateTime_end": "2057-12-31 00:00:00", - "ofdName": "ООО \"Эвотор ОФД\"", - "bootVersion": "5.8.100", - "ffdVersion": "120", - "fnExecution": "Эмулятор ФН с поддержкой ФФД 1.2 ", - "INN": "1111222233 " -} diff --git a/files/date (2).json b/files/date (2).json deleted file mode 100644 index 2c6cf34..0000000 --- a/files/date (2).json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "modelName": "АТОЛ 27Ф", - "serialNumber": "00108722318182", - "RNM": "0006025947045252", - "organizationName": "ООО АЛЕКС-СЕРВИС", - "fn_serial": "7281440701487984", - "datetime_reg": "2024-02-27 09:13:00", - "dateTime_end": "2025-04-12 00:00:00", - "ofdName": "ООО Такском", - "bootVersion": "5.10.0", - "ffdVersion": "120", - "fnExecution": "ФН-1.2 исполнение Ин15-3", - "INN": "5258144870 " -} \ No newline at end of file diff --git a/sd_api.py b/sd_api.py index 1d02cd1..035c767 100644 --- a/sd_api.py +++ b/sd_api.py @@ -79,7 +79,7 @@ class ServiceDeskClient: """Получает список всех рабочих станций.""" log.info("Запрос списка всех рабочих станций из ServiceDesk...") params = { - 'attrs': 'UUID,owner,AnyDesk,Teamviewer' + 'attrs': 'UUID,owner,AnyDesk,Teamviewerm,lastModifiedDate' } workstations = self._make_request('POST', config.FIND_WORKSTATIONS_URL, params=params) log.info(f"Получено {len(workstations)} записей о рабочих станциях.") diff --git a/sync_logic.py b/sync_logic.py index bfe1c74..2b7162b 100644 --- a/sync_logic.py +++ b/sync_logic.py @@ -17,9 +17,7 @@ log = logging.getLogger(__name__) def _clean_sd_remote_id(raw_id: Optional[str]) -> Optional[str]: """ - Очищает ID удаленного доступа, полученный из ServiceDesk. - (Эта функция была ранее в ftp_parser, но перенесена сюда, - так как "грязные" ID приходят из SD). + Очищает ID удаленного доступа, полученный из ServiceDesk.. """ if not raw_id or raw_id.lower() == 'none': return None @@ -157,9 +155,10 @@ class Synchronizer: ws['UUID'], owner['UUID'], _clean_sd_remote_id(ws.get('AnyDesk')), - _clean_sd_remote_id(ws.get('Teamviewer')) + _clean_sd_remote_id(ws.get('Teamviewer')), + ws.get('lastModifiedDate') )) - self.db.bulk_insert('workstations', ['uuid', 'owner_uuid', 'clean_anydesk_id', 'clean_teamviewer_id'], workstations_data) + self.db.bulk_insert('workstations', ['uuid', 'owner_uuid', 'clean_anydesk_id', 'clean_teamviewer_id', 'lastModifiedDate'], workstations_data) # 6. Логика синхронизации self._update_existing_frs() @@ -172,52 +171,110 @@ class Synchronizer: def _update_existing_frs(self): - """Находит и обновляет ФР с отличающимися датами.""" - log.info("Поиск ФР для обновления даты окончания ФН...") + """ + Находит и обновляет ФР с отличающимися датами окончания ФН, + воспроизводя проверенную логику в более производительном виде. + """ + log.info("Поиск ФР для обновления...") + + # 1. SQL-запрос для поиска расхождений. + # Он объединяет таблицы по serialNumber и выбирает все необходимые поля + # сразу, чтобы избежать вложенных циклов. query = """ SELECT pos.serialNumber, - pos.dateTime_end AS pos_date, - pos.datetime_reg AS pos_reg_date, + pos.dateTime_end AS pos_date_end, + pos.datetime_reg AS pos_date_reg, pos.fn_serial AS pos_fn_serial, pos.RNM AS pos_rnm, pos.bootVersion AS pos_boot_version, pos.organizationName AS pos_org_name, pos.INN AS pos_inn, sd.UUID AS sd_uuid, - sd.dateTime_end AS sd_date + sd.dateTime_end AS sd_date_end FROM pos_fiscals pos JOIN sd_fiscals sd ON pos.serialNumber = sd.serialNumber - WHERE pos.dateTime_end != sd.dateTime_end """ - # Дополнительное условие по дате, как в старом коде, можно добавить сюда, если нужно - # WHERE date(pos.dateTime_end) > date(sd.dateTime_end, '-65 day') AND pos.dateTime_end != sd.dateTime_end + + records_to_check = self.db._execute_query(query, fetch='all') + update_counter = 0 - records_to_update = self.db._execute_query(query, fetch='all') - log.info(f"Найдено {len(records_to_update)} ФР для обновления.") - - for rec in records_to_update: - # Проверка, что дата действительно новее (избегаем "старых" данных с FTP) - # Это простая проверка, можно усложнить, если нужно - if parser.parse(rec['pos_date']) < parser.parse(rec['sd_date']): - log.warning(f"Пропуск обновления для S/N {rec['serialNumber']}: дата с FTP ({rec['pos_date']}) " - f"старше, чем в SD ({rec['sd_date']}).") - continue - - legal_name = f"{rec['pos_org_name']} ИНН:{rec['pos_inn']}" if rec['pos_org_name'] and rec['pos_inn'] else "ЗАКОНЧИЛСЯ ФН" - - params_to_update = { - 'FNNumber': rec['pos_fn_serial'], - 'FNExpireDate': parser.parse(rec['pos_date']).strftime('%Y.%m.%d %H:%M:%S'), - 'KKTRegDate': parser.parse(rec['pos_reg_date']).strftime('%Y.%m.%d %H:%M:%S'), - 'LegalName': legal_name, - 'RNKKT': rec['pos_rnm'], - 'FRDownloader': rec['pos_boot_version'] - } + for rec in records_to_check: try: - self.sd.update_fr(rec['sd_uuid'], params_to_update) - except ServiceDeskAPIError as e: - log.error(f"Не удалось обновить ФР с UUID {rec['sd_uuid']}: {e}") + # 2. Приводим даты к одному "знаменателю" - объектам datetime + # dateutil.parser отлично справляется с разными форматами + # Проверяем, что обе даты существуют, прежде чем их парсить + if not rec['pos_date_end'] or not rec['sd_date_end']: + log.warning(f"Пропуск сравнения для S/N {rec['serialNumber']}: одна из дат отсутствует.") + continue + + pos_date = parser.parse(rec['pos_date_end']) + sd_date = parser.parse(rec['sd_date_end']) + + # 3. Сравниваем даты. Если они различаются, готовим обновление. + # Проверяем на неравенство. Величина различия не важна. + if pos_date != sd_date: + log.info(f"Найдено расхождение в дате для S/N {rec['serialNumber']} (UUID: {rec['sd_uuid']}). " + f"FTP: {pos_date}, SD: {sd_date}. Подготовка к обновлению.") + + # 4. Формируем данные для обновления + + # 5. Проверяем, закончился ли ФН + if rec['pos_rnm'] == '0000000000000000': + legal_name = 'ЗАКОНЧИЛСЯ ФН' + else: + legal_name = f"{rec['pos_org_name']} ИНН:{rec['pos_inn']}" if rec['pos_org_name'] and rec['pos_inn'] else rec['pos_org_name'] + + # Форматируем даты для API ServiceDesk + formatted_expire_date = pos_date.strftime('%Y.%m.%d %H:%M:%S') + # Убеждаемся, что дата регистрации тоже есть, прежде чем форматировать + formatted_reg_date = parser.parse(rec['pos_date_reg']).strftime('%Y.%m.%d %H:%M:%S') if rec['pos_date_reg'] else None + + params_to_update = { + 'FNNumber': rec['pos_fn_serial'], + 'FNExpireDate': formatted_expire_date, + 'LegalName': legal_name, + 'RNKKT': rec['pos_rnm'], + 'FRDownloader': rec['pos_boot_version'], + 'KKTRegDate': formatted_reg_date + } + + # Удаляем ключи с None-значениями, чтобы не отправлять их в API, если они не обязательны + params_to_update = {k: v for k, v in params_to_update.items() if v is not None} + + try: + self.sd.update_fr(rec['sd_uuid'], params_to_update) + update_counter += 1 + except ServiceDeskAPIError as e: + log.error(f"Не удалось обновить ФР с UUID {rec['sd_uuid']}: {e}") + + except (parser.ParserError, TypeError) as e: + log.error(f"Ошибка парсинга даты для S/N {rec['serialNumber']}. " + f"FTP_date='{rec['pos_date_end']}', SD_date='{rec['sd_date_end']}'. Ошибка: {e}") + continue + + log.info(f"Проверка обновлений завершена. Обновлено записей: {update_counter}.") + + def _find_lookup_uuid_by_substring(self, lookup_type: str, substring: str) -> Optional[str]: + """ + Ищет UUID в кэше справочников по подстроке в ключе (title). + Например, ищет '13' в ключе '13 мес.'. + + :param lookup_type: Тип справочника ('SrokiFN', 'FFD', etc.). + :param substring: Подстрока для поиска (например, '13'). + :return: UUID или None, если не найдено. + """ + if not substring or lookup_type not in self.lookup_cache: + return None + + # Проходим по всем парам (title, uuid) в нужном справочнике + for title, uuid in self.lookup_cache[lookup_type].items(): + # Ищем точное вхождение подстроки, окруженное не-цифрами или границами строки, + # чтобы '15' не совпало с '150'. + if re.search(r'\b' + re.escape(substring) + r'\b', title): + return uuid + return None + def _create_new_frs(self): """Находит, определяет владельца и создает новые ФР.""" @@ -238,10 +295,14 @@ class Synchronizer: model_uuid = self.lookup_cache.get('ModeliFR', {}).get(fr['modelName']) srok_fn_str = _extract_srok_fn_from_execution(fr['fnExecution']) - srok_fn_uuid = self.lookup_cache.get('SrokiFN', {}).get(srok_fn_str) if srok_fn_str else None + if not srok_fn_str: + srok_fn_str = '13' + log.warning(f"Для S/N {fr['serialNumber']} не удалось определить срок ФН из fnExecution. " + f"Установлен срок по умолчанию: {srok_fn_str} мес.") + srok_fn_uuid = self._find_lookup_uuid_by_substring('SrokiFN', srok_fn_str) ffd_version_str = _map_ffd_version(fr['ffdVersion']) - ffd_uuid = self.lookup_cache.get('FFD', {}).get(ffd_version_str) if ffd_version_str else None + ffd_uuid = self._find_lookup_uuid_by_substring('FFD', ffd_version_str) if not all([model_uuid, srok_fn_uuid, ffd_uuid]): log.error(f"Не удалось создать ФР с S/N {fr['serialNumber']}: не найдены UUID для справочников. " @@ -270,20 +331,45 @@ class Synchronizer: log.error(f"Не удалось создать ФР с S/N {fr['serialNumber']}: {e}") def _find_owner_uuid(self, anydesk_id: Optional[str], teamviewer_id: Optional[str]) -> str: - """Ищет владельца по ID. Возвращает UUID владельца или UUID по умолчанию.""" + """ + Ищет владельца по ID. При наличии нескольких совпадений для одного ID, + выбирает запись с самой свежей датой lastModifiedDate. + Возвращает UUID владельца или UUID по умолчанию. + """ - def query_owner(id_value, id_type_column): + def query_best_owner(id_value: Optional[str], id_type_column: str) -> Optional[str]: + """ + Запрашивает всех владельцев по ID и возвращает UUID самого "свежего". + """ if not id_value: return None - query = f"SELECT owner_uuid FROM workstations WHERE {id_type_column} = ?" - result = self.db._execute_query(query, (id_value,), fetch='one') - return result['owner_uuid'] if result else None + + # Запрашиваем всех кандидатов, сортируя по дате в порядке убывания (самые свежие - первые) + query = f""" + SELECT owner_uuid, lastModifiedDate + FROM workstations + WHERE {id_type_column} = ? + ORDER BY lastModifiedDate DESC + """ + + results = self.db._execute_query(query, (id_value,), fetch='all') + + if not results: + return None + + if len(results) > 1: + log.warning(f"Найдено несколько ({len(results)}) рабочих станций с {id_type_column} = {id_value}. " + f"Выбрана самая свежая запись от {results[0]['lastModifiedDate']}.") + + # Так как мы отсортировали по DESC, первая запись и есть самая свежая + return results[0]['owner_uuid'] - owner_by_anydesk = query_owner(anydesk_id, 'clean_anydesk_id') - owner_by_teamviewer = query_owner(teamviewer_id, 'clean_teamviewer_id') + owner_by_anydesk = query_best_owner(anydesk_id, 'clean_anydesk_id') + owner_by_teamviewer = query_best_owner(teamviewer_id, 'clean_teamviewer_id') + # Логика выбора остается прежней, но теперь она работает с "лучшими" кандидатами if owner_by_anydesk and owner_by_teamviewer and owner_by_anydesk == owner_by_teamviewer: - log.info(f"Владелец {owner_by_anydesk} найден по обоим ID: Anydesk={anydesk_id}, TV={teamviewer_id}.") + log.info(f"Владелец {owner_by_anydesk} уверенно найден по обоим ID: Anydesk={anydesk_id}, TV={teamviewer_id}.") return owner_by_anydesk if owner_by_anydesk: @@ -296,4 +382,5 @@ class Synchronizer: log.warning(f"Владелец не найден для Anydesk={anydesk_id}, TV={teamviewer_id}. " f"Будет назначен владелец по умолчанию: {config.DEFAULT_OWNER_UUID}") - return config.DEFAULT_OWNER_UUID \ No newline at end of file + return config.DEFAULT_OWNER_UUID + \ No newline at end of file