From 9980c1f002981ca644d813501e8e4a5ebd76fe0a Mon Sep 17 00:00:00 2001 From: "MH.Dmitrii" Date: Thu, 8 Jan 2026 17:51:12 +0300 Subject: [PATCH] api 1.6 --- server/backend/api/realisation.py | 186 +++++++++++++++--- .../handlers/ozon_purchases_handler.py | 6 +- .../handlers/ozon_wb_yandex_com_handler.py | 2 +- .../backend/handlers/wb_purchases_handler.py | 2 - server/backend/schemas/pydantic.py | 8 +- server/backend/services/excel.py | 157 +++++++++++++-- server/backend/services/validating_files.py | 2 +- 7 files changed, 316 insertions(+), 47 deletions(-) diff --git a/server/backend/api/realisation.py b/server/backend/api/realisation.py index f6866ff..4eb4bec 100644 --- a/server/backend/api/realisation.py +++ b/server/backend/api/realisation.py @@ -1,30 +1,170 @@ import requests import json import pandas as pd -from server.backend.schemas.pydantic import settings, CreateDocumentParams +from server.backend.schemas.pydantic import settings from server.backend.api.session import get_session -session = get_session({ - "Content-Type": "application/json", - "Accept": "application/json", -}) +class DocumentCreation: + def __init__(self, URL): + self.session = get_session({ + "Content-Type": "application/json", + "Accept": "application/json", + }) + self.url = URL -def create_document(**kwargs) -> int: - data = { - "Дата": DATE, - #"ВидОперации": params.OPERATION, - "Организация_Key": settings.COMPANY, - "Контрагент_Key": CONTRACTOR, - "Склад_Key": settings.STORE, - "ДоговорКонтрагента_Key": CONTRACT, - "ДокументБезНДС": "false", - "СуммаВключаетНДС": "true", - "СчетУчетаРасчетовЗаПосредническиеУслуги_Key": ACCOUNT_INTERMEDIARY_SERVICES, - "СчетУчетаРасчетовПоАвансамПолученным_Key": ACCOUNT_ADVANCES_RECEIVED, - "СчетУчетаРасчетовПоАвансамВыданным_Key": ACCOUNT_ADVANCES_ISSUED, - "СчетУчетаРасчетовСКонтрагентом_Key": ACCOUNT_WITH_COUNTERPARTY, - } + def save_key_txt(self, document_key: str, filename: str = "./excel_files/documents.txt"): + with open(filename, "a", encoding="utf-8") as f: + f.write(document_key + "\n") - response = session.post(settings.URL_REALISATION, json=data) - response.raise_for_status() - return response.status_code \ No newline at end of file + def build_dict(self, **kwargs) -> dict: + return dict(kwargs) + + def create_document(self, **kwargs) -> str: + data = self.build_dict(**kwargs) + + self.contragent = kwargs.get("Контрагент_Key") + response = self.session.post(self.url, json=data) + response.raise_for_status() + result = response.json() + document_key = result.get("Ref_Key") or result.get("Ссылка_Key") + + if not document_key: + raise RuntimeError(f"Не удалось получить ключ документа: {result}") + + self.save_key_txt(document_key) + return document_key + + def fill_document_items_purchase(self, document_key: str, validated_rows: list): + line_url = f"{self.url}({document_key})" + + response = self.session.get(line_url) + response.raise_for_status() + document_data = response.json() + + items = document_data.get("Товары", []) + start_line = len(items) + + for idx, row in enumerate(validated_rows, start=1): + new_item = { + "LineNumber": str(start_line + idx), + "Номенклатура_Key": row.ref_key, + "Количество": row.counts, + "ЕдиницаИзмерения_Key": settings.MEASURE, + "Цена": row.price / row.counts if row.counts else 0, + "Сумма": row.price, + "СчетУчета_Key": settings.A45_02, + "СчетДоходов_Key": settings.A90_01_1, + "СчетРасходов_Key": settings.A90_02_1, + "СтавкаНДС": "НДС5", + "СуммаНДС": row.price * 5 / 105, + "СчетУчетаНДСПоРеализации_Key": settings.A90_03, + } + items.append(new_item) + + document_data["Товары"] = items + patch_response = self.session.patch(line_url, json=document_data) + patch_response.raise_for_status() + def fill_document_items_to_real(self, document_key: str, validated_rows: list): + line_url = f"{self.url}({document_key})" + + response = self.session.get(line_url) + response.raise_for_status() + document_data = response.json() + + items = document_data.get("Товары", []) + start_line = len(items) + + for idx, row in enumerate(validated_rows, start=1): + new_item = { + "LineNumber": str(start_line + idx), + "Номенклатура_Key": row.ref_key, + "Количество": row.counts, + "ЕдиницаИзмерения_Key": settings.MEASURE, + "Цена": row.price / row.counts if row.counts else 0, + "Сумма": row.price, + "СчетУчета_Key": settings.A43, + "ПереданныеСчетУчета_Key": settings.A45_02, + "СтавкаНДС": "НДС5", + "СуммаНДС": row.price * 5 / 105, + } + items.append(new_item) + + document_data["Товары"] = items + patch_response = self.session.patch(line_url, json=document_data) + patch_response.raise_for_status() +def fill_document_items_report(self, document_key:str, validated_rows:list): + line_url = f"{self.url}({document_key})" + + response = self.session.get(line_url) + response.raise_for_status() + document_data = response.json() + items = document_data.get("Покупатели",[]) + new_item={ + "LineNumber": str(len(items) + 1), + "Покупатель_Key": self.contragent + } + items.append(new_item) + document_data["Покупатели"] = items + patch_response = self.session.patch(line_url, json=document_data) + patch_response.raise_for_status() + + items = document_data.get("Товары", []) + start_line = len(items) + + for idx, row in enumerate(validated_rows, start=1): + new_item = { + "LineNumber": str(start_line + idx), + "Номенклатура_Key": row.ref_key, + "Количество": row.counts, + "ЕдиницаИзмерения_Key": settings.MEASURE, + "Цена": row.price / row.counts if row.counts else 0, + "Сумма": row.price, + "СчетУчета_Key": settings.A45_02, + "СчетДоходов_Key": settings.A90_01_1, + "СчетРасходов_Key": settings.A90_02_1, + "СтавкаНДС": "НДС5", + "СуммаНДС": row.price * 5 / 105, + "СчетУчетаНДСПоРеализации_Key": settings.A90_03, + } + items.append(new_item) + + document_data["Товары"] = items + patch_response = self.session.patch(line_url, json=document_data) + patch_response.raise_for_status() + + items = document_data.get("Возвраты", []) + new_item = { + "LineNumber": str(len(items) + 1), + "Покупатель_Key": settings.BUYER + } + items.append(new_item) + document_data["Возвраты"] = items + patch_response = self.session.patch(line_url, json=document_data) + patch_response.raise_for_status() + + + items = document_data.get("ТоварыВозвращенные", []) + start_line = len(items) + + for idx, row in enumerate(validated_rows, start=1): + new_item = { + "LineNumber": str(start_line + idx), + "Номенклатура_Key": row.ref_key, + "Количество": row.counts, + "ЕдиницаИзмерения_Key": settings.MEASURE, + "Цена": row.price / row.counts if row.counts else 0, + "Сумма": row.price, + "СчетУчета_Key": settings.A45_02, + "СчетДоходов_Key": settings.A90_01_1, + "СчетРасходов_Key": settings.A90_02_1, + "СтавкаНДС": "НДС5", + "СуммаНДС": row.price * 5 / 105, + "СчетУчетаНДСПоРеализации_Key": settings.A90_03, + "Себестоимость": 100, + "ОтражениеВУСН": "Принимаются", + } + items.append(new_item) + + document_data["ТоварыВозвращенные"] = items + patch_response = self.session.patch(line_url, json=document_data) + patch_response.raise_for_status() \ No newline at end of file diff --git a/server/backend/handlers/ozon_purchases_handler.py b/server/backend/handlers/ozon_purchases_handler.py index 40f0172..24a1d34 100644 --- a/server/backend/handlers/ozon_purchases_handler.py +++ b/server/backend/handlers/ozon_purchases_handler.py @@ -14,7 +14,6 @@ def report_date(df, date_format: str): return dt.strftime(date_format) def process_sheet(df, real_arti:int, real_quantity:int, real_sum_1:int): df = df.iloc[2:].reset_index(drop=True) - # выбор нужных столбцов ПО ПОЗИЦИИ df = df.iloc[:, [real_arti, real_quantity, real_sum_1]].copy().dropna() df = df[(df != 0).all(axis=1)] @@ -29,6 +28,7 @@ def process_sheet(df, real_arti:int, real_quantity:int, real_sum_1:int): df['counts'] = df['counts'].astype(int) #Группировка + df = df.groupby('arti', as_index=False).agg({'counts': 'sum', 'price': 'sum'}) #groupping df = processing(df) #vlookup for ref_keys validated_rows, errors = [], [] for i, row in df.iterrows(): @@ -43,6 +43,6 @@ def process_sheet(df, real_arti:int, real_quantity:int, real_sum_1:int): ) return validated_rows def evaluating(dfs): - validated_rows_1 = process_sheet(dfs["Отчет о выкупленных товарах"], real_arti=3,real_quantity=10, real_sum_1=11) # номера столбцов от озона - date=report_date(dfs["Отчет о выкупленных товарах"], date_format=settings.TIMEFORMAT) + validated_rows_1 = process_sheet(dfs["Отчёт о выкупленных товарах"], real_arti=3,real_quantity=10, real_sum_1=11) # номера столбцов от озона + date=report_date(dfs["Отчёт о выкупленных товарах"], date_format=settings.TIMEFORMAT) return validated_rows_1, date \ No newline at end of file diff --git a/server/backend/handlers/ozon_wb_yandex_com_handler.py b/server/backend/handlers/ozon_wb_yandex_com_handler.py index 4ed21ca..00c4f6c 100644 --- a/server/backend/handlers/ozon_wb_yandex_com_handler.py +++ b/server/backend/handlers/ozon_wb_yandex_com_handler.py @@ -23,7 +23,7 @@ def process_sheet(df, real_arti:str, real_quantity:str, real_sum_1:str): #Группировка df = df.groupby('arti', as_index=False).agg({'counts': 'sum', 'price': 'sum'}) #groupping - #df = processing(df) #vlookup for ref_keys + df = processing(df) #vlookup for ref_keys validated_rows, errors = [], [] for i, row in df.iterrows(): #проходит построчно по df, где i - индекс строки, row - данные строки try: diff --git a/server/backend/handlers/wb_purchases_handler.py b/server/backend/handlers/wb_purchases_handler.py index f51e268..9fbd847 100644 --- a/server/backend/handlers/wb_purchases_handler.py +++ b/server/backend/handlers/wb_purchases_handler.py @@ -35,8 +35,6 @@ def process_sheet(df, real_arti:str, real_quantity:str, real_sum_1:str): raise Exception(f"There are some errors with validation in Sheet1, check it ", errors) return validated_rows def evaluating(dfs): -# validated_rows_1 = process_sheet(dfs["Sheet1"], real_arti='Артикул',real_quantity="Количество", real_sum_1='''Сумма выкупа, руб., -# (вкл. НДС)''') # номера столбцов от озона validated_rows_1 = process_sheet(dfs["Sheet1"], real_arti=1,real_quantity=3, real_sum_1=4) # номера столбцов от озона date=report_date(dfs["Sheet1"], date_format=settings.TIMEFORMAT) return validated_rows_1, date \ No newline at end of file diff --git a/server/backend/schemas/pydantic.py b/server/backend/schemas/pydantic.py index 5cca9fc..4023708 100644 --- a/server/backend/schemas/pydantic.py +++ b/server/backend/schemas/pydantic.py @@ -4,7 +4,7 @@ class ExcelInfo(BaseModel): arti:str = Field(..., min_length=5, max_length=12, description="arti of the clothes") counts:int = Field(..., gt=0, description="the quantity of the clothes") price:float = Field(..., gt=0, description="the price of the clothes") - #ref_key:str = Field(..., description="reffering key from db") + ref_key:str = Field(..., description="reffering key from db") class ExcelRealization(BaseModel): pass class ExcelReturning(BaseModel): @@ -42,6 +42,12 @@ class Settings(BaseSettings): A90_02_1:str A90_03:str A76_09:str + A44_01:str + A19_04:str + TYPE1:str + TYPE2:str + TYPE3:str + TYPE4:str model_config = SettingsConfigDict( env_file=".env", env_file_encoding="utf-8" diff --git a/server/backend/services/excel.py b/server/backend/services/excel.py index 7203584..0ee465c 100644 --- a/server/backend/services/excel.py +++ b/server/backend/services/excel.py @@ -5,7 +5,8 @@ import server.backend.handlers.ozon_handler as ozon_handler import server.backend.handlers.ozon_purchases_handler as ozon_purchases_handler import server.backend.handlers.wb_purchases_handler as wb_purchases_handler import server.backend.handlers.ozon_wb_yandex_com_handler as ozon_wb_yandex_com_handler -from server.backend.schemas.pydantic import settings, CreateDocumentParams +from server.backend.schemas.pydantic import settings +from server.backend.api.realisation import fill_document_items, create_document class BaseHandler: def __init__(self, file_path): self.file_path = file_path @@ -29,8 +30,26 @@ class YandexHandler(BaseHandler): # вызываем функцию evaluating validated_data = yandex_handler.evaluating(dfs) print("Реализация Яндекс завершена, валидированных строк:", len(validated_data[0]), "Реализация", len(validated_data[1]), "Возвраты", validated_data[2], "Дата") - - + doc_creator = DocumentCreation(URL=settings.URL_REPORT) + doc_key = doc_creator.create_document( + Date=date, + ВидОперации=settings.TYPE3, + Контрагент_Key=settings.CONTRAGENT_YANDEX, + ДоговорКонтрагента_Key=settings.CONTRACT_YAN, + Организация_Key=settings.COMPANY, + Склад_Key=settings.STORE, + ДокументБезНДС="false", + СуммаВключаетНДС="true", + УдержатьВознаграждение="true", + СчетУчетаРасчетовПоАвансамПолученным_Key=settings.A76_09, + СчетУчетаРасчетовСКонтрагентом_Key=settings.A76_09, + СчетУчетаРасчетовПоАвансамВыданным_Key=settings.A60_02, + СчетУчетаРасчетовЗаПосредническиеУслуги_Key=settings.A60_01, + СтавкаНДСВознаграждения="БезНДС", + СчетУчетаЗатрат_Key=settings.A44_01, + СчетУчетаНДС_Key=settings.A19_04 + ) + doc_creator.fill_document_items_purchase(doc_key, validated_data) class WBHandler(BaseHandler): def process(self): dfs = self.read() @@ -38,7 +57,26 @@ class WBHandler(BaseHandler): raise Exception(f"В файле {self.file_path.name} отсутствуют необходимые листы") validated_data = wb_handler.evaluating(dfs) print("Реализация WB завершена, валидированных строк:", len(validated_data[0]), "Реализация", len(validated_data[1]), "Возвраты", validated_data[2], "Дата") - + doc_creator = DocumentCreation(URL=settings.URL_REPORT) + doc_key = doc_creator.create_document( + Date=date, + ВидОперации=settings.TYPE3, + Контрагент_Key=settings.CONTRAGENT_RWB, + ДоговорКонтрагента_Key=settings.CONTRACT_RWB, + Организация_Key=settings.COMPANY, + Склад_Key=settings.STORE, + ДокументБезНДС="false", + СуммаВключаетНДС="true", + УдержатьВознаграждение="true", + СчетУчетаРасчетовПоАвансамПолученным_Key=settings.A62_02, + СчетУчетаРасчетовСКонтрагентом_Key=settings.A62_01, + СчетУчетаРасчетовПоАвансамВыданным_Key=settings.A60_02, + СчетУчетаРасчетовЗаПосредническиеУслуги_Key=settings.A60_01, + СтавкаНДСВознаграждения="НДС20", + СчетУчетаЗатрат_Key=settings.A44_01, + СчетУчетаНДС_Key=settings.A19_04 + ) + doc_creator.fill_document_items_purchase(doc_key, validated_data) class OZONHandler(BaseHandler): def process(self): dfs = self.read(skiprows=14, skipfooter=17) @@ -46,24 +84,70 @@ class OZONHandler(BaseHandler): raise Exception(f"В файле {self.file_path.name} отсутствуют необходимые листы") validated_data = ozon_handler.evaluating(dfs) print("Реализация OZON завершена, валидированных строк:", len(validated_data[0]), "Реализация", len(validated_data[1]), "Возвраты", validated_data[2], "Дата") + doc_creator = DocumentCreation(URL=settings.URL_REPORT) + doc_key = doc_creator.create_document( + Date=date, + ВидОперации=settings.TYPE3, + Контрагент_Key=settings.CONTRAGENT_OZON, + ДоговорКонтрагента_Key=settings.CONTRACT_OZON, + Организация_Key=settings.COMPANY, + Склад_Key=settings.STORE, + ДокументБезНДС="false", + СуммаВключаетНДС="true", + УдержатьВознаграждение="true", + СчетУчетаРасчетовПоАвансамПолученным_Key=settings.A62_02, + СчетУчетаРасчетовСКонтрагентом_Key=settings.A62_01, + СчетУчетаРасчетовПоАвансамВыданным_Key=settings.A60_02, + СчетУчетаРасчетовЗаПосредническиеУслуги_Key=settings.A60_01, + СтавкаНДСВознаграждения="НДС20", + СчетУчетаЗатрат_Key=settings.A44_01, + СчетУчетаНДС_Key=settings.A19_04 + ) + doc_creator.fill_document_items_purchase(doc_key, validated_data) + class OZONPurchasesHandler(BaseHandler): def process(self): dfs = self.read(skiprows=[0,3,4,5,6,7,8,9,10,11], skipfooter=1) - if "Отчет о выкупленных товарах" not in dfs: + if "Отчёт о выкупленных товарах" not in dfs: raise Exception(f"В файле {self.file_path.name} отсутствуют необходимые листы") - validated_data = ozon_purchases_handler.evaluating(dfs) - print("Выкупы OZON завершены, валидированных строк:", len(validated_data[0]), "Реализация", validated_data[1], "Дата") - + validated_data, date = ozon_purchases_handler.evaluating(dfs) + print("Выкупы OZON завершены, валидированных строк:", len(validated_data), "Реализация", date, "Дата") + doc_creator = DocumentCreation(URL=settings.URL_REALISATION) + doc_key = doc_creator.create_document( + Date=date, + ВидОперации=settings.TYPE1, + Контрагент_Key=settings.CONTRAGENT_OZON, + ДоговорКонтрагента_Key=settings.CONTRACT_OZON, + Организация_Key=settings.COMPANY, + Склад_Key=settings.STORE, + ДокументБезНДС="false", + СуммаВключаетНДС="true", + СчетУчетаРасчетовПоАвансам_Key=settings.A62_02, + СчетУчетаРасчетовСКонтрагентом_Key=settings.A62_01, + ) + doc_creator.fill_document_items_purchase(doc_key, validated_data) class WBPurchasesHandler(BaseHandler): def process(self): dfs = self.read(skiprows=[0,3,4,5,6,7,8], skipfooter=7) if "Sheet1" not in dfs: raise Exception(f"В файле {self.file_path.name} отсутствуют необходимые листы") - validated_data = wb_purchases_handler.evaluating(dfs) - print("Выкупы WB завершены, валидированных строк:", len(validated_data[0]), "Реализация", validated_data[1], "Дата" ) - + validated_data, date = wb_purchases_handler.evaluating(dfs) + print("Выкупы WB завершены, валидированных строк:", len(validated_data), "Реализация", date, "Дата" ) + doc_creator = DocumentCreation(URL=settings.URL_REALISATION) + doc_key = doc_creator.create_document( + Date=date, + ВидОперации=settings.TYPE1, + Контрагент_Key=settings.CONTRAGENT_RWB, + ДоговорКонтрагента_Key=settings.CONTRACT_RWB, + Организация_Key=settings.COMPANY, + Склад_Key=settings.STORE, + ДокументБезНДС="false", + СуммаВключаетНДС="true", + СчетУчетаРасчетовПоАвансам_Key=settings.A62_02, + СчетУчетаРасчетовСКонтрагентом_Key=settings.A62_01 + ) + doc_creator.fill_document_items_purchase(doc_key, validated_data) class OZONComHandler(BaseHandler): - #Убрать двойной вызов pd.read def process(self): dfs = self.read(skipfooter=1) if "Лист_1" not in dfs: @@ -73,9 +157,22 @@ class OZONComHandler(BaseHandler): cont = df.iloc[1, 0] if cont != "«Интернет решения» ООО": raise Exception(f"В файле {self.file_path.name} неверный контрагент") - validated_data = ozon_wb_yandex_com_handler.evaluating(dfs) - print("Передача на коммисию OZON завершена, валидированных строк:", len(validated_data[0]), "Реализация", validated_data[1], "Дата") - + validated_data, date = ozon_wb_yandex_com_handler.evaluating(dfs) + print("Передача на коммисию OZON завершена, валидированных строк:", len(validated_data), "Реализация", date, "Дата") + doc_creator = DocumentCreation(URL=settings.URL_REALISATION) + doc_key = doc_creator.create_document( + Date=date, + ВидОперации=settings.TYPE2, + Контрагент_Key=settings.CONTRAGENT_OZON, + ДоговорКонтрагента_Key=settings.CONTRACT_OZON, + Организация_Key=settings.COMPANY, + Склад_Key=settings.STORE, + ДокументБезНДС="false", + СуммаВключаетНДС="true", + СчетУчетаРасчетовПоАвансам_Key=settings.A62_02, + СчетУчетаРасчетовСКонтрагентом_Key=settings.A62_01 + ) + doc_creator.fill_document_items_to_real(doc_key, validated_data) class WBComHandler(BaseHandler): def process(self): dfs = self.read(skipfooter=1) @@ -88,6 +185,20 @@ class WBComHandler(BaseHandler): raise Exception(f"В файле {self.file_path.name} неверный контрагент") validated_data = ozon_wb_yandex_com_handler.evaluating(dfs) print("Передача на коммисию WB завершена, валидированных строк:", len(validated_data[0]), "Реализация", validated_data[1], "Дата") + doc_creator = DocumentCreation(URL=settings.URL_REALISATION) + doc_key = doc_creator.create_document( + Date=date, + ВидОперации=settings.TYPE2, + Контрагент_Key=settings.CONTRAGENT_RWB, + ДоговорКонтрагента_Key=settings.CONTRACT_RWB, + Организация_Key=settings.COMPANY, + Склад_Key=settings.STORE, + ДокументБезНДС="false", + СуммаВключаетНДС="true", + СчетУчетаРасчетовПоАвансам_Key=settings.A62_02, + СчетУчетаРасчетовСКонтрагентом_Key=settings.A76_09 + ) + doc_creator.fill_document_items_to_real(doc_key, validated_data) class YandexComHandler(BaseHandler): def process(self): @@ -101,4 +212,18 @@ class YandexComHandler(BaseHandler): raise Exception(f"В файле {self.file_path.name} неверный контрагент") validated_data = ozon_wb_yandex_com_handler.evaluating(dfs) print("Передача на коммисию YANDEX завершена, валидированных строк:", len(validated_data[0]), "Реализация", validated_data[1], "Дата") - + doc_creator = DocumentCreation(URL=settings.URL_REALISATION) + doc_key = doc_creator.create_document( + Date=date, + ВидОперации=settings.TYPE2, + Контрагент_Key=settings.CONTRAGENT_YANDEX, + ДоговорКонтрагента_Key=settings.CONTRACT_YAN, + Организация_Key=settings.COMPANY, + Склад_Key=settings.STORE, + ДокументБезНДС="false", + СуммаВключаетНДС="true", + СчетУчетаРасчетовПоАвансам_Key=settings.A62_02, + СчетУчетаРасчетовСКонтрагентом_Key=settings.A76_09 + ) + doc_creator.fill_document_items_to_real(doc_key, validated_data) + diff --git a/server/backend/services/validating_files.py b/server/backend/services/validating_files.py index 4fc1fda..f081f31 100644 --- a/server/backend/services/validating_files.py +++ b/server/backend/services/validating_files.py @@ -29,7 +29,7 @@ def validating(): match file: case _ if "period_closure_income" in name: label = "period_closure_income" - case _ if name == "0": + case _ if name == "0" or "weekly_report" in name: label = "0" case _ if "отчет о реализации товара" in name: label = "отчет о реализации товара"