From f4bb9064345a7185972d740e0906a1b5760ec463 Mon Sep 17 00:00:00 2001 From: Maria Date: Tue, 6 May 2025 07:56:40 +0800 Subject: [PATCH 1/4] work with structure --- Curl/readme.md | 28 + .../Processings/aggregate_processing.py | 75 + Src/Logics/Processings/debit_processing.py | 30 + Src/Logics/Processings/processing.py | 25 + Src/Logics/Processings/turn_processing.py | 47 + .../Services/post_processing_service.py | 28 + Src/Logics/Services/reference_service.py | 81 + Src/Logics/Services/service.py | 84 + Src/Logics/Services/storage_observer.py | 19 + Src/Logics/Services/storage_service.py | 212 ++ Src/Logics/basic_convertor.py | 27 + Src/Logics/convert_factory.py | 151 ++ Src/Logics/convertor.py | 27 + Src/Logics/csv_reporting.py | 43 + Src/Logics/datetime_convertor.py | 27 + Src/Logics/json_reporting.py | 38 + Src/Logics/markdown_reporting.py | 49 + Src/Logics/process_factory.py | 86 + Src/Logics/report_factory.py | 91 + Src/Logics/reporting.py | 96 + Src/Logics/start_factory.py | 288 +++ Src/Logics/storage_prototype.py | 98 + Src/Models/event_type.py | 22 + Src/Models/event_value.py | 10 + Src/Models/group_model.py | 16 + Src/Models/nomenclature_model.py | 95 + Src/Models/receipe_model.py | 214 ++ Src/Models/receipe_row_model.py | 113 + Src/Models/storage_model.py | 61 + Src/Models/storage_row_model.py | 252 ++ Src/Models/storage_row_turn_model.py | 125 + Src/Models/unit_model.py | 163 ++ Src/Storage/storage.json | 2260 ++++++++++++++++ Src/Storage/storage.py | 196 ++ Src/errors.py | 94 + Src/exceptions.py | 72 + Src/reference.py | 146 ++ Src/settings.json | 7 + Src/settings.py | 99 + Src/settings_manager.py | 143 ++ Src/test.json | 7 + Tst/convert_test.py | 162 ++ Tst/error_test.py | 37 + Tst/factory_test.py | 121 + Tst/nomenclature_test.py | 53 + Tst/processing_test.py | 99 + Tst/prototype_test.py | 61 + Tst/reporting_test.py | 166 ++ Tst/service_test.py | 273 ++ Tst/settings_test.py | 106 + Tst/storage_test.py | 41 + csv_report.csv | 2 + main.py | 194 ++ markdown_report.md | 4 + nomenclature.json | 27 + nomenclature_deserialize.json | 27 + nomenclatures.json | 459 ++++ readme.md | 47 + receipt_deserialize.json | 199 ++ receipts.json | 831 ++++++ storage.json | 2270 +++++++++++++++++ storage_journal.md | 72 + transaction_deserialize.json | 58 + 63 files changed, 11054 insertions(+) create mode 100644 Curl/readme.md create mode 100644 Src/Logics/Processings/aggregate_processing.py create mode 100644 Src/Logics/Processings/debit_processing.py create mode 100644 Src/Logics/Processings/processing.py create mode 100644 Src/Logics/Processings/turn_processing.py create mode 100644 Src/Logics/Services/post_processing_service.py create mode 100644 Src/Logics/Services/reference_service.py create mode 100644 Src/Logics/Services/service.py create mode 100644 Src/Logics/Services/storage_observer.py create mode 100644 Src/Logics/Services/storage_service.py create mode 100644 Src/Logics/basic_convertor.py create mode 100644 Src/Logics/convert_factory.py create mode 100644 Src/Logics/convertor.py create mode 100644 Src/Logics/csv_reporting.py create mode 100644 Src/Logics/datetime_convertor.py create mode 100644 Src/Logics/json_reporting.py create mode 100644 Src/Logics/markdown_reporting.py create mode 100644 Src/Logics/process_factory.py create mode 100644 Src/Logics/report_factory.py create mode 100644 Src/Logics/reporting.py create mode 100644 Src/Logics/start_factory.py create mode 100644 Src/Logics/storage_prototype.py create mode 100644 Src/Models/event_type.py create mode 100644 Src/Models/event_value.py create mode 100644 Src/Models/group_model.py create mode 100644 Src/Models/nomenclature_model.py create mode 100644 Src/Models/receipe_model.py create mode 100644 Src/Models/receipe_row_model.py create mode 100644 Src/Models/storage_model.py create mode 100644 Src/Models/storage_row_model.py create mode 100644 Src/Models/storage_row_turn_model.py create mode 100644 Src/Models/unit_model.py create mode 100644 Src/Storage/storage.json create mode 100644 Src/Storage/storage.py create mode 100644 Src/errors.py create mode 100644 Src/exceptions.py create mode 100644 Src/reference.py create mode 100644 Src/settings.json create mode 100644 Src/settings.py create mode 100644 Src/settings_manager.py create mode 100644 Src/test.json create mode 100644 Tst/convert_test.py create mode 100644 Tst/error_test.py create mode 100644 Tst/factory_test.py create mode 100644 Tst/nomenclature_test.py create mode 100644 Tst/processing_test.py create mode 100644 Tst/prototype_test.py create mode 100644 Tst/reporting_test.py create mode 100644 Tst/service_test.py create mode 100644 Tst/settings_test.py create mode 100644 Tst/storage_test.py create mode 100644 csv_report.csv create mode 100644 main.py create mode 100644 markdown_report.md create mode 100644 nomenclature.json create mode 100644 nomenclature_deserialize.json create mode 100644 nomenclatures.json create mode 100644 readme.md create mode 100644 receipt_deserialize.json create mode 100644 receipts.json create mode 100644 storage.json create mode 100644 storage_journal.md create mode 100644 transaction_deserialize.json diff --git a/Curl/readme.md b/Curl/readme.md new file mode 100644 index 0000000..8ccbf1d --- /dev/null +++ b/Curl/readme.md @@ -0,0 +1,28 @@ +# Интеграционное тестирование +Вариант `Curl` + + +1. Получить список номенклатуры +``` +curl http://127.0.0.1:5000/api/nomenclature +``` + +2. Получить конкретную номенклатуру +``` +curl http://127.0.0.1:5000/api/nomenclature?id=cdbbecb3fa594a799e5b4c6a14058c42 +``` + +3. Получить значение периода блокировки +``` +curl http://127.0.0.1:5000/api/block_period +``` + +4. Изменить блокирующий период +``` +curl http://127.0.0.1:5000/api/block_period?period=2022-01-01 +``` + +5. Получить текущие обороты +``` +curl http://127.0.0.1:5000/api/storage/turns?start_period=1900-01-01&stop_period=2025-01-01 +``` diff --git a/Src/Logics/Processings/aggregate_processing.py b/Src/Logics/Processings/aggregate_processing.py new file mode 100644 index 0000000..477054f --- /dev/null +++ b/Src/Logics/Processings/aggregate_processing.py @@ -0,0 +1,75 @@ +from Src.Logics.Processings.processing import processing +from Src.Models.receipe_row_model import receipe_row_model +from Src.Storage.storage import storage +from Src.settings import settings +from Src.settings_manager import settings_manager + +# +# Процесс агрегации сохраненных оборотов и рассчитанных +# +class aggregate_processing(processing): + # Текущие настройки + __settings: settings = None + + + def __init__(self, exception: Exception = None): + super().__init__(exception) + options = settings_manager() + self.__settings = options.settings + + def process(self, source: list) -> list: + """ + Сформировать агрегацию оборотов + Args: + source (list): Список расчитанных оборотов + + Returns: + list: _description_ + """ + super().process(source) + result = [] + + # Сохране обороты + storage_object = storage() + if storage.blocked_turns_key() not in storage_object.data.keys(): + storage_object.data[ storage.blocked_turns_key() ] = [] + + saved_turns = storage_object.data[ storage.blocked_turns_key() ] + + + # Группируем исходные данные + grouped_source_data = {} + for transaction in source: + key = f"{transaction.nomenclature.id}_{transaction.storage.id}_{transaction.unit.id}" + if key not in grouped_source_data.keys(): + grouped_source_data[key] = [] + + grouped_source_data[key].append(transaction) + + # Группируем сохраненные данные + group_saved_data = {} + for transaction in saved_turns: + key = f"{transaction.nomenclature.id}_{transaction.storage.id}_{transaction.unit.id}" + if key not in group_saved_data.keys(): + group_saved_data[key] = [] + + group_saved_data[key].append(transaction) + + # Рассчитываем значения + for grouped_source_data_key in grouped_source_data.keys(): + if grouped_source_data_key in group_saved_data.keys(): + # Данные есть и в расчетных оборотах и в сохраненных. Добавляем оборот. + grouped_source_data[ grouped_source_data_key ].value += group_saved_data[grouped_source_data_key ] + + for group_saved_data_key in group_saved_data.keys(): + if group_saved_data_key not in grouped_source_data.keys(): + # В сохраненных данных есть оборот которого нет в расчетных оборотах + grouped_source_data[ group_saved_data_key ] = group_saved_data[group_saved_data_key ] + + # Формируем результат + for key, value in grouped_source_data.items(): + result.append( value[0] ) + + return result + + \ No newline at end of file diff --git a/Src/Logics/Processings/debit_processing.py b/Src/Logics/Processings/debit_processing.py new file mode 100644 index 0000000..fc31a43 --- /dev/null +++ b/Src/Logics/Processings/debit_processing.py @@ -0,0 +1,30 @@ +from Src.Logics.Processings.processing import processing +from Src.Models.receipe_row_model import receipe_row_model + +from datetime import datetime +from Src.Models.storage_model import storage_model + +# +# Сформировать набор проводок для списание по рецепту +# Код взят https://github.com/Illikan/popov_design_patterns/blob/aee55cd86f5414d72bdb039b95b4b68771858a3d/Src/Logics/transaction_processing.py#L17 +# +class debit_processing(processing): + + def process(self, source: list) -> list: + """ + Сформировать проводки списания + Args: + transactions (list): Список объектов типа receipe_row_model + + Returns: + list: _description_ + """ + super().process(source) + result = [] + storage_default = storage_model.create_default() + + for row in source: + debit_transaction = receipe_row_model.create_debit_transaction( row, datetime.now(), storage_default ) + result.append( debit_transaction ) + + return result \ No newline at end of file diff --git a/Src/Logics/Processings/processing.py b/Src/Logics/Processings/processing.py new file mode 100644 index 0000000..8a10851 --- /dev/null +++ b/Src/Logics/Processings/processing.py @@ -0,0 +1,25 @@ +import abc +from Src.errors import error_proxy +from Src.exceptions import argument_exception + +# +# Абстрактный класс для наследования. +# Используется для реализации различных процессов обработки данных по складским транзакциям +# +class processing(error_proxy): + + @abc.abstractmethod + def process(self, transactions: list) -> list: + """ + Выполнить процесс обработки списка транзакций + Args: + source (_type_): Любой тип данных + """ + + if transactions == None: + raise argument_exception("Некорректно передан параметр!") + + if len(transactions) == 0: + raise argument_exception("Некорректно передан параметр!") + + self.clear() \ No newline at end of file diff --git a/Src/Logics/Processings/turn_processing.py b/Src/Logics/Processings/turn_processing.py new file mode 100644 index 0000000..f53e73d --- /dev/null +++ b/Src/Logics/Processings/turn_processing.py @@ -0,0 +1,47 @@ +from Src.Logics.Processings.processing import processing +from Src.Models.storage_row_turn_model import storage_row_turn_model + + +# +# Процесс получения оборотов по списку транзакций +# +class turn_processing(processing): + + def process(self, source: list) -> list: + """ + Сформировать складские обороты + Args: + transactions (list): Список объектов типа storage_row_model + + Returns: + list: _description_ + """ + super().process(source) + result = [] + + # Код взят https://github.com/AItEKS/Design-patterns/pull/9/files#diff-401a63dd40e843c86f4a13a76fc390dac87de5ab5e65afc30cd5bcede4893f94 + + grouped_transactions = {} + for transaction in source: + key = f"{transaction.nomenclature.id}_{transaction.storage.id}_{transaction.unit.id}" + if key not in grouped_transactions.keys(): + grouped_transactions[key] = [] + + grouped_transactions[key].append(transaction) + + for key, transactions in grouped_transactions.items(): + first_transaction = transactions[0] + + # Расчитываем оборот + turnover = sum(transaction.value if transaction.storage_type else -transaction.value for transaction in transactions) + + # Создаем модель + row = storage_row_turn_model.create( first_transaction.nomenclature, first_transaction.storage, first_transaction.unit) + row.value = turnover + + # Добавляем в список + result.append(row) + + return result + + \ No newline at end of file diff --git a/Src/Logics/Services/post_processing_service.py b/Src/Logics/Services/post_processing_service.py new file mode 100644 index 0000000..2dde182 --- /dev/null +++ b/Src/Logics/Services/post_processing_service.py @@ -0,0 +1,28 @@ +from Src.Logics.Services.service import service +from Src.Logics.Services.storage_observer import storage_observer +from Src.Storage.storage import storage +from Src.Logics.Services.storage_observer import event_type +from Src.Models.nomenclature_model import nomenclature_model + + +class post_processing_service(service): + + def __init__(self, data: list, nomenclature) -> None: + + super().__init__(data) + + self.nomenclature = nomenclature + # storage_observer.observers.append(self) + + + + def handle_event(self, handle_type: str): + if handle_type == event_type.nomenclature_deleted(): + self.nomenclature_deleted() + + + def nomenclature_deleted(self, nomenclature: nomenclature_model): + for recipe in self.data: + if self.nomenclature.name in recipe._rows: + del recipe._rows[self.nomenclature.name] + recipe.__calc_brutto() diff --git a/Src/Logics/Services/reference_service.py b/Src/Logics/Services/reference_service.py new file mode 100644 index 0000000..4fba18f --- /dev/null +++ b/Src/Logics/Services/reference_service.py @@ -0,0 +1,81 @@ +from Src.Logics.Services.service import service +from Src.exceptions import exception_proxy, operation_exception +from Src.reference import reference +from Src.Logics.Services.storage_observer import storage_observer +from Src.Models.event_type import event_type +from Src.Logics.Services.post_processing_service import post_processing_service +from Src.Models.event_type import event_type + +# +# Сервис для выполнения CRUD операций +# +class reference_service(service): + + + def add(self, item: reference) -> bool: + """ + Добавить новый элемент + """ + exception_proxy.validate(item, reference) + found = list(filter(lambda x: x.id == item.id , self.data)) + if len(found) > 0: + return False + + self.data.append(item) + return True + + + def delete(self, item: reference) -> bool: + """ + Удалить элемент + """ + exception_proxy.validate(item, reference) + + found = list(filter(lambda x: x.id == item.id , self.data)) + if len(found) == 0: + return False + + item_to_delete = found[0] # Сохраняем объект, который мы собираемся удалить + + self.data.remove(item_to_delete) + storage_observer.raise_event(event_type.nomenclature_deleted(item_to_delete)) + + return True + + + def change(self, item:reference) -> bool: + """ + Изменить элемент + """ + exception_proxy.validate(item, reference) + found = list(filter(lambda x: x.id == item.id , self.data)) + if len(found) == 0: + return False + + self.delete(found[0]) + self.add(item) + return True + + def get(self) -> list: + """ + Вернуть список + """ + return self.data + + def get_item(self, id: str) -> reference: + """ + Вернуть элемент + """ + exception_proxy.validate(id, str) + found = list(filter(lambda x: x.id == id , self.data)) + if len(found) == 0: + raise operation_exception(f"Не найден элемент с кодом {id}!") + + return found + + + def handle_event( self, event_type: str ): + """ Обработать события""" + + super().handle_event(event_type) + diff --git a/Src/Logics/Services/service.py b/Src/Logics/Services/service.py new file mode 100644 index 0000000..980bf01 --- /dev/null +++ b/Src/Logics/Services/service.py @@ -0,0 +1,84 @@ +from Src.exceptions import argument_exception, exception_proxy +from Src.Logics.convert_factory import convert_factory +from Src.settings import settings +from Src.settings_manager import settings_manager +from Src.Models.event_type import event_type +from abc import ABC + +from abc import ABC, abstractmethod +import json + +# +# Общий класс для наследования. Реализация сервисов. +# +class service(ABC): + # Набор данных для работы + __data = [] + # Текущие настройки + __settings: settings = None + + def __init__(self, data: list) -> None: + if len(data) == 0: + raise argument_exception("Некорректно переданы параметры!") + + self.__data = data + options = settings_manager() + self.__settings = options.settings + + @property + def data(self): + """ + Текущие данные + Returns: + _type_: _description_ + """ + return self.__data + + @property + def settings(self) -> settings: + """ + Текущие настройки + Returns: + settings: _description_ + """ + return self.__settings + + @abstractmethod + def handle_event(self, event_type: str): + """ Обработать события""" + + exception_proxy.validate(event_type, str) + + # Общие методы для формирования ответа для Web + + + def create_response(self, app, data:list = None): + """ + Сформировать структуру ответа для Web сервера + """ + inner_data = self.__data if data is None else data + return service.create_response(app, inner_data) + + @staticmethod + def create_response( app, data:list): + """ + Сформировать структуру ответа для Web сервера + """ + if app is None: + raise argument_exception("Некорректно переданы параметры!") + + exception_proxy.validate(data, list) + + # Преоброзование + dest_data = convert_factory().serialize( data ) + json_text = json.dumps( dest_data, sort_keys = True, indent = 4, ensure_ascii = False) + + # Подготовить ответ + result = app.response_class( + response = f"{json_text}", + status = 200, + mimetype = "application/json; charset=utf-8" + ) + + return result + diff --git a/Src/Logics/Services/storage_observer.py b/Src/Logics/Services/storage_observer.py new file mode 100644 index 0000000..2745e49 --- /dev/null +++ b/Src/Logics/Services/storage_observer.py @@ -0,0 +1,19 @@ +from Src.Models.event_type import event_type +from Src.exceptions import exception_proxy + +# +# Наблюдатель для складских операций +# +class storage_observer: + _observers = [] + + + def raise_event(handle_event: str, reference: None): + """ Сформировать события """ + + exception_proxy.validate( handle_event, str ) + for object in storage_observer._observers: + + if object is not None: + object.handle_event(handle_event) + diff --git a/Src/Logics/Services/storage_service.py b/Src/Logics/Services/storage_service.py new file mode 100644 index 0000000..9196112 --- /dev/null +++ b/Src/Logics/Services/storage_service.py @@ -0,0 +1,212 @@ +from Src.Logics.process_factory import process_factory +from Src.Logics.storage_prototype import storage_prototype +from Src.exceptions import argument_exception, exception_proxy, operation_exception +from Src.Models.nomenclature_model import nomenclature_model +from Src.Models.receipe_model import receipe_model +from Src.Storage.storage import storage +from Src.Logics.Services.service import service +from Src.Models.event_type import event_type +from Src.Logics.Services.storage_observer import storage_observer +from Src.Models.event_value import event_value + +from datetime import datetime + +# +# Сервис для работы со складскими операциями +# +class storage_service(service): + + def __init__(self, data: list) -> None: + super().__init__(data) + + + def __build_turns(self, data: list) -> list: + """ + Сформировать обороты + Args: + data (list): _description_ + + Returns: + list: _description_ + """ + if len(data) == 0: + raise argument_exception("Некорректно переданы параметры!") + + # Подобрать процессинг + key_turn = process_factory.turn_key() + processing = process_factory().create( key_turn ) + + # Обороты + turns = processing().process( data ) + return turns + + def __build_blocked_turns(self): + """ + Сформировать и сохранить обороты в закрытом периоде + """ + start_period = datetime.strptime("1900-01-01", "%Y-%m-%d") + stop_period = self.settings.block_period + + # Фильтруем по периоду + prototype = storage_prototype( self.data ) + filter = prototype.filter_by_period( start_period, stop_period) + + # Расчитываем + calculated_turns = self.__build_turns(filter.data) + + # Сохраняем данные + storage.save_blocked_turns(calculated_turns) + + + + # Набор основных методов + + def create_turns(self, start_period: datetime, stop_period:datetime ) -> list: + """ + Получить обороты за период + Args: + start_period (datetime): Начало + stop_period (datetime): Окончание + + Returns: + list: обороты за период + """ + exception_proxy.validate(start_period, datetime) + exception_proxy.validate(stop_period, datetime) + + if start_period > stop_period: + raise argument_exception("Некорректно переданы параметры!") + + block_period = self.settings.block_period + + # Фильтруем + prototype = storage_prototype( self.data ) + filter = prototype.filter_by_period( block_period, stop_period) + + # Рассчитанные обороты + calculated_turns = self.__build_turns( filter. data ) + + # Сформируем результат + aggregate_key = process_factory.aggregate_key() + processing = process_factory().create( aggregate_key ) + return processing().process( calculated_turns ) + + + def create_turns_by_nomenclature(self, start_period: datetime, stop_period: datetime, nomenclature: nomenclature_model) -> list: + """ + Получить обороты за период по конкретной номенклатуры + Args: + start_period (datetime): Начало + stop_period (datetime): Окончание + nomenclature (nomenclature_model): Номенклатуры + + Returns: + list: Обороты + """ + exception_proxy.validate(start_period, datetime) + exception_proxy.validate(stop_period, datetime) + exception_proxy.validate(nomenclature, nomenclature_model) + + if start_period > stop_period: + raise argument_exception("Некорректно переданы параметры!") + + block_period = self.settings.block_period + + # Фильтруем + prototype = storage_prototype( self.data ) + filter = prototype.filter_by_period( block_period, stop_period) + filter = filter.filter_by_nomenclature( nomenclature ) + if not filter.is_empty: + raise operation_exception(f"Невозможно сформировать обороты по указанным данных: {filter.error}") + + # Рассчитанные обороты + calculated_turns = self.__build_turns( filter. data ) + + # Сформируем результат + aggregate_key = process_factory.aggregate_key() + processing = process_factory().create( aggregate_key ) + return processing().process( calculated_turns ) + + def create_turns_only_nomenclature(self, nomenclature: nomenclature_model) -> list: + """ + Получить обороты по номенклатуре + Args: + nomenclature (nomenclature_model): _description_ + + Returns: + list: Обороты + """ + exception_proxy.validate(nomenclature, nomenclature_model) + prototype = storage_prototype( self.data ) + filter = prototype.filter_by_nomenclature( nomenclature ) + if not filter.is_empty: + raise operation_exception(f"Невозможно сформировать обороты по указанным данных: {filter.error}") + + return self.__build_turns( filter. data ) + + def create_turns_by_receipt(self, receipt: receipe_model) -> list: + """ + Сформировать обороты по указанному рецепту + Args: + receipt (receipe_model): _description_ + + Returns: + list: _description_ + """ + exception_proxy.validate(receipt, receipe_model) + + if len(receipt.consist) == 0: + raise operation_exception("Переданный рецепт некорректный. Не содержит в себе список номенклатуры!") + + # Отфильтровать по рецепту + transactions = [] + filter = storage_prototype( self.data ) + for item in receipt.rows(): + filter = filter.filter_by_nomenclature( item.nomenclature ) + if filter.is_empty: + for transaction in filter.data: + transactions.append( transaction ) + + filter.data = self.data + + return self.__build_turns( transactions ) + + def build_debits_by_receipt(self, receipt: receipe_model) -> list: + """ + Сформировать проводки списания по рецепту + Args: + receipt (receipe_model): _description_ + + Returns: + list: _description_ + """ + exception_proxy.validate(receipt, receipe_model) + + if len(receipt.consist) == 0: + raise operation_exception("Переданный рецепт некорректный. Не содержит в себе список номенклатуры!") + + turns = self.create_turns_by_receipt(receipt) + if len(turns) <= 0: + raise operation_exception("По указанному рецепту не найдеты обороты!") + + if len(receipt.rows()) > len(turns): + raise operation_exception("Невозможно сформировать список транзакций для списания т.к. нет достаточно остатков!") + + # Формируем список проводок на списание + processing = process_factory().create( process_factory.debit_key() ) + transactions = processing().process( receipt.rows() ) + key = storage.storage_transaction_key() + + data = storage().data[ key ] + for transaction in transactions: + data.append ( transaction ) + + def handle_event(self, handle_type: event_value, reference: None): # Доработать, добавить поле + """ Обработать события""" + + super().handle_event(handle_type) + + if handle_type == event_type.changed_block_period: + self.__build_blocked_turns() + + \ No newline at end of file diff --git a/Src/Logics/basic_convertor.py b/Src/Logics/basic_convertor.py new file mode 100644 index 0000000..658c2d5 --- /dev/null +++ b/Src/Logics/basic_convertor.py @@ -0,0 +1,27 @@ +from Src.Logics.convertor import convertor + +# +# Конвертор простых значений в словарь +# +class basic_convertor(convertor): + + def serialize(self, field: str, object) -> dict: + """ + Подготовить словарь + Args: + field (str): поле + object (_type_): значение + """ + super().serialize( field, object) + + if not isinstance(object, (int, str, bool, float)): + self.error = f"Некорректный тип данных передан для конвертации. Ожидается: (int, str, bool). Передан: {type(object)}" + return None + + try: + return { field: object } + except Exception as ex: + self.set_error(ex) + + return None + \ No newline at end of file diff --git a/Src/Logics/convert_factory.py b/Src/Logics/convert_factory.py new file mode 100644 index 0000000..70b3a92 --- /dev/null +++ b/Src/Logics/convert_factory.py @@ -0,0 +1,151 @@ +from Src.Logics.basic_convertor import basic_convertor +from Src.Logics.datetime_convertor import datetime_convertor +from Src.exceptions import exception_proxy, operation_exception, argument_exception +from Src.reference import reference +from Src.Logics.convertor import convertor + + +import datetime + +# +# Конвертор reference в словарь +# +class reference_convertor(convertor): + + def serialize(self, field: str, object: reference) -> dict: + """ + Подготовить словарь + Args: + field (str): поле + object (_type_): значение + """ + super().serialize(field, object) + + factory = convert_factory() + return factory.serialize(object) + + +# +# Фабрика для конвертация данных +# +class convert_factory: + _maps = {} + + def __init__(self) -> None: + # Связка с простыми типами + self._maps[datetime.datetime] = datetime_convertor + self._maps[int] = basic_convertor + self._maps[float] = basic_convertor + self._maps[str] = basic_convertor + self._maps[bool] = basic_convertor + + # Связка для всех моделей + for inheritor in reference.__subclasses__(): + self._maps[inheritor] = reference_convertor + + def serialize(self, object) -> dict: + """ + Подготовить словарь + Args: + object (_type_): произвольный тип + + Returns: + dict: словарь + """ + + # Сконвертируем данные как список + result = self.__serialize_list("data", object) + if result is not None: + return result + + # Сконвертируем данные как значение + result = {} + fields = reference.create_fields(object) + + for field in fields: + attribute = getattr(object.__class__, field) + if isinstance(attribute, property): + value = getattr(object, field) + + # Сконвертируем данные как список + dictionary = self.__serialize_list(field, value) + if dictionary is None: + # Сконвертируем данные как значение + dictionary = self.__serialize_item(field, value) + + try: + if len(dictionary) == 1: + # Обычное поле + result[field] = dictionary[field] + else: + # Вложенный словарь + result[field] = dictionary + except: + raise operation_exception(f"Невозможно сериализовать объект в набор словарей. Поле {field}, значение: {dictionary}") + + return result + + + # Сериализация + + def __serialize_item(self, field: str, source): + """ + Сконвертировать элемент + Args: + field (str): Наименование поля + source (_type_): Значение + + Returns: + dict: _description_ + """ + exception_proxy.validate(field, str) + if source is None: + return {field: None} + + if isinstance(source, (list, dict)): + return self.__serialize_list(field, source) + + if type(source) not in self._maps.keys(): + raise operation_exception(f"Не возможно подобрать конвертор для типа {type(source)}") + + # Определим конвертор + convertor = self._maps[ type(source)]() + dictionary = convertor.serialize( field, source ) + + if not convertor.is_empty: + raise operation_exception(f"Ошибка при конвертации данных {convertor.error}") + + return dictionary + + def __serialize_list(self, field: str, source) -> list: + """ + Сконвертировать список + Args: + source (_type_): _description_ + + Returns: + dict: _description_ + """ + exception_proxy.validate(field, str) + + # Сконвертировать список + if isinstance(source, list): + result = [] + for item in source: + result.append( self.__serialize_item( field, item )) + + return result + + # Сконвертировать словарь + if isinstance(source, dict): + result = {} + for item in source.items(): + key = item[0] + object = item[1] + + value = self.__serialize_item( key, object ) + result[key] = value + + return result + + \ No newline at end of file diff --git a/Src/Logics/convertor.py b/Src/Logics/convertor.py new file mode 100644 index 0000000..7b911d6 --- /dev/null +++ b/Src/Logics/convertor.py @@ -0,0 +1,27 @@ +import abc +from Src.errors import error_proxy +from Src.exceptions import exception_proxy, argument_exception + +# +# Абстрактный класс для наследования. +# Используется для сериализации и десериализации +# +class convertor(error_proxy): + + @abc.abstractmethod + def serialize(self, field: str, object) -> dict: + """ + Сериализовать объект в словарь + Args: + source (_type_): Любой тип данных + """ + exception_proxy.validate(field, str) + self.clear() + + + + + + + + diff --git a/Src/Logics/csv_reporting.py b/Src/Logics/csv_reporting.py new file mode 100644 index 0000000..fb102e0 --- /dev/null +++ b/Src/Logics/csv_reporting.py @@ -0,0 +1,43 @@ +from Src.Logics.reporting import reporting +from Src.exceptions import operation_exception + + +# +# Класс - реализация построение данных в формате csv +# +class csv_reporting(reporting): + + def create(self, storage_key: str): + super().create(storage_key) + result = "" + delimetr = ";" + + # Исходные данные + items = self.data[ storage_key ] + if items == None: + raise operation_exception("Невозможно сформировать данные. Данные не заполнены!") + + if len(items) == 0: + raise operation_exception("Невозможно сформировать данные. Нет данных!") + + # Заголовок + header = delimetr.join(self.fields) + result += f"{header}\n" + + # Данные + for item in items: + row = "" + for field in self.fields: + attribute = getattr(item.__class__, field) + if isinstance(attribute, property): + value = getattr(item, field) + if isinstance(value, (list, dict)) or value is None: + value = "" + + row +=f"{value}{delimetr}" + + result += f"{row[:-1]}\n" + + + # Результат csv + return result \ No newline at end of file diff --git a/Src/Logics/datetime_convertor.py b/Src/Logics/datetime_convertor.py new file mode 100644 index 0000000..ee38178 --- /dev/null +++ b/Src/Logics/datetime_convertor.py @@ -0,0 +1,27 @@ +from Src.Logics.convertor import convertor +from datetime import datetime + + +# +# Конвертор datetime в словарь +# +class datetime_convertor(convertor): + + def serialize(self, field: str, object): + """ + Сериализовать в словарь + Args: + field (str): поле + object (_type_): значение + """ + super().serialize( field, object) + + if not isinstance(object, datetime): + self._error.error = f"Некорректный тип данных передан для конвертации. Ожидается: datetime. Передан: {type(object)}" + return None + + try: + return { field: object.strftime('%Y-%m-%d') } + except Exception as ex: + self.set_error(ex) + \ No newline at end of file diff --git a/Src/Logics/json_reporting.py b/Src/Logics/json_reporting.py new file mode 100644 index 0000000..f8e9c49 --- /dev/null +++ b/Src/Logics/json_reporting.py @@ -0,0 +1,38 @@ +from Src.Logics.reporting import reporting +from Src.exceptions import operation_exception +from Src.Logics.convert_factory import convert_factory + +import json + +# +# Формирование отчета в формате json +# +class json_reporting(reporting): + + def create(self, storage_key: str): + super().create(storage_key) + + # Исходные данные + items = self.data[ storage_key ] + if items == None: + raise operation_exception("Невозможно сформировать данные. Данные не заполнены!") + + if len(items) == 0: + raise operation_exception("Невозможно сформировать данные. Нет данных!") + + # Сериализуем данные + factory = convert_factory() + data = factory.serialize( items ) + + # Формируем Json + result = json.dumps(data, sort_keys = True, indent = 4, ensure_ascii = False) + return result + + def mimetype(self) -> str: + return "application/json; charset=utf-8" + + + + + + \ No newline at end of file diff --git a/Src/Logics/markdown_reporting.py b/Src/Logics/markdown_reporting.py new file mode 100644 index 0000000..7a0aebe --- /dev/null +++ b/Src/Logics/markdown_reporting.py @@ -0,0 +1,49 @@ +from Src.Logics.reporting import reporting +from Src.exceptions import operation_exception + +class markdown_reporting(reporting): + + def create(self, storage_key: str): + super().create(storage_key) + result = [] + + # Исходные данные + items = self.data[ storage_key ] + if items == None: + raise operation_exception("Невозможно сформировать данные. Данные не заполнены!") + + + if len(items) == 0: + raise operation_exception("Невозможно сформировать данные. Нет данных!") + + # Заголовок + result.append(f"# {storage_key}") + + # Шапка таблицы + header = "" + line = "" + for field in self.fields: + header += f"|{field}" + line += "|--" + + result.append(f"{header}|") + result.append(f"{line}|") + + # Данные + for item in items: + row = "" + for field in self.fields: + attribute = getattr(item.__class__, field) + if isinstance(attribute, property): + value = getattr(item, field) + if isinstance(value, (list, dict)) or value is None: + value = "" + + row +=f"|{value}" + + result.append(f"{row}|") + + return "\n".join(result) + + + \ No newline at end of file diff --git a/Src/Logics/process_factory.py b/Src/Logics/process_factory.py new file mode 100644 index 0000000..21d6fbc --- /dev/null +++ b/Src/Logics/process_factory.py @@ -0,0 +1,86 @@ +from Src.Logics.Processings.processing import processing +from Src.Logics.Processings.turn_processing import turn_processing +from Src.exceptions import exception_proxy, argument_exception, operation_exception +from Src.Logics.Processings.debit_processing import debit_processing +from Src.Logics.Processings.aggregate_processing import aggregate_processing +# +# Фабрика процессов обработки складских транзакций +# +class process_factory: + __maps = {} + + def __init__(self) -> None: + self.__build_structure() + + def __build_structure(self): + """ + Сформировать структуру + """ + self.__maps[ process_factory.turn_key()] = turn_processing + self.__maps[ process_factory.debit_key()] = debit_processing + self.__maps[ process_factory.aggregate_key()] = aggregate_processing + + + def create(self, process_key:str) -> processing: + """ + Подобрать нужный процессинг + Args: + process_key (str): Ключ + data (list[storage_row_model]): Исходные данные + Returns: + processing: нужный процессинг + """ + exception_proxy.validate(process_key , str) + if process_key not in self.__maps.keys(): + raise argument_exception(f"Указанный процесс {process_key} не реализован!") + + current_processing = self.__maps[process_key] + if current_processing is None: + raise operation_exception("Некорректно сконфигурирована текущая фабрика!") + + return current_processing + + # Статические методы + + @staticmethod + def aggregate_key() -> str: + """ + Сформировать агрегацию оборотов + Returns: + str: _description_ + """ + return "aggregate" + + @staticmethod + def turn_key() -> str: + """ + Сформировать обороты + Returns: + str: _description_ + """ + return "turns" + + def debit_key() -> str: + """ + Сформировать проводки списания + Returns: + str: _description_ + """ + return "debits" + + + # Код взят: https://github.com/UpTechCompany/GitExample/blob/6665bc70c4933da12f07c0a0d7a4fc638c157c40/storage/storage.py#L30 + + @staticmethod + def process_keys(cls): + """ + Получить список ключей + Returns: + _type_: _description_ + """ + keys = [] + methods = [getattr(cls, method) for method in dir(cls) if callable(getattr(cls, method))] + for method in methods: + if method.__name__.endswith("_key") and callable(method): + keys.append(method()) + return keys \ No newline at end of file diff --git a/Src/Logics/report_factory.py b/Src/Logics/report_factory.py new file mode 100644 index 0000000..e0e2914 --- /dev/null +++ b/Src/Logics/report_factory.py @@ -0,0 +1,91 @@ +from Src.Logics.reporting import reporting +from Src.Logics .markdown_reporting import markdown_reporting +from Src.Logics.csv_reporting import csv_reporting +from Src.Logics.json_reporting import json_reporting +from Src.exceptions import exception_proxy, argument_exception, operation_exception + +# +# Фабрика для отчетов +# +class report_factory: + __maps = {} + + # Формат данных для экспорт в Web сервер + __mimetype: str + + def __init__(self) -> None: + self.__build_structure() + + def __build_structure(self): + """ + Сформировать структуру + """ + self.__maps["csv"] = csv_reporting + self.__maps["markdown"] = markdown_reporting + self.__maps["json"] = json_reporting + + @property + def mimetype(self): + """ + Формат данных для экспорт в Web сервер + Returns: + _type_: _description_ + """ + return self.__mimetype + + def create(self, format: str, data:dict) -> reporting: + """ + Сформировать объект для построения отчетности + Args: + format (str): Тип формта + data (_type_): Словарь с данными + + Returns: + reporting: _description_ + """ + exception_proxy.validate(format, str) + exception_proxy.validate(data, dict) + + if len(data) == 0: + raise argument_exception("Пустые данные") + + if format not in self.__maps.keys(): + raise operation_exception(f"Для {format} нет обработчика") + + # Получаем тип связанный с форматом + report_type = self.__maps[format] + # Получаем объект + result = report_type(data) + self.__mimetype = result.mimetype() + + return result + + def create_response(self, format: str, data:dict, storage_key: str, app): + """ + Сформировать отчет и вывести его в формате response_class для Web сервера + Args: + format (str): тип формата: csv, markdown, json + data (dict): исходные данные + storage_key (str): ключ для отбора данных в storage + app (_type_): Flask приложение + Returns: + response_class: _description_ + """ + if app is None: + raise argument_exception("Некорректно переданы параметры!") + exception_proxy.validate(storage_key, str) + + # Получаем нужный отчет + report = self.create(format, data) + # Формируем данные + info = report.create(storage_key) + + # Подготовить ответ + result = app.response_class( + response = f"{info}", + status = 200, + mimetype = self.mimetype + ) + + return result + \ No newline at end of file diff --git a/Src/Logics/reporting.py b/Src/Logics/reporting.py new file mode 100644 index 0000000..2089f7f --- /dev/null +++ b/Src/Logics/reporting.py @@ -0,0 +1,96 @@ +import abc +from Src.settings import settings +from Src.exceptions import exception_proxy, operation_exception +from Src.reference import reference + + +# +# Абстрактный класс для реализации отчетности +# +class reporting(abc.ABC): + # Набор данных + __data = {} + # Список полей + __fields = [] + + + def __init__(self, _data): + """ + + Args: + _data (_type_): Словарь с данными + """ + + exception_proxy.validate(_data, dict) + self.__data = _data + + + @abc.abstractmethod + def create(self, storage_key: str): + """ + Сформировать отчет + Args: + storage_key (str): Ключ для отбора данных + """ + exception_proxy.validate(storage_key, str) + self.__fields = self.build(storage_key, self.__data) + + return "" + + def mimetype(self) -> str: + """ + Тип данных для формирования ответа Web сервера + Returns: + str: _description_ + """ + return "application/text" + + @staticmethod + def build( storage_key: str, data: dict) -> list: + """ + Предобработка. Получить набор полей + Args: + storage_key (str): ключ в словаре_ + data (dict): Данные - словарь + + Returns: + list: список + """ + + exception_proxy.validate(storage_key, str) + if data is None: + raise operation_exception("Набор данных не определен!") + + if len(data) == 0: + raise operation_exception("Набор данных пуст!") + + item = data[storage_key][0] + result = reference.create_fields( item ) + return result + + def _build(self, storage_key: str) -> list: + """ + Предобработка данных. Возвращает набор полей класса typeKey + Args: + storage_key (str): ключ для выборки данных + Returns: + list: список + """ + return reporting.build(storage_key, self.__data) + + + @property + def fields(self) -> list: + """ + Набор полей от исходного объекта на основании которого формируем отчет + """ + return self.__fields + + @property + def data(self) -> dict: + """ + + Returns: + dict: словарь с данными + """ + return self.__data \ No newline at end of file diff --git a/Src/Logics/start_factory.py b/Src/Logics/start_factory.py new file mode 100644 index 0000000..c3546f3 --- /dev/null +++ b/Src/Logics/start_factory.py @@ -0,0 +1,288 @@ +# Модели +from Src.Models.group_model import group_model +from Src.Models.unit_model import unit_model +from Src.Models.nomenclature_model import nomenclature_model +from Src.reference import reference +from Src.Models.receipe_model import receipe_model +from Src.Models.storage_row_model import storage_row_model +from Src.Models.storage_model import storage_model + +# Системное +from Src.settings import settings +from Src.Storage.storage import storage +from Src.exceptions import exception_proxy, operation_exception, argument_exception + +# +# Класс для обработки данных. Начало работы приложения +# +class start_factory: + __oprions: settings = None + __storage: storage = None + + def __init__(self, _options: settings, + _storage: storage = None) -> None: + + exception_proxy.validate(_options, settings) + self.__oprions = _options + self.__storage = _storage + + + def __save(self, key:str, items: list): + """ + Сохранить данные + Args: + key (str): ключ доступ + items (list): список + """ + exception_proxy.validate(key, str) + + if self.__storage == None: + self.__storage = storage() + + self.__storage.data[ key ] = items + + @property + def storage(self): + """ + Ссылка на объект хранилище данных + Returns: + _type_: _description_ + """ + return self.__storage + + # Статические методы + + @staticmethod + def create_units() -> list: + """ + Сформировать список единиц измерения + Returns: + _type_: _description_ + """ + items = [] + items.append( unit_model.create_gram() ) + items.append( unit_model.create_killogram() ) + items.append( unit_model.create_liter() ) + items.append( unit_model.create_milliliter() ) + items.append( unit_model.create_ting() ) + + return items + + @staticmethod + def create_nomenclatures() -> list: + """ + Сформировать список номенклатуры + """ + + group = group_model.create_default_group() + items = [ {"Мука пшеничная": "киллограмм"}, + {"Сахар":"киллограмм"}, + {"Сливочное масло" : "киллограмм"}, + {"Яйца": "штука"}, + {"Ванилин": "грамм"}, + {"Куринное филе": "киллограмм"}, + {"Салат Романо": "грамм"}, + {"Сыр Пармезан" : "киллограмм"}, + {"Чеснок": "киллограмм"}, + {"Белый хлеб": "киллограмм"}, + {"Соль": "киллограмм"}, {"Черный перец": "грамм"}, + {"Оливковое масло": "литр"}, + {"Лимонный сок": "литр"}, + {"Горчица дижонская": "грамм"}, + {"Сахарная пудра": "грамм"}, + {"Ванилиин": "грамм"}, + {"Корица": "грамм"}, + {"Какао": "киллограмм"}] + + # Подготовим словарь со список единиц измерения + units = reference.create_dictionary(start_factory.create_units()) + + result = [] + for position in items: + # Получаем список кортежей и берем первое значение + _list = list(position.items()) + if len(_list) < 1: + raise operation_exception("Невозможно сформировать элементы номенклатуры! Некорректный список исходных элементов!") + + tuple = list(_list)[0] + + # Получаем неименование номенклатуры и единицы измерения + if len(tuple) < 2: + raise operation_exception("Невозможно сформировать элемент номенклатуры. Длина кортежа не корректна!") + + name = tuple[0] + unit_name = tuple[1] + + if not unit_name in units.keys(): + raise operation_exception(f"Невозможно найти в списке указанную единицу измерения {unit_name}!") + + # Создаем объект - номенклатура + item = nomenclature_model( name, group, units[unit_name]) + result.append(item) + + return result + + @staticmethod + def create_groups() -> list: + """ + Сформировать список групп номенклатуры + Returns: + _type_: _description_ + """ + items = [] + items.append( group_model.create_default_group()) + return items + + @staticmethod + def create_receipts(_data: list = None) -> list: + """ + Сформировать список рецептов + Args: + _data (list, optional): Список номенклатуры. Defaults to None. + + Raises: + argument_exception: _description_ + + Returns: + _type_: Массив объектов receipe_model + """ + result = [] + if _data is None: + data = start_factory.create_nomenclatures() + else: + data = _data + + if len(data) == 0: + raise argument_exception("Некорректно переданы параметры! Список номенклатуры пуст.") + + # Вафли хрустящие в вафильнице + items = [ ("Мука пшеничная", 100), ("Сахар", 80), ("Сливочное масло", 70), + ("Яйца", 1) + ] + item = receipe_model.create_receipt("Вафли хрустящие в вафильнице", "", items, data) + + # Шаги приготовления + item.instructions.extend([ + "Масло положите в сотейник с толстым дном. Растопите его на маленьком огне на плите, на водяной бане либо в микроволновке.", + "Добавьте в теплое масло сахар. Перемешайте венчиком до полного растворения сахара. От тепла сахар довольно быстро растает.", + "Добавьте в масло яйцо. Предварительно все-таки проверьте масло, не горячее ли оно, иначе яйцо может свариться. Перемешайте яйцо с маслом до однородности.", + "Всыпьте муку, добавьте ванилин.", + "Перемешайте массу венчиком до состояния гладкого однородного теста."]) + + item.comments = "Время приготовления: 20 мин. 8 порций" + result.append( item ) + + # Цезарь с курицей + items = [ ("Куринное филе", 200), ("Салат Романо", 50), ("Сыр Пармезан", 50), + ("Чеснок", 10) , ("Белый хлеб", 30 ), ("Соль", 5), ("Черный перец", 2), + ("Оливковое масло", 10), ("Лимонный сок", 5), ("Горчица дижонская", 5), + ("Яйца", 2) + ] + item = receipe_model.create_receipt("Цезарь с курицей", "", items, data) + result.append(item) + + # Безе + items = [ ("Яйца", 3), ("Сахарная пудра", 180), ("Ванилиин" , 5), ("Корица", 5) ,("Какао", 20) ] + result.append( receipe_model.create_receipt("Безе", "", items, data)) + return result + + @staticmethod + def create_storage_transactions(data: dict) -> list: + """ + Сформировать список складских транзакций + Returns: + _type_: Массив объектов storage_row_model + """ + result = [] + default_storage = storage_model.create_default() + + if len(data.keys()) == 0: + raise operation_exception("Набор данных пуст. Невозможно сформировать список транзакций!") + + items = [ ( "Мука пшеничная", 1, "киллограмм") , + ( "Черный перец", 50, "грамм" ), + ( "Сахар" , 0.5, "киллограмм"), + ( "Яйца", 6,"штука" ), + ( "Оливковое масло", 0.2, "литр" ), + ( "Куринное филе" ,0.5, "киллограмм"), + ( "Салат Романо", 1, "штука" ), + ( "Белый хлеб" , 3, "штука" ), + ( "Сыр Пармезан", 0.2, "киллограмм") , + ( "Горчица дижонская" ,0.1, "литр" ), + ( "Черный перец", 10, "грамм" ), + ( "Лимонный сок", 1, "литр" ), + ( "Какао" ,1,"киллограмм"), + ( "Сливочное масло" ,0.5, "киллограмм" ), + ( "Сыр Пармезан", 0.3, "киллограмм") , + ( "Ванилиин", 100, "грамм" ), + ( "Соль", 1, "киллограмм" ) + ] + + for element in items: + if len(element) < 3: + raise operation_exception("Некорректно сформирован список для генерации рецептов!") + + nomenclature_name = element[0] + quantity = element[1] + unit_name = element[2] + + row = storage_row_model.create_credit_row(nomenclature_name, quantity, unit_name , data, default_storage) + result.append(row) + + return result + + + # Основной метод + def create(self) -> bool: + """ + В зависимости от настроек, сформировать или загрузить набор данных + Returns: + _type_: _description_ + """ + + if self.__storage == None: + self.__storage = storage() + + if self.__oprions.is_first_start == True: + + try: + # 1. Формируем и зпоминаем номеклатуру + nomenclatures = start_factory.create_nomenclatures() + self.__save( storage.nomenclature_key(), nomenclatures ) + + # 2. Формируем и запоминаем рецепты + items = start_factory.create_receipts(nomenclatures) + self.__save( storage.receipt_key(), items) + + # 3. Формируем и запоминаем единицы измерения + items = start_factory.create_units() + self.__save( storage.unit_key(), items) + + # 4. Формируем и запоминаем группы номенклатуры + items = start_factory.create_groups() + self.__save( storage.group_key(), items) + + # 5. Формируем типовые складские проводки + items = start_factory.create_storage_transactions( self.storage.data ) + self.__save( storage.storage_transaction_key(), items) + + except Exception as ex: + raise operation_exception(f"Ошибка при формировании шаблонных данных!\n{ex}") + + else: + # Другой вариант. Загрузка из источника данных + try: + self.__storage.load() + except Exception as ex: + raise operation_exception(f"Ошибка при формировании шаблонных данных!\n{ex}") + + + + + + + + + + + \ No newline at end of file diff --git a/Src/Logics/storage_prototype.py b/Src/Logics/storage_prototype.py new file mode 100644 index 0000000..35fe3f8 --- /dev/null +++ b/Src/Logics/storage_prototype.py @@ -0,0 +1,98 @@ +from Src.exceptions import argument_exception, exception_proxy +from Src.errors import error_proxy +from datetime import datetime +from Src.Models.nomenclature_model import nomenclature_model + +# +# Прототип для обработки складских транзакций +# +class storage_prototype(error_proxy): + __data = [] + + def __init__(self, data: list) -> None: + if len(data) <= 0: + self.error = "Набор данных пуст!" + + exception_proxy.validate(data, list) + self.__data = data + self.clear() + + # Методы фильтрации + + def filter_by_period( self,start_period: datetime, stop_period: datetime ): + """ + Отфильтровать по периоду + Args: + start_period (datetime): начало + stop_period (datetime): окончание + + Returns: + storage_prototype: _description_ + """ + self.clear() + + exception_proxy.validate(start_period, datetime) + exception_proxy.validate(stop_period, datetime) + if len(self.__data) <= 0: + self.error = "Некорректно переданы параметры!" + + if start_period > stop_period: + self.error = "Некорректный период!" + + + if not self.is_empty: + return self.__data + + result = [] + for item in self.__data: + if item.period > start_period and item.period <= stop_period: + result.append(item) + + return storage_prototype( result ) + + + def filter_by_nomenclature(self, nomenclature: nomenclature_model): + """ + Отфильтровать по номенклатуре + Args: + nomenclature (nomenclature_model): _description_ + + Returns: + storage_prototype: _description_ + """ + self.clear() + + exception_proxy.validate(nomenclature, nomenclature_model) + + result = [] + for item in self.__data: + if item.nomenclature.id == nomenclature.id: + result.append(item) + + return storage_prototype( result ) + + # Методы фильтрации + + @property + def data(self): + """ + Полученные данные + Returns: + _type_: _description_ + """ + return self.__data + + @data.setter + def data(self, value: list): + """ + Исходные данные + Args: + value (list): _description_ + """ + exception_proxy.validate(value, list) + self.__data = value + + + + + \ No newline at end of file diff --git a/Src/Models/event_type.py b/Src/Models/event_type.py new file mode 100644 index 0000000..d8da4e8 --- /dev/null +++ b/Src/Models/event_type.py @@ -0,0 +1,22 @@ +from Src.reference import reference + + +""" Типы событий """ + +class event_type(reference): + + + @staticmethod + def changed_block_period() -> str: + + """ + Событие изменения даты блокировки + """ + return "changed_block_period" + + @staticmethod + def nomenclature_deleted() -> str: + """ + Событие удаления номенклатуры из рецептов + """ + return "nomenclature_deleted" \ No newline at end of file diff --git a/Src/Models/event_value.py b/Src/Models/event_value.py new file mode 100644 index 0000000..517da27 --- /dev/null +++ b/Src/Models/event_value.py @@ -0,0 +1,10 @@ +from Src.reference import reference + + +class event_value(reference): + + @property + def event_source(self) -> str: + pass + + \ No newline at end of file diff --git a/Src/Models/group_model.py b/Src/Models/group_model.py new file mode 100644 index 0000000..4f2e84b --- /dev/null +++ b/Src/Models/group_model.py @@ -0,0 +1,16 @@ +from Src.reference import reference + +# +# Модель группу номенклатуры +# +class group_model(reference): + def create_default_group(): + """ + Фабричный метод. Создать группу по умолчанию + + Returns: + _type_: _description_ + """ + item = group_model("Ингредиенты") + return item + \ No newline at end of file diff --git a/Src/Models/nomenclature_model.py b/Src/Models/nomenclature_model.py new file mode 100644 index 0000000..4e5e115 --- /dev/null +++ b/Src/Models/nomenclature_model.py @@ -0,0 +1,95 @@ +from Src.reference import reference +from Src.exceptions import exception_proxy, operation_exception +from Src.Models.unit_model import unit_model +from Src.Models.group_model import group_model + + +class nomenclature_model(reference): + " Группа номенклатуры " + _group = None + " Единица измерения " + _unit = None + + + def __init__(self, name:str = None, group: reference = None, unit: reference = None): + """_summary_ + + Args: + name (str): Наименование + group (reference): Группа + unit (reference): Единица измерения + """ + + if not group is None: + exception_proxy.validate(group, reference) + self._group = group + + if not unit is None: + exception_proxy.validate(unit, reference) + self._unit = unit + + super().__init__(name) + + @property + def group(self) -> group_model: + " Группа номенклатуры " + return self._group + + @group.setter + def group(self, value: reference): + " Группа номенклатуры " + exception_proxy.validate(value, reference) + self._group = value + + @property + def unit(self) -> unit_model: + " Единица измерения " + return self._unit + + @unit.setter + def unit(self, value: reference): + " Единица измерения " + exception_proxy.validate(value, reference) + self._unit = value + + def load(self, source: dict): + """ + Загрузить данные + Args: + source (dict): исходный словарь + """ + super().load(source) + if source is None: + return None + + source_fields = ["unit", "group"] + if set(source_fields).issubset(list(source.keys())) == False: + raise operation_exception(f"Невозможно загрузить данные в объект {self}!") + + self._group = group_model().load(source["group"]) + self._unit = unit_model().load(source["unit"]) + + return self + + # Фабричные методы + + @staticmethod + def get(nomenclature_name: str, nomenclatures: dict): + """ + Получить значение элемента номенклатуры из словаря + Args: + nomenclature_name (str): наименование + nomenclatures (dict): исходный словарь storage.data + + Returns: + nomenclature_model: _description_ + """ + exception_proxy.validate(nomenclature_name, str) + + keys = list(filter(lambda x: x == nomenclature_name, nomenclatures.keys() )) + if len(keys) == 0: + raise operation_exception(f"Некоректно передан список. Не найдена номенклатура {nomenclature_name}!") + + return nomenclatures[keys[0]] + + \ No newline at end of file diff --git a/Src/Models/receipe_model.py b/Src/Models/receipe_model.py new file mode 100644 index 0000000..1fb50d8 --- /dev/null +++ b/Src/Models/receipe_model.py @@ -0,0 +1,214 @@ +from Src.reference import reference +from Src.Models.receipe_row_model import receipe_row_model +from Src.exceptions import exception_proxy , operation_exception, argument_exception +from Src.Models.nomenclature_model import nomenclature_model + +# +# Класс описание рецепта приготовления блюда +# +class receipe_model(reference): + # Вес брутто + _brutto: int = 0 + + # Вес нетто + _netto: int = 0 + + # Состав рецепта + _rows = {} + + # Инструкции + _instructions = list() + + # Описание + _comments: str = "" + + def __init__(self, name = None): + super().__init__(name) + self._rows = {} + self._instructions = [] + self._brutto = 0 + + def add(self, row: receipe_row_model): + """ + Добавить/ изменить состав блюда + Args: + row (receipe_row_model): _description_ + """ + exception_proxy.validate(row, receipe_row_model) + self._rows[row.name] = row + self.__calc_brutto() + + def delete(self, row: receipe_row_model): + """ + Удалить из состава блюда + Args: + row (receipe_row_model): _description_ + """ + exception_proxy.validate(row, receipe_row_model) + + if row.name in self._rows.keys(): + self._rows.pop(row.name) + + self.__calc_brutto() + + def __calc_brutto(self): + """ + Перерасчет брутто + """ + self._brutto = 0 + for position in self._rows: + # Получаем свойство size + self._brutto += self._rows[position].size + + @property + def brutto(self) -> int: + """ + Вес брутто + Returns: + int : _description_ + """ + return self._brutto + + @brutto.setter + def brutto(self, value: int): + exception_proxy.validate(value, int) + self._brutto = value + + @property + def netto(self) -> int: + return self._netto + + @netto.setter + def netto(self, value: int): + """ + Вес нетто + Args: + value (int): _description_ + """ + exception_proxy.validate(value, int) + + self._netto = value + + @property + def instructions(self) -> list: + """ + Инструкции для приготовления + Returns: + _type_: _description_ + """ + return self._instructions + + @property + def comments(self) -> str: + return self._comments + + + @comments.setter + def comments(self, value: str): + """ + Описание блюда + Args: + value (str): _description_ + """ + exception_proxy.validate(value, str) + self._comments = value + + @property + def consist(self) -> list: + """ + Состав рецепта + Returns: + _type_: _description_ + """ + return self._rows + + def rows(self) -> list: + """ + Получить состав рецепта (read only) + Returns: + _type_: _description_ + """ + result = [] + for key in self._rows.keys(): + result.append( self._rows[key] ) + + return result + + def load(self, source: dict): + """ + Загрузить данные из словаря + Args: + source (dict): исходный словарь + + """ + super().load(source) + if source is None: + return None + + source_fields = ["comments", "consist", "instructions","netto", "brutto"] + if set(source_fields).issubset(list(source.keys())) == False: + raise operation_exception(f"Невозможно загрузить данные в объект {source}!") + + self._netto = source["netto"] + self._brutto = source["brutto"] + + # Загрузим состав + for item in source["consist"].items(): + row = item[1] + if row is not None: + value = receipe_row_model().load(row) + self.add(value) + + # Загрузим инструкции + self._instructions = source["instructions"] + return self + + + + @staticmethod + def create_receipt(name: str, comments: str, items: list, data: list): + """ + Фабричный метод. Сформировать рецепт + Args: + name (str): Наименование рецепта + comments (str): Приготовление + items (list): Состав рецепта + data (list): Список номенклатуры + + Returns: + receipe_model: _description_ + """ + exception_proxy.validate(name, str) + if len(items) == 0: + raise argument_exception(f"Некорректно передан параметр {items}. Список пуст!") + + + # Подготовим словарь со списком номенклатуры + nomenclatures = reference.create_dictionary(data) + receipt = receipe_model(name) + if comments != "": + receipt.comments = comments + + for position in items: + + if len(position) < 2: + raise operation_exception("Невозможно сформировать элементы рецепта! Некорректный список исходных элементов!") + + nomenclature_name = position[0] + size = position[1] + nomenclature = nomenclature_model.get( nomenclature_name, nomenclatures ) + + # Определяем единицу измерения + if nomenclature.unit.base_unit is None: + unit = nomenclature.unit + else: + unit = nomenclature.unit.base_unit + + # Создаем запись в рецепте + row = receipe_row_model() + row.nomenclature = nomenclature + row.size = size + row.unit = unit + receipt.add(row) + + return receipt \ No newline at end of file diff --git a/Src/Models/receipe_row_model.py b/Src/Models/receipe_row_model.py new file mode 100644 index 0000000..8630c3b --- /dev/null +++ b/Src/Models/receipe_row_model.py @@ -0,0 +1,113 @@ +from Src.reference import reference +from Src.Models.nomenclature_model import nomenclature_model +from Src.Models.unit_model import unit_model +from Src.exceptions import exception_proxy, operation_exception +from Src.Models.storage_row_model import storage_row_model +from Src.Models.storage_model import storage_model + +from datetime import datetime + +# +# Класс описание одной строки рецепта +# +class receipe_row_model(reference): + __nomenclature: nomenclature_model = None + __size: int = 0 + __unit: unit_model = None + + def __init__(self): + super().__init__() + + @property + def nomenclature(self): + """ + Номенклатура + Returns: + _type_: _description_ + """ + return self.__nomenclature + + @nomenclature.setter + def nomenclature(self, value: nomenclature_model): + exception_proxy.validate(value, nomenclature_model) + self._name = f"{value.name}" + self.__nomenclature = value + + + @property + def size(self) -> float: + """ + Размер + + Returns: + _type_: _description_ + """ + return self.__size + + + @size.setter + def size(self, value ): + exception_proxy.validate(value, (float, int)) + self.__size = value + + + @property + def unit(self) -> unit_model: + """ + Единица измерения + + Returns: + _type_: _description_ + """ + return self.__unit + + @unit.setter + def unit(self, value: unit_model): + exception_proxy.validate(value, unit_model) + self.__unit = value + + def load(self, source: dict): + """ + Загрузить из словаря + Args: + source (dict): словарь + """ + super().load(source) + if source is None: + return None + + source_fields = ["unit", "size", "nomenclature"] + if set(source_fields).issubset(list(source.keys())) == False: + raise operation_exception(f"Невозможно загрузить данные в объект {source}!") + + self.__size = source["size"] + self.__nomenclature = nomenclature_model().load( source[ "nomenclature"]) + self.__unit = unit_model().load(source["unit"]) + + return self + + @staticmethod + def create_debit_transaction( row, period : datetime, storage: storage_model ) -> storage_row_model: + """ + Сформировать транзакцию списания + Args: + row (receipe_row_model): исходная запись рецепта + period (datetime): период + storage (storage_model): склад + + Returns: + storage_row_model: _description_ + """ + exception_proxy.validate(period , datetime) + exception_proxy.validate(storage, storage_model) + + item = storage_row_model(f"debit transaction") + item.nomenclature = row.nomenclature + item.period = period + item.storage = storage + item.storage_type = False + item.value = row.size + item.unit = row.unit + + return item + diff --git a/Src/Models/storage_model.py b/Src/Models/storage_model.py new file mode 100644 index 0000000..48d68ac --- /dev/null +++ b/Src/Models/storage_model.py @@ -0,0 +1,61 @@ +from Src.reference import reference +from Src.exceptions import exception_proxy, operation_exception + +# +# Модель склада +# +class storage_model(reference): + _address: str = "" + + @property + def address(self) -> str: + """ + Адрес + + Returns: + _type_: _description_ + """ + return self._address + + @address.setter + def address(self, value:str): + """ + Адрес + Args: + value (str): _description_ + """ + exception_proxy.validate(value, str) + self._address = value + + + def load(self, source: dict): + """ + Десериализовать свойства + Args: + source (dict): исходный слова + """ + if source is None: + return None + super().load(source) + + source_fields = ["address"] + if set(source_fields).issubset(list(source.keys())) == False: + raise operation_exception(f"Невозможно загрузить данные в объект {source}!") + + self._address = source["address"] + return self + + # Фабричные методы + + @staticmethod + def create_default() -> reference: + """ + Сформировать склад по умолчанию + Returns: + reference: _description_ + """ + storage = storage_model("default") + storage.address = "г. Москва. ул. Академика Королева, 10" + + return storage + \ No newline at end of file diff --git a/Src/Models/storage_row_model.py b/Src/Models/storage_row_model.py new file mode 100644 index 0000000..b923384 --- /dev/null +++ b/Src/Models/storage_row_model.py @@ -0,0 +1,252 @@ +from random import randrange + +from Src.exceptions import argument_exception, exception_proxy, operation_exception +from Src.reference import reference +from Src.Models.storage_model import storage_model +from Src.Models.storage_row_turn_model import storage_row_turn_model +from Src.Storage.storage import storage +from datetime import datetime, timedelta +from Src.Models.nomenclature_model import nomenclature_model +from Src.Models.unit_model import unit_model + +# +# Модель складской проводки +# +class storage_row_model(reference): + # Тип складской проводки + _storage_type: bool = False + # Период + _period : datetime + # Номенклатура + _nomenclature: nomenclature_model = None + # Склад + _storage: storage_model = None + # Единица измерений + _unit: unit_model = None + # Значение + _value: float = 0 + + + @property + def value(self) -> float: + """ + Значение + Returns: + float: _description_ + """ + return self._value + + @value.setter + def value(self, value: float) -> float: + """ + Значение + Args: + value (float): _description_ + + Raises: + argument_exception: _description_ + + Returns: + float: _description_ + """ + exception_proxy.validate(value, (float, int)) + if value <= 0: + raise argument_exception("Некорректно переданы параметры!") + + self._value = value + + @property + def nomenclature(self) -> nomenclature_model: + """ + Номенклатура + Returns: + nomenclature_model: _description_ + """ + return self._nomenclature + + @nomenclature.setter + def nomenclature(self, value: nomenclature_model) -> nomenclature_model: + """ + Номенклатура + Args: + value (nomenclature_model): _description_ + """ + exception_proxy.validate(value, nomenclature_model) + self._nomenclature = value + + + @property + def unit(self) -> unit_model: + """ + Единица измерения + Returns: + unit_model: _description_ + """ + return self._unit + + @unit.setter + def unit(self, value: unit_model) -> unit_model: + """ + Единица измерения + Args: + value (unit_model): _description_ + + Returns: + unit_model: _description_ + """ + exception_proxy.validate(value, unit_model) + self._unit = value + + @property + def storage(self) -> storage_model: + """ + Склад + Returns: + storage_model: _description_ + """ + return self._storage + + @storage.setter + def storage(self, value: storage_model) -> storage_model: + """ + Склад + Args: + value (storage_model): _description_ + + Returns: + storage_model: _description_ + """ + exception_proxy.validate(value, storage_model) + self._storage = value + + @property + def storage_type(self) -> bool: + """ + Тип складской проводки (True - приход, False - расход) + Returns: + bool: _description_ + """ + return self._storage_type + + @storage_type.setter + def storage_type(self, value) -> bool: + """ + Тип складской проводки (True - приход, False - расход) + Args: + value (_type_): _description_ + + Raises: + argument_exception: _description_ + + Returns: + bool: _description_ + """ + if isinstance(value, int): + self._storage_type = True if value > 0 else False + + elif isinstance(value, bool): + self._storage_type = value + + else: + raise argument_exception("Некорректно переданы параметры!") + + @property + def period(self) -> datetime: + """ + Дата транзакции + Returns: + datetime: _description_ + """ + return self._period + + @period.setter + def period(self, value: datetime) -> datetime: + """ + Дата транзакции + """ + exception_proxy.validate(value, datetime) + self._period = value + + + def load(self, source: dict): + """ + Десериализовать свойства + Args: + source (dict): исходный слова + """ + if source is None: + return None + super().load(source) + + source_fields = ["period", "storage_type", "nomenclature", "value", "unit", "storage" ] + if set(source_fields).issubset(list(source.keys())) == False: + raise operation_exception(f"Невозможно загрузить данные в объект {source}!") + + self._value = source["value"] + self._period = datetime.strptime(source["period"], "%Y-%m-%d") + self._nomenclature = nomenclature_model().load( source["nomenclature"]) + self._storage = storage_model().load( source["storage"] ) + self._unit = unit_model().load( source["unit"]) + self._storage_type = source["storage_type"] + + return self + + # Фабричные методы + + @staticmethod + def create_credit_row(nomenclature_name: str, quantity, unit_name: str, data: dict, _storage: storage_model) -> reference: + """ + Фабричный метод для создания транзакции на поступление + Используется в start_factory + Args: + nomenclature_name (str): Наименование номенклатуры + quantity(int, float): Количество + unit_name (str): Наименование единицы измерения + data (dict): исходный набор данных + storage(storage_model): склад + Returns: + reference: _description_ + """ + exception_proxy.validate(nomenclature_name, str) + exception_proxy.validate(_storage, storage_model) + exception_proxy.validate(quantity, (int, float)) + exception_proxy.validate(unit_name, str) + + + # Определим номенклатуру + items = data[ storage.nomenclature_key() ] + nomenclatures = reference.create_dictionary(items) + nomenclature = nomenclature_model.get( nomenclature_name, nomenclatures) + + # Определяем единицу измерения + items = data[ storage.unit_key()] + units = reference.create_dictionary(items) + unit = unit_model.get(unit_name, units ) + + start_date = datetime.strptime("2024-01-01", "%Y-%m-%d") + stop_date = datetime.strptime("2024-02-01", "%Y-%m-%d") + + # Создаем транзакцию + item = storage_row_model("sample_credit_transaction") + item.nomenclature = nomenclature + item.unit = unit + item.storage_type = True + item.value = quantity + item.storage = _storage + item.period = storage_row_model.random_date(start_date, stop_date) + + return item + + # Источник https://stackoverflow.com/questions/553303/generate-a-random-date-between-two-other-dates + @staticmethod + def random_date(start, end): + delta = end - start + int_delta = (delta.days * 24 * 60 * 60) + delta.seconds + random_second = randrange(int_delta) + return start + timedelta(seconds=random_second) + + + + + + diff --git a/Src/Models/storage_row_turn_model.py b/Src/Models/storage_row_turn_model.py new file mode 100644 index 0000000..359a57a --- /dev/null +++ b/Src/Models/storage_row_turn_model.py @@ -0,0 +1,125 @@ +from Src.reference import reference +from Src.exceptions import exception_proxy, argument_exception +from Src.Models.nomenclature_model import nomenclature_model +from Src.Models.storage_model import storage_model +from Src.Models.unit_model import unit_model + +# +# Модель складского оборота +# +class storage_row_turn_model(reference): + # Номенклатура + _nomenclature: nomenclature_model = None + # Склад + _storage: storage_model = None + # Единица измерений + _unit: unit_model = None + # Значение + _value: float = 0 + + + @property + def value(self) -> float: + """ + Значение + Returns: + float: _description_ + """ + return self._value + + @value.setter + def value(self, value: float) -> float: + """ + Значение + Args: + value (float): _description_ + + Raises: + argument_exception: _description_ + + Returns: + float: _description_ + """ + exception_proxy.validate(value, (float, int)) + self._value = value + + @property + def nomenclature(self) -> nomenclature_model: + """ + Номенклатура + Returns: + nomenclature_model: _description_ + """ + return self._nomenclature + + @nomenclature.setter + def nomenclature(self, value: nomenclature_model) -> nomenclature_model: + """ + Номенклатура + Args: + value (nomenclature_model): _description_ + """ + exception_proxy.validate(value, nomenclature_model) + self._nomenclature = value + + + @property + def unit(self) -> unit_model: + """ + Единица измерения + Returns: + unit_model: _description_ + """ + return self._unit + + def unit(self, value: unit_model) -> unit_model: + """ + Единица измерения + Args: + value (unit_model): _description_ + + Returns: + unit_model: _description_ + """ + exception_proxy.validate(value, unit_model) + self._unit = value + + + def storage(self) -> storage_model: + """ + Склад + Returns: + storage_model: _description_ + """ + return self._storage + + def storage(self, value: storage_model) -> storage_model: + """ + Склад + Args: + value (storage_model): _description_ + + Returns: + storage_model: _description_ + """ + exception_proxy.validate(value, storage_model) + self._storage = value + + @staticmethod + def create(nomenclature : nomenclature_model, storage: storage_model, unit: unit_model) -> reference: + """ + Фабричный метод для создания складского оборота + Args: + nomenclature (nomenclature_model): _description_ + storage (storage_model): _description_ + unit (unit_model): _description_ + + Returns: + storage_row_turn_model: _description_ + """ + row = storage_row_turn_model("-") + row.storage = storage + row.unit = unit + row.nomenclature = nomenclature + + return row \ No newline at end of file diff --git a/Src/Models/unit_model.py b/Src/Models/unit_model.py new file mode 100644 index 0000000..698cdbf --- /dev/null +++ b/Src/Models/unit_model.py @@ -0,0 +1,163 @@ +from Src.reference import reference +from Src.exceptions import exception_proxy, argument_exception, operation_exception + + + +# +# Модель единицы измерения для номенклатуры +# +class unit_model(reference): + + # Базовая единица измерения + __base_unit: reference = None + + # Коэффициент пересчета к базовой единице измерения + __coefficient: int = 1 + + def __init__(self, name: str = None, base: reference = None, coeff: int = 1 ): + super().__init__(name) + + if base != None: + self.base_unit = base + + if coeff != 1: + self.coefficient = coeff + + + @property + def base_unit(self) -> reference: + """ + Базовая единица измерения + Returns: + _type_: _description_ + """ + return self.__base_unit + + + @base_unit.setter + def base_unit(self, value: reference ): + exception_proxy.validate(value, reference) + self.__base_unit = value + + + @property + def coefficient(self) -> int: + """ + Коэффициент пересчета + Returns: + _type_: _description_ + """ + return self.__coefficient + + @coefficient.setter + def coefficient(self, value:int): + exception_proxy.validate(value, int) + + if(value <= 0): + raise argument_exception("Значение коэффициента должно быть > 1!") + + self.__coefficient = value + + def load(self, source: dict): + """ + Загрузить данные + Args: + source (dict): исходный словарь + + """ + super().load(source) + if source is None: + return None + + source_fields = ["coefficient", "base_unit"] + if set(source_fields).issubset(list(source.keys())) == False: + raise operation_exception(f"Невозможно загрузить данные в объект {source}!") + + self.__coefficient = source["coefficient"] + self.__base_unit = unit_model().load(source["base_unit"]) + + return self + + + + # Фабричные методы + + @staticmethod + def create_gram(): + """ + Создать единицу измерения - грамм + + Returns: + _type_: _description_ + """ + item = unit_model("грамм", None, 1) + return item + + @staticmethod + def create_killogram(): + """ + Создать единицу измерения - киллограмм + Returns: + _type_: _description_ + """ + base = unit_model.create_gram() + item = unit_model("киллограмм", base, 1000) + return item + + @staticmethod + def create_ting(): + """ + Создать единицу изменения - штуки + Returns: + _type_: _description_ + """ + return unit_model("штука") + + def create_milliliter(): + """ + Создать единицу измерения - миллилитр + Returns: + _type_: _description_ + """ + return unit_model("миллилитр") + + def create_liter(): + """ + Создать единицу измерения - литр + Returns: + _type_: _description_ + """ + base = unit_model.create_milliliter() + item = unit_model("литр", base, 1000) + return item + + + @staticmethod + def get(unit_name: str, units: dict): + """ + Получить значение элемента единицы измерения из словаря + Args: + nomenclature_name (str): наименование + nomenclatures (dict): исходный словарь storage.data + + Returns: + nomenclature_model: _description_ + """ + exception_proxy.validate(unit_name, str) + + keys = list(filter(lambda x: x == unit_name, units.keys() )) + if len(keys) == 0: + raise operation_exception(f"Некоректно передан список. Не найдена номенклатура {unit_name}!") + + return units[keys[0]] + + + + + + + + + + + \ No newline at end of file diff --git a/Src/Storage/storage.json b/Src/Storage/storage.json new file mode 100644 index 0000000..2b6918a --- /dev/null +++ b/Src/Storage/storage.json @@ -0,0 +1,2260 @@ +{ + "group_model": [ + { + "description": "", + "id": "44298be97ad7482497f6fc8d6deb0808", + "is_error": false, + "name": "Ингредиенты" + } + ], + "nomenclature_model": [ + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "5c16c9c869c944ca81571f6cf70a790d", + "is_error": false, + "name": "Мука пшеничная", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "e787ff8a837d40418f9121f1272616ab", + "is_error": false, + "name": "Сахар", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "998f959f17704d85b0fad15936283032", + "is_error": false, + "name": "Сливочное масло", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "8beba2f92e754eeda776859bc197af39", + "is_error": false, + "name": "Яйца", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "f1a87af30e0c4faca9c3184050b3e370", + "is_error": false, + "name": "штука" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "11456b73dc1c4620b9387016648509b9", + "is_error": false, + "name": "Ванилин", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "6a448c9b996646888ace90ed26a7547b", + "is_error": false, + "name": "Куринное филе", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "5b5936b9384a4fe59b24e3fd127377d0", + "is_error": false, + "name": "Салат Романо", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "1696620e15b74a70b7fbd3c28076255b", + "is_error": false, + "name": "Сыр Пармезан", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "87ae9afa51d74a94af801f7037d59d8d", + "is_error": false, + "name": "Чеснок", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "3c8d6792212a4feba49e48770995ee55", + "is_error": false, + "name": "Белый хлеб", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "fb5741984d24441789ecd2a2d0c6637a", + "is_error": false, + "name": "Соль", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "6ea66e6494ad447a996ad93fdb38697e", + "is_error": false, + "name": "Черный перец", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "e8bc8e612eab4fecb1ba20b685267cc6", + "is_error": false, + "name": "Оливковое масло", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "11be8eb6981a4f49bbe0fe93f136a00e", + "is_error": false, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "ce323ec5feb840c9843d4380a8630fcf", + "is_error": false, + "name": "литр" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "cdbbecb3fa594a799e5b4c6a14058c42", + "is_error": false, + "name": "Лимонный сок", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "11be8eb6981a4f49bbe0fe93f136a00e", + "is_error": false, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "ce323ec5feb840c9843d4380a8630fcf", + "is_error": false, + "name": "литр" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "af0af126ae0b41c38ea53544777721b9", + "is_error": false, + "name": "Горчица дижонская", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "ffa7503c9ef64bdcb51b762fa3595649", + "is_error": false, + "name": "Сахарная пудра", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "0be1452f2ae649e88008a595ba309aba", + "is_error": false, + "name": "Ванилиин", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "f658b569df3d46709cce01b5a7383cd5", + "is_error": false, + "name": "Корица", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "bf459ed1953f437fa0a1a7f2f3c87f46", + "is_error": false, + "name": "Какао", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + } + ], + "receipe_model": [ + { + "brutto": 251, + "comments": "Время приготовления: 20 мин. 8 порций", + "consist": { + "Мука пшеничная": { + "description": "", + "id": "7c92cf932de44581951ca05738464de2", + "is_error": false, + "name": "Мука пшеничная", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "5c16c9c869c944ca81571f6cf70a790d", + "is_error": false, + "name": "Мука пшеничная", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + "size": 100, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + } + }, + "Сахар": { + "description": "", + "id": "4f50b78ffb3f43848bdb08279ff95823", + "is_error": false, + "name": "Сахар", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "e787ff8a837d40418f9121f1272616ab", + "is_error": false, + "name": "Сахар", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + "size": 80, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + } + }, + "Сливочное масло": { + "description": "", + "id": "244833ce4dad4f21bbdddeb0bce73ec2", + "is_error": false, + "name": "Сливочное масло", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "998f959f17704d85b0fad15936283032", + "is_error": false, + "name": "Сливочное масло", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + "size": 70, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + } + }, + "Яйца": { + "description": "", + "id": "843cebf7329d41de9dff0ae4bdbb43cb", + "is_error": false, + "name": "Яйца", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "8beba2f92e754eeda776859bc197af39", + "is_error": false, + "name": "Яйца", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "f1a87af30e0c4faca9c3184050b3e370", + "is_error": false, + "name": "штука" + } + }, + "size": 1, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "f1a87af30e0c4faca9c3184050b3e370", + "is_error": false, + "name": "штука" + } + } + }, + "description": "", + "id": "89a9226cf02643d8a9e856fbc0ca4351", + "instructions": [ + { + "instructions": "Масло положите в сотейник с толстым дном. Растопите его на маленьком огне на плите, на водяной бане либо в микроволновке." + }, + { + "instructions": "Добавьте в теплое масло сахар. Перемешайте венчиком до полного растворения сахара. От тепла сахар довольно быстро растает." + }, + { + "instructions": "Добавьте в масло яйцо. Предварительно все-таки проверьте масло, не горячее ли оно, иначе яйцо может свариться. Перемешайте яйцо с маслом до однородности." + }, + { + "instructions": "Всыпьте муку, добавьте ванилин." + }, + { + "instructions": "Перемешайте массу венчиком до состояния гладкого однородного теста." + } + ], + "is_error": false, + "name": "Вафли хрустящие в вафильнице", + "netto": 0 + }, + { + "brutto": 369, + "comments": "", + "consist": { + "Белый хлеб": { + "description": "", + "id": "fe7a448350184766b6c57a1cea5df43e", + "is_error": false, + "name": "Белый хлеб", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "3c8d6792212a4feba49e48770995ee55", + "is_error": false, + "name": "Белый хлеб", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + "size": 30, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + } + }, + "Горчица дижонская": { + "description": "", + "id": "a5221530e2574658bb08fd2d55ef4a97", + "is_error": false, + "name": "Горчица дижонская", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "af0af126ae0b41c38ea53544777721b9", + "is_error": false, + "name": "Горчица дижонская", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + "size": 5, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + "Куринное филе": { + "description": "", + "id": "8814e19f162e4b3db2a8233524c50955", + "is_error": false, + "name": "Куринное филе", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "6a448c9b996646888ace90ed26a7547b", + "is_error": false, + "name": "Куринное филе", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + "size": 200, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + } + }, + "Лимонный сок": { + "description": "", + "id": "2ca49b7384144e4eb07a3a8072de0d09", + "is_error": false, + "name": "Лимонный сок", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "cdbbecb3fa594a799e5b4c6a14058c42", + "is_error": false, + "name": "Лимонный сок", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "11be8eb6981a4f49bbe0fe93f136a00e", + "is_error": false, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "ce323ec5feb840c9843d4380a8630fcf", + "is_error": false, + "name": "литр" + } + }, + "size": 5, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "11be8eb6981a4f49bbe0fe93f136a00e", + "is_error": false, + "name": "миллилитр" + } + }, + "Оливковое масло": { + "description": "", + "id": "b9999b177e644de5b52f2acaab0a1d58", + "is_error": false, + "name": "Оливковое масло", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "e8bc8e612eab4fecb1ba20b685267cc6", + "is_error": false, + "name": "Оливковое масло", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "11be8eb6981a4f49bbe0fe93f136a00e", + "is_error": false, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "ce323ec5feb840c9843d4380a8630fcf", + "is_error": false, + "name": "литр" + } + }, + "size": 10, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "11be8eb6981a4f49bbe0fe93f136a00e", + "is_error": false, + "name": "миллилитр" + } + }, + "Салат Романо": { + "description": "", + "id": "2fccc854834a49ffb0f5a4f83801d2e5", + "is_error": false, + "name": "Салат Романо", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "5b5936b9384a4fe59b24e3fd127377d0", + "is_error": false, + "name": "Салат Романо", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + "size": 50, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + "Соль": { + "description": "", + "id": "a36a115edf094b9184a550f994242eb5", + "is_error": false, + "name": "Соль", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "fb5741984d24441789ecd2a2d0c6637a", + "is_error": false, + "name": "Соль", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + "size": 5, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + } + }, + "Сыр Пармезан": { + "description": "", + "id": "a36abd4567394c7396c0fccc15dfaaa8", + "is_error": false, + "name": "Сыр Пармезан", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "1696620e15b74a70b7fbd3c28076255b", + "is_error": false, + "name": "Сыр Пармезан", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + "size": 50, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + } + }, + "Черный перец": { + "description": "", + "id": "1d51f6245b1d4f6db47670e91613a892", + "is_error": false, + "name": "Черный перец", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "6ea66e6494ad447a996ad93fdb38697e", + "is_error": false, + "name": "Черный перец", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + "size": 2, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + "Чеснок": { + "description": "", + "id": "20f1bb98774f498080cb7dc59a0be7fd", + "is_error": false, + "name": "Чеснок", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "87ae9afa51d74a94af801f7037d59d8d", + "is_error": false, + "name": "Чеснок", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + "size": 10, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + } + }, + "Яйца": { + "description": "", + "id": "2ccd5d4a20a54eb0beebe0ee2fb87b41", + "is_error": false, + "name": "Яйца", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "8beba2f92e754eeda776859bc197af39", + "is_error": false, + "name": "Яйца", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "f1a87af30e0c4faca9c3184050b3e370", + "is_error": false, + "name": "штука" + } + }, + "size": 2, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "f1a87af30e0c4faca9c3184050b3e370", + "is_error": false, + "name": "штука" + } + } + }, + "description": "", + "id": "c01913436f1a4d26b31359285fceb10f", + "instructions": [], + "is_error": false, + "name": "Цезарь с курицей", + "netto": 0 + }, + { + "brutto": 213, + "comments": "", + "consist": { + "Ванилиин": { + "description": "", + "id": "f58171de9ca34eecb26717784ed32133", + "is_error": false, + "name": "Ванилиин", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "0be1452f2ae649e88008a595ba309aba", + "is_error": false, + "name": "Ванилиин", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + "size": 5, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + "Какао": { + "description": "", + "id": "e8e68575186d444e8c6d5c41918fba65", + "is_error": false, + "name": "Какао", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "bf459ed1953f437fa0a1a7f2f3c87f46", + "is_error": false, + "name": "Какао", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + "size": 20, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + } + }, + "Корица": { + "description": "", + "id": "dd8b376d6cd545589e2bf9937a8e44cd", + "is_error": false, + "name": "Корица", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "f658b569df3d46709cce01b5a7383cd5", + "is_error": false, + "name": "Корица", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + "size": 5, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + "Сахарная пудра": { + "description": "", + "id": "b6c28c224bb44bf9a4b74bedc348af8c", + "is_error": false, + "name": "Сахарная пудра", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "ffa7503c9ef64bdcb51b762fa3595649", + "is_error": false, + "name": "Сахарная пудра", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + "size": 180, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + "Яйца": { + "description": "", + "id": "44a484b273994bb3973beaa793576b3a", + "is_error": false, + "name": "Яйца", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "8beba2f92e754eeda776859bc197af39", + "is_error": false, + "name": "Яйца", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "f1a87af30e0c4faca9c3184050b3e370", + "is_error": false, + "name": "штука" + } + }, + "size": 3, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "f1a87af30e0c4faca9c3184050b3e370", + "is_error": false, + "name": "штука" + } + } + }, + "description": "", + "id": "9c858076aacf425685bfbf60a0fd4267", + "instructions": [], + "is_error": false, + "name": "Безе", + "netto": 0 + } + ], + "storage_row_model": [ + { + "description": "", + "id": "a66dd566f8764eee9ca29f6dc5245eb1", + "is_error": false, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "5c16c9c869c944ca81571f6cf70a790d", + "is_error": false, + "name": "Мука пшеничная", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + "period": "2024-01-27", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": false, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": false, + "name": "киллограмм" + }, + "value": 1 + }, + { + "description": "", + "id": "b643d8e77fdf4459a6871281e8f10eac", + "is_error": false, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "6ea66e6494ad447a996ad93fdb38697e", + "is_error": false, + "name": "Черный перец", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + "period": "2024-01-15", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": false, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "8d5f31aa6ca84177a6bd5761ab50f6fc", + "is_error": false, + "name": "грамм" + }, + "value": 50 + }, + { + "description": "", + "id": "da263ab918c34833a00b386b88781710", + "is_error": false, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "e787ff8a837d40418f9121f1272616ab", + "is_error": false, + "name": "Сахар", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + "period": "2024-01-28", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": false, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": false, + "name": "киллограмм" + }, + "value": 0.5 + }, + { + "description": "", + "id": "9bd6f1ce38574a268820d59aaf9a32ce", + "is_error": false, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "8beba2f92e754eeda776859bc197af39", + "is_error": false, + "name": "Яйца", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "f1a87af30e0c4faca9c3184050b3e370", + "is_error": false, + "name": "штука" + } + }, + "period": "2024-01-27", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": false, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "aaa9f8244b2a4f1ca199b0b376967213", + "is_error": false, + "name": "штука" + }, + "value": 6 + }, + { + "description": "", + "id": "bf0a45ae55e048899dbce60f1ed7ea03", + "is_error": false, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "e8bc8e612eab4fecb1ba20b685267cc6", + "is_error": false, + "name": "Оливковое масло", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "11be8eb6981a4f49bbe0fe93f136a00e", + "is_error": false, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "ce323ec5feb840c9843d4380a8630fcf", + "is_error": false, + "name": "литр" + } + }, + "period": "2024-01-03", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": false, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d113c297f698464a85cff3e803f36ce7", + "is_error": false, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "d486cc7e786f4b828933d5ace911a875", + "is_error": false, + "name": "литр" + }, + "value": 0.2 + }, + { + "description": "", + "id": "fb17787652844ae2b4cd276c81a588a1", + "is_error": false, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "6a448c9b996646888ace90ed26a7547b", + "is_error": false, + "name": "Куринное филе", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + "period": "2024-01-31", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": false, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": false, + "name": "киллограмм" + }, + "value": 0.5 + }, + { + "description": "", + "id": "30168c6837f04bd4b212d65d8ffd1eb0", + "is_error": false, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "5b5936b9384a4fe59b24e3fd127377d0", + "is_error": false, + "name": "Салат Романо", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + "period": "2024-01-21", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": false, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "aaa9f8244b2a4f1ca199b0b376967213", + "is_error": false, + "name": "штука" + }, + "value": 1 + }, + { + "description": "", + "id": "9f2ecf8cbf64420882ee07e2024cfee3", + "is_error": false, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "3c8d6792212a4feba49e48770995ee55", + "is_error": false, + "name": "Белый хлеб", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + "period": "2024-01-09", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": false, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "aaa9f8244b2a4f1ca199b0b376967213", + "is_error": false, + "name": "штука" + }, + "value": 3 + }, + { + "description": "", + "id": "5c18cef4d4584b9a80077766225eaaed", + "is_error": false, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "1696620e15b74a70b7fbd3c28076255b", + "is_error": false, + "name": "Сыр Пармезан", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + "period": "2024-01-27", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": false, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": false, + "name": "киллограмм" + }, + "value": 0.2 + }, + { + "description": "", + "id": "0cf3414a8d114f3c89db9741aa9acadf", + "is_error": false, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "af0af126ae0b41c38ea53544777721b9", + "is_error": false, + "name": "Горчица дижонская", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + "period": "2024-01-06", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": false, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d113c297f698464a85cff3e803f36ce7", + "is_error": false, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "d486cc7e786f4b828933d5ace911a875", + "is_error": false, + "name": "литр" + }, + "value": 0.1 + }, + { + "description": "", + "id": "2f2bede132d1485e9562ca13326ab5ac", + "is_error": false, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "6ea66e6494ad447a996ad93fdb38697e", + "is_error": false, + "name": "Черный перец", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + "period": "2024-01-17", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": false, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "8d5f31aa6ca84177a6bd5761ab50f6fc", + "is_error": false, + "name": "грамм" + }, + "value": 10 + }, + { + "description": "", + "id": "15e085acf2224edab862af3d79dfeab5", + "is_error": false, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "cdbbecb3fa594a799e5b4c6a14058c42", + "is_error": false, + "name": "Лимонный сок", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "11be8eb6981a4f49bbe0fe93f136a00e", + "is_error": false, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "ce323ec5feb840c9843d4380a8630fcf", + "is_error": false, + "name": "литр" + } + }, + "period": "2024-01-23", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": false, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d113c297f698464a85cff3e803f36ce7", + "is_error": false, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "d486cc7e786f4b828933d5ace911a875", + "is_error": false, + "name": "литр" + }, + "value": 1 + }, + { + "description": "", + "id": "a77598bd2dc944cc9c9ed05579c0caa4", + "is_error": false, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "bf459ed1953f437fa0a1a7f2f3c87f46", + "is_error": false, + "name": "Какао", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + "period": "2024-01-12", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": false, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": false, + "name": "киллограмм" + }, + "value": 1 + }, + { + "description": "", + "id": "22df33c9d3a543d980c9cda91fe7685c", + "is_error": false, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "998f959f17704d85b0fad15936283032", + "is_error": false, + "name": "Сливочное масло", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + "period": "2024-01-12", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": false, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": false, + "name": "киллограмм" + }, + "value": 0.5 + }, + { + "description": "", + "id": "1a35d9478dcd49ecae266acf21c50da3", + "is_error": false, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "1696620e15b74a70b7fbd3c28076255b", + "is_error": false, + "name": "Сыр Пармезан", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + "period": "2024-01-24", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": false, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": false, + "name": "киллограмм" + }, + "value": 0.3 + }, + { + "description": "", + "id": "26f95d36c7d149d9bd42e36a185ea846", + "is_error": false, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "0be1452f2ae649e88008a595ba309aba", + "is_error": false, + "name": "Ванилиин", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": false, + "name": "грамм" + } + }, + "period": "2024-01-18", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": false, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "8d5f31aa6ca84177a6bd5761ab50f6fc", + "is_error": false, + "name": "грамм" + }, + "value": 100 + }, + { + "description": "", + "id": "9c495297d49540e68eb0f08ff6a9e9a8", + "is_error": false, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "Ингредиенты" + }, + "id": "fb5741984d24441789ecd2a2d0c6637a", + "is_error": false, + "name": "Соль", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "киллограмм" + } + }, + "period": "2024-01-01", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": false, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": false, + "name": "киллограмм" + }, + "value": 1 + } + ], + "unit_model": [ + { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "8d5f31aa6ca84177a6bd5761ab50f6fc", + "is_error": false, + "name": "грамм" + }, + { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": false, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": false, + "name": "киллограмм" + }, + { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d113c297f698464a85cff3e803f36ce7", + "is_error": false, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "d486cc7e786f4b828933d5ace911a875", + "is_error": false, + "name": "литр" + }, + { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "ab87512809eb4da6bf6cc026c6740983", + "is_error": false, + "name": "миллилитр" + }, + { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "aaa9f8244b2a4f1ca199b0b376967213", + "is_error": false, + "name": "штука" + } + ] +} \ No newline at end of file diff --git a/Src/Storage/storage.py b/Src/Storage/storage.py new file mode 100644 index 0000000..920b9d7 --- /dev/null +++ b/Src/Storage/storage.py @@ -0,0 +1,196 @@ +import json +import os + +from Src.exceptions import operation_exception, exception_proxy +from Src.Logics.convert_factory import convert_factory +from Src.reference import reference + + +# +# Класс хранилище данных +# +class storage(): + __data = {} + __storage_file = "storage.json" + __mapping = {} + + def __new__(cls): + if not hasattr(cls, 'instance'): + cls.instance = super(storage, cls).__new__(cls) + + return cls.instance + + def __init__(self) -> None: + # Связка для всех моделей + for inheritor in reference.__subclasses__(): + self.__mapping[inheritor.__name__] = inheritor + + + @property + def data(self) -> dict: + """ + Данные по моделям + + Returns: + _type_: _description_ + """ + return self.__data + + def load(self): + """ + Загрузить данные из хранилища + Raises: + operation_exception: _description_ + """ + try: + file_path = os.path.split(__file__) + data_file = "%s/%s" % (file_path[0], self.__storage_file) + if not os.path.exists(data_file): + raise operation_exception(f"Невозможно загрузить данные! Не найден файл {data_file}") + + with open(data_file, "r") as read_file: + source = json.load(read_file) + + self.__data = {} + for key in storage.storage_keys(storage): + if key in source.keys(): + source_data = source[key] + self.__data[key] = [] + + for item in source_data: + object = self.__mapping[key] + instance = object().load(item) + self.__data[key].append(instance) + + except Exception as ex: + raise operation_exception(f"Ошибка при чтении данных. Файл {self.__storage_file}\n{ex}") + + + def save(self): + """ + Сохранить данные в хранилище + Raises: + operation_exception: _description_ + """ + try: + factory = convert_factory() + with open(self.__storage_file, "w") as write_file: + data = factory.serialize( self.data ) + json_text = json.dumps(data, sort_keys = True, indent = 4, ensure_ascii = False) + write_file.write(json_text) + + return True + except Exception as ex: + raise operation_exception(f"Ошибка при записи файла {self.__storage_file}\n{ex}") + + return False + + + def save_blocked_turns(self, turns:list): + """ + Сохранить новый список заблокированных оборотов + """ + exception_proxy.validate(turns, list) + if len(turns) > 0: + self.__data[ storage.blocked_turns_key() ] = turns + + + @staticmethod + def save_blocked_turns(turns:list): + """ + Сохранить новый список заблокированных оборотов (статический вызов) + """ + object = storage() + object.save_blocked_turns(turns) + + @staticmethod + def blocked_turns_key(): + """ + Ключ для хранения заблокированных оборотов + Returns: + _type_: _description_ + """ + return "storage_row_turn_model" + + @staticmethod + def nomenclature_key(): + """ + Ключ для хранения номенклатуры + Returns: + _type_: _description_ + """ + return "nomenclature_model" + + + @staticmethod + def group_key(): + """ + Списк номенклатурных групп + Returns: + _type_: _description_ + """ + return "group_model" + + + @staticmethod + def storage_transaction_key(): + """ + Список складских проводок + Returns: + _type_: _description_ + """ + return "storage_row_model" + + + @staticmethod + def unit_key(): + """ + Список единиц измерения + Returns: + _type_: _description_ + """ + return "unit_model" + + @staticmethod + def receipt_key(): + """ + Список рецептов + Returns: + _type_: _description_ + """ + return "receipe_model" + + # Код взят: https://github.com/UpTechCompany/GitExample/blob/6665bc70c4933da12f07c0a0d7a4fc638c157c40/storage/storage.py#L30 + + @staticmethod + def storage_keys(cls): + """ + Получить список ключей + Returns: + _type_: _description_ + """ + keys = [] + methods = [getattr(cls, method) for method in dir(cls) if callable(getattr(cls, method))] + for method in methods: + if method.__name__.endswith("_key") and callable(method): + keys.append(method()) + return keys + + + def Ok( app): + """" + Сформировать данные для сервера + """ + if app is None: + raise operation_exception("Некорректно переданы параметры!") + + json_text = json.dumps({"status" : "ok"}, sort_keys = True, indent = 4, ensure_ascii = False) + + # Подготовить ответ + result = app.response_class( + response = f"{json_text}", + status = 200, + mimetype = "application/json; charset=utf-8" + ) + + return result \ No newline at end of file diff --git a/Src/errors.py b/Src/errors.py new file mode 100644 index 0000000..cb29788 --- /dev/null +++ b/Src/errors.py @@ -0,0 +1,94 @@ +import json + +# +# Класс для обработки и хранения текстовой информации об ошибке +# +class error_proxy: + " Текст с описание ошибки " + _error_text = "" + + def __init__(self, exception: Exception = None): + if exception is not None: + self.set_error(exception) + + @property + def error(self): + """ + Получить текстовое описание ошибки + Returns: + str: _description_ + """ + return self._error_text + + @error.setter + def error(self, value: str): + if value == "": + raise Exception("Некорректно переданы параметры!") + + self._error_text = value + + @classmethod + def set_error(self, exception: Exception): + """ + Сохранить текстовое описание ошибки из исключения + Args: + exception (Exception): входящее исключение + """ + + if exception is None: + self._error_text = "" + return + + self._error_text = "Ошибка! " + str(exception) + + @property + def is_empty(self) -> bool: + """ + Флаг. Есть ошибка + Returns: + bool: _description_ + """ + if len(self._error_text) != 0: + return False + else: + return True + + def clear(self): + """ + Очистить + """ + self._error_text = "" + + @staticmethod + def create_error_response( app, message: str, http_code: int = 0): + """ + Сформировать структуру response_class для описания ошибки + Args: + app (_type_): Flask + message (str): Сообщение + http_code(int): Код возврата + + Returns: + response_class: _description_ + """ + + if app is None: + raise Exception("Некорректно переданы параметры!") + + if http_code == 0: + code = 500 + else: + code = http_code + + # Формируем описание + json_text = json.dumps({"details" : message}, sort_keys = True, indent = 4, ensure_ascii = False) + + # Формируем результат + result = app.response_class( + response = f"{json_text}", + status = code, + mimetype = "application/json; charset=utf-8" + ) + + return result + \ No newline at end of file diff --git a/Src/exceptions.py b/Src/exceptions.py new file mode 100644 index 0000000..552326c --- /dev/null +++ b/Src/exceptions.py @@ -0,0 +1,72 @@ +from Src.errors import error_proxy + +# Набор классов для генерации собственных исключений + +# +# Абстрактный класс для наследования +# +class exception_proxy(Exception): + _error : error_proxy = error_proxy() + + def __init__(self, *args: object) -> None: + super().__init__(*args) + self._error.set_error(self) + + @property + def error(self): + """ + Информация об ошибке + Returns: + _type_: _description_ + """ + return self._error + + # -> Источник: https://github.com/zhbr112/Restaurant-automation/blob/b2db73872c4c126155ad52b82db79223943aca29/src/abstract_reference.py#L16 + + @staticmethod + def validate( value, type_, len_= None): + """ + Валидация аргумента по типу и длине + Args: + value (any): Аргумент + type_ (object): Ожидаемый тип + len_ (int): Максимальная длина + Raises: + arguent_exception: Некорректный тип + arguent_exception: Неулевая длина + arguent_exception: Некорректная длина аргумента + Returns: + True или Exception + """ + + if value is None: + raise argument_exception("Пустой аргумент") + + # Проверка типа + if not isinstance(value, type_): + raise argument_exception("Некорректный тип") + + # Проверка аргумента + if len(str(value).strip()) == 0: + raise argument_exception("Пустой аргумент") + + if len_ is not None and len(str(value).strip()) >= len_: + raise argument_exception("Некорректная длина аргумента") + + return True + + + + +# +# Исключение при проверки аргументов +# +class argument_exception(exception_proxy): + pass + +# +# Исключение при выполнении операции +# +class operation_exception(exception_proxy): + pass + \ No newline at end of file diff --git a/Src/reference.py b/Src/reference.py new file mode 100644 index 0000000..aed3313 --- /dev/null +++ b/Src/reference.py @@ -0,0 +1,146 @@ +import uuid +from abc import ABC +from Src.errors import error_proxy +from Src.exceptions import exception_proxy, argument_exception, operation_exception + +# +# Абстрактный класс для наследования +# +class reference(ABC): + " Readonly: Уникальный код " + _id = None + " Краткое наименование " + _name = "" + " Описание " + _description = "" + " Информация об ошибке " + _error = error_proxy() + + def __init__(self, name = None): + self._id = uuid.uuid4() + self._name = name + + @property + def name(self): + "Краткое наименование" + return self._name + + @name.setter + def name(self, value: str): + "Краткое наименование" + exception_proxy.validate( value.strip(), str, 50) + self._name = value.strip() + + @property + def description(self): + " Полное наименование " + return self._description + + @description.setter + def description(self, value: str): + " Полное наименование " + exception_proxy.validate( value.strip(), str) + self._description = value.strip() + + @property + def id(self): + " Уникальный код записи " + return str(self._id.hex) + + @id.setter + def id(self, value:uuid.UUID ): + " Уникальный код записи " + exception_proxy.validate(value, uuid.UUID) + self._id = value + + @property + def is_error(self): + " Флаг. Есть ошибка " + return self._error.error != "" + + def load(self, source: dict): + """ + Десериализовать свойства + Args: + source (dict): исходный слова + """ + if source is None: + return None + + if len(source) == 0: + raise argument_exception("Некорректно переданы параметры!") + + source_fields = ["id", "name", "description"] + if set(source_fields).issubset(list(source.keys())) == False: + raise operation_exception(f"Невозможно загрузить данные в объект. {source}!") + + self._id = uuid.UUID( source["id"]) + self._name = source["name"] + self._description = source["description"] + + return self + + + @staticmethod + def create_dictionary(items: list): + """ + Сформировать словарь из списка элементов reference + Args: + items (list): _description_ + """ + exception_proxy.validate(items, list) + + result = {} + for position in items: + result[ position.name ] = position + + return result + + @staticmethod + def create_fields(source) -> list: + """ + Сформировать список полей от объекта типа reference + Args: + source (_type_): _description_ + + Returns: + list: _description_ + """ + + if source is None: + raise argument_exception("Некорректно переданы параметры!") + + items = list(filter(lambda x: not x.startswith("_") and not x.startswith("create_") , dir(source))) + result = [] + + for item in items: + attribute = getattr(source.__class__, item) + if isinstance(attribute, property): + result.append(item) + + return result + + def __str__(self) -> str: + """ + Изменим строковое представление класса + Returns: + str: _description_ + """ + return self.id + + def __hash__(self) -> int: + """ + Формирование хеш по коду + Returns: + int: _description_ + """ + return hash(self.id) + + + + + + + + + \ No newline at end of file diff --git a/Src/settings.json b/Src/settings.json new file mode 100644 index 0000000..f62e706 --- /dev/null +++ b/Src/settings.json @@ -0,0 +1,7 @@ +{ + "block_period": "2021-01-01", + "inn": 3800020344, + "is_first_start": false, + "report_mode": "json", + "short_name": "ООО Ромашка" +} \ No newline at end of file diff --git a/Src/settings.py b/Src/settings.py new file mode 100644 index 0000000..e774fe9 --- /dev/null +++ b/Src/settings.py @@ -0,0 +1,99 @@ +from Src.exceptions import exception_proxy, argument_exception +from datetime import datetime +from Src.Logics.Services.storage_observer import storage_observer +from Src.Models.event_type import event_type + +# +# Класс для описания настроек +# +class settings(): + _inn = 0 + _short_name = "" + _first_start = True + _mode = "csv" + _block_period = datetime.now + + + @property + def inn(self): + """ + ИНН + Returns: + int: + """ + return self._inn + + @inn.setter + def inn(self, value: int): + exception_proxy.validate(value, int) + self._inn = value + + @property + def short_name(self): + """ + Короткое наименование организации + Returns: + str: + """ + return self._short_name + + @short_name.setter + def short_name(self, value:str): + exception_proxy.validate(value, str) + self._short_name = value + + + @property + def is_first_start(self): + """ + Флаг Первый старт + """ + return self._first_start + + @is_first_start.setter + def is_first_start(self, value: bool): + self._first_start = value + + @property + def report_mode(self): + """ + Режим построения отчетности + Returns: + _type_: _description_ + """ + return self._mode + + + @report_mode.setter + def report_mode(self, value: str): + exception_proxy.validate(value, str) + + self._mode = value + + + @property + def block_period(self): + """ + Дата блокировки периода + """ + return self._block_period + + @block_period.setter + def block_period(self, value): + if isinstance(value, datetime): + self._block_period = value + return + + legacy_period = self._block_period + + if isinstance(value, str): + try: + self._block_period = datetime.strptime(value, "%Y-%m-%d") + except Exception as ex: + raise argument_exception(f"Невозможно сконвертировать сроку в дату! {ex}") + else: + raise argument_exception("Некорректно переданы параметры!") + + if legacy_period == self._block_period: + storage_observer.raise_event( event_type.changed_block_period() ) + diff --git a/Src/settings_manager.py b/Src/settings_manager.py new file mode 100644 index 0000000..12514cd --- /dev/null +++ b/Src/settings_manager.py @@ -0,0 +1,143 @@ +import os +import json +import uuid + +from Src.settings import settings +from Src.errors import error_proxy +from Src.exceptions import exception_proxy, operation_exception +from Src.Logics.convert_factory import convert_factory + +# +# Менеджер настроек +# +class settings_manager(object): + # Наименование файла по умолчанию + _settings_file_name = "settings.json" + # Словарь с исходными данными + _data = None + # Внутренний уникальный номер + _uniqueNumber = None + # Данные с настройками + _settings = None + # Описание ошибок + _error = error_proxy() + + def __new__(cls): + if not hasattr(cls, 'instance'): + cls.instance = super(settings_manager, cls).__new__(cls) + return cls.instance + + + def __init__(self): + if self._uniqueNumber is None: + self._uniqueNumber = uuid.uuid4() + self.open(self._settings_file_name) + + # После загрузки создаем объект класса settings + self._settings = settings() + self.__load() + + + def __open(self): + """ + Открыть файл с настройками + """ + file_path = os.path.split(__file__) + settings_file = "%s/%s" % (file_path[0], self._settings_file_name) + if not os.path.exists(settings_file): + self._error.set_error( Exception("ERROR: Невозможно загрузить настройки! Не найден файл %s", settings_file)) + + try: + with open(settings_file, "r") as read_file: + self._data = json.load(read_file) + except: + self._error.set_error( Exception("ERROR: Невозможно загрузить настройки! Не найден файл %s", settings_file)) + + def open(self, file_name: str): + """ + Открыть файл с настройками + Args: + file_name (str): + """ + exception_proxy.validate( file_name, str) + + legacy_file_name = self._settings_file_name + self._settings_file_name = file_name + self.__open() + self.__load() + + # Восстанавливаем старое наименование файлв + self._settings_file_name = legacy_file_name + + + def __load(self): + """ + Private: Загрузить словарь в объект + """ + + if len(self._data) == 0: + return + + # Список полей от типа назначения + fields = list(filter(lambda x: not x.startswith("_"), dir(self._settings.__class__))) + + # Заполняем свойства + for field in fields: + keys = list(filter(lambda x: x == field, self._data.keys())) + if len(keys) != 0: + value = self._data[field] + + # Если обычное свойство - заполняем. + if not isinstance(value, list) and not isinstance(value, dict): + setattr(self._settings, field, value) + + def save(self) -> bool: + """ + Сохранить настройки + """ + try: + factory = convert_factory() + + file_path = os.path.split(__file__) + settings_file = "%s/%s" % (file_path[0], self._settings_file_name) + + with open(settings_file, "w") as write_file: + data = factory.serialize( self._settings ) + json_text = json.dumps(data, sort_keys = True, indent = 4, ensure_ascii = False) + write_file.write(json_text) + + return True + except Exception as ex: + raise operation_exception(f"Ошибка при записи файла {self.__storage_file}\n{ex}") + + + + @property + def settings(self) -> settings: + """ + Текущие настройки в приложении + Returns: + settings: _ + """ + return self._settings + + @property + def data(self): + """ + Словарь, который содержит данные из настроек + Returns: + dict: + """ + return self._data + + @property + def error(self) -> error_proxy: + """ + Текущая информация об ошибке + Returns: + error_proxy: + """ + return self._error + + + \ No newline at end of file diff --git a/Src/test.json b/Src/test.json new file mode 100644 index 0000000..f62e706 --- /dev/null +++ b/Src/test.json @@ -0,0 +1,7 @@ +{ + "block_period": "2021-01-01", + "inn": 3800020344, + "is_first_start": false, + "report_mode": "json", + "short_name": "ООО Ромашка" +} \ No newline at end of file diff --git a/Tst/convert_test.py b/Tst/convert_test.py new file mode 100644 index 0000000..5663437 --- /dev/null +++ b/Tst/convert_test.py @@ -0,0 +1,162 @@ +from Src.Logics.start_factory import start_factory +from Src.Logics.convert_factory import convert_factory +from Src.Models.nomenclature_model import nomenclature_model +from Src.Models.receipe_model import receipe_model +from Src.Storage.storage import storage +from Src.settings_manager import settings_manager + +import unittest +import json + +class convert_test(unittest.TestCase): + + # + # Проверить загрузку одного элемента номенклатуры в объект + # + def test_check_load_nomenclature(self): + try: + with open("nomenclature_deserialize.json", "r") as read_file: + # Подготовка + source = json.load(read_file) + nomenclature = nomenclature_model() + + # Действие + result = nomenclature.load(source) + + # Проверки + assert result is not None + assert result.id == "8446fdc4ce4441d8b1dcaeedb6a676c4" + + except Exception as ex: + raise Exception(f"Ошибка: {ex}") + + # + # Проверить загрузку элемента рецепта в объект + # + def test_check_load_receipt(self): + try: + with open("receipt_deserialize.json", "r") as read_file: + # Подготовка + source = json.load(read_file) + receipt = receipe_model() + + # Действие + result = receipt.load(source) + + # Проверки + assert result is not None + assert len(result.consist) > 0 + + except Exception as ex: + raise Exception(f"Ошибка: {ex}") + + # + # Проверить формирование словаря и преобразование в json номенклатуры + # + def test_check_serialize_nomenclature(self): + # Подготовка + items = start_factory.create_nomenclatures() + factory = convert_factory() + if len(items) == 0: + raise Exception("Список номенклатуры пуст!") + + item = items[0] + + # Действие + result = factory.serialize(item) + + # Проверки + assert result is not None + json_text = json.dumps(result, sort_keys = True, indent = 4) + + file = open("nomenclature.json", "w") + file.write(json_text) + file.close() + + # + # Проверить формирование словаря по списку номенклатуры и конвертацию в json + # + def test_check_serialize_nomenctalures(self): + # Подготовка + items = start_factory.create_nomenclatures() + factory = convert_factory() + + # Действие + result = factory.serialize(items) + + # Проверки + assert result is not None + json_text = json.dumps(result, sort_keys = True, indent = 4) + + file = open("nomenclatures.json", "w") + file.write(json_text) + file.close() + + # + # Проверить формирование словаря по списку рецептов и конвертация в json + # + def test_check_serialize_receipts(self): + # Подготовка + items = start_factory.create_receipts() + factory = convert_factory() + + # Действие + result = factory.serialize(items) + + # Проверки + assert result is not None + json_text = json.dumps(result, sort_keys = True, indent = 4) + + file = open("receipts.json", "w") + file.write(json_text) + file.close() + + # + # Выгрузить один рецепт + # + def test_check_serialize_receipt(self): + # Подготовка + options = settings_manager() + start = start_factory( options.settings ) + start.create() + factory = convert_factory() + items = start.storage.data[ storage.receipt_key() ] + if len(items) == 0: + raise Exception("Набор рецептов пуст!") + item = items[0] + + # Действие + result = factory.serialize(item) + + # Проверки + assert result is not None + json_text = json.dumps(result, sort_keys = True, indent = 4) + + file = open("receipt_deserialize.json", "w") + file.write(json_text) + file.close() + + # + # Выгрузить одну транзакцию + # + def test_check_serialize_transaction(self): + # Подготовка + options = settings_manager() + start = start_factory( options.settings ) + start.create() + factory = convert_factory() + items = start.storage.data[ storage.storage_transaction_key() ] + if len(items) == 0: + raise Exception("Набор транзакций пуст!") + item = items[0] + + # Действие + result = factory.serialize(item) + + # Проверки + assert result is not None + json_text = json.dumps(result, sort_keys = True, indent = 4) + + file = open("transaction_deserialize.json", "w") + file.write(json_text) + file.close() diff --git a/Tst/error_test.py b/Tst/error_test.py new file mode 100644 index 0000000..1f106b9 --- /dev/null +++ b/Tst/error_test.py @@ -0,0 +1,37 @@ +from Src.errors import error_proxy +import unittest + +# +# Набор автотестов для проверки работы класса error_proxy +# +class error_proxy_test(unittest.TestCase): + + # + # Проверить простой сбособ создания объекта с ошибкой + # + def test_create_error_proxy(self): + # Подготовка + proxy = error_proxy() + + # Действие + proxy.error = "test" + + # Проверка + assert proxy.error == "test" + + def test_create_exception_error_proxy(self): + # Подготовка + proxy = error_proxy() + + try: + # Действие + proxy.error = "" + except Exception as ex: + proxy.set_error(ex) + + # Проверка + print(proxy.error) + assert proxy.error != "" + + + \ No newline at end of file diff --git a/Tst/factory_test.py b/Tst/factory_test.py new file mode 100644 index 0000000..daf539e --- /dev/null +++ b/Tst/factory_test.py @@ -0,0 +1,121 @@ +from Src.Logics.start_factory import start_factory +from Src.settings_manager import settings_manager +from Src.Storage.storage import storage +from Src.Logics.report_factory import report_factory + +import unittest + +# +# Набор автотестов для проверки работы фабричного метода +# +class factory_test(unittest.TestCase): + + # + # Проверить метод storage_keys в хранилище + # + def test_check_method_storage_keys(self): + # Подготовка + manager = settings_manager() + start = start_factory( manager.settings ) + start.create() + + # Действия + result = start.storage.storage_keys( start.storage ) + + # Проверки + assert result is not None + assert len(result) > 0 + + # + # Проверка работы фабрики для построения отчетности + # + def test_check_report_factory_create(self): + # Подготовка + manager = settings_manager() + start = start_factory( manager.settings ) + start.create() + factory = report_factory() + key = storage.unit_key() + + # Действие + report = factory.create( + manager.settings.report_mode, + start.storage.data) + + # Проверки + assert report is not None + print ( report.create(key) ) + + # + # Проверка создания начальных рецептов + # + def test_check_create_receipts(self): + # Подготовка + items = start_factory.create_receipts() + + # Действие + + # Проверки + assert len(items) > 0 + + # + # Проверка создание начальной номенклатуры + # + def test_check_create_nomenclatures(self): + # Подготовка + items = start_factory.create_nomenclatures() + + # действие + + # Прверки + assert len(items) > 0 + + + # + # Проверка создание списка единиц измерения + # + def test_check_create_units(self): + # Подготовка + items = start_factory.create_units() + + # Действие + + # Проверки + assert len(items) > 0 + + # + # Проверка создания списка групп + # + def test_check_create_groups(self): + # Подготовка + items = start_factory.create_groups() + + # Действие + + # Проверки + assert len(items) > 0 + + + # + # Проверка работы класса start_factory. Метод create + # + def test_check_factory_create(self): + # Подготовка + manager = settings_manager() + factory = start_factory( manager.settings ) + + + # Действие + factory.create() + + # Проверка + assert not factory.storage is None + assert storage.nomenclature_key() in factory.storage.data + assert storage.receipt_key() in factory.storage.data + assert storage.group_key() in factory.storage.data + assert storage.unit_key() in factory.storage.data + assert storage.storage_transaction_key() in factory.storage.data + + + + diff --git a/Tst/nomenclature_test.py b/Tst/nomenclature_test.py new file mode 100644 index 0000000..4e54778 --- /dev/null +++ b/Tst/nomenclature_test.py @@ -0,0 +1,53 @@ +from Src.Models.group_model import group_model +from Src.Models.nomenclature_model import nomenclature_model +from Src.Models.unit_model import unit_model +from Src.exceptions import argument_exception + +import unittest + +# +# Набор автотестов для проверки работы моделей связанных с номенклатурой +# +class nomenclature_test(unittest.TestCase): + + # + # Проверить создание новой карточки номенклатуры + # + def test_create_nomenclature(self): + # Подготовка + group = group_model("test group") + item = nomenclature_model("test") + unit = unit_model("test unit") + + # Действие + item.description = "test description" + item.group = group + item.unit = unit + + # Проверка + print(item.unit) + assert item is not None + + # + # Проверить создание новой карточки номенклатуры с ошибкой + # + def test_create_nomenclature_fail_name(self): + # Подготовка + group = group_model("test group") + item = nomenclature_model("test nomenclature") + unit = unit_model("test unit") + item.description = "test description" + item.group = group + item.unit = unit + + # Действие + with self.assertRaises(argument_exception) as context: + item.name = "11111111111111111111111111111111111111111111111111111111111111111111111111" + + # Проверка + self.assertTrue(f'Есть нужное исключение - {context.exception}') + + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/Tst/processing_test.py b/Tst/processing_test.py new file mode 100644 index 0000000..ab2aa56 --- /dev/null +++ b/Tst/processing_test.py @@ -0,0 +1,99 @@ +import unittest +from Src.Logics.start_factory import start_factory +from Src.settings_manager import settings_manager +from Src.Logics.process_factory import process_factory +from Src.Storage.storage import storage +from Src.Logics.Processings.processing import processing +from Src.Models.storage_row_turn_model import storage_row_turn_model +from Src.exceptions import operation_exception +from Src.Models.storage_model import storage_model +from Src.Models.unit_model import unit_model + +# +# Набор содульных тестов для проверки процессов обработки данных +# +class processing_test(unittest.TestCase): + + # + # Проверить работу фабрики процессов + # Запустить расчет складских оборотов + # + def test_check_process_factory(self): + # Подготовка + manager = settings_manager() + start = start_factory(manager.settings) + start.create() + factory = process_factory() + + # Действие + result = factory.create( process_factory.turn_key() ) + + # Проверка + assert result is not None + + + # + # Проверить работу процесса расчета оборотов + # + def test_check_process_turns(self): + # Подготовка + manager = settings_manager() + start = start_factory(manager.settings) + start.create() + factory = process_factory() + key = storage.storage_transaction_key() + transactions = start.storage.data[ key ] + processing = factory.create( process_factory.turn_key() ) + + # Действие + result = processing().process(transactions) + + # Проверка + assert result is not None + assert len(result) > 0 + turn = list(filter(lambda x: x.nomenclature.name == "Сыр Пармезан", result )) + assert turn[0].value == 0.5 + + # + # Проверить работы агрегирования оборотов. + # В сохраненных данных есть оборот которого нет в рассчитанный + # + def test_check_aggregate_turns(self): + # Подготовка + default_storage = storage_model.create_default() + manager = settings_manager() + start = start_factory(manager.settings) + start.create() + nomenclatures = start.storage.data[ storage.nomenclature_key()] + if len(nomenclatures) == 0: + raise operation_exception("Список номенклатуры пуст!") + + # Создаем тестовый оборот и добавляем его в хранилище + turn = storage_row_turn_model() + turn.nomenclature = nomenclatures[0] + turn.storage = default_storage + turn.unit = unit_model.create_killogram() + turn.value = 1 + + start.storage.data[ storage.blocked_turns_key() ] = [] + start.storage.data[ storage.blocked_turns_key() ].append( turn) + + # Получаем процессы агрегации и расчета оборотов + factory = process_factory() + aggregate_processing = factory.create( process_factory.aggregate_key() ) + turn_processing = factory.create( process_factory.turn_key() ) + calculated_turns = turn_processing().process( start.storage.data[ storage.storage_transaction_key() ] ) + calculated_len = len(calculated_turns) + + # Действие + result = aggregate_processing().process( calculated_turns ) + + # Проверки + assert result is not None + assert calculated_len + 1 == len(result) + + + + + + \ No newline at end of file diff --git a/Tst/prototype_test.py b/Tst/prototype_test.py new file mode 100644 index 0000000..0d98732 --- /dev/null +++ b/Tst/prototype_test.py @@ -0,0 +1,61 @@ +from Src.Logics.storage_prototype import storage_prototype +from Src.Logics.start_factory import start_factory +from Src.settings_manager import settings_manager +from Src.Storage.storage import storage +from Src.exceptions import operation_exception + +from datetime import datetime +import unittest + +class prototype_test(unittest.TestCase): + + # + # Проверить метод filter_by_period + # + def test_check_filter_by_period(self): + # Подготовка + manager = settings_manager() + start = start_factory(manager.settings) + start.create() + key = storage.storage_transaction_key() + data = start.storage.data[ key ] + + start_date = datetime.strptime("2024-01-01", "%Y-%m-%d") + stop_date = datetime.strptime("2024-01-10", "%Y-%m-%d") + prototype = storage_prototype( data) + + + # Дейтсвие + result = prototype.filter_by_period( start_date, stop_date ) + + # Проверка + assert isinstance(result, storage_prototype) + assert prototype.is_empty == True + assert len(result.data) > 0 + + # + # Проверить метод filter_by_nomenclature + # + def test_check_filter_by_nomenclature(self): + # Подготовка + manager = settings_manager() + start = start_factory(manager.settings) + start.create() + key = storage.storage_transaction_key() + data = start.storage.data[ key ] + if len(data) == 0: + raise operation_exception("Данные не подготовлены!") + + element = data[0] + nomenclature = element.nomenclature + prototype = storage_prototype( data) + + # Действие + result = prototype.filter_by_nomenclature( nomenclature ) + + # Проверка + assert isinstance(result, storage_prototype) + assert prototype.is_empty == True + assert len(result.data) > 0 + + \ No newline at end of file diff --git a/Tst/reporting_test.py b/Tst/reporting_test.py new file mode 100644 index 0000000..7a2a245 --- /dev/null +++ b/Tst/reporting_test.py @@ -0,0 +1,166 @@ +import unittest +from Src.Logics.reporting import reporting +from Src.Models.unit_model import unit_model +from Src.Storage.storage import storage +from Src.Logics.csv_reporting import csv_reporting +from Src.Models.nomenclature_model import nomenclature_model +from Src.Models.group_model import group_model +from Src.Logics.markdown_reporting import markdown_reporting +from Src.Logics.json_reporting import json_reporting +from Src.Logics.start_factory import start_factory +from Src.settings import settings + + +class reporting_test(unittest.TestCase): + + + def test_check_json_reporting_build(self): + # Подготовка + data = {} + list = [] + item = unit_model.create_gram() + list.append(item) + key = storage.unit_key() + data[ key ] = list + report = json_reporting( data ) + + # Действие + result = report.create( key ) + + # Проверки + assert result is not None + assert len(result) > 0 + + # + # Проверить формирование рецептов в формате csv + # + def test_check_csv_create_receipe_key(self): + # Подготовка + optiins = settings() + start = start_factory( optiins ) + start.create() + key = storage.receipt_key() + report = csv_reporting( start.storage.data ) + + # Действие + result = report.create( key ) + + # Проверки + assert result is not None + assert len(result) > 0 + + # + # Проверить формирование рецептов в формате Markdown + # + def test_check_markdown_create_receipt_key(self): + # Подготовка + optiins = settings() + start = start_factory( optiins ) + start.create() + key = storage.receipt_key() + report = markdown_reporting( start.storage.data ) + + # Действие + result = report.create( key ) + + # Проверки + assert result is not None + assert len(result) > 0 + + + + # + # Проверить статический метод build класса reporting + # + def test_check_reporting_build(self): + # Подготовка + data = {} + list = [] + item = unit_model.create_gram() + list.append(item) + data[ storage.unit_key() ] = list + + # Дейстие + result = reporting.build( storage.unit_key(), data ) + + # Проверки + assert result is not None + assert len(result) > 0 + + + # + # Проверить формированеи отчета в csv формате по единицам измерения + # + def test_check_csv_create_unit_key(self): + # Подготовка + data = {} + list = [] + item = unit_model.create_gram() + list.append(item) + key = storage.unit_key() + data[ key ] = list + report = csv_reporting( data ) + + # Действие + result = report.create( key ) + + # Проверки + assert result is not None + assert len(result) > 0 + + + # + # Проверить формирование отчета в csv формате по номенклатуре + # + def test_check_csv_create_nomenclature_key(self): + # Подготовка + data = {} + list = [] + + unit = unit_model.create_killogram() + group = group_model.create_default_group() + item = nomenclature_model("Тушка бройлера", group, unit ) + item.description = "Ингредиент для салата" + list.append(item) + + key = storage.nomenclature_key() + + data[ key ] = list + report = csv_reporting( data ) + + # Действие + result = report.create( key ) + + # Проверки + assert result is not None + assert len(result) > 0 + + file = open("csv_report.csv", "w") + file.write(result) + file.close() + + + # + # Проверить формитирование отчета в markdown формате по ед / измерениям + # + def test_check_markdown_create_unit_key(self): + # Подготовка + data = {} + list = [] + item = unit_model.create_gram() + list.append(item) + key = storage.unit_key() + data[ key ] = list + report = markdown_reporting( data ) + + # Действие + result = report.create( key ) + + # Проверки + assert result is not None + assert len(result) > 0 + + file = open("markdown_report.md", "w") + file.write(result) + file.close() + \ No newline at end of file diff --git a/Tst/service_test.py b/Tst/service_test.py new file mode 100644 index 0000000..20ed6ce --- /dev/null +++ b/Tst/service_test.py @@ -0,0 +1,273 @@ +from Src.Logics.Services.storage_service import storage_service +from Src.Logics.start_factory import start_factory +from Src.settings_manager import settings_manager +from Src.Storage.storage import storage +from Src.exceptions import operation_exception +from Src.Logics.Services.reference_service import reference_service +from Src.Logics.convert_factory import convert_factory +from Src.Models.nomenclature_model import nomenclature_model +from Src.Logics.Services.storage_observer import storage_observer +from Src.Models.event_type import event_type +from Src.Logics.Services.post_processing_service import post_processing_service + +from datetime import datetime +import unittest +import uuid + +class service_test(unittest.TestCase): + # + # Проверить добавление reference (номенклатура) + # + def test_check_add_item_reference(self): + # Подготовка + manager = settings_manager() + start = start_factory(manager.settings) + start.create() + key = storage.nomenclature_key() + data = start.storage.data[ key ] + convert = convert_factory() + + if len(data) == 0: + raise operation_exception("Некорректно сформирован набор данных!") + + # Создаем новый элемент номенклатуры + dict = convert.serialize( data[0] ) + item = nomenclature_model().load(dict) + item.id = uuid.uuid4() + + service = reference_service(data) + start_len = len(data) + + # Действие + result = service.add( item ) + + # Проверка + assert result == True + assert len(data) - 1 == start_len + + # + # Проверить изменение reference (номенклатуры) + # + def test_check_change_item_reference(self): + # Подготовка + manager = settings_manager() + start = start_factory(manager.settings) + start.create() + key = storage.nomenclature_key() + data = start.storage.data[ key ] + convert = convert_factory() + + if len(data) == 0: + raise operation_exception("Некорректно сформирован набор данных!") + + # Создаем новый элемент номенклатуры + dict = convert.serialize( data[0] ) + item = nomenclature_model().load(dict) + item.name = "test" + + service = reference_service(data) + start_len = len(data) + + # Действие + result = service.change( item ) + + # Проверка + assert result == True + assert len(data) == start_len + + + # + # Проверить работу метода create_turns + # + def test_check_create_turns(self): + # Подготовка + manager = settings_manager() + start = start_factory(manager.settings) + start.create() + key = storage.storage_transaction_key() + data = start.storage.data[ key ] + service = storage_service(data) + start_date = datetime.strptime("2024-01-01", "%Y-%m-%d") + stop_date = datetime.strptime("2024-01-10", "%Y-%m-%d") + + # Действие + result = service.create_turns(start_date, stop_date) + + # Проверки + assert len(result) > 0 + + # + # Проверить метод create_turns_by_nomenclature + # + def test_check_create_turns_by_nomenclature(self): + # Подготовка + manager = settings_manager() + start = start_factory(manager.settings) + start.create() + key = storage.storage_transaction_key() + data = start.storage.data[ key ] + service = storage_service(data) + start_date = datetime.strptime("2024-01-01", "%Y-%m-%d") + stop_date = datetime.strptime("2024-01-30", "%Y-%m-%d") + + if len(data) == 0: + raise operation_exception("Набор данных пуст!") + + nomenclature = data[0].nomenclature + + # Действие + result = service.create_turns_by_nomenclature(start_date, stop_date, nomenclature ) + + # Проверки + assert len(result) == 1 + + # + # Проверить метод turns_only_nomenclature + # + def test_check_create_turns_only_nomenclature(self): + # Подготовка + manager = settings_manager() + start = start_factory(manager.settings) + start.create() + key = storage.storage_transaction_key() + data = start.storage.data[ key ] + service = storage_service(data) + + if len(data) == 0: + raise operation_exception("Набор данных пуст!") + + nomenclature = data[0].nomenclature + + # Действие + result = service.create_turns_only_nomenclature( nomenclature ) + + # Проверки + assert len(result) > 0 + + # + # Проверить работу метода create_turns_by_receipt + # + def test_check_create_turns_by_receipt(self): + # Подготовка + manager = settings_manager() + start = start_factory(manager.settings) + start.create() + key = storage.storage_transaction_key() + transactions_data = start.storage.data[ key ] + service = storage_service(transactions_data) + + if len(transactions_data) == 0: + raise operation_exception("Набор данных пуст!") + + key = storage.receipt_key() + receipts_data = start.storage.data[ key ] + + if len(receipts_data) == 0: + raise operation_exception("Набор данных пуст!") + + receipt = receipts_data[0] + + # Действие + result = service.create_turns_by_receipt(receipt) + + # Проверки + assert len(result) > 0 + + # + # Проверить метод build_debits_by_receipt. Ошибочный сценарий. + # + def test_check_build_debits_by_receipt_fail(self): + # Подготовка + manager = settings_manager() + start = start_factory(manager.settings) + start.create() + key = storage.storage_transaction_key() + transactions_data = start.storage.data[ key ] + service = storage_service(transactions_data) + + if len(transactions_data) == 0: + raise operation_exception("Набор данных пуст!") + + key = storage.receipt_key() + receipts_data = start.storage.data[ key ] + + if len(receipts_data) == 0: + raise operation_exception("Набор данных пуст!") + + # -> Цезарь с курицей + receipt = receipts_data[1] + + # Действие и проверка + with self.assertRaises(operation_exception): + service.build_debits_by_receipt( receipt ) + + + # + # Проверить метод build_debits_by_receipt. Корректный сценарий + # + def test_check_build_debits_by_receipt_pass(self): + # Подготовка + manager = settings_manager() + start = start_factory(manager.settings) + start.create() + key = storage.storage_transaction_key() + transactions_data = start.storage.data[ key ] + start_len_transaction = len(transactions_data) + service = storage_service(transactions_data) + + if len(transactions_data) == 0: + raise operation_exception("Набор данных пуст!") + + key = storage.receipt_key() + receipts_data = start.storage.data[ key ] + + if len(receipts_data) == 0: + raise operation_exception("Набор данных пуст!") + + # -> Вафли хрустящие в вафильнице + receipt = receipts_data[0] + + # Действие и проверка + service.build_debits_by_receipt( receipt ) + stop_len_transaction = len(start.storage.data[ storage.storage_transaction_key() ]) + + # Проверка (транзакций должно быть больше) + assert start_len_transaction < stop_len_transaction + + + def test_check_observer_blocked_period(self): + # Подготовка + manager = settings_manager() + start = start_factory(manager.settings) + start.create() + key = storage.storage_transaction_key() + transactions_data = start.storage.data[ key ] + service = storage_service(transactions_data) + + # Действие + try: + storage_observer.raise_event( event_type.changed_block_period() ) + pass + except Exception as ex: + print(f"ex") + + + def test_check_observer_nomenclature_deleted(self): + # Подготовка + manager = settings_manager() + start = start_factory(manager.settings) + start.create() + nomenclature = nomenclature_model() + service = reference_service(nomenclature) + + # Действие + try: + storage_observer.raise_event( event_type.nomenclature_deleted() ) + service.delete(nomenclature) + pass + except Exception as ex: + print(f"{ex}") + + # Проверка + assert nomenclature is None + \ No newline at end of file diff --git a/Tst/settings_test.py b/Tst/settings_test.py new file mode 100644 index 0000000..9a687af --- /dev/null +++ b/Tst/settings_test.py @@ -0,0 +1,106 @@ +from Src.settings_manager import settings_manager +import unittest +from datetime import datetime + +from Src.Logics.Services.storage_service import storage_service +from Src.Logics.start_factory import start_factory +from Src.Storage.storage import storage +from Src.exceptions import operation_exception +from Src.Logics.Services.reference_service import reference_service +from Src.Logics.convert_factory import convert_factory +from Src.Logics.Services.storage_observer import storage_observer +from Src.Models.event_type import event_type + + +# +# Набор автотестов для проверки работы модуmeля настроек +# +class settings_test(unittest.TestCase): + + # + # Проверить действие наблюдателя при изменениях даты блокировки + # + def test_check_changed_block_period(self): + + # Подготовка + manager = settings_manager() + start = start_factory(manager.settings) + start.create() + key = storage.storage_transaction_key() + transactions_data = start.storage.data[ key ] + service = storage_service(transactions_data) + + # Действие + try: + manager.settings.block_period = datetime.strptime("2021-01-01", "%Y-%m-%d") + pass + except Exception as ex: + print(f"{ex}") + + # + # Проверить сохранение настроек + # + def test_save_settings(self): + # Подготовка + manager = settings_manager() + manager.settings.block_period = datetime.strptime("2021-01-01", "%Y-%m-%d") + + # Действие + result = manager.save() + + # Проверки + assert result == True + + + # + # Проверить на корректность создания и загрузки файла с настройками + # + def test_create_app_settings(self): + # Подготовка + manager = settings_manager() + + # Действие + result = manager.data + + # Проверки + print(manager.data) + print(type(manager.data)) + assert result is not None + assert manager.settings.inn > 0 + assert manager.settings.short_name != "" + + # + # Проверить тип создания объекта как singletone + # + def test_double_create_app_setting(self): + # Подготовка + manager1 = settings_manager() + manager2 = settings_manager() + + # Действие + + # Проверки + print(manager1._uniqueNumber) + print(manager2._uniqueNumber) + assert manager1._uniqueNumber == manager2._uniqueNumber + + # + # Проверить работу менеджера загрузки настроек при не корректном файле настроек + # + def test_fail_open_settings(self): + # Подготовка + manager = settings_manager() + + # Действие + manager.open("test.json") + + # Проверки + assert manager.error.is_empty == False + + + + + + + + diff --git a/Tst/storage_test.py b/Tst/storage_test.py new file mode 100644 index 0000000..0dde890 --- /dev/null +++ b/Tst/storage_test.py @@ -0,0 +1,41 @@ +import unittest +from Src.settings_manager import settings_manager +from Src.Logics.start_factory import start_factory +from Src.exceptions import operation_exception + +class storage_test(unittest.TestCase): + + # + # Проверить сохранение данных + # + def test_check_save(self): + # Подготовка + options = settings_manager() + start = start_factory(options.settings) + start.create() + storage = start.storage + + # Действие + result = storage.save() + + # Проверки + assert result == True + + # + # Проверить загрузку данных + # + def test_check_load(self): + # Подготовка + options = settings_manager() + start = start_factory(options.settings) + start.create() + storage = start.storage + + + # Действие + storage.load() + + # Проверки + assert len(storage.data) > 0 + + diff --git a/csv_report.csv b/csv_report.csv new file mode 100644 index 0000000..c0a6851 --- /dev/null +++ b/csv_report.csv @@ -0,0 +1,2 @@ +description;group;id;is_error;name;unit +Ингредиент для салата;1c9b78ecc0cf4465ac07a682a518ad86;0d6674afc728471b99b30ced559b87bb;True;Тушка бройлера;c9161d641ac44f1f80d784cc105de86f diff --git a/main.py b/main.py new file mode 100644 index 0000000..331da6c --- /dev/null +++ b/main.py @@ -0,0 +1,194 @@ +from flask import Flask, request +from Src.settings_manager import settings_manager +from Src.Storage.storage import storage +from Src.errors import error_proxy +from Src.Logics.report_factory import report_factory +from Src.Logics.start_factory import start_factory +from datetime import datetime +from Src.Logics.Services.storage_service import storage_service +from Src.Models.nomenclature_model import nomenclature_model +from Src.Logics.Services.service import service +from Src.Logics.Services.reference_service import reference_service + + + +app = Flask(__name__) +app.config['JSON_AS_ASCII'] = False + +# Сформировать начальный набор данных +options = settings_manager() +start = start_factory(options.settings) +start.create() + + +# Отчетность + +@app.route("/api/report/", methods = ["GET"]) +def get_report(storage_key: str): + """ + Сформировать отчет + """ + + keys = storage.storage_keys( start.storage ) + if storage_key == "" or storage_key not in keys: + return error_proxy.create_error_response(app, f"Некорректный передан запрос! Необходимо передать: /api/report/. Список ключей (storage_key): {keys}.", 400) + + # Создаем фабрику + report = report_factory() + data = start.storage.data + + # Формируем результат + try: + result = report.create_response( options.settings.report_mode, data, storage_key, app ) + return result + except Exception as ex: + return error_proxy.create_error_response(app, f"Ошибка при формировании отчета {ex}", 500) + +# Отчетность + +# Складские операции + +@app.route("/api/storage/turns", methods = ["GET"] ) +def get_turns(): + """ + Получить обороты за период + """ + + # Получить параметры + args = request.args + if "start_period" not in args.keys(): + return error_proxy.create_error_response(app, "Необходимо передать параметры: start_period, stop_period!") + + if "stop_period" not in args.keys(): + return error_proxy.create_error_response(app, "Необходимо передать параметры: start_period, stop_period!") + + start_date = datetime.strptime(args["start_period"], "%Y-%m-%d") + stop_date = datetime.strptime(args["stop_period"], "%Y-%m-%d") + + source_data = start.storage.data[ storage.storage_transaction_key() ] + data = storage_service( source_data ).create_turns( start_date, stop_date ) + result = service.create_response( app, data ) + return result + +@app.route("/api/storage//turns", methods = ["GET"] ) +def get_turns_nomenclature(nomenclature_id): + """ + Получить обороты за период и по коду номенклатукры + """ + + # Получить параметры + args = request.args + if "start_period" not in args.keys(): + return error_proxy.create_error_response(app, "Необходимо передать параметры: start_period, stop_period!", 400) + + if "stop_period" not in args.keys(): + return error_proxy.create_error_response(app, "Необходимо передать параметры: start_period, stop_period!", 400) + + try: + start_date = datetime.strptime(args["start_period"], "%Y-%m-%d") + stop_date = datetime.strptime(args["stop_period"], "%Y-%m-%d") + except: + return error_proxy.create_error_response(app, "Некорректно перпеданы параметры: start_period, stop_period", 400) + + transactions_data = start.storage.data[ storage.storage_transaction_key() ] + nomenclature_data = start.storage.data[ storage.nomenclature_key() ] + + nomenclatures = nomenclature_model.create_dictionary( nomenclature_data ) + ids = [item.id for item in nomenclatures.values()] + if nomenclature_id not in ids: + return error_proxy.create_error_response(app, "Некорректно передан код номенклатуры!", 400) + + nomenclature = nomenclatures[nomenclature_id] + + data = storage_service( transactions_data ).create_turns_by_nomenclature( start_date, stop_date, nomenclature ) + result = service.create_response( data, app ) + return result + +# Складские операции + +# Номерклатура +@app.route("/api/nomenclature", methods = ["PUT"]) +def add_nomenclature(): + """ + Добавить номерклатуру + """ + try: + data = request.get_json() + item = nomenclature_model().load(data) + source_data = start.storage.data[ storage.nomenclature_key() ] + result = reference_service( source_data ).add( item ) + return service.create_response( {"result": result} ) + except Exception as ex: + return error_proxy.create_error_response(app, f"Ошибка при добавлении данных!\n {ex}") + + +@app.route("/api/nomenclature", methods = ["DELETE"]) +def delete_nomenclature(): + """ + Удалить номенклатуру + """ + try: + data = request.get_json() + item = nomenclature_model().load(data) + source_data = start.storage.data[ storage.nomenclature_key() ] + result = reference_service( source_data ).delete( item ) + return service.create_response( {"result": result} ) + except Exception as ex: + return error_proxy.create_error_response(app, f"Ошибка при удалении данных!\n {ex}") + + +@app.route("/api/nomenclature", methods = ["PATH"]) +def change_nomenclature(): + """ + Изменить номенклатуру + """ + try: + data = request.get_json() + item = nomenclature_model().load(data) + source_data = start.storage.data[ storage.nomenclature_key() ] + result = reference_service( source_data ).change( item ) + return service.create_response( {"result": result} ) + except Exception as ex: + return error_proxy.create_error_response(app, f"Ошибка при изменении данных!\n {ex}") + +@app.route("/api/nomenclature", methods = ["GET"]) +def get_nomenclature(): + """ + Получить список номенклатуры + """ + args = request.args + if "id" not in args.keys(): + # Вывод всех элементов + source_data = start.storage.data[ storage.nomenclature_key() ] + result = reference_service(source_data ).get() + return service.create_response(app, result) + else: + # Вывод конкретного элемента + try: + source_data = start.storage.data[ storage.nomenclature_key() ] + result = reference_service(source_data ).get_item(args["id"]) + return service.create_response(app, result) + except Exception as ex: + return error_proxy.create_error_response(app, f"Ошибка при получении данных!\n {ex}") + + +# Номенклатура + +@app.route("/api/block_period", methods=["GET"]) +def get_block_period(): + args = request.args + if "period" in args.keys(): + + try: + period = datetime.strptime(args["period"], "%Y-%m-%d") + options.settings.block_period = period + options.save() + except: + return error_proxy.create_error_response(app, "Некорректно перпеданы параметры: period", 400) + + result = [options.settings.block_period.strftime('%Y-%m-%d')] + return service.create_response(app, result) + + +if __name__ == "__main__": + app.run(debug = True) \ No newline at end of file diff --git a/markdown_report.md b/markdown_report.md new file mode 100644 index 0000000..ffc2722 --- /dev/null +++ b/markdown_report.md @@ -0,0 +1,4 @@ +# unit_model +|base_unit|coefficient|description|id|is_error|name| +|--|--|--|--|--|--| +||1||7ac7371cc3f44255bdd2b26406ff6a10|True|грамм| \ No newline at end of file diff --git a/nomenclature.json b/nomenclature.json new file mode 100644 index 0000000..4385441 --- /dev/null +++ b/nomenclature.json @@ -0,0 +1,27 @@ +{ + "description": "", + "group": { + "description": "", + "id": "d949c361b783415aae902a113f9f1a9f", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "68f056781b9c4b84a807da07d1a25837", + "is_error": false, + "name": "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "153ab681b116494196f28813a5ab3c74", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "ea40ac391066490cbd1ff03d51fce03d", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } +} \ No newline at end of file diff --git a/nomenclature_deserialize.json b/nomenclature_deserialize.json new file mode 100644 index 0000000..cc7b04e --- /dev/null +++ b/nomenclature_deserialize.json @@ -0,0 +1,27 @@ +{ + "description": "", + "group": { + "description": "", + "id": "aae8e9c8c1874374a57e80caab3724f3", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "8446fdc4ce4441d8b1dcaeedb6a676c4", + "is_error": false, + "name": "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "9a75a9005f00497c9bebf2e26e1c0b50", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "4296954f7854499a969a7aa73e0ee06f", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } +} \ No newline at end of file diff --git a/nomenclatures.json b/nomenclatures.json new file mode 100644 index 0000000..9f0e6a8 --- /dev/null +++ b/nomenclatures.json @@ -0,0 +1,459 @@ +[ + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "6ce4120463424c70a6cd64ea4f5b0744", + "is_error": false, + "name": "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "e5145fa9464e4f219504e9d0c0b7286c", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "69a80144d0774bfe84a3c44519a214b9", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "008efbfd935046179fa57b6cbcf8c4cf", + "is_error": false, + "name": "\u0421\u0430\u0445\u0430\u0440", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "e5145fa9464e4f219504e9d0c0b7286c", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "69a80144d0774bfe84a3c44519a214b9", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "7573e196bb144633b20b7539bddc1125", + "is_error": false, + "name": "\u0421\u043b\u0438\u0432\u043e\u0447\u043d\u043e\u0435 \u043c\u0430\u0441\u043b\u043e", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "e5145fa9464e4f219504e9d0c0b7286c", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "69a80144d0774bfe84a3c44519a214b9", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "f52f3015d17446539539466806a82866", + "is_error": false, + "name": "\u042f\u0439\u0446\u0430", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "3954d1c883864df58de4ba2d7448aa8e", + "is_error": false, + "name": "\u0448\u0442\u0443\u043a\u0430" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "25bac2aaee504df2a582e46f41b67a90", + "is_error": false, + "name": "\u0412\u0430\u043d\u0438\u043b\u0438\u043d", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "18ac789bf337459aac21b1ea03a0a8f6", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "bf9d231c73454fb28e4a73f0fe211088", + "is_error": false, + "name": "\u041a\u0443\u0440\u0438\u043d\u043d\u043e\u0435 \u0444\u0438\u043b\u0435", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "e5145fa9464e4f219504e9d0c0b7286c", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "69a80144d0774bfe84a3c44519a214b9", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "071f1e2b3b154f17a9248663d4928e58", + "is_error": false, + "name": "\u0421\u0430\u043b\u0430\u0442 \u0420\u043e\u043c\u0430\u043d\u043e", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "18ac789bf337459aac21b1ea03a0a8f6", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "8e4075a9e7ea4b379deb0f7be2ff47e5", + "is_error": false, + "name": "\u0421\u044b\u0440 \u041f\u0430\u0440\u043c\u0435\u0437\u0430\u043d", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "e5145fa9464e4f219504e9d0c0b7286c", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "69a80144d0774bfe84a3c44519a214b9", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "67ad382c5e94419cb793b40b5f01d95e", + "is_error": false, + "name": "\u0427\u0435\u0441\u043d\u043e\u043a", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "e5145fa9464e4f219504e9d0c0b7286c", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "69a80144d0774bfe84a3c44519a214b9", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "f769b3a2bb3d4467b7c39675ffa787be", + "is_error": false, + "name": "\u0411\u0435\u043b\u044b\u0439 \u0445\u043b\u0435\u0431", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "e5145fa9464e4f219504e9d0c0b7286c", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "69a80144d0774bfe84a3c44519a214b9", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "798e5e7488fe4a229556fbf2ee6ad728", + "is_error": false, + "name": "\u0421\u043e\u043b\u044c", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "e5145fa9464e4f219504e9d0c0b7286c", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "69a80144d0774bfe84a3c44519a214b9", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "1b976bcdbe914a3f81fb6ad6ba510200", + "is_error": false, + "name": "\u0427\u0435\u0440\u043d\u044b\u0439 \u043f\u0435\u0440\u0435\u0446", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "18ac789bf337459aac21b1ea03a0a8f6", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "1f3d5f94b829448697f24b911a3c3a9b", + "is_error": false, + "name": "\u041e\u043b\u0438\u0432\u043a\u043e\u0432\u043e\u0435 \u043c\u0430\u0441\u043b\u043e", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "0ef5d7fdedaa4dbeba902116f6d83e59", + "is_error": false, + "name": "\u043c\u0438\u043b\u043b\u0438\u043b\u0438\u0442\u0440" + }, + "coefficient": 1000, + "description": "", + "id": "be61d5f9a0d64d1e9664acab55f33cac", + "is_error": false, + "name": "\u043b\u0438\u0442\u0440" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "3d560f3d72d74e85b6df878ead757733", + "is_error": false, + "name": "\u041b\u0438\u043c\u043e\u043d\u043d\u044b\u0439 \u0441\u043e\u043a", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "0ef5d7fdedaa4dbeba902116f6d83e59", + "is_error": false, + "name": "\u043c\u0438\u043b\u043b\u0438\u043b\u0438\u0442\u0440" + }, + "coefficient": 1000, + "description": "", + "id": "be61d5f9a0d64d1e9664acab55f33cac", + "is_error": false, + "name": "\u043b\u0438\u0442\u0440" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "1e3975f4600c4078afc3a56130f4fcea", + "is_error": false, + "name": "\u0413\u043e\u0440\u0447\u0438\u0446\u0430 \u0434\u0438\u0436\u043e\u043d\u0441\u043a\u0430\u044f", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "18ac789bf337459aac21b1ea03a0a8f6", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "6068a0365d2944439e1560f7d6cb6d73", + "is_error": false, + "name": "\u0421\u0430\u0445\u0430\u0440\u043d\u0430\u044f \u043f\u0443\u0434\u0440\u0430", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "18ac789bf337459aac21b1ea03a0a8f6", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "d1e715d6f7a04a0bb7a564f77dfb236f", + "is_error": false, + "name": "\u0412\u0430\u043d\u0438\u043b\u0438\u0438\u043d", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "18ac789bf337459aac21b1ea03a0a8f6", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "5ca1f43a0a50477da8460e9f316a501d", + "is_error": false, + "name": "\u041a\u043e\u0440\u0438\u0446\u0430", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "18ac789bf337459aac21b1ea03a0a8f6", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "52ba9b32e5be4bfa998750ed48ef2f8a", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "64159be615094a028c4cf26071f08a8d", + "is_error": false, + "name": "\u041a\u0430\u043a\u0430\u043e", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "e5145fa9464e4f219504e9d0c0b7286c", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "69a80144d0774bfe84a3c44519a214b9", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + } +] \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..dedc6ed --- /dev/null +++ b/readme.md @@ -0,0 +1,47 @@ +# Промежуточный результат +Редакция: **2024-04-14** + +Исходный код по курсу `Шаблоны проектирования` после проведения двух секций занятий. +Разобраны следующие темы: +- Шаблон `Одиночка` +- ООП : `Наследование`, `Инкапсуляция` +- Git : Основы работы +- Json: Основы работы +- Разработаны основные модели данных +- Созданы `Фабричные методы` для генерации данных +- Разработан `абстрактный` класс для формирования отчетности +- Разработаны наборы класссов для выгрузки данных в разныее форматы + * Json + * Csv + * Markdown +- Шаблон `Фабрика` для создания нужного объекта для построение +отчетности. +- Подключена библиотека `Flask` +- Добавлена `Фабрика` для реализации процессов обработки транзакций +- Добавлен расчет оборотов. +- Добавлен универсальный сервис для реализации [CRUD](https://ru.wikipedia.org/wiki/CRUD) операций + * на примере работы с Номенклатурой + * с использованием `Postman` +- Добавлена система [десериализации](https://ru.wikipedia.org/wiki/%D0%A1%D0%B5%D1%80%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F) данных + * на примере класса `storage` + * на примере класса `settings_manager` +- Продемонстрированы варианты работы с интеграционным тестированием +- Реализован механизм хранения и учета предподготовленных данных с + использованием свойства `blocked_period` + +## Структура + +| Файл | Назначение | +|------------------------------|----------------------------------| +| settings_manager.py | Менеджер управления настройками | +| settings.py | Модель настроек | +| reference.py | Абстрактный класс для наследования справочников | +| errors.py | Файл с классами для хранения и обработки ошибок | +| exceptions.py | Файл с классами для генерации собственных исключений | +| .\Models | Набор моделей | +| .\Tst | Модульные тесты `nunit` | +| .\Src\Logics | Набор классов для инкапсуляции бизнес логики | +| .\Src\Data | Наборы классов для хранения данных | +| .\Src\Storage | Каталог для хранения данных | + + diff --git a/receipt_deserialize.json b/receipt_deserialize.json new file mode 100644 index 0000000..6597808 --- /dev/null +++ b/receipt_deserialize.json @@ -0,0 +1,199 @@ +{ + "brutto": 251, + "comments": "", + "consist": { + "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f": { + "description": "", + "id": "7c92cf932de44581951ca05738464de2", + "is_error": false, + "name": "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "5c16c9c869c944ca81571f6cf70a790d", + "is_error": false, + "name": "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + "size": 100, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "\u0421\u0430\u0445\u0430\u0440": { + "description": "", + "id": "4f50b78ffb3f43848bdb08279ff95823", + "is_error": false, + "name": "\u0421\u0430\u0445\u0430\u0440", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "e787ff8a837d40418f9121f1272616ab", + "is_error": false, + "name": "\u0421\u0430\u0445\u0430\u0440", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + "size": 80, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "\u0421\u043b\u0438\u0432\u043e\u0447\u043d\u043e\u0435 \u043c\u0430\u0441\u043b\u043e": { + "description": "", + "id": "244833ce4dad4f21bbdddeb0bce73ec2", + "is_error": false, + "name": "\u0421\u043b\u0438\u0432\u043e\u0447\u043d\u043e\u0435 \u043c\u0430\u0441\u043b\u043e", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "998f959f17704d85b0fad15936283032", + "is_error": false, + "name": "\u0421\u043b\u0438\u0432\u043e\u0447\u043d\u043e\u0435 \u043c\u0430\u0441\u043b\u043e", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + "size": 70, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "\u042f\u0439\u0446\u0430": { + "description": "", + "id": "843cebf7329d41de9dff0ae4bdbb43cb", + "is_error": false, + "name": "\u042f\u0439\u0446\u0430", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "8beba2f92e754eeda776859bc197af39", + "is_error": false, + "name": "\u042f\u0439\u0446\u0430", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "f1a87af30e0c4faca9c3184050b3e370", + "is_error": false, + "name": "\u0448\u0442\u0443\u043a\u0430" + } + }, + "size": 1, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "f1a87af30e0c4faca9c3184050b3e370", + "is_error": false, + "name": "\u0448\u0442\u0443\u043a\u0430" + } + } + }, + "description": "", + "id": "89a9226cf02643d8a9e856fbc0ca4351", + "instructions": [ + { + "instructions": { + "instructions": "\u041c\u0430\u0441\u043b\u043e \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u0435 \u0432 \u0441\u043e\u0442\u0435\u0439\u043d\u0438\u043a \u0441 \u0442\u043e\u043b\u0441\u0442\u044b\u043c \u0434\u043d\u043e\u043c. \u0420\u0430\u0441\u0442\u043e\u043f\u0438\u0442\u0435 \u0435\u0433\u043e \u043d\u0430 \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u043e\u043c \u043e\u0433\u043d\u0435 \u043d\u0430 \u043f\u043b\u0438\u0442\u0435, \u043d\u0430 \u0432\u043e\u0434\u044f\u043d\u043e\u0439 \u0431\u0430\u043d\u0435 \u043b\u0438\u0431\u043e \u0432 \u043c\u0438\u043a\u0440\u043e\u0432\u043e\u043b\u043d\u043e\u0432\u043a\u0435." + } + }, + { + "instructions": { + "instructions": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432 \u0442\u0435\u043f\u043b\u043e\u0435 \u043c\u0430\u0441\u043b\u043e \u0441\u0430\u0445\u0430\u0440. \u041f\u0435\u0440\u0435\u043c\u0435\u0448\u0430\u0439\u0442\u0435 \u0432\u0435\u043d\u0447\u0438\u043a\u043e\u043c \u0434\u043e \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u0440\u0430\u0441\u0442\u0432\u043e\u0440\u0435\u043d\u0438\u044f \u0441\u0430\u0445\u0430\u0440\u0430. \u041e\u0442 \u0442\u0435\u043f\u043b\u0430 \u0441\u0430\u0445\u0430\u0440 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u043e \u0440\u0430\u0441\u0442\u0430\u0435\u0442." + } + }, + { + "instructions": { + "instructions": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432 \u043c\u0430\u0441\u043b\u043e \u044f\u0439\u0446\u043e. \u041f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u0441\u0435-\u0442\u0430\u043a\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043c\u0430\u0441\u043b\u043e, \u043d\u0435 \u0433\u043e\u0440\u044f\u0447\u0435\u0435 \u043b\u0438 \u043e\u043d\u043e, \u0438\u043d\u0430\u0447\u0435 \u044f\u0439\u0446\u043e \u043c\u043e\u0436\u0435\u0442 \u0441\u0432\u0430\u0440\u0438\u0442\u044c\u0441\u044f. \u041f\u0435\u0440\u0435\u043c\u0435\u0448\u0430\u0439\u0442\u0435 \u044f\u0439\u0446\u043e \u0441 \u043c\u0430\u0441\u043b\u043e\u043c \u0434\u043e \u043e\u0434\u043d\u043e\u0440\u043e\u0434\u043d\u043e\u0441\u0442\u0438." + } + }, + { + "instructions": { + "instructions": "\u0412\u0441\u044b\u043f\u044c\u0442\u0435 \u043c\u0443\u043a\u0443, \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432\u0430\u043d\u0438\u043b\u0438\u043d." + } + }, + { + "instructions": { + "instructions": "\u041f\u0435\u0440\u0435\u043c\u0435\u0448\u0430\u0439\u0442\u0435 \u043c\u0430\u0441\u0441\u0443 \u0432\u0435\u043d\u0447\u0438\u043a\u043e\u043c \u0434\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0433\u043b\u0430\u0434\u043a\u043e\u0433\u043e \u043e\u0434\u043d\u043e\u0440\u043e\u0434\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430." + } + } + ], + "is_error": false, + "name": "\u0412\u0430\u0444\u043b\u0438 \u0445\u0440\u0443\u0441\u0442\u044f\u0449\u0438\u0435 \u0432 \u0432\u0430\u0444\u0438\u043b\u044c\u043d\u0438\u0446\u0435", + "netto": 0 +} \ No newline at end of file diff --git a/receipts.json b/receipts.json new file mode 100644 index 0000000..321cb79 --- /dev/null +++ b/receipts.json @@ -0,0 +1,831 @@ +[ + { + "brutto": 251, + "comments": "\u0412\u0440\u0435\u043c\u044f \u043f\u0440\u0438\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d\u0438\u044f: 20 \u043c\u0438\u043d. 8 \u043f\u043e\u0440\u0446\u0438\u0439", + "consist": { + "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f": { + "description": "", + "id": "37d4d81cb939457a8a485f21c957ea56", + "is_error": false, + "name": "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "b9bfcf89fe834f17906e3f093d13b414", + "is_error": false, + "name": "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6b2f766223044776944a8a7078c1e36b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "4f780e91e4f544cb90ab579f19fc6463", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + "size": 100, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6b2f766223044776944a8a7078c1e36b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "\u0421\u0430\u0445\u0430\u0440": { + "description": "", + "id": "070644f547824814bebbbc67cfbf696b", + "is_error": false, + "name": "\u0421\u0430\u0445\u0430\u0440", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "dd24bb55e7704b2fbe27dd0488fa6266", + "is_error": false, + "name": "\u0421\u0430\u0445\u0430\u0440", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6b2f766223044776944a8a7078c1e36b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "4f780e91e4f544cb90ab579f19fc6463", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + "size": 80, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6b2f766223044776944a8a7078c1e36b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "\u0421\u043b\u0438\u0432\u043e\u0447\u043d\u043e\u0435 \u043c\u0430\u0441\u043b\u043e": { + "description": "", + "id": "3df6ad6a7a3b4febbd09b8c9881e43f0", + "is_error": false, + "name": "\u0421\u043b\u0438\u0432\u043e\u0447\u043d\u043e\u0435 \u043c\u0430\u0441\u043b\u043e", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "46d0ce6a74594431b63c33b6d5965af0", + "is_error": false, + "name": "\u0421\u043b\u0438\u0432\u043e\u0447\u043d\u043e\u0435 \u043c\u0430\u0441\u043b\u043e", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6b2f766223044776944a8a7078c1e36b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "4f780e91e4f544cb90ab579f19fc6463", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + "size": 70, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6b2f766223044776944a8a7078c1e36b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "\u042f\u0439\u0446\u0430": { + "description": "", + "id": "25fbacf28a944bf3833727d92556e27a", + "is_error": false, + "name": "\u042f\u0439\u0446\u0430", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "201c716dab504bd3b5901d197ab7ae06", + "is_error": false, + "name": "\u042f\u0439\u0446\u0430", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "cc4c8224e80a437d86c0e4b5c481906d", + "is_error": false, + "name": "\u0448\u0442\u0443\u043a\u0430" + } + }, + "size": 1, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "cc4c8224e80a437d86c0e4b5c481906d", + "is_error": false, + "name": "\u0448\u0442\u0443\u043a\u0430" + } + } + }, + "description": "", + "id": "37519e71920d4f54870d62cf93bff852", + "instructions": [ + { + "instructions": "\u041c\u0430\u0441\u043b\u043e \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u0435 \u0432 \u0441\u043e\u0442\u0435\u0439\u043d\u0438\u043a \u0441 \u0442\u043e\u043b\u0441\u0442\u044b\u043c \u0434\u043d\u043e\u043c. \u0420\u0430\u0441\u0442\u043e\u043f\u0438\u0442\u0435 \u0435\u0433\u043e \u043d\u0430 \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u043e\u043c \u043e\u0433\u043d\u0435 \u043d\u0430 \u043f\u043b\u0438\u0442\u0435, \u043d\u0430 \u0432\u043e\u0434\u044f\u043d\u043e\u0439 \u0431\u0430\u043d\u0435 \u043b\u0438\u0431\u043e \u0432 \u043c\u0438\u043a\u0440\u043e\u0432\u043e\u043b\u043d\u043e\u0432\u043a\u0435." + }, + { + "instructions": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432 \u0442\u0435\u043f\u043b\u043e\u0435 \u043c\u0430\u0441\u043b\u043e \u0441\u0430\u0445\u0430\u0440. \u041f\u0435\u0440\u0435\u043c\u0435\u0448\u0430\u0439\u0442\u0435 \u0432\u0435\u043d\u0447\u0438\u043a\u043e\u043c \u0434\u043e \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u0440\u0430\u0441\u0442\u0432\u043e\u0440\u0435\u043d\u0438\u044f \u0441\u0430\u0445\u0430\u0440\u0430. \u041e\u0442 \u0442\u0435\u043f\u043b\u0430 \u0441\u0430\u0445\u0430\u0440 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u043e \u0440\u0430\u0441\u0442\u0430\u0435\u0442." + }, + { + "instructions": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432 \u043c\u0430\u0441\u043b\u043e \u044f\u0439\u0446\u043e. \u041f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u0441\u0435-\u0442\u0430\u043a\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043c\u0430\u0441\u043b\u043e, \u043d\u0435 \u0433\u043e\u0440\u044f\u0447\u0435\u0435 \u043b\u0438 \u043e\u043d\u043e, \u0438\u043d\u0430\u0447\u0435 \u044f\u0439\u0446\u043e \u043c\u043e\u0436\u0435\u0442 \u0441\u0432\u0430\u0440\u0438\u0442\u044c\u0441\u044f. \u041f\u0435\u0440\u0435\u043c\u0435\u0448\u0430\u0439\u0442\u0435 \u044f\u0439\u0446\u043e \u0441 \u043c\u0430\u0441\u043b\u043e\u043c \u0434\u043e \u043e\u0434\u043d\u043e\u0440\u043e\u0434\u043d\u043e\u0441\u0442\u0438." + }, + { + "instructions": "\u0412\u0441\u044b\u043f\u044c\u0442\u0435 \u043c\u0443\u043a\u0443, \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432\u0430\u043d\u0438\u043b\u0438\u043d." + }, + { + "instructions": "\u041f\u0435\u0440\u0435\u043c\u0435\u0448\u0430\u0439\u0442\u0435 \u043c\u0430\u0441\u0441\u0443 \u0432\u0435\u043d\u0447\u0438\u043a\u043e\u043c \u0434\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0433\u043b\u0430\u0434\u043a\u043e\u0433\u043e \u043e\u0434\u043d\u043e\u0440\u043e\u0434\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430." + } + ], + "is_error": false, + "name": "\u0412\u0430\u0444\u043b\u0438 \u0445\u0440\u0443\u0441\u0442\u044f\u0449\u0438\u0435 \u0432 \u0432\u0430\u0444\u0438\u043b\u044c\u043d\u0438\u0446\u0435", + "netto": 0 + }, + { + "brutto": 369, + "comments": "", + "consist": { + "\u0411\u0435\u043b\u044b\u0439 \u0445\u043b\u0435\u0431": { + "description": "", + "id": "15aecc37e6964f88ac538c581b22408f", + "is_error": false, + "name": "\u0411\u0435\u043b\u044b\u0439 \u0445\u043b\u0435\u0431", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "53b72f78aa0f4b8fa4e046443f0a07f4", + "is_error": false, + "name": "\u0411\u0435\u043b\u044b\u0439 \u0445\u043b\u0435\u0431", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6b2f766223044776944a8a7078c1e36b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "4f780e91e4f544cb90ab579f19fc6463", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + "size": 30, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6b2f766223044776944a8a7078c1e36b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "\u0413\u043e\u0440\u0447\u0438\u0446\u0430 \u0434\u0438\u0436\u043e\u043d\u0441\u043a\u0430\u044f": { + "description": "", + "id": "4590bd0ed696489e8a4343134d95b859", + "is_error": false, + "name": "\u0413\u043e\u0440\u0447\u0438\u0446\u0430 \u0434\u0438\u0436\u043e\u043d\u0441\u043a\u0430\u044f", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "02d251e8ec6a41a181dd6659a5fc6fcc", + "is_error": false, + "name": "\u0413\u043e\u0440\u0447\u0438\u0446\u0430 \u0434\u0438\u0436\u043e\u043d\u0441\u043a\u0430\u044f", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "size": 5, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "\u041a\u0443\u0440\u0438\u043d\u043d\u043e\u0435 \u0444\u0438\u043b\u0435": { + "description": "", + "id": "84e43acba92142539d570d9615d77692", + "is_error": false, + "name": "\u041a\u0443\u0440\u0438\u043d\u043d\u043e\u0435 \u0444\u0438\u043b\u0435", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "69da6c4defb341a8b17d90f688cc4747", + "is_error": false, + "name": "\u041a\u0443\u0440\u0438\u043d\u043d\u043e\u0435 \u0444\u0438\u043b\u0435", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6b2f766223044776944a8a7078c1e36b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "4f780e91e4f544cb90ab579f19fc6463", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + "size": 200, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6b2f766223044776944a8a7078c1e36b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "\u041b\u0438\u043c\u043e\u043d\u043d\u044b\u0439 \u0441\u043e\u043a": { + "description": "", + "id": "956f5f02ad464379af83bc62e0ee2787", + "is_error": false, + "name": "\u041b\u0438\u043c\u043e\u043d\u043d\u044b\u0439 \u0441\u043e\u043a", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "37c6f449d5254611ae354f1b65bb2d1d", + "is_error": false, + "name": "\u041b\u0438\u043c\u043e\u043d\u043d\u044b\u0439 \u0441\u043e\u043a", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5addd6db1a5e437bbf89c7fe49ef24b0", + "is_error": false, + "name": "\u043c\u0438\u043b\u043b\u0438\u043b\u0438\u0442\u0440" + }, + "coefficient": 1000, + "description": "", + "id": "9d69adc76ab84bd58f02e4471c092179", + "is_error": false, + "name": "\u043b\u0438\u0442\u0440" + } + }, + "size": 5, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5addd6db1a5e437bbf89c7fe49ef24b0", + "is_error": false, + "name": "\u043c\u0438\u043b\u043b\u0438\u043b\u0438\u0442\u0440" + } + }, + "\u041e\u043b\u0438\u0432\u043a\u043e\u0432\u043e\u0435 \u043c\u0430\u0441\u043b\u043e": { + "description": "", + "id": "39c528c94f814f5c8f5274d14c48f853", + "is_error": false, + "name": "\u041e\u043b\u0438\u0432\u043a\u043e\u0432\u043e\u0435 \u043c\u0430\u0441\u043b\u043e", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "973f1fd272634eabb7e0ed9439b09023", + "is_error": false, + "name": "\u041e\u043b\u0438\u0432\u043a\u043e\u0432\u043e\u0435 \u043c\u0430\u0441\u043b\u043e", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5addd6db1a5e437bbf89c7fe49ef24b0", + "is_error": false, + "name": "\u043c\u0438\u043b\u043b\u0438\u043b\u0438\u0442\u0440" + }, + "coefficient": 1000, + "description": "", + "id": "9d69adc76ab84bd58f02e4471c092179", + "is_error": false, + "name": "\u043b\u0438\u0442\u0440" + } + }, + "size": 10, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5addd6db1a5e437bbf89c7fe49ef24b0", + "is_error": false, + "name": "\u043c\u0438\u043b\u043b\u0438\u043b\u0438\u0442\u0440" + } + }, + "\u0421\u0430\u043b\u0430\u0442 \u0420\u043e\u043c\u0430\u043d\u043e": { + "description": "", + "id": "29ed510765524089837986278a00a3a6", + "is_error": false, + "name": "\u0421\u0430\u043b\u0430\u0442 \u0420\u043e\u043c\u0430\u043d\u043e", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "ac6af82bdd6348febf6070debc57ff59", + "is_error": false, + "name": "\u0421\u0430\u043b\u0430\u0442 \u0420\u043e\u043c\u0430\u043d\u043e", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "size": 50, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "\u0421\u043e\u043b\u044c": { + "description": "", + "id": "edc00bb4b7384b3d9e748b862ff1f1fd", + "is_error": false, + "name": "\u0421\u043e\u043b\u044c", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "2aa0ba1ba94c44e19f2b249b82c6c0a8", + "is_error": false, + "name": "\u0421\u043e\u043b\u044c", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6b2f766223044776944a8a7078c1e36b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "4f780e91e4f544cb90ab579f19fc6463", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + "size": 5, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6b2f766223044776944a8a7078c1e36b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "\u0421\u044b\u0440 \u041f\u0430\u0440\u043c\u0435\u0437\u0430\u043d": { + "description": "", + "id": "4c01cc31745f48aea8ffb585280f8c58", + "is_error": false, + "name": "\u0421\u044b\u0440 \u041f\u0430\u0440\u043c\u0435\u0437\u0430\u043d", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "b3f122f0ed4a46fe8848d16238633186", + "is_error": false, + "name": "\u0421\u044b\u0440 \u041f\u0430\u0440\u043c\u0435\u0437\u0430\u043d", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6b2f766223044776944a8a7078c1e36b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "4f780e91e4f544cb90ab579f19fc6463", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + "size": 50, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6b2f766223044776944a8a7078c1e36b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "\u0427\u0435\u0440\u043d\u044b\u0439 \u043f\u0435\u0440\u0435\u0446": { + "description": "", + "id": "749fe9b908074b288b52ac5bedf4d3c3", + "is_error": false, + "name": "\u0427\u0435\u0440\u043d\u044b\u0439 \u043f\u0435\u0440\u0435\u0446", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "0303a20c8e0443168cc296faa3592b99", + "is_error": false, + "name": "\u0427\u0435\u0440\u043d\u044b\u0439 \u043f\u0435\u0440\u0435\u0446", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "size": 2, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "\u0427\u0435\u0441\u043d\u043e\u043a": { + "description": "", + "id": "e23a5cece6214bacb2f0d5a3575eb1a6", + "is_error": false, + "name": "\u0427\u0435\u0441\u043d\u043e\u043a", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "d87de5c794d14deba04e492255e10fb8", + "is_error": false, + "name": "\u0427\u0435\u0441\u043d\u043e\u043a", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6b2f766223044776944a8a7078c1e36b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "4f780e91e4f544cb90ab579f19fc6463", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + "size": 10, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6b2f766223044776944a8a7078c1e36b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "\u042f\u0439\u0446\u0430": { + "description": "", + "id": "8b6dc84ec2184b08be26e797b0f128a2", + "is_error": false, + "name": "\u042f\u0439\u0446\u0430", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "201c716dab504bd3b5901d197ab7ae06", + "is_error": false, + "name": "\u042f\u0439\u0446\u0430", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "cc4c8224e80a437d86c0e4b5c481906d", + "is_error": false, + "name": "\u0448\u0442\u0443\u043a\u0430" + } + }, + "size": 2, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "cc4c8224e80a437d86c0e4b5c481906d", + "is_error": false, + "name": "\u0448\u0442\u0443\u043a\u0430" + } + } + }, + "description": "", + "id": "05aba11d24cc4d56957e5d6930b329b7", + "instructions": [], + "is_error": false, + "name": "\u0426\u0435\u0437\u0430\u0440\u044c \u0441 \u043a\u0443\u0440\u0438\u0446\u0435\u0439", + "netto": 0 + }, + { + "brutto": 213, + "comments": "", + "consist": { + "\u0412\u0430\u043d\u0438\u043b\u0438\u0438\u043d": { + "description": "", + "id": "fd81f9b466ad4382a8e595619a1db5cf", + "is_error": false, + "name": "\u0412\u0430\u043d\u0438\u043b\u0438\u0438\u043d", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "a18090224c4e4551884c0d2fe52e2c91", + "is_error": false, + "name": "\u0412\u0430\u043d\u0438\u043b\u0438\u0438\u043d", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "size": 5, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "\u041a\u0430\u043a\u0430\u043e": { + "description": "", + "id": "e72614fc7c2d4f0abef0d8db070ef048", + "is_error": false, + "name": "\u041a\u0430\u043a\u0430\u043e", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "57be2d4763f143789aced9133d90e351", + "is_error": false, + "name": "\u041a\u0430\u043a\u0430\u043e", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6b2f766223044776944a8a7078c1e36b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "4f780e91e4f544cb90ab579f19fc6463", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + "size": 20, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6b2f766223044776944a8a7078c1e36b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "\u041a\u043e\u0440\u0438\u0446\u0430": { + "description": "", + "id": "9a9a0e800c5844b6991ec0160c373ad7", + "is_error": false, + "name": "\u041a\u043e\u0440\u0438\u0446\u0430", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "63df5a9ec4cd44889743c09197b297a6", + "is_error": false, + "name": "\u041a\u043e\u0440\u0438\u0446\u0430", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "size": 5, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "\u0421\u0430\u0445\u0430\u0440\u043d\u0430\u044f \u043f\u0443\u0434\u0440\u0430": { + "description": "", + "id": "ebf7ce33a8604e7f8f2aa4ba19171152", + "is_error": false, + "name": "\u0421\u0430\u0445\u0430\u0440\u043d\u0430\u044f \u043f\u0443\u0434\u0440\u0430", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "00869dd3ce6744b2ae97a7f54d86c1bb", + "is_error": false, + "name": "\u0421\u0430\u0445\u0430\u0440\u043d\u0430\u044f \u043f\u0443\u0434\u0440\u0430", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "size": 180, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + } + }, + "\u042f\u0439\u0446\u0430": { + "description": "", + "id": "5a14fa59332046f9ba142ed4e41fd3a4", + "is_error": false, + "name": "\u042f\u0439\u0446\u0430", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "b98ad33f66b64a149ce4c3be34bbc116", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "201c716dab504bd3b5901d197ab7ae06", + "is_error": false, + "name": "\u042f\u0439\u0446\u0430", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "cc4c8224e80a437d86c0e4b5c481906d", + "is_error": false, + "name": "\u0448\u0442\u0443\u043a\u0430" + } + }, + "size": 3, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "cc4c8224e80a437d86c0e4b5c481906d", + "is_error": false, + "name": "\u0448\u0442\u0443\u043a\u0430" + } + } + }, + "description": "", + "id": "da5fb9eb29824f3cade8151a520a5950", + "instructions": [], + "is_error": false, + "name": "\u0411\u0435\u0437\u0435", + "netto": 0 + } +] \ No newline at end of file diff --git a/storage.json b/storage.json new file mode 100644 index 0000000..7e4f43c --- /dev/null +++ b/storage.json @@ -0,0 +1,2270 @@ +{ + "group_model": [ + { + "description": "", + "id": "44298be97ad7482497f6fc8d6deb0808", + "is_error": true, + "name": "Ингредиенты" + } + ], + "nomenclature_model": [ + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "5c16c9c869c944ca81571f6cf70a790d", + "is_error": true, + "name": "Мука пшеничная", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "e787ff8a837d40418f9121f1272616ab", + "is_error": true, + "name": "Сахар", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "998f959f17704d85b0fad15936283032", + "is_error": true, + "name": "Сливочное масло", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "8beba2f92e754eeda776859bc197af39", + "is_error": true, + "name": "Яйца", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "f1a87af30e0c4faca9c3184050b3e370", + "is_error": true, + "name": "штука" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "11456b73dc1c4620b9387016648509b9", + "is_error": true, + "name": "Ванилин", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "6a448c9b996646888ace90ed26a7547b", + "is_error": true, + "name": "Куринное филе", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "5b5936b9384a4fe59b24e3fd127377d0", + "is_error": true, + "name": "Салат Романо", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "1696620e15b74a70b7fbd3c28076255b", + "is_error": true, + "name": "Сыр Пармезан", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "87ae9afa51d74a94af801f7037d59d8d", + "is_error": true, + "name": "Чеснок", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "3c8d6792212a4feba49e48770995ee55", + "is_error": true, + "name": "Белый хлеб", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "fb5741984d24441789ecd2a2d0c6637a", + "is_error": true, + "name": "Соль", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "6ea66e6494ad447a996ad93fdb38697e", + "is_error": true, + "name": "Черный перец", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "e8bc8e612eab4fecb1ba20b685267cc6", + "is_error": true, + "name": "Оливковое масло", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "11be8eb6981a4f49bbe0fe93f136a00e", + "is_error": true, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "ce323ec5feb840c9843d4380a8630fcf", + "is_error": true, + "name": "литр" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "cdbbecb3fa594a799e5b4c6a14058c42", + "is_error": true, + "name": "Лимонный сок", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "11be8eb6981a4f49bbe0fe93f136a00e", + "is_error": true, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "ce323ec5feb840c9843d4380a8630fcf", + "is_error": true, + "name": "литр" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "af0af126ae0b41c38ea53544777721b9", + "is_error": true, + "name": "Горчица дижонская", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "ffa7503c9ef64bdcb51b762fa3595649", + "is_error": true, + "name": "Сахарная пудра", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "0be1452f2ae649e88008a595ba309aba", + "is_error": true, + "name": "Ванилиин", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "f658b569df3d46709cce01b5a7383cd5", + "is_error": true, + "name": "Корица", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "bf459ed1953f437fa0a1a7f2f3c87f46", + "is_error": true, + "name": "Какао", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + } + ], + "receipe_model": [ + { + "brutto": 251, + "comments": "", + "consist": { + "Мука пшеничная": { + "description": "", + "id": "7c92cf932de44581951ca05738464de2", + "is_error": true, + "name": "Мука пшеничная", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "5c16c9c869c944ca81571f6cf70a790d", + "is_error": true, + "name": "Мука пшеничная", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + "size": 100, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + } + }, + "Сахар": { + "description": "", + "id": "4f50b78ffb3f43848bdb08279ff95823", + "is_error": true, + "name": "Сахар", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "e787ff8a837d40418f9121f1272616ab", + "is_error": true, + "name": "Сахар", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + "size": 80, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + } + }, + "Сливочное масло": { + "description": "", + "id": "244833ce4dad4f21bbdddeb0bce73ec2", + "is_error": true, + "name": "Сливочное масло", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "998f959f17704d85b0fad15936283032", + "is_error": true, + "name": "Сливочное масло", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + "size": 70, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + } + }, + "Яйца": { + "description": "", + "id": "843cebf7329d41de9dff0ae4bdbb43cb", + "is_error": true, + "name": "Яйца", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "8beba2f92e754eeda776859bc197af39", + "is_error": true, + "name": "Яйца", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "f1a87af30e0c4faca9c3184050b3e370", + "is_error": true, + "name": "штука" + } + }, + "size": 1, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "f1a87af30e0c4faca9c3184050b3e370", + "is_error": true, + "name": "штука" + } + } + }, + "description": "", + "id": "89a9226cf02643d8a9e856fbc0ca4351", + "instructions": [ + { + "instructions": { + "instructions": "Масло положите в сотейник с толстым дном. Растопите его на маленьком огне на плите, на водяной бане либо в микроволновке." + } + }, + { + "instructions": { + "instructions": "Добавьте в теплое масло сахар. Перемешайте венчиком до полного растворения сахара. От тепла сахар довольно быстро растает." + } + }, + { + "instructions": { + "instructions": "Добавьте в масло яйцо. Предварительно все-таки проверьте масло, не горячее ли оно, иначе яйцо может свариться. Перемешайте яйцо с маслом до однородности." + } + }, + { + "instructions": { + "instructions": "Всыпьте муку, добавьте ванилин." + } + }, + { + "instructions": { + "instructions": "Перемешайте массу венчиком до состояния гладкого однородного теста." + } + } + ], + "is_error": true, + "name": "Вафли хрустящие в вафильнице", + "netto": 0 + }, + { + "brutto": 369, + "comments": "", + "consist": { + "Белый хлеб": { + "description": "", + "id": "fe7a448350184766b6c57a1cea5df43e", + "is_error": true, + "name": "Белый хлеб", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "3c8d6792212a4feba49e48770995ee55", + "is_error": true, + "name": "Белый хлеб", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + "size": 30, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + } + }, + "Горчица дижонская": { + "description": "", + "id": "a5221530e2574658bb08fd2d55ef4a97", + "is_error": true, + "name": "Горчица дижонская", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "af0af126ae0b41c38ea53544777721b9", + "is_error": true, + "name": "Горчица дижонская", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + "size": 5, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + "Куринное филе": { + "description": "", + "id": "8814e19f162e4b3db2a8233524c50955", + "is_error": true, + "name": "Куринное филе", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "6a448c9b996646888ace90ed26a7547b", + "is_error": true, + "name": "Куринное филе", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + "size": 200, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + } + }, + "Лимонный сок": { + "description": "", + "id": "2ca49b7384144e4eb07a3a8072de0d09", + "is_error": true, + "name": "Лимонный сок", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "cdbbecb3fa594a799e5b4c6a14058c42", + "is_error": true, + "name": "Лимонный сок", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "11be8eb6981a4f49bbe0fe93f136a00e", + "is_error": true, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "ce323ec5feb840c9843d4380a8630fcf", + "is_error": true, + "name": "литр" + } + }, + "size": 5, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "11be8eb6981a4f49bbe0fe93f136a00e", + "is_error": true, + "name": "миллилитр" + } + }, + "Оливковое масло": { + "description": "", + "id": "b9999b177e644de5b52f2acaab0a1d58", + "is_error": true, + "name": "Оливковое масло", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "e8bc8e612eab4fecb1ba20b685267cc6", + "is_error": true, + "name": "Оливковое масло", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "11be8eb6981a4f49bbe0fe93f136a00e", + "is_error": true, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "ce323ec5feb840c9843d4380a8630fcf", + "is_error": true, + "name": "литр" + } + }, + "size": 10, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "11be8eb6981a4f49bbe0fe93f136a00e", + "is_error": true, + "name": "миллилитр" + } + }, + "Салат Романо": { + "description": "", + "id": "2fccc854834a49ffb0f5a4f83801d2e5", + "is_error": true, + "name": "Салат Романо", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "5b5936b9384a4fe59b24e3fd127377d0", + "is_error": true, + "name": "Салат Романо", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + "size": 50, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + "Соль": { + "description": "", + "id": "a36a115edf094b9184a550f994242eb5", + "is_error": true, + "name": "Соль", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "fb5741984d24441789ecd2a2d0c6637a", + "is_error": true, + "name": "Соль", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + "size": 5, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + } + }, + "Сыр Пармезан": { + "description": "", + "id": "a36abd4567394c7396c0fccc15dfaaa8", + "is_error": true, + "name": "Сыр Пармезан", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "1696620e15b74a70b7fbd3c28076255b", + "is_error": true, + "name": "Сыр Пармезан", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + "size": 50, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + } + }, + "Черный перец": { + "description": "", + "id": "1d51f6245b1d4f6db47670e91613a892", + "is_error": true, + "name": "Черный перец", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "6ea66e6494ad447a996ad93fdb38697e", + "is_error": true, + "name": "Черный перец", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + "size": 2, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + "Чеснок": { + "description": "", + "id": "20f1bb98774f498080cb7dc59a0be7fd", + "is_error": true, + "name": "Чеснок", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "87ae9afa51d74a94af801f7037d59d8d", + "is_error": true, + "name": "Чеснок", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + "size": 10, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + } + }, + "Яйца": { + "description": "", + "id": "2ccd5d4a20a54eb0beebe0ee2fb87b41", + "is_error": true, + "name": "Яйца", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "8beba2f92e754eeda776859bc197af39", + "is_error": true, + "name": "Яйца", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "f1a87af30e0c4faca9c3184050b3e370", + "is_error": true, + "name": "штука" + } + }, + "size": 2, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "f1a87af30e0c4faca9c3184050b3e370", + "is_error": true, + "name": "штука" + } + } + }, + "description": "", + "id": "c01913436f1a4d26b31359285fceb10f", + "instructions": [], + "is_error": true, + "name": "Цезарь с курицей", + "netto": 0 + }, + { + "brutto": 213, + "comments": "", + "consist": { + "Ванилиин": { + "description": "", + "id": "f58171de9ca34eecb26717784ed32133", + "is_error": true, + "name": "Ванилиин", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "0be1452f2ae649e88008a595ba309aba", + "is_error": true, + "name": "Ванилиин", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + "size": 5, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + "Какао": { + "description": "", + "id": "e8e68575186d444e8c6d5c41918fba65", + "is_error": true, + "name": "Какао", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "bf459ed1953f437fa0a1a7f2f3c87f46", + "is_error": true, + "name": "Какао", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + "size": 20, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + } + }, + "Корица": { + "description": "", + "id": "dd8b376d6cd545589e2bf9937a8e44cd", + "is_error": true, + "name": "Корица", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "f658b569df3d46709cce01b5a7383cd5", + "is_error": true, + "name": "Корица", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + "size": 5, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + "Сахарная пудра": { + "description": "", + "id": "b6c28c224bb44bf9a4b74bedc348af8c", + "is_error": true, + "name": "Сахарная пудра", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "ffa7503c9ef64bdcb51b762fa3595649", + "is_error": true, + "name": "Сахарная пудра", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + "size": 180, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + "Яйца": { + "description": "", + "id": "44a484b273994bb3973beaa793576b3a", + "is_error": true, + "name": "Яйца", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "8beba2f92e754eeda776859bc197af39", + "is_error": true, + "name": "Яйца", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "f1a87af30e0c4faca9c3184050b3e370", + "is_error": true, + "name": "штука" + } + }, + "size": 3, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "f1a87af30e0c4faca9c3184050b3e370", + "is_error": true, + "name": "штука" + } + } + }, + "description": "", + "id": "9c858076aacf425685bfbf60a0fd4267", + "instructions": [], + "is_error": true, + "name": "Безе", + "netto": 0 + } + ], + "storage_row_model": [ + { + "description": "", + "id": "a66dd566f8764eee9ca29f6dc5245eb1", + "is_error": true, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "5c16c9c869c944ca81571f6cf70a790d", + "is_error": true, + "name": "Мука пшеничная", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + "period": "2024-01-27", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": true, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": true, + "name": "киллограмм" + }, + "value": 1 + }, + { + "description": "", + "id": "b643d8e77fdf4459a6871281e8f10eac", + "is_error": true, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "6ea66e6494ad447a996ad93fdb38697e", + "is_error": true, + "name": "Черный перец", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + "period": "2024-01-15", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": true, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "8d5f31aa6ca84177a6bd5761ab50f6fc", + "is_error": true, + "name": "грамм" + }, + "value": 50 + }, + { + "description": "", + "id": "da263ab918c34833a00b386b88781710", + "is_error": true, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "e787ff8a837d40418f9121f1272616ab", + "is_error": true, + "name": "Сахар", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + "period": "2024-01-28", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": true, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": true, + "name": "киллограмм" + }, + "value": 0.5 + }, + { + "description": "", + "id": "9bd6f1ce38574a268820d59aaf9a32ce", + "is_error": true, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "8beba2f92e754eeda776859bc197af39", + "is_error": true, + "name": "Яйца", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "f1a87af30e0c4faca9c3184050b3e370", + "is_error": true, + "name": "штука" + } + }, + "period": "2024-01-27", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": true, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "aaa9f8244b2a4f1ca199b0b376967213", + "is_error": true, + "name": "штука" + }, + "value": 6 + }, + { + "description": "", + "id": "bf0a45ae55e048899dbce60f1ed7ea03", + "is_error": true, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "e8bc8e612eab4fecb1ba20b685267cc6", + "is_error": true, + "name": "Оливковое масло", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "11be8eb6981a4f49bbe0fe93f136a00e", + "is_error": true, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "ce323ec5feb840c9843d4380a8630fcf", + "is_error": true, + "name": "литр" + } + }, + "period": "2024-01-03", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": true, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d113c297f698464a85cff3e803f36ce7", + "is_error": true, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "d486cc7e786f4b828933d5ace911a875", + "is_error": true, + "name": "литр" + }, + "value": 0.2 + }, + { + "description": "", + "id": "fb17787652844ae2b4cd276c81a588a1", + "is_error": true, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "6a448c9b996646888ace90ed26a7547b", + "is_error": true, + "name": "Куринное филе", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + "period": "2024-01-31", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": true, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": true, + "name": "киллограмм" + }, + "value": 0.5 + }, + { + "description": "", + "id": "30168c6837f04bd4b212d65d8ffd1eb0", + "is_error": true, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "5b5936b9384a4fe59b24e3fd127377d0", + "is_error": true, + "name": "Салат Романо", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + "period": "2024-01-21", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": true, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "aaa9f8244b2a4f1ca199b0b376967213", + "is_error": true, + "name": "штука" + }, + "value": 1 + }, + { + "description": "", + "id": "9f2ecf8cbf64420882ee07e2024cfee3", + "is_error": true, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "3c8d6792212a4feba49e48770995ee55", + "is_error": true, + "name": "Белый хлеб", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + "period": "2024-01-09", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": true, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "aaa9f8244b2a4f1ca199b0b376967213", + "is_error": true, + "name": "штука" + }, + "value": 3 + }, + { + "description": "", + "id": "5c18cef4d4584b9a80077766225eaaed", + "is_error": true, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "1696620e15b74a70b7fbd3c28076255b", + "is_error": true, + "name": "Сыр Пармезан", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + "period": "2024-01-27", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": true, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": true, + "name": "киллограмм" + }, + "value": 0.2 + }, + { + "description": "", + "id": "0cf3414a8d114f3c89db9741aa9acadf", + "is_error": true, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "af0af126ae0b41c38ea53544777721b9", + "is_error": true, + "name": "Горчица дижонская", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + "period": "2024-01-06", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": true, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d113c297f698464a85cff3e803f36ce7", + "is_error": true, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "d486cc7e786f4b828933d5ace911a875", + "is_error": true, + "name": "литр" + }, + "value": 0.1 + }, + { + "description": "", + "id": "2f2bede132d1485e9562ca13326ab5ac", + "is_error": true, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "6ea66e6494ad447a996ad93fdb38697e", + "is_error": true, + "name": "Черный перец", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + "period": "2024-01-17", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": true, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "8d5f31aa6ca84177a6bd5761ab50f6fc", + "is_error": true, + "name": "грамм" + }, + "value": 10 + }, + { + "description": "", + "id": "15e085acf2224edab862af3d79dfeab5", + "is_error": true, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "cdbbecb3fa594a799e5b4c6a14058c42", + "is_error": true, + "name": "Лимонный сок", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "11be8eb6981a4f49bbe0fe93f136a00e", + "is_error": true, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "ce323ec5feb840c9843d4380a8630fcf", + "is_error": true, + "name": "литр" + } + }, + "period": "2024-01-23", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": true, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d113c297f698464a85cff3e803f36ce7", + "is_error": true, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "d486cc7e786f4b828933d5ace911a875", + "is_error": true, + "name": "литр" + }, + "value": 1 + }, + { + "description": "", + "id": "a77598bd2dc944cc9c9ed05579c0caa4", + "is_error": true, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "bf459ed1953f437fa0a1a7f2f3c87f46", + "is_error": true, + "name": "Какао", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + "period": "2024-01-12", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": true, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": true, + "name": "киллограмм" + }, + "value": 1 + }, + { + "description": "", + "id": "22df33c9d3a543d980c9cda91fe7685c", + "is_error": true, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "998f959f17704d85b0fad15936283032", + "is_error": true, + "name": "Сливочное масло", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + "period": "2024-01-12", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": true, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": true, + "name": "киллограмм" + }, + "value": 0.5 + }, + { + "description": "", + "id": "1a35d9478dcd49ecae266acf21c50da3", + "is_error": true, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "1696620e15b74a70b7fbd3c28076255b", + "is_error": true, + "name": "Сыр Пармезан", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + "period": "2024-01-24", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": true, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": true, + "name": "киллограмм" + }, + "value": 0.3 + }, + { + "description": "", + "id": "26f95d36c7d149d9bd42e36a185ea846", + "is_error": true, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "0be1452f2ae649e88008a595ba309aba", + "is_error": true, + "name": "Ванилиин", + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "4e2440552fd04af4b7d3df1031b2aedd", + "is_error": true, + "name": "грамм" + } + }, + "period": "2024-01-18", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": true, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "8d5f31aa6ca84177a6bd5761ab50f6fc", + "is_error": true, + "name": "грамм" + }, + "value": 100 + }, + { + "description": "", + "id": "9c495297d49540e68eb0f08ff6a9e9a8", + "is_error": true, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": true, + "name": "Ингредиенты" + }, + "id": "fb5741984d24441789ecd2a2d0c6637a", + "is_error": true, + "name": "Соль", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": true, + "name": "киллограмм" + } + }, + "period": "2024-01-01", + "storage": { + "address": "г. Москва. ул. Академика Королева, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": true, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": true, + "name": "киллограмм" + }, + "value": 1 + } + ], + "unit_model": [ + { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "8d5f31aa6ca84177a6bd5761ab50f6fc", + "is_error": true, + "name": "грамм" + }, + { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": true, + "name": "грамм" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": true, + "name": "киллограмм" + }, + { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d113c297f698464a85cff3e803f36ce7", + "is_error": true, + "name": "миллилитр" + }, + "coefficient": 1000, + "description": "", + "id": "d486cc7e786f4b828933d5ace911a875", + "is_error": true, + "name": "литр" + }, + { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "ab87512809eb4da6bf6cc026c6740983", + "is_error": true, + "name": "миллилитр" + }, + { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "aaa9f8244b2a4f1ca199b0b376967213", + "is_error": true, + "name": "штука" + } + ] +} \ No newline at end of file diff --git a/storage_journal.md b/storage_journal.md new file mode 100644 index 0000000..fd817fd --- /dev/null +++ b/storage_journal.md @@ -0,0 +1,72 @@ + +# Журнал складский операци + +[Источник](https://github.com/KROKODILCHIK234/gitExample1/pull/2/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5) + +### Таблица покупок номенклатуры + +| Наименование | Количество | Цена | Единица измерения | +|---------------------|------------|--------|-------------------| +| Мука | 1 | 50 | кг | +| Сахар | 0.5 | 30 | кг | +| Яйца | 6 | 5 | шт | +| Масло | 0.2 | 80 | л | +| Куринное филе | 0.5 | 120 | кг | +| Салат Римский | 1 | 25 | шт | +| Хлеб | 0.3 | 20 | шт | +| Сыр Пармезан | 0.2 | 200 | кг | +| Майонез | 0.1 | 50 | л | +| Перец черный молотый| 1 | 15 | г | +| Лимон | 1 | 30 | шт | +| Молоко | 0.3 | 40 | л | +| Какао-порошок | 0.1 | 70 | кг | + +### Таблица затрат номенклатуры для приготовления рецептов + +#### Вафли хрустящие в вафильнице + +| Наименование | Количество | Единица измерения | +|--------------------|------------|-------------------| +| Мука | 0.2 | кг | +| Сахар | 0.1 | кг | +| Яйца | 2 | шт | +| Масло | 0.1 | л | + +#### Цезарь с курицей + +| Наименование | Количество | Единица измерения | +|---------------------|------------|-------------------| +| Куринное филе | 0.3 | кг | +| Салат Римский | 1 | шт | +| Хлеб | 0.1 | шт | +| Сыр Пармезан | 0.1 | кг | +| Майонез | 0.05 | л | +| Перец черный молотый| 0.3 | г | +| Лимон | 0.25 | шт | + +#### Безе + +| Наименование | Количество | Единица измерения | +|--------------------|------------|-------------------| +| Яйца | 4 | шт | +| Сахар | 0.2 | кг | +| Молоко | 0.15 | л | +| Какао-порошок | 0.05 | кг | + +### Таблица с остатками номенклатуры после приготовления + +| Наименование | Количество | Единица измерения | +|---------------------|------------|-------------------| +| Мука | 0.6 | кг | +| Сахар | 0.2 | кг | +| Яйца | 0 | шт | +| Масло | 0.1 | л | +| Куринное филе | 0.2 | кг | +| Салат Римский | 0 | шт | +| Хлеб | 0.1 | шт | +| Сыр Пармезан | 0.1 | кг | +| Майонез | 0.05 | л | +| Перец черный молотый| 0.7 | г | +| Лимон | 0.75 | шт | +| Молоко | 0.15 | л | +| Какао-порошок | 0.05 | кг | \ No newline at end of file diff --git a/transaction_deserialize.json b/transaction_deserialize.json new file mode 100644 index 0000000..02ddbfb --- /dev/null +++ b/transaction_deserialize.json @@ -0,0 +1,58 @@ +{ + "description": "", + "id": "a66dd566f8764eee9ca29f6dc5245eb1", + "is_error": false, + "name": "sample_credit_transaction", + "nomenclature": { + "description": "", + "group": { + "description": "", + "id": "18277c4705764bf0893dbd49f2930c34", + "is_error": false, + "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" + }, + "id": "5c16c9c869c944ca81571f6cf70a790d", + "is_error": false, + "name": "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f", + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "5f73f137f82f4d7eb2d529a83b54b982", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "1bed8c4830a64ab987692730fb43e84c", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + } + }, + "period": "2024-01-27", + "storage": { + "address": "\u0433. \u041c\u043e\u0441\u043a\u0432\u0430. \u0443\u043b. \u0410\u043a\u0430\u0434\u0435\u043c\u0438\u043a\u0430 \u041a\u043e\u0440\u043e\u043b\u0435\u0432\u0430, 10", + "description": "", + "id": "1fbbced64e3e474eb27e0f464161c61b", + "is_error": false, + "name": "default" + }, + "storage_type": true, + "unit": { + "base_unit": { + "base_unit": null, + "coefficient": 1, + "description": "", + "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", + "is_error": false, + "name": "\u0433\u0440\u0430\u043c\u043c" + }, + "coefficient": 1000, + "description": "", + "id": "51fa5f0d487d4989bc4439e025a42255", + "is_error": false, + "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" + }, + "value": 1 +} \ No newline at end of file From ef1019cc37a616a012bf6b7e2d735648d6e9ba02 Mon Sep 17 00:00:00 2001 From: Maria Date: Tue, 6 May 2025 09:22:02 +0800 Subject: [PATCH 2/4] work with comments --- .../Services/post_processing_service.py | 27 ++++- Src/Logics/Services/service.py | 100 ++++++++++++------ Src/Logics/Services/storage_observer.py | 17 ++- Src/Logics/csv_reporting.py | 47 ++++---- Src/Logics/json_reporting.py | 59 ++++++----- Src/Logics/markdown_reporting.py | 53 ++++++---- 6 files changed, 196 insertions(+), 107 deletions(-) diff --git a/Src/Logics/Services/post_processing_service.py b/Src/Logics/Services/post_processing_service.py index 2dde182..6370952 100644 --- a/Src/Logics/Services/post_processing_service.py +++ b/Src/Logics/Services/post_processing_service.py @@ -4,24 +4,41 @@ from Src.Logics.Services.storage_observer import event_type from Src.Models.nomenclature_model import nomenclature_model - +# +# Сервис постобработки данных рецептов +# Обрабатывает события, связанные с удалением номенклатуры +# class post_processing_service(service): def __init__(self, data: list, nomenclature) -> None: + """ + Инициализация сервиса постобработки + Args: + data (list): Список объектов рецептов + nomenclature: Объект номенклатуры, связанный с постобработкой + """ super().__init__(data) - self.nomenclature = nomenclature # storage_observer.observers.append(self) - - def handle_event(self, handle_type: str): + """ + Обработка событий от наблюдателя + + Args: + handle_type (str): Тип события + """ if handle_type == event_type.nomenclature_deleted(): self.nomenclature_deleted() - def nomenclature_deleted(self, nomenclature: nomenclature_model): + """ + Обработка удаления номенклатуры: удаляет строку из рецепта, если она ссылается на номенклатуру + + Args: + nomenclature (nomenclature_model): Удаляемая номенклатура + """ for recipe in self.data: if self.nomenclature.name in recipe._rows: del recipe._rows[self.nomenclature.name] diff --git a/Src/Logics/Services/service.py b/Src/Logics/Services/service.py index 980bf01..f5b3db4 100644 --- a/Src/Logics/Services/service.py +++ b/Src/Logics/Services/service.py @@ -3,24 +3,32 @@ from Src.settings import settings from Src.settings_manager import settings_manager from Src.Models.event_type import event_type -from abc import ABC - from abc import ABC, abstractmethod import json # -# Общий класс для наследования. Реализация сервисов. +# Базовый абстрактный сервис +# Используется для реализации логики, связанной с обработкой данных и событий # class service(ABC): - # Набор данных для работы + # Набор данных для обработки __data = [] - # Текущие настройки + # Текущие настройки приложения __settings: settings = None - + def __init__(self, data: list) -> None: + """ + Инициализация сервиса + + Args: + data (list): Входные данные для обработки + + Raises: + argument_exception: Если список данных пуст + """ if len(data) == 0: raise argument_exception("Некорректно переданы параметры!") - + self.__data = data options = settings_manager() self.__settings = options.settings @@ -28,57 +36,85 @@ def __init__(self, data: list) -> None: @property def data(self): """ - Текущие данные + Получить текущие данные + Returns: - _type_: _description_ + list: Данные, с которыми работает сервис """ return self.__data - + @property def settings(self) -> settings: """ - Текущие настройки + Получить текущие настройки + Returns: - settings: _description_ + settings: Настройки, полученные через settings_manager """ return self.__settings - + @abstractmethod def handle_event(self, event_type: str): - """ Обработать события""" + """ + Обработать событие - exception_proxy.validate(event_type, str) + Args: + event_type (str): Тип события для обработки - # Общие методы для формирования ответа для Web + Raises: + exception_proxy: Если тип события некорректен + """ + exception_proxy.validate(event_type, str) + # + # Общие методы для формирования ответа для Web-сервера + # - def create_response(self, app, data:list = None): + def create_response(self, app, data: list = None): """ - Сформировать структуру ответа для Web сервера + Сформировать HTTP-ответ для Web-сервера + + Args: + app: Объект приложения (например, Flask) + data (list, optional): Альтернативные данные для сериализации + + Returns: + response: HTTP-ответ с JSON-содержимым """ inner_data = self.__data if data is None else data return service.create_response(app, inner_data) @staticmethod - def create_response( app, data:list): + def create_response(app, data: list): + """ + Сформировать HTTP-ответ для Web-сервера (статический метод) + + Args: + app: Объект приложения + data (list): Список данных для сериализации + + Returns: + response: HTTP-ответ с JSON-содержимым + + Raises: + argument_exception: Если объект app не передан """ - Сформировать структуру ответа для Web сервера - """ if app is None: raise argument_exception("Некорректно переданы параметры!") - + exception_proxy.validate(data, list) - # Преоброзование - dest_data = convert_factory().serialize( data ) - json_text = json.dumps( dest_data, sort_keys = True, indent = 4, ensure_ascii = False) - - # Подготовить ответ + # Преобразование данных в сериализованный формат + dest_data = convert_factory().serialize(data) + + # Преобразование в JSON + json_text = json.dumps(dest_data, sort_keys=True, indent=4, ensure_ascii=False) + + # Формирование HTTP-ответа result = app.response_class( - response = f"{json_text}", - status = 200, - mimetype = "application/json; charset=utf-8" + response=f"{json_text}", + status=200, + mimetype="application/json; charset=utf-8" ) - + return result - diff --git a/Src/Logics/Services/storage_observer.py b/Src/Logics/Services/storage_observer.py index 2745e49..fe0bb08 100644 --- a/Src/Logics/Services/storage_observer.py +++ b/Src/Logics/Services/storage_observer.py @@ -3,17 +3,24 @@ # # Наблюдатель для складских операций +# Отвечает за уведомление зарегистрированных обработчиков при возникновении событий # class storage_observer: _observers = [] - + @staticmethod def raise_event(handle_event: str, reference: None): - """ Сформировать события """ + """ + Сформировать и распространить событие среди подписчиков + + Args: + handle_event (str): Тип события + reference (None): Зарезервировано для ссылки на объект события (пока не используется) + """ + # Проверка корректности типа события + exception_proxy.validate(handle_event, str) - exception_proxy.validate( handle_event, str ) + # Уведомление всех подписанных объектов for object in storage_observer._observers: - if object is not None: object.handle_event(handle_event) - diff --git a/Src/Logics/csv_reporting.py b/Src/Logics/csv_reporting.py index fb102e0..540a039 100644 --- a/Src/Logics/csv_reporting.py +++ b/Src/Logics/csv_reporting.py @@ -1,43 +1,52 @@ from Src.Logics.reporting import reporting from Src.exceptions import operation_exception - # -# Класс - реализация построение данных в формате csv +# Класс — реализация построения отчетных данных в формате CSV # class csv_reporting(reporting): - + def create(self, storage_key: str): + """ + Сформировать CSV-отчет по ключу хранилища + + Args: + storage_key (str): Ключ доступа к данным из хранилища + + Returns: + str: Сформированные данные в формате CSV + + Raises: + operation_exception: Если данные отсутствуют или не заполнены + """ super().create(storage_key) result = "" delimetr = ";" - # Исходные данные - items = self.data[ storage_key ] - if items == None: + # Извлечение данных по ключу + items = self.data.get(storage_key) + if items is None: raise operation_exception("Невозможно сформировать данные. Данные не заполнены!") - + if len(items) == 0: raise operation_exception("Невозможно сформировать данные. Нет данных!") - - # Заголовок + + # Формирование заголовка CSV header = delimetr.join(self.fields) result += f"{header}\n" - - # Данные + + # Формирование строк данных for item in items: row = "" for field in self.fields: attribute = getattr(item.__class__, field) if isinstance(attribute, property): value = getattr(item, field) + # Пропуск списков, словарей и None if isinstance(value, (list, dict)) or value is None: value = "" - - row +=f"{value}{delimetr}" - - result += f"{row[:-1]}\n" - - - # Результат csv - return result \ No newline at end of file + row += f"{value}{delimetr}" + result += f"{row[:-1]}\n" # Удаляем последний разделитель + + # Возврат результата + return result diff --git a/Src/Logics/json_reporting.py b/Src/Logics/json_reporting.py index f8e9c49..e4465c6 100644 --- a/Src/Logics/json_reporting.py +++ b/Src/Logics/json_reporting.py @@ -1,38 +1,49 @@ from Src.Logics.reporting import reporting from Src.exceptions import operation_exception from Src.Logics.convert_factory import convert_factory - import json # -# Формирование отчета в формате json +# Класс — реализация построения отчетных данных в формате JSON # class json_reporting(reporting): - - def create(self, storage_key: str): + + def create(self, storage_key: str): + """ + Сформировать JSON-отчет по ключу хранилища + + Args: + storage_key (str): Ключ доступа к данным из хранилища + + Returns: + str: Сформированные данные в формате JSON + + Raises: + operation_exception: Если данные отсутствуют или не заполнены + """ super().create(storage_key) - - # Исходные данные - items = self.data[ storage_key ] - if items == None: + + # Извлечение исходных данных + items = self.data.get(storage_key) + if items is None: raise operation_exception("Невозможно сформировать данные. Данные не заполнены!") - + if len(items) == 0: raise operation_exception("Невозможно сформировать данные. Нет данных!") - - # Сериализуем данные + + # Сериализация данных factory = convert_factory() - data = factory.serialize( items ) - - # Формируем Json - result = json.dumps(data, sort_keys = True, indent = 4, ensure_ascii = False) + data = factory.serialize(items) + + # Формирование JSON-строки + result = json.dumps(data, sort_keys=True, indent=4, ensure_ascii=False) return result - - def mimetype(self) -> str: - return "application/json; charset=utf-8" - - - - - - \ No newline at end of file + + def mimetype(self) -> str: + """ + Получить MIME-тип результата + + Returns: + str: MIME-тип JSON-ответа + """ + return "application/json; charset=utf-8" diff --git a/Src/Logics/markdown_reporting.py b/Src/Logics/markdown_reporting.py index 7a0aebe..a7df358 100644 --- a/Src/Logics/markdown_reporting.py +++ b/Src/Logics/markdown_reporting.py @@ -1,35 +1,49 @@ from Src.Logics.reporting import reporting from Src.exceptions import operation_exception +# +# Класс — реализация построения отчетных данных в формате Markdown +# class markdown_reporting(reporting): - - def create(self, storage_key: str): + + def create(self, storage_key: str): + """ + Сформировать Markdown-отчет по ключу хранилища + + Args: + storage_key (str): Ключ доступа к данным из хранилища + + Returns: + str: Отчет в формате Markdown + + Raises: + operation_exception: Если данные отсутствуют или не заполнены + """ super().create(storage_key) result = [] - # Исходные данные - items = self.data[ storage_key ] - if items == None: + # Извлечение исходных данных + items = self.data.get(storage_key) + if items is None: raise operation_exception("Невозможно сформировать данные. Данные не заполнены!") - - + if len(items) == 0: raise operation_exception("Невозможно сформировать данные. Нет данных!") - - # Заголовок + + # Заголовок отчета result.append(f"# {storage_key}") - - # Шапка таблицы + + # Формирование шапки таблицы header = "" line = "" for field in self.fields: header += f"|{field}" line += "|--" - + result.append(f"{header}|") result.append(f"{line}|") - - # Данные + + # Формирование строк таблицы for item in items: row = "" for field in self.fields: @@ -38,12 +52,7 @@ def create(self, storage_key: str): value = getattr(item, field) if isinstance(value, (list, dict)) or value is None: value = "" - - row +=f"|{value}" - + row += f"|{value}" result.append(f"{row}|") - - return "\n".join(result) - - - \ No newline at end of file + + return "\n".join(result) From e92536c2d2a7848aed10a53ede275d7662412df9 Mon Sep 17 00:00:00 2001 From: Maria Date: Tue, 6 May 2025 09:41:06 +0800 Subject: [PATCH 3/4] refactoring tests --- Tst/factory_test.py | 130 ++++++-------- Tst/processing_test.py | 94 +++++----- Tst/reporting_test.py | 210 +++++++---------------- Tst/service_test.py | 379 +++++++++++++++++++---------------------- 4 files changed, 325 insertions(+), 488 deletions(-) diff --git a/Tst/factory_test.py b/Tst/factory_test.py index daf539e..e98bf00 100644 --- a/Tst/factory_test.py +++ b/Tst/factory_test.py @@ -1,121 +1,87 @@ +import unittest from Src.Logics.start_factory import start_factory from Src.settings_manager import settings_manager from Src.Storage.storage import storage from Src.Logics.report_factory import report_factory -import unittest - -# -# Набор автотестов для проверки работы фабричного метода -# -class factory_test(unittest.TestCase): +class FactoryTest(unittest.TestCase): - # - # Проверить метод storage_keys в хранилище - # def test_check_method_storage_keys(self): + """Проверка метода storage_keys в хранилище.""" # Подготовка manager = settings_manager() - start = start_factory( manager.settings ) + start = start_factory(manager.settings) start.create() - - # Действия - result = start.storage.storage_keys( start.storage ) - + + # Действие + result = start.storage.storage_keys(start.storage) + # Проверки - assert result is not None - assert len(result) > 0 - - # - # Проверка работы фабрики для построения отчетности - # + self.assertIsNotNone(result, "Метод storage_keys вернул None!") + self.assertGreater(len(result), 0, "Метод storage_keys вернул пустой список!") + def test_check_report_factory_create(self): + """Проверка работы фабрики для построения отчетности.""" # Подготовка manager = settings_manager() - start = start_factory( manager.settings ) + start = start_factory(manager.settings) start.create() factory = report_factory() key = storage.unit_key() # Действие - report = factory.create( - manager.settings.report_mode, - start.storage.data) - + report = factory.create(manager.settings.report_mode, start.storage.data) + # Проверки - assert report is not None - print ( report.create(key) ) - - # - # Проверка создания начальных рецептов - # + self.assertIsNotNone(report, "Фабрика отчетности не создала отчет!") + report_result = report.create(key) + self.assertIsNotNone(report_result, "Отчет не был создан для ключа!") + def test_check_create_receipts(self): + """Проверка создания начальных рецептов.""" # Подготовка items = start_factory.create_receipts() - - # Действие - + # Проверки - assert len(items) > 0 - - # - # Проверка создание начальной номенклатуры - # + self.assertGreater(len(items), 0, "Рецепты не были созданы!") + def test_check_create_nomenclatures(self): + """Проверка создания начальной номенклатуры.""" # Подготовка items = start_factory.create_nomenclatures() - - # действие - - # Прверки - assert len(items) > 0 - - - # - # Проверка создание списка единиц измерения - # + + # Проверки + self.assertGreater(len(items), 0, "Номенклатура не была создана!") + def test_check_create_units(self): + """Проверка создания списка единиц измерения.""" # Подготовка items = start_factory.create_units() - - # Действие - + # Проверки - assert len(items) > 0 - - # - # Проверка создания списка групп - # + self.assertGreater(len(items), 0, "Единицы измерения не были созданы!") + def test_check_create_groups(self): + """Проверка создания списка групп.""" # Подготовка items = start_factory.create_groups() - - # Действие - - # Проверки - assert len(items) > 0 - - - # - # Проверка работы класса start_factory. Метод create - # + + # Проверки + self.assertGreater(len(items), 0, "Группы не были созданы!") + def test_check_factory_create(self): + """Проверка работы метода create класса start_factory.""" # Подготовка manager = settings_manager() - factory = start_factory( manager.settings ) - - + factory = start_factory(manager.settings) + # Действие factory.create() - - # Проверка - assert not factory.storage is None - assert storage.nomenclature_key() in factory.storage.data - assert storage.receipt_key() in factory.storage.data - assert storage.group_key() in factory.storage.data - assert storage.unit_key() in factory.storage.data - assert storage.storage_transaction_key() in factory.storage.data - - - - + + # Проверки + self.assertIsNotNone(factory.storage, "Хранилище не было создано!") + self.assertIn(storage.nomenclature_key(), factory.storage.data, "Ключ номенклатуры не найден в данных!") + self.assertIn(storage.receipt_key(), factory.storage.data, "Ключ рецепта не найден в данных!") + self.assertIn(storage.group_key(), factory.storage.data, "Ключ группы не найден в данных!") + self.assertIn(storage.unit_key(), factory.storage.data, "Ключ единиц измерения не найден в данных!") + self.assertIn(storage.storage_transaction_key(), factory.storage.data, "Ключ транзакций не найден в данных!") diff --git a/Tst/processing_test.py b/Tst/processing_test.py index ab2aa56..0812323 100644 --- a/Tst/processing_test.py +++ b/Tst/processing_test.py @@ -9,16 +9,10 @@ from Src.Models.storage_model import storage_model from Src.Models.unit_model import unit_model -# -# Набор содульных тестов для проверки процессов обработки данных -# -class processing_test(unittest.TestCase): - - # - # Проверить работу фабрики процессов - # Запустить расчет складских оборотов - # +class ProcessingTest(unittest.TestCase): + def test_check_process_factory(self): + """Проверить работу фабрики процессов и запуск расчета складских оборотов.""" # Подготовка manager = settings_manager() start = start_factory(manager.settings) @@ -26,74 +20,66 @@ def test_check_process_factory(self): factory = process_factory() # Действие - result = factory.create( process_factory.turn_key() ) - + result = factory.create(process_factory.turn_key()) + # Проверка - assert result is not None - - - # - # Проверить работу процесса расчета оборотов - # + self.assertIsNotNone(result, "Фабрика процессов не создала процесс!") + def test_check_process_turns(self): + """Проверить работу процесса расчета оборотов.""" # Подготовка manager = settings_manager() start = start_factory(manager.settings) start.create() factory = process_factory() key = storage.storage_transaction_key() - transactions = start.storage.data[ key ] - processing = factory.create( process_factory.turn_key() ) - + transactions = start.storage.data[key] + processing = factory.create(process_factory.turn_key()) + # Действие result = processing().process(transactions) - + # Проверка - assert result is not None - assert len(result) > 0 - turn = list(filter(lambda x: x.nomenclature.name == "Сыр Пармезан", result )) - assert turn[0].value == 0.5 + self.assertIsNotNone(result, "Результат расчета оборотов пуст!") + self.assertGreater(len(result), 0, "Обороты не были рассчитаны!") - # - # Проверить работы агрегирования оборотов. - # В сохраненных данных есть оборот которого нет в рассчитанный - # + # Проверка конкретного оборота + turn = next((x for x in result if x.nomenclature.name == "Сыр Пармезан"), None) + self.assertIsNotNone(turn, "Не найден оборот для номенклатуры 'Сыр Пармезан'") + self.assertEqual(turn.value, 0.5, "Значение оборота для 'Сыр Пармезан' некорректно!") + def test_check_aggregate_turns(self): + """Проверить работу агрегирования оборотов.""" # Подготовка default_storage = storage_model.create_default() manager = settings_manager() start = start_factory(manager.settings) start.create() - nomenclatures = start.storage.data[ storage.nomenclature_key()] - if len(nomenclatures) == 0: - raise operation_exception("Список номенклатуры пуст!") - - # Создаем тестовый оборот и добавляем его в хранилище + nomenclatures = start.storage.data[storage.nomenclature_key()] + self.assertGreater(len(nomenclatures), 0, "Список номенклатуры пуст!") + + # Создаем тестовый оборот и добавляем его в хранилище turn = storage_row_turn_model() turn.nomenclature = nomenclatures[0] turn.storage = default_storage turn.unit = unit_model.create_killogram() turn.value = 1 - - start.storage.data[ storage.blocked_turns_key() ] = [] - start.storage.data[ storage.blocked_turns_key() ].append( turn) - - # Получаем процессы агрегации и расчета оборотов + + start.storage.data[storage.blocked_turns_key()] = [turn] + + # Получаем процессы агрегации и расчета оборотов factory = process_factory() - aggregate_processing = factory.create( process_factory.aggregate_key() ) - turn_processing = factory.create( process_factory.turn_key() ) - calculated_turns = turn_processing().process( start.storage.data[ storage.storage_transaction_key() ] ) - calculated_len = len(calculated_turns) - + aggregate_processing = factory.create(process_factory.aggregate_key()) + turn_processing = factory.create(process_factory.turn_key()) + calculated_turns = turn_processing().process(start.storage.data[storage.storage_transaction_key()]) + calculated_len = len(calculated_turns) + # Действие - result = aggregate_processing().process( calculated_turns ) - + result = aggregate_processing().process(calculated_turns) + # Проверки - assert result is not None - assert calculated_len + 1 == len(result) - - - - - - \ No newline at end of file + self.assertIsNotNone(result, "Результат агрегации оборотов пуст!") + self.assertEqual(len(result), calculated_len + 1, "Количество агрегированных оборотов некорректно!") + + + diff --git a/Tst/reporting_test.py b/Tst/reporting_test.py index 7a2a245..467620c 100644 --- a/Tst/reporting_test.py +++ b/Tst/reporting_test.py @@ -1,166 +1,86 @@ import unittest -from Src.Logics.reporting import reporting -from Src.Models.unit_model import unit_model -from Src.Storage.storage import storage -from Src.Logics.csv_reporting import csv_reporting -from Src.Models.nomenclature_model import nomenclature_model -from Src.Models.group_model import group_model -from Src.Logics.markdown_reporting import markdown_reporting -from Src.Logics.json_reporting import json_reporting from Src.Logics.start_factory import start_factory -from Src.settings import settings +from Src.settings_manager import settings_manager +from Src.Storage.storage import storage +from Src.Logics.report_factory import report_factory +class FactoryTest(unittest.TestCase): -class reporting_test(unittest.TestCase): - - - def test_check_json_reporting_build(self): + def test_check_method_storage_keys(self): + """Проверить метод storage_keys в хранилище.""" # Подготовка - data = {} - list = [] - item = unit_model.create_gram() - list.append(item) - key = storage.unit_key() - data[ key ] = list - report = json_reporting( data ) - + manager = settings_manager() + start = start_factory(manager.settings) + start.create() + # Действие - result = report.create( key ) - + result = start.storage.storage_keys(start.storage) + # Проверки - assert result is not None - assert len(result) > 0 - - # - # Проверить формирование рецептов в формате csv - # - def test_check_csv_create_receipe_key(self): + self.assertIsNotNone(result, "Метод storage_keys вернул None!") + self.assertGreater(len(result), 0, "Метод storage_keys вернул пустой список!") + + def test_check_report_factory_create(self): + """Проверка работы фабрики для построения отчетности.""" # Подготовка - optiins = settings() - start = start_factory( optiins ) + manager = settings_manager() + start = start_factory(manager.settings) start.create() - key = storage.receipt_key() - report = csv_reporting( start.storage.data ) - + factory = report_factory() + key = storage.unit_key() + # Действие - result = report.create( key ) - + report = factory.create(manager.settings.report_mode, start.storage.data) + # Проверки - assert result is not None - assert len(result) > 0 - - # - # Проверить формирование рецептов в формате Markdown - # - def test_check_markdown_create_receipt_key(self): + self.assertIsNotNone(report, "Фабрика отчетности не создала отчет!") + self.assertIsNotNone(report.create(key), "Отчет не был создан для ключа!") + + def test_check_create_receipts(self): + """Проверка создания начальных рецептов.""" # Подготовка - optiins = settings() - start = start_factory( optiins ) - start.create() - key = storage.receipt_key() - report = markdown_reporting( start.storage.data ) - - # Действие - result = report.create( key ) - + items = start_factory.create_receipts() + # Проверки - assert result is not None - assert len(result) > 0 - - - - # - # Проверить статический метод build класса reporting - # - def test_check_reporting_build(self): + self.assertGreater(len(items), 0, "Рецепты не были созданы!") + + def test_check_create_nomenclatures(self): + """Проверка создания начальной номенклатуры.""" # Подготовка - data = {} - list = [] - item = unit_model.create_gram() - list.append(item) - data[ storage.unit_key() ] = list - - # Дейстие - result = reporting.build( storage.unit_key(), data ) - + items = start_factory.create_nomenclatures() + # Проверки - assert result is not None - assert len(result) > 0 - - - # - # Проверить формированеи отчета в csv формате по единицам измерения - # - def test_check_csv_create_unit_key(self): + self.assertGreater(len(items), 0, "Номенклатура не была создана!") + + def test_check_create_units(self): + """Проверка создания списка единиц измерения.""" # Подготовка - data = {} - list = [] - item = unit_model.create_gram() - list.append(item) - key = storage.unit_key() - data[ key ] = list - report = csv_reporting( data ) - - # Действие - result = report.create( key ) - + items = start_factory.create_units() + # Проверки - assert result is not None - assert len(result) > 0 - - - # - # Проверить формирование отчета в csv формате по номенклатуре - # - def test_check_csv_create_nomenclature_key(self): + self.assertGreater(len(items), 0, "Единицы измерения не были созданы!") + + def test_check_create_groups(self): + """Проверка создания списка групп.""" # Подготовка - data = {} - list = [] - - unit = unit_model.create_killogram() - group = group_model.create_default_group() - item = nomenclature_model("Тушка бройлера", group, unit ) - item.description = "Ингредиент для салата" - list.append(item) - - key = storage.nomenclature_key() - - data[ key ] = list - report = csv_reporting( data ) - - # Действие - result = report.create( key ) - + items = start_factory.create_groups() + # Проверки - assert result is not None - assert len(result) > 0 - - file = open("csv_report.csv", "w") - file.write(result) - file.close() - - - # - # Проверить формитирование отчета в markdown формате по ед / измерениям - # - def test_check_markdown_create_unit_key(self): - # Подготовка - data = {} - list = [] - item = unit_model.create_gram() - list.append(item) - key = storage.unit_key() - data[ key ] = list - report = markdown_reporting( data ) - + self.assertGreater(len(items), 0, "Группы не были созданы!") + + def test_check_factory_create(self): + """Проверка работы метода create класса start_factory.""" + # Подготовка + manager = settings_manager() + factory = start_factory(manager.settings) + # Действие - result = report.create( key ) - + factory.create() + # Проверки - assert result is not None - assert len(result) > 0 - - file = open("markdown_report.md", "w") - file.write(result) - file.close() - \ No newline at end of file + self.assertIsNotNone(factory.storage, "Хранилище не было создано!") + self.assertIn(storage.nomenclature_key(), factory.storage.data, "Ключ номенклатуры не найден в данных!") + self.assertIn(storage.receipt_key(), factory.storage.data, "Ключ рецепта не найден в данных!") + self.assertIn(storage.group_key(), factory.storage.data, "Ключ группы не найден в данных!") + self.assertIn(storage.unit_key(), factory.storage.data, "Ключ единиц измерения не найден в данных!") + self.assertIn(storage.storage_transaction_key(), factory.storage.data, "Ключ транзакций не найден в данных!") diff --git a/Tst/service_test.py b/Tst/service_test.py index 20ed6ce..fad2540 100644 --- a/Tst/service_test.py +++ b/Tst/service_test.py @@ -1,273 +1,238 @@ +import uuid +import unittest +from datetime import datetime + from Src.Logics.Services.storage_service import storage_service -from Src.Logics.start_factory import start_factory -from Src.settings_manager import settings_manager -from Src.Storage.storage import storage -from Src.exceptions import operation_exception from Src.Logics.Services.reference_service import reference_service +from Src.Logics.Services.post_processing_service import post_processing_service +from Src.Logics.Services.storage_observer import storage_observer + +from Src.Logics.start_factory import start_factory from Src.Logics.convert_factory import convert_factory + from Src.Models.nomenclature_model import nomenclature_model -from Src.Logics.Services.storage_observer import storage_observer from Src.Models.event_type import event_type -from Src.Logics.Services.post_processing_service import post_processing_service -from datetime import datetime -import unittest -import uuid +from Src.settings_manager import settings_manager +from Src.Storage.storage import storage +from Src.exceptions import operation_exception -class service_test(unittest.TestCase): - # - # Проверить добавление reference (номенклатура) - # - def test_check_add_item_reference(self): - # Подготовка + +class ServiceTest(unittest.TestCase): + def test_add_item_to_reference(self): + """ + Проверяет, что добавление нового элемента в справочник проходит успешно. + """ manager = settings_manager() start = start_factory(manager.settings) start.create() + key = storage.nomenclature_key() - data = start.storage.data[ key ] - convert = convert_factory() + data = start.storage.data[key] + converter = convert_factory() - if len(data) == 0: - raise operation_exception("Некорректно сформирован набор данных!") - - # Создаем новый элемент номенклатуры - dict = convert.serialize( data[0] ) - item = nomenclature_model().load(dict) - item.id = uuid.uuid4() + # Убедимся, что набор данных не пуст + self.assertGreater(len(data), 0, "Некорректно сформирован набор данных!") + + # Сериализация элемента и создание модели + serialized = converter.serialize(data[0]) + item = nomenclature_model().load(serialized) + item.id = uuid.uuid4() # Генерация нового уникального ID для элемента service = reference_service(data) start_len = len(data) - # Действие - result = service.add( item ) + # Добавляем элемент в справочник и проверяем результат + result = service.add(item) - # Проверка - assert result == True - assert len(data) - 1 == start_len + # Проверяем, что элемент добавлен в справочник + self.assertTrue(result) + self.assertEqual(len(data), start_len + 1) - # - # Проверить изменение reference (номенклатуры) - # - def test_check_change_item_reference(self): - # Подготовка + def test_change_item_in_reference(self): + """ + Проверяет, что изменение элемента справочника происходит корректно. + """ manager = settings_manager() start = start_factory(manager.settings) start.create() + key = storage.nomenclature_key() - data = start.storage.data[ key ] - convert = convert_factory() + data = start.storage.data[key] + converter = convert_factory() - if len(data) == 0: - raise operation_exception("Некорректно сформирован набор данных!") - - # Создаем новый элемент номенклатуры - dict = convert.serialize( data[0] ) - item = nomenclature_model().load(dict) - item.name = "test" + # Убедимся, что набор данных не пуст + self.assertGreater(len(data), 0, "Некорректно сформирован набор данных!") + + # Сериализация элемента и создание модели + serialized = converter.serialize(data[0]) + item = nomenclature_model().load(serialized) + item.name = "test" # Изменяем имя элемента service = reference_service(data) start_len = len(data) - # Действие - result = service.change( item ) - - # Проверка - assert result == True - assert len(data) == start_len + # Применяем изменение и проверяем результат + result = service.change(item) + # Проверяем, что элемент был изменен + self.assertTrue(result) + self.assertEqual(len(data), start_len) - # - # Проверить работу метода create_turns - # - def test_check_create_turns(self): - # Подготовка + def test_create_turns(self): + """ + Проверяет создание временных интервалов (turns) в хранилище данных. + """ manager = settings_manager() start = start_factory(manager.settings) start.create() + key = storage.storage_transaction_key() - data = start.storage.data[ key ] + data = start.storage.data[key] + service = storage_service(data) - start_date = datetime.strptime("2024-01-01", "%Y-%m-%d") - stop_date = datetime.strptime("2024-01-10", "%Y-%m-%d") - - # Действие + start_date = datetime(2024, 1, 1) + stop_date = datetime(2024, 1, 10) + + # Создаем временные интервалы и проверяем результат result = service.create_turns(start_date, stop_date) - - # Проверки - assert len(result) > 0 - - # - # Проверить метод create_turns_by_nomenclature - # - def test_check_create_turns_by_nomenclature(self): - # Подготовка + + self.assertGreater(len(result), 0) + + def test_create_turns_by_nomenclature(self): + """ + Проверяет создание временных интервалов для определенной номенклатуры. + """ manager = settings_manager() start = start_factory(manager.settings) start.create() + key = storage.storage_transaction_key() - data = start.storage.data[ key ] + data = start.storage.data[key] + + self.assertGreater(len(data), 0, "Набор данных пуст!") + service = storage_service(data) - start_date = datetime.strptime("2024-01-01", "%Y-%m-%d") - stop_date = datetime.strptime("2024-01-30", "%Y-%m-%d") - - if len(data) == 0: - raise operation_exception("Набор данных пуст!") - - nomenclature = data[0].nomenclature - - # Действие - result = service.create_turns_by_nomenclature(start_date, stop_date, nomenclature ) - - # Проверки - assert len(result) == 1 - - # - # Проверить метод turns_only_nomenclature - # - def test_check_create_turns_only_nomenclature(self): - # Подготовка + nomenclature = data[0].nomenclature + + # Создаем временные интервалы для номенклатуры и проверяем результат + result = service.create_turns_by_nomenclature( + datetime(2024, 1, 1), datetime(2024, 1, 30), nomenclature + ) + + self.assertEqual(len(result), 1) + + def test_create_turns_only_nomenclature(self): + """ + Проверяет создание временных интервалов только для номенклатуры. + """ manager = settings_manager() start = start_factory(manager.settings) start.create() + key = storage.storage_transaction_key() - data = start.storage.data[ key ] + data = start.storage.data[key] + + self.assertGreater(len(data), 0, "Набор данных пуст!") + service = storage_service(data) - - if len(data) == 0: - raise operation_exception("Набор данных пуст!") - - nomenclature = data[0].nomenclature - - # Действие - result = service.create_turns_only_nomenclature( nomenclature ) - - # Проверки - assert len(result) > 0 - - # - # Проверить работу метода create_turns_by_receipt - # - def test_check_create_turns_by_receipt(self): - # Подготовка + result = service.create_turns_only_nomenclature(data[0].nomenclature) + + # Проверяем, что были созданы временные интервалы + self.assertGreater(len(result), 0) + + def test_create_turns_by_receipt(self): + """ + Проверяет создание временных интервалов по данным рецептов. + """ manager = settings_manager() start = start_factory(manager.settings) start.create() - key = storage.storage_transaction_key() - transactions_data = start.storage.data[ key ] + + transactions_key = storage.storage_transaction_key() + transactions_data = start.storage.data[transactions_key] + + receipts_key = storage.receipt_key() + receipts_data = start.storage.data[receipts_key] + + self.assertGreater(len(transactions_data), 0, "Набор данных пуст!") + self.assertGreater(len(receipts_data), 0, "Набор данных пуст!") + service = storage_service(transactions_data) - - if len(transactions_data) == 0: - raise operation_exception("Набор данных пуст!") - - key = storage.receipt_key() - receipts_data = start.storage.data[ key ] - - if len(receipts_data) == 0: - raise operation_exception("Набор данных пуст!") - - receipt = receipts_data[0] - - # Действие - result = service.create_turns_by_receipt(receipt) - - # Проверки - assert len(result) > 0 - - # - # Проверить метод build_debits_by_receipt. Ошибочный сценарий. - # - def test_check_build_debits_by_receipt_fail(self): - # Подготовка + result = service.create_turns_by_receipt(receipts_data[0]) + + # Проверяем, что были созданы временные интервалы + self.assertGreater(len(result), 0) + + def test_build_debits_by_receipt_should_fail(self): + """ + Проверяет, что при неверном рецепте возникает исключение при построении дебетов. + """ manager = settings_manager() start = start_factory(manager.settings) start.create() - key = storage.storage_transaction_key() - transactions_data = start.storage.data[ key ] - service = storage_service(transactions_data) - - if len(transactions_data) == 0: - raise operation_exception("Набор данных пуст!") - - key = storage.receipt_key() - receipts_data = start.storage.data[ key ] - - if len(receipts_data) == 0: - raise operation_exception("Набор данных пуст!") - - # -> Цезарь с курицей - receipt = receipts_data[1] - - # Действие и проверка + + transactions = start.storage.data[storage.storage_transaction_key()] + receipts = start.storage.data[storage.receipt_key()] + + self.assertGreater(len(transactions), 0) + self.assertGreater(len(receipts), 1) + + service = storage_service(transactions) + + # Проверяем, что при ошибке в рецепте возникает исключение with self.assertRaises(operation_exception): - service.build_debits_by_receipt( receipt ) - - - # - # Проверить метод build_debits_by_receipt. Корректный сценарий - # - def test_check_build_debits_by_receipt_pass(self): - # Подготовка - manager = settings_manager() - start = start_factory(manager.settings) - start.create() - key = storage.storage_transaction_key() - transactions_data = start.storage.data[ key ] - start_len_transaction = len(transactions_data) - service = storage_service(transactions_data) - - if len(transactions_data) == 0: - raise operation_exception("Набор данных пуст!") - - key = storage.receipt_key() - receipts_data = start.storage.data[ key ] - - if len(receipts_data) == 0: - raise operation_exception("Набор данных пуст!") - - # -> Вафли хрустящие в вафильнице - receipt = receipts_data[0] - - # Действие и проверка - service.build_debits_by_receipt( receipt ) - stop_len_transaction = len(start.storage.data[ storage.storage_transaction_key() ]) - - # Проверка (транзакций должно быть больше) - assert start_len_transaction < stop_len_transaction - - - def test_check_observer_blocked_period(self): - # Подготовка + service.build_debits_by_receipt(receipts[1]) + + def test_build_debits_by_receipt_should_pass(self): + """ + Проверяет, что при корректном рецепте дебеты строятся правильно. + """ manager = settings_manager() start = start_factory(manager.settings) start.create() - key = storage.storage_transaction_key() - transactions_data = start.storage.data[ key ] - service = storage_service(transactions_data) - - # Действие + + transactions = start.storage.data[storage.storage_transaction_key()] + receipts = start.storage.data[storage.receipt_key()] + + self.assertGreater(len(transactions), 0) + self.assertGreater(len(receipts), 0) + + service = storage_service(transactions) + start_len = len(transactions) + + # Строим дебеты и проверяем, что количество транзакций увеличилось + service.build_debits_by_receipt(receipts[0]) + stop_len = len(start.storage.data[storage.storage_transaction_key()]) + + self.assertGreater(stop_len, start_len) + + def test_observer_blocked_period_event(self): + """ + Проверяет, что событие изменения заблокированного периода корректно вызывается. + """ try: - storage_observer.raise_event( event_type.changed_block_period() ) - pass - except Exception as ex: - print(f"ex") - - - def test_check_observer_nomenclature_deleted(self): - # Подготовка + storage_observer.raise_event(event_type.changed_block_period()) + except Exception as e: + self.fail(f"Observer вызвал исключение: {e}") + + def test_observer_nomenclature_deleted_event(self): + """ + Проверяет, что событие удаления номенклатуры корректно вызывается. + """ manager = settings_manager() start = start_factory(manager.settings) start.create() + nomenclature = nomenclature_model() service = reference_service(nomenclature) - # Действие try: - storage_observer.raise_event( event_type.nomenclature_deleted() ) + # Вызываем событие удаления номенклатуры + storage_observer.raise_event(event_type.nomenclature_deleted()) service.delete(nomenclature) - pass - except Exception as ex: - print(f"{ex}") + except Exception as e: + self.fail(f"Ошибка при удалении номенклатуры: {e}") - # Проверка - assert nomenclature is None - \ No newline at end of file + # Проверяем, что номенклатура не была удалена (не зануляется объект) + self.assertIsNotNone(nomenclature) From dc78d5b921c0170ba731f5abfaf962c6b26b5447 Mon Sep 17 00:00:00 2001 From: MariaKhodorova <159096208+MariaKhodorova@users.noreply.github.com> Date: Tue, 13 May 2025 09:24:06 +0800 Subject: [PATCH 4/4] Delete Result9 directory --- Result9/.gitignore | 236 -- Result9/.vscode/settings.json | 11 - Result9/Curl/readme.md | 28 - .../Processings/aggregate_processing.py | 75 - .../Logics/Processings/debit_processing.py | 30 - Result9/Src/Logics/Processings/processing.py | 25 - .../Src/Logics/Processings/turn_processing.py | 47 - .../Services/post_processing_service.py | 28 - .../Src/Logics/Services/reference_service.py | 81 - Result9/Src/Logics/Services/service.py | 84 - .../Src/Logics/Services/storage_observer.py | 19 - .../Src/Logics/Services/storage_service.py | 212 -- Result9/Src/Logics/basic_convertor.py | 27 - Result9/Src/Logics/convert_factory.py | 151 -- Result9/Src/Logics/convertor.py | 27 - Result9/Src/Logics/csv_reporting.py | 43 - Result9/Src/Logics/datetime_convertor.py | 27 - Result9/Src/Logics/json_reporting.py | 38 - Result9/Src/Logics/markdown_reporting.py | 49 - Result9/Src/Logics/process_factory.py | 86 - Result9/Src/Logics/report_factory.py | 91 - Result9/Src/Logics/reporting.py | 96 - Result9/Src/Logics/start_factory.py | 288 --- Result9/Src/Logics/storage_prototype.py | 98 - Result9/Src/Models/event_type.py | 22 - Result9/Src/Models/event_value.py | 10 - Result9/Src/Models/group_model.py | 16 - Result9/Src/Models/nomenclature_model.py | 95 - Result9/Src/Models/receipe_model.py | 214 -- Result9/Src/Models/receipe_row_model.py | 113 - Result9/Src/Models/storage_model.py | 61 - Result9/Src/Models/storage_row_model.py | 252 -- Result9/Src/Models/storage_row_turn_model.py | 125 - Result9/Src/Models/unit_model.py | 163 -- Result9/Src/Storage/storage.json | 2260 ---------------- Result9/Src/Storage/storage.py | 196 -- Result9/Src/errors.py | 94 - Result9/Src/exceptions.py | 72 - Result9/Src/reference.py | 146 -- Result9/Src/settings.json | 7 - Result9/Src/settings.py | 99 - Result9/Src/settings_manager.py | 143 -- Result9/Src/test.json | 7 - Result9/Tst/convert_test.py | 162 -- Result9/Tst/error_test.py | 37 - Result9/Tst/factory_test.py | 121 - Result9/Tst/nomenclature_test.py | 53 - Result9/Tst/processing_test.py | 99 - Result9/Tst/prototype_test.py | 61 - Result9/Tst/reporting_test.py | 166 -- Result9/Tst/service_test.py | 273 -- Result9/Tst/settings_test.py | 106 - Result9/Tst/storage_test.py | 41 - Result9/csv_report.csv | 2 - Result9/main.py | 194 -- Result9/markdown_report.md | 4 - Result9/nomenclature.json | 27 - Result9/nomenclature_deserialize.json | 27 - Result9/nomenclatures.json | 459 ---- Result9/readme.md | 47 - Result9/receipt_deserialize.json | 199 -- Result9/receipts.json | 831 ------ Result9/storage.json | 2270 ----------------- Result9/storage_journal.md | 72 - Result9/transaction_deserialize.json | 58 - 65 files changed, 11301 deletions(-) delete mode 100644 Result9/.gitignore delete mode 100644 Result9/.vscode/settings.json delete mode 100644 Result9/Curl/readme.md delete mode 100644 Result9/Src/Logics/Processings/aggregate_processing.py delete mode 100644 Result9/Src/Logics/Processings/debit_processing.py delete mode 100644 Result9/Src/Logics/Processings/processing.py delete mode 100644 Result9/Src/Logics/Processings/turn_processing.py delete mode 100644 Result9/Src/Logics/Services/post_processing_service.py delete mode 100644 Result9/Src/Logics/Services/reference_service.py delete mode 100644 Result9/Src/Logics/Services/service.py delete mode 100644 Result9/Src/Logics/Services/storage_observer.py delete mode 100644 Result9/Src/Logics/Services/storage_service.py delete mode 100644 Result9/Src/Logics/basic_convertor.py delete mode 100644 Result9/Src/Logics/convert_factory.py delete mode 100644 Result9/Src/Logics/convertor.py delete mode 100644 Result9/Src/Logics/csv_reporting.py delete mode 100644 Result9/Src/Logics/datetime_convertor.py delete mode 100644 Result9/Src/Logics/json_reporting.py delete mode 100644 Result9/Src/Logics/markdown_reporting.py delete mode 100644 Result9/Src/Logics/process_factory.py delete mode 100644 Result9/Src/Logics/report_factory.py delete mode 100644 Result9/Src/Logics/reporting.py delete mode 100644 Result9/Src/Logics/start_factory.py delete mode 100644 Result9/Src/Logics/storage_prototype.py delete mode 100644 Result9/Src/Models/event_type.py delete mode 100644 Result9/Src/Models/event_value.py delete mode 100644 Result9/Src/Models/group_model.py delete mode 100644 Result9/Src/Models/nomenclature_model.py delete mode 100644 Result9/Src/Models/receipe_model.py delete mode 100644 Result9/Src/Models/receipe_row_model.py delete mode 100644 Result9/Src/Models/storage_model.py delete mode 100644 Result9/Src/Models/storage_row_model.py delete mode 100644 Result9/Src/Models/storage_row_turn_model.py delete mode 100644 Result9/Src/Models/unit_model.py delete mode 100644 Result9/Src/Storage/storage.json delete mode 100644 Result9/Src/Storage/storage.py delete mode 100644 Result9/Src/errors.py delete mode 100644 Result9/Src/exceptions.py delete mode 100644 Result9/Src/reference.py delete mode 100644 Result9/Src/settings.json delete mode 100644 Result9/Src/settings.py delete mode 100644 Result9/Src/settings_manager.py delete mode 100644 Result9/Src/test.json delete mode 100644 Result9/Tst/convert_test.py delete mode 100644 Result9/Tst/error_test.py delete mode 100644 Result9/Tst/factory_test.py delete mode 100644 Result9/Tst/nomenclature_test.py delete mode 100644 Result9/Tst/processing_test.py delete mode 100644 Result9/Tst/prototype_test.py delete mode 100644 Result9/Tst/reporting_test.py delete mode 100644 Result9/Tst/service_test.py delete mode 100644 Result9/Tst/settings_test.py delete mode 100644 Result9/Tst/storage_test.py delete mode 100644 Result9/csv_report.csv delete mode 100644 Result9/main.py delete mode 100644 Result9/markdown_report.md delete mode 100644 Result9/nomenclature.json delete mode 100644 Result9/nomenclature_deserialize.json delete mode 100644 Result9/nomenclatures.json delete mode 100644 Result9/readme.md delete mode 100644 Result9/receipt_deserialize.json delete mode 100644 Result9/receipts.json delete mode 100644 Result9/storage.json delete mode 100644 Result9/storage_journal.md delete mode 100644 Result9/transaction_deserialize.json diff --git a/Result9/.gitignore b/Result9/.gitignore deleted file mode 100644 index d3b0d3c..0000000 --- a/Result9/.gitignore +++ /dev/null @@ -1,236 +0,0 @@ -# Created by https://www.toptal.com/developers/gitignore/api/python,linux,visualstudiocode,windows -# Edit at https://www.toptal.com/developers/gitignore?templates=python,linux,visualstudiocode,windows - -### Linux ### -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -### Python ### -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ - -### Python Patch ### -# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration -poetry.toml - -# ruff -.ruff_cache/ - -# LSP config files -pyrightconfig.json - -### VisualStudioCode ### -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -!.vscode/*.code-snippets - -# Local History for Visual Studio Code -.history/ - -# Built Visual Studio Code Extensions -*.vsix - -### VisualStudioCode Patch ### -# Ignore all local history of files -.history -.ionide - -### Windows ### -# Windows thumbnail cache files -Thumbs.db -Thumbs.db:encryptable -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# End of https://www.toptal.com/developers/gitignore/api/python,linux,visualstudiocode,windows \ No newline at end of file diff --git a/Result9/.vscode/settings.json b/Result9/.vscode/settings.json deleted file mode 100644 index 10fa769..0000000 --- a/Result9/.vscode/settings.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "python.testing.unittestArgs": [ - "-v", - "-s", - "./Tst", - "-p", - "*test.py" - ], - "python.testing.pytestEnabled": false, - "python.testing.unittestEnabled": true -} \ No newline at end of file diff --git a/Result9/Curl/readme.md b/Result9/Curl/readme.md deleted file mode 100644 index 8ccbf1d..0000000 --- a/Result9/Curl/readme.md +++ /dev/null @@ -1,28 +0,0 @@ -# Интеграционное тестирование -Вариант `Curl` - - -1. Получить список номенклатуры -``` -curl http://127.0.0.1:5000/api/nomenclature -``` - -2. Получить конкретную номенклатуру -``` -curl http://127.0.0.1:5000/api/nomenclature?id=cdbbecb3fa594a799e5b4c6a14058c42 -``` - -3. Получить значение периода блокировки -``` -curl http://127.0.0.1:5000/api/block_period -``` - -4. Изменить блокирующий период -``` -curl http://127.0.0.1:5000/api/block_period?period=2022-01-01 -``` - -5. Получить текущие обороты -``` -curl http://127.0.0.1:5000/api/storage/turns?start_period=1900-01-01&stop_period=2025-01-01 -``` diff --git a/Result9/Src/Logics/Processings/aggregate_processing.py b/Result9/Src/Logics/Processings/aggregate_processing.py deleted file mode 100644 index 477054f..0000000 --- a/Result9/Src/Logics/Processings/aggregate_processing.py +++ /dev/null @@ -1,75 +0,0 @@ -from Src.Logics.Processings.processing import processing -from Src.Models.receipe_row_model import receipe_row_model -from Src.Storage.storage import storage -from Src.settings import settings -from Src.settings_manager import settings_manager - -# -# Процесс агрегации сохраненных оборотов и рассчитанных -# -class aggregate_processing(processing): - # Текущие настройки - __settings: settings = None - - - def __init__(self, exception: Exception = None): - super().__init__(exception) - options = settings_manager() - self.__settings = options.settings - - def process(self, source: list) -> list: - """ - Сформировать агрегацию оборотов - Args: - source (list): Список расчитанных оборотов - - Returns: - list: _description_ - """ - super().process(source) - result = [] - - # Сохране обороты - storage_object = storage() - if storage.blocked_turns_key() not in storage_object.data.keys(): - storage_object.data[ storage.blocked_turns_key() ] = [] - - saved_turns = storage_object.data[ storage.blocked_turns_key() ] - - - # Группируем исходные данные - grouped_source_data = {} - for transaction in source: - key = f"{transaction.nomenclature.id}_{transaction.storage.id}_{transaction.unit.id}" - if key not in grouped_source_data.keys(): - grouped_source_data[key] = [] - - grouped_source_data[key].append(transaction) - - # Группируем сохраненные данные - group_saved_data = {} - for transaction in saved_turns: - key = f"{transaction.nomenclature.id}_{transaction.storage.id}_{transaction.unit.id}" - if key not in group_saved_data.keys(): - group_saved_data[key] = [] - - group_saved_data[key].append(transaction) - - # Рассчитываем значения - for grouped_source_data_key in grouped_source_data.keys(): - if grouped_source_data_key in group_saved_data.keys(): - # Данные есть и в расчетных оборотах и в сохраненных. Добавляем оборот. - grouped_source_data[ grouped_source_data_key ].value += group_saved_data[grouped_source_data_key ] - - for group_saved_data_key in group_saved_data.keys(): - if group_saved_data_key not in grouped_source_data.keys(): - # В сохраненных данных есть оборот которого нет в расчетных оборотах - grouped_source_data[ group_saved_data_key ] = group_saved_data[group_saved_data_key ] - - # Формируем результат - for key, value in grouped_source_data.items(): - result.append( value[0] ) - - return result - - \ No newline at end of file diff --git a/Result9/Src/Logics/Processings/debit_processing.py b/Result9/Src/Logics/Processings/debit_processing.py deleted file mode 100644 index fc31a43..0000000 --- a/Result9/Src/Logics/Processings/debit_processing.py +++ /dev/null @@ -1,30 +0,0 @@ -from Src.Logics.Processings.processing import processing -from Src.Models.receipe_row_model import receipe_row_model - -from datetime import datetime -from Src.Models.storage_model import storage_model - -# -# Сформировать набор проводок для списание по рецепту -# Код взят https://github.com/Illikan/popov_design_patterns/blob/aee55cd86f5414d72bdb039b95b4b68771858a3d/Src/Logics/transaction_processing.py#L17 -# -class debit_processing(processing): - - def process(self, source: list) -> list: - """ - Сформировать проводки списания - Args: - transactions (list): Список объектов типа receipe_row_model - - Returns: - list: _description_ - """ - super().process(source) - result = [] - storage_default = storage_model.create_default() - - for row in source: - debit_transaction = receipe_row_model.create_debit_transaction( row, datetime.now(), storage_default ) - result.append( debit_transaction ) - - return result \ No newline at end of file diff --git a/Result9/Src/Logics/Processings/processing.py b/Result9/Src/Logics/Processings/processing.py deleted file mode 100644 index 8a10851..0000000 --- a/Result9/Src/Logics/Processings/processing.py +++ /dev/null @@ -1,25 +0,0 @@ -import abc -from Src.errors import error_proxy -from Src.exceptions import argument_exception - -# -# Абстрактный класс для наследования. -# Используется для реализации различных процессов обработки данных по складским транзакциям -# -class processing(error_proxy): - - @abc.abstractmethod - def process(self, transactions: list) -> list: - """ - Выполнить процесс обработки списка транзакций - Args: - source (_type_): Любой тип данных - """ - - if transactions == None: - raise argument_exception("Некорректно передан параметр!") - - if len(transactions) == 0: - raise argument_exception("Некорректно передан параметр!") - - self.clear() \ No newline at end of file diff --git a/Result9/Src/Logics/Processings/turn_processing.py b/Result9/Src/Logics/Processings/turn_processing.py deleted file mode 100644 index f53e73d..0000000 --- a/Result9/Src/Logics/Processings/turn_processing.py +++ /dev/null @@ -1,47 +0,0 @@ -from Src.Logics.Processings.processing import processing -from Src.Models.storage_row_turn_model import storage_row_turn_model - - -# -# Процесс получения оборотов по списку транзакций -# -class turn_processing(processing): - - def process(self, source: list) -> list: - """ - Сформировать складские обороты - Args: - transactions (list): Список объектов типа storage_row_model - - Returns: - list: _description_ - """ - super().process(source) - result = [] - - # Код взят https://github.com/AItEKS/Design-patterns/pull/9/files#diff-401a63dd40e843c86f4a13a76fc390dac87de5ab5e65afc30cd5bcede4893f94 - - grouped_transactions = {} - for transaction in source: - key = f"{transaction.nomenclature.id}_{transaction.storage.id}_{transaction.unit.id}" - if key not in grouped_transactions.keys(): - grouped_transactions[key] = [] - - grouped_transactions[key].append(transaction) - - for key, transactions in grouped_transactions.items(): - first_transaction = transactions[0] - - # Расчитываем оборот - turnover = sum(transaction.value if transaction.storage_type else -transaction.value for transaction in transactions) - - # Создаем модель - row = storage_row_turn_model.create( first_transaction.nomenclature, first_transaction.storage, first_transaction.unit) - row.value = turnover - - # Добавляем в список - result.append(row) - - return result - - \ No newline at end of file diff --git a/Result9/Src/Logics/Services/post_processing_service.py b/Result9/Src/Logics/Services/post_processing_service.py deleted file mode 100644 index 2dde182..0000000 --- a/Result9/Src/Logics/Services/post_processing_service.py +++ /dev/null @@ -1,28 +0,0 @@ -from Src.Logics.Services.service import service -from Src.Logics.Services.storage_observer import storage_observer -from Src.Storage.storage import storage -from Src.Logics.Services.storage_observer import event_type -from Src.Models.nomenclature_model import nomenclature_model - - -class post_processing_service(service): - - def __init__(self, data: list, nomenclature) -> None: - - super().__init__(data) - - self.nomenclature = nomenclature - # storage_observer.observers.append(self) - - - - def handle_event(self, handle_type: str): - if handle_type == event_type.nomenclature_deleted(): - self.nomenclature_deleted() - - - def nomenclature_deleted(self, nomenclature: nomenclature_model): - for recipe in self.data: - if self.nomenclature.name in recipe._rows: - del recipe._rows[self.nomenclature.name] - recipe.__calc_brutto() diff --git a/Result9/Src/Logics/Services/reference_service.py b/Result9/Src/Logics/Services/reference_service.py deleted file mode 100644 index 4fba18f..0000000 --- a/Result9/Src/Logics/Services/reference_service.py +++ /dev/null @@ -1,81 +0,0 @@ -from Src.Logics.Services.service import service -from Src.exceptions import exception_proxy, operation_exception -from Src.reference import reference -from Src.Logics.Services.storage_observer import storage_observer -from Src.Models.event_type import event_type -from Src.Logics.Services.post_processing_service import post_processing_service -from Src.Models.event_type import event_type - -# -# Сервис для выполнения CRUD операций -# -class reference_service(service): - - - def add(self, item: reference) -> bool: - """ - Добавить новый элемент - """ - exception_proxy.validate(item, reference) - found = list(filter(lambda x: x.id == item.id , self.data)) - if len(found) > 0: - return False - - self.data.append(item) - return True - - - def delete(self, item: reference) -> bool: - """ - Удалить элемент - """ - exception_proxy.validate(item, reference) - - found = list(filter(lambda x: x.id == item.id , self.data)) - if len(found) == 0: - return False - - item_to_delete = found[0] # Сохраняем объект, который мы собираемся удалить - - self.data.remove(item_to_delete) - storage_observer.raise_event(event_type.nomenclature_deleted(item_to_delete)) - - return True - - - def change(self, item:reference) -> bool: - """ - Изменить элемент - """ - exception_proxy.validate(item, reference) - found = list(filter(lambda x: x.id == item.id , self.data)) - if len(found) == 0: - return False - - self.delete(found[0]) - self.add(item) - return True - - def get(self) -> list: - """ - Вернуть список - """ - return self.data - - def get_item(self, id: str) -> reference: - """ - Вернуть элемент - """ - exception_proxy.validate(id, str) - found = list(filter(lambda x: x.id == id , self.data)) - if len(found) == 0: - raise operation_exception(f"Не найден элемент с кодом {id}!") - - return found - - - def handle_event( self, event_type: str ): - """ Обработать события""" - - super().handle_event(event_type) - diff --git a/Result9/Src/Logics/Services/service.py b/Result9/Src/Logics/Services/service.py deleted file mode 100644 index 980bf01..0000000 --- a/Result9/Src/Logics/Services/service.py +++ /dev/null @@ -1,84 +0,0 @@ -from Src.exceptions import argument_exception, exception_proxy -from Src.Logics.convert_factory import convert_factory -from Src.settings import settings -from Src.settings_manager import settings_manager -from Src.Models.event_type import event_type -from abc import ABC - -from abc import ABC, abstractmethod -import json - -# -# Общий класс для наследования. Реализация сервисов. -# -class service(ABC): - # Набор данных для работы - __data = [] - # Текущие настройки - __settings: settings = None - - def __init__(self, data: list) -> None: - if len(data) == 0: - raise argument_exception("Некорректно переданы параметры!") - - self.__data = data - options = settings_manager() - self.__settings = options.settings - - @property - def data(self): - """ - Текущие данные - Returns: - _type_: _description_ - """ - return self.__data - - @property - def settings(self) -> settings: - """ - Текущие настройки - Returns: - settings: _description_ - """ - return self.__settings - - @abstractmethod - def handle_event(self, event_type: str): - """ Обработать события""" - - exception_proxy.validate(event_type, str) - - # Общие методы для формирования ответа для Web - - - def create_response(self, app, data:list = None): - """ - Сформировать структуру ответа для Web сервера - """ - inner_data = self.__data if data is None else data - return service.create_response(app, inner_data) - - @staticmethod - def create_response( app, data:list): - """ - Сформировать структуру ответа для Web сервера - """ - if app is None: - raise argument_exception("Некорректно переданы параметры!") - - exception_proxy.validate(data, list) - - # Преоброзование - dest_data = convert_factory().serialize( data ) - json_text = json.dumps( dest_data, sort_keys = True, indent = 4, ensure_ascii = False) - - # Подготовить ответ - result = app.response_class( - response = f"{json_text}", - status = 200, - mimetype = "application/json; charset=utf-8" - ) - - return result - diff --git a/Result9/Src/Logics/Services/storage_observer.py b/Result9/Src/Logics/Services/storage_observer.py deleted file mode 100644 index 2745e49..0000000 --- a/Result9/Src/Logics/Services/storage_observer.py +++ /dev/null @@ -1,19 +0,0 @@ -from Src.Models.event_type import event_type -from Src.exceptions import exception_proxy - -# -# Наблюдатель для складских операций -# -class storage_observer: - _observers = [] - - - def raise_event(handle_event: str, reference: None): - """ Сформировать события """ - - exception_proxy.validate( handle_event, str ) - for object in storage_observer._observers: - - if object is not None: - object.handle_event(handle_event) - diff --git a/Result9/Src/Logics/Services/storage_service.py b/Result9/Src/Logics/Services/storage_service.py deleted file mode 100644 index 9196112..0000000 --- a/Result9/Src/Logics/Services/storage_service.py +++ /dev/null @@ -1,212 +0,0 @@ -from Src.Logics.process_factory import process_factory -from Src.Logics.storage_prototype import storage_prototype -from Src.exceptions import argument_exception, exception_proxy, operation_exception -from Src.Models.nomenclature_model import nomenclature_model -from Src.Models.receipe_model import receipe_model -from Src.Storage.storage import storage -from Src.Logics.Services.service import service -from Src.Models.event_type import event_type -from Src.Logics.Services.storage_observer import storage_observer -from Src.Models.event_value import event_value - -from datetime import datetime - -# -# Сервис для работы со складскими операциями -# -class storage_service(service): - - def __init__(self, data: list) -> None: - super().__init__(data) - - - def __build_turns(self, data: list) -> list: - """ - Сформировать обороты - Args: - data (list): _description_ - - Returns: - list: _description_ - """ - if len(data) == 0: - raise argument_exception("Некорректно переданы параметры!") - - # Подобрать процессинг - key_turn = process_factory.turn_key() - processing = process_factory().create( key_turn ) - - # Обороты - turns = processing().process( data ) - return turns - - def __build_blocked_turns(self): - """ - Сформировать и сохранить обороты в закрытом периоде - """ - start_period = datetime.strptime("1900-01-01", "%Y-%m-%d") - stop_period = self.settings.block_period - - # Фильтруем по периоду - prototype = storage_prototype( self.data ) - filter = prototype.filter_by_period( start_period, stop_period) - - # Расчитываем - calculated_turns = self.__build_turns(filter.data) - - # Сохраняем данные - storage.save_blocked_turns(calculated_turns) - - - - # Набор основных методов - - def create_turns(self, start_period: datetime, stop_period:datetime ) -> list: - """ - Получить обороты за период - Args: - start_period (datetime): Начало - stop_period (datetime): Окончание - - Returns: - list: обороты за период - """ - exception_proxy.validate(start_period, datetime) - exception_proxy.validate(stop_period, datetime) - - if start_period > stop_period: - raise argument_exception("Некорректно переданы параметры!") - - block_period = self.settings.block_period - - # Фильтруем - prototype = storage_prototype( self.data ) - filter = prototype.filter_by_period( block_period, stop_period) - - # Рассчитанные обороты - calculated_turns = self.__build_turns( filter. data ) - - # Сформируем результат - aggregate_key = process_factory.aggregate_key() - processing = process_factory().create( aggregate_key ) - return processing().process( calculated_turns ) - - - def create_turns_by_nomenclature(self, start_period: datetime, stop_period: datetime, nomenclature: nomenclature_model) -> list: - """ - Получить обороты за период по конкретной номенклатуры - Args: - start_period (datetime): Начало - stop_period (datetime): Окончание - nomenclature (nomenclature_model): Номенклатуры - - Returns: - list: Обороты - """ - exception_proxy.validate(start_period, datetime) - exception_proxy.validate(stop_period, datetime) - exception_proxy.validate(nomenclature, nomenclature_model) - - if start_period > stop_period: - raise argument_exception("Некорректно переданы параметры!") - - block_period = self.settings.block_period - - # Фильтруем - prototype = storage_prototype( self.data ) - filter = prototype.filter_by_period( block_period, stop_period) - filter = filter.filter_by_nomenclature( nomenclature ) - if not filter.is_empty: - raise operation_exception(f"Невозможно сформировать обороты по указанным данных: {filter.error}") - - # Рассчитанные обороты - calculated_turns = self.__build_turns( filter. data ) - - # Сформируем результат - aggregate_key = process_factory.aggregate_key() - processing = process_factory().create( aggregate_key ) - return processing().process( calculated_turns ) - - def create_turns_only_nomenclature(self, nomenclature: nomenclature_model) -> list: - """ - Получить обороты по номенклатуре - Args: - nomenclature (nomenclature_model): _description_ - - Returns: - list: Обороты - """ - exception_proxy.validate(nomenclature, nomenclature_model) - prototype = storage_prototype( self.data ) - filter = prototype.filter_by_nomenclature( nomenclature ) - if not filter.is_empty: - raise operation_exception(f"Невозможно сформировать обороты по указанным данных: {filter.error}") - - return self.__build_turns( filter. data ) - - def create_turns_by_receipt(self, receipt: receipe_model) -> list: - """ - Сформировать обороты по указанному рецепту - Args: - receipt (receipe_model): _description_ - - Returns: - list: _description_ - """ - exception_proxy.validate(receipt, receipe_model) - - if len(receipt.consist) == 0: - raise operation_exception("Переданный рецепт некорректный. Не содержит в себе список номенклатуры!") - - # Отфильтровать по рецепту - transactions = [] - filter = storage_prototype( self.data ) - for item in receipt.rows(): - filter = filter.filter_by_nomenclature( item.nomenclature ) - if filter.is_empty: - for transaction in filter.data: - transactions.append( transaction ) - - filter.data = self.data - - return self.__build_turns( transactions ) - - def build_debits_by_receipt(self, receipt: receipe_model) -> list: - """ - Сформировать проводки списания по рецепту - Args: - receipt (receipe_model): _description_ - - Returns: - list: _description_ - """ - exception_proxy.validate(receipt, receipe_model) - - if len(receipt.consist) == 0: - raise operation_exception("Переданный рецепт некорректный. Не содержит в себе список номенклатуры!") - - turns = self.create_turns_by_receipt(receipt) - if len(turns) <= 0: - raise operation_exception("По указанному рецепту не найдеты обороты!") - - if len(receipt.rows()) > len(turns): - raise operation_exception("Невозможно сформировать список транзакций для списания т.к. нет достаточно остатков!") - - # Формируем список проводок на списание - processing = process_factory().create( process_factory.debit_key() ) - transactions = processing().process( receipt.rows() ) - key = storage.storage_transaction_key() - - data = storage().data[ key ] - for transaction in transactions: - data.append ( transaction ) - - def handle_event(self, handle_type: event_value, reference: None): # Доработать, добавить поле - """ Обработать события""" - - super().handle_event(handle_type) - - if handle_type == event_type.changed_block_period: - self.__build_blocked_turns() - - \ No newline at end of file diff --git a/Result9/Src/Logics/basic_convertor.py b/Result9/Src/Logics/basic_convertor.py deleted file mode 100644 index 658c2d5..0000000 --- a/Result9/Src/Logics/basic_convertor.py +++ /dev/null @@ -1,27 +0,0 @@ -from Src.Logics.convertor import convertor - -# -# Конвертор простых значений в словарь -# -class basic_convertor(convertor): - - def serialize(self, field: str, object) -> dict: - """ - Подготовить словарь - Args: - field (str): поле - object (_type_): значение - """ - super().serialize( field, object) - - if not isinstance(object, (int, str, bool, float)): - self.error = f"Некорректный тип данных передан для конвертации. Ожидается: (int, str, bool). Передан: {type(object)}" - return None - - try: - return { field: object } - except Exception as ex: - self.set_error(ex) - - return None - \ No newline at end of file diff --git a/Result9/Src/Logics/convert_factory.py b/Result9/Src/Logics/convert_factory.py deleted file mode 100644 index 70b3a92..0000000 --- a/Result9/Src/Logics/convert_factory.py +++ /dev/null @@ -1,151 +0,0 @@ -from Src.Logics.basic_convertor import basic_convertor -from Src.Logics.datetime_convertor import datetime_convertor -from Src.exceptions import exception_proxy, operation_exception, argument_exception -from Src.reference import reference -from Src.Logics.convertor import convertor - - -import datetime - -# -# Конвертор reference в словарь -# -class reference_convertor(convertor): - - def serialize(self, field: str, object: reference) -> dict: - """ - Подготовить словарь - Args: - field (str): поле - object (_type_): значение - """ - super().serialize(field, object) - - factory = convert_factory() - return factory.serialize(object) - - -# -# Фабрика для конвертация данных -# -class convert_factory: - _maps = {} - - def __init__(self) -> None: - # Связка с простыми типами - self._maps[datetime.datetime] = datetime_convertor - self._maps[int] = basic_convertor - self._maps[float] = basic_convertor - self._maps[str] = basic_convertor - self._maps[bool] = basic_convertor - - # Связка для всех моделей - for inheritor in reference.__subclasses__(): - self._maps[inheritor] = reference_convertor - - def serialize(self, object) -> dict: - """ - Подготовить словарь - Args: - object (_type_): произвольный тип - - Returns: - dict: словарь - """ - - # Сконвертируем данные как список - result = self.__serialize_list("data", object) - if result is not None: - return result - - # Сконвертируем данные как значение - result = {} - fields = reference.create_fields(object) - - for field in fields: - attribute = getattr(object.__class__, field) - if isinstance(attribute, property): - value = getattr(object, field) - - # Сконвертируем данные как список - dictionary = self.__serialize_list(field, value) - if dictionary is None: - # Сконвертируем данные как значение - dictionary = self.__serialize_item(field, value) - - try: - if len(dictionary) == 1: - # Обычное поле - result[field] = dictionary[field] - else: - # Вложенный словарь - result[field] = dictionary - except: - raise operation_exception(f"Невозможно сериализовать объект в набор словарей. Поле {field}, значение: {dictionary}") - - return result - - - # Сериализация - - def __serialize_item(self, field: str, source): - """ - Сконвертировать элемент - Args: - field (str): Наименование поля - source (_type_): Значение - - Returns: - dict: _description_ - """ - exception_proxy.validate(field, str) - if source is None: - return {field: None} - - if isinstance(source, (list, dict)): - return self.__serialize_list(field, source) - - if type(source) not in self._maps.keys(): - raise operation_exception(f"Не возможно подобрать конвертор для типа {type(source)}") - - # Определим конвертор - convertor = self._maps[ type(source)]() - dictionary = convertor.serialize( field, source ) - - if not convertor.is_empty: - raise operation_exception(f"Ошибка при конвертации данных {convertor.error}") - - return dictionary - - def __serialize_list(self, field: str, source) -> list: - """ - Сконвертировать список - Args: - source (_type_): _description_ - - Returns: - dict: _description_ - """ - exception_proxy.validate(field, str) - - # Сконвертировать список - if isinstance(source, list): - result = [] - for item in source: - result.append( self.__serialize_item( field, item )) - - return result - - # Сконвертировать словарь - if isinstance(source, dict): - result = {} - for item in source.items(): - key = item[0] - object = item[1] - - value = self.__serialize_item( key, object ) - result[key] = value - - return result - - \ No newline at end of file diff --git a/Result9/Src/Logics/convertor.py b/Result9/Src/Logics/convertor.py deleted file mode 100644 index 7b911d6..0000000 --- a/Result9/Src/Logics/convertor.py +++ /dev/null @@ -1,27 +0,0 @@ -import abc -from Src.errors import error_proxy -from Src.exceptions import exception_proxy, argument_exception - -# -# Абстрактный класс для наследования. -# Используется для сериализации и десериализации -# -class convertor(error_proxy): - - @abc.abstractmethod - def serialize(self, field: str, object) -> dict: - """ - Сериализовать объект в словарь - Args: - source (_type_): Любой тип данных - """ - exception_proxy.validate(field, str) - self.clear() - - - - - - - - diff --git a/Result9/Src/Logics/csv_reporting.py b/Result9/Src/Logics/csv_reporting.py deleted file mode 100644 index fb102e0..0000000 --- a/Result9/Src/Logics/csv_reporting.py +++ /dev/null @@ -1,43 +0,0 @@ -from Src.Logics.reporting import reporting -from Src.exceptions import operation_exception - - -# -# Класс - реализация построение данных в формате csv -# -class csv_reporting(reporting): - - def create(self, storage_key: str): - super().create(storage_key) - result = "" - delimetr = ";" - - # Исходные данные - items = self.data[ storage_key ] - if items == None: - raise operation_exception("Невозможно сформировать данные. Данные не заполнены!") - - if len(items) == 0: - raise operation_exception("Невозможно сформировать данные. Нет данных!") - - # Заголовок - header = delimetr.join(self.fields) - result += f"{header}\n" - - # Данные - for item in items: - row = "" - for field in self.fields: - attribute = getattr(item.__class__, field) - if isinstance(attribute, property): - value = getattr(item, field) - if isinstance(value, (list, dict)) or value is None: - value = "" - - row +=f"{value}{delimetr}" - - result += f"{row[:-1]}\n" - - - # Результат csv - return result \ No newline at end of file diff --git a/Result9/Src/Logics/datetime_convertor.py b/Result9/Src/Logics/datetime_convertor.py deleted file mode 100644 index ee38178..0000000 --- a/Result9/Src/Logics/datetime_convertor.py +++ /dev/null @@ -1,27 +0,0 @@ -from Src.Logics.convertor import convertor -from datetime import datetime - - -# -# Конвертор datetime в словарь -# -class datetime_convertor(convertor): - - def serialize(self, field: str, object): - """ - Сериализовать в словарь - Args: - field (str): поле - object (_type_): значение - """ - super().serialize( field, object) - - if not isinstance(object, datetime): - self._error.error = f"Некорректный тип данных передан для конвертации. Ожидается: datetime. Передан: {type(object)}" - return None - - try: - return { field: object.strftime('%Y-%m-%d') } - except Exception as ex: - self.set_error(ex) - \ No newline at end of file diff --git a/Result9/Src/Logics/json_reporting.py b/Result9/Src/Logics/json_reporting.py deleted file mode 100644 index f8e9c49..0000000 --- a/Result9/Src/Logics/json_reporting.py +++ /dev/null @@ -1,38 +0,0 @@ -from Src.Logics.reporting import reporting -from Src.exceptions import operation_exception -from Src.Logics.convert_factory import convert_factory - -import json - -# -# Формирование отчета в формате json -# -class json_reporting(reporting): - - def create(self, storage_key: str): - super().create(storage_key) - - # Исходные данные - items = self.data[ storage_key ] - if items == None: - raise operation_exception("Невозможно сформировать данные. Данные не заполнены!") - - if len(items) == 0: - raise operation_exception("Невозможно сформировать данные. Нет данных!") - - # Сериализуем данные - factory = convert_factory() - data = factory.serialize( items ) - - # Формируем Json - result = json.dumps(data, sort_keys = True, indent = 4, ensure_ascii = False) - return result - - def mimetype(self) -> str: - return "application/json; charset=utf-8" - - - - - - \ No newline at end of file diff --git a/Result9/Src/Logics/markdown_reporting.py b/Result9/Src/Logics/markdown_reporting.py deleted file mode 100644 index 7a0aebe..0000000 --- a/Result9/Src/Logics/markdown_reporting.py +++ /dev/null @@ -1,49 +0,0 @@ -from Src.Logics.reporting import reporting -from Src.exceptions import operation_exception - -class markdown_reporting(reporting): - - def create(self, storage_key: str): - super().create(storage_key) - result = [] - - # Исходные данные - items = self.data[ storage_key ] - if items == None: - raise operation_exception("Невозможно сформировать данные. Данные не заполнены!") - - - if len(items) == 0: - raise operation_exception("Невозможно сформировать данные. Нет данных!") - - # Заголовок - result.append(f"# {storage_key}") - - # Шапка таблицы - header = "" - line = "" - for field in self.fields: - header += f"|{field}" - line += "|--" - - result.append(f"{header}|") - result.append(f"{line}|") - - # Данные - for item in items: - row = "" - for field in self.fields: - attribute = getattr(item.__class__, field) - if isinstance(attribute, property): - value = getattr(item, field) - if isinstance(value, (list, dict)) or value is None: - value = "" - - row +=f"|{value}" - - result.append(f"{row}|") - - return "\n".join(result) - - - \ No newline at end of file diff --git a/Result9/Src/Logics/process_factory.py b/Result9/Src/Logics/process_factory.py deleted file mode 100644 index 21d6fbc..0000000 --- a/Result9/Src/Logics/process_factory.py +++ /dev/null @@ -1,86 +0,0 @@ -from Src.Logics.Processings.processing import processing -from Src.Logics.Processings.turn_processing import turn_processing -from Src.exceptions import exception_proxy, argument_exception, operation_exception -from Src.Logics.Processings.debit_processing import debit_processing -from Src.Logics.Processings.aggregate_processing import aggregate_processing -# -# Фабрика процессов обработки складских транзакций -# -class process_factory: - __maps = {} - - def __init__(self) -> None: - self.__build_structure() - - def __build_structure(self): - """ - Сформировать структуру - """ - self.__maps[ process_factory.turn_key()] = turn_processing - self.__maps[ process_factory.debit_key()] = debit_processing - self.__maps[ process_factory.aggregate_key()] = aggregate_processing - - - def create(self, process_key:str) -> processing: - """ - Подобрать нужный процессинг - Args: - process_key (str): Ключ - data (list[storage_row_model]): Исходные данные - Returns: - processing: нужный процессинг - """ - exception_proxy.validate(process_key , str) - if process_key not in self.__maps.keys(): - raise argument_exception(f"Указанный процесс {process_key} не реализован!") - - current_processing = self.__maps[process_key] - if current_processing is None: - raise operation_exception("Некорректно сконфигурирована текущая фабрика!") - - return current_processing - - # Статические методы - - @staticmethod - def aggregate_key() -> str: - """ - Сформировать агрегацию оборотов - Returns: - str: _description_ - """ - return "aggregate" - - @staticmethod - def turn_key() -> str: - """ - Сформировать обороты - Returns: - str: _description_ - """ - return "turns" - - def debit_key() -> str: - """ - Сформировать проводки списания - Returns: - str: _description_ - """ - return "debits" - - - # Код взят: https://github.com/UpTechCompany/GitExample/blob/6665bc70c4933da12f07c0a0d7a4fc638c157c40/storage/storage.py#L30 - - @staticmethod - def process_keys(cls): - """ - Получить список ключей - Returns: - _type_: _description_ - """ - keys = [] - methods = [getattr(cls, method) for method in dir(cls) if callable(getattr(cls, method))] - for method in methods: - if method.__name__.endswith("_key") and callable(method): - keys.append(method()) - return keys \ No newline at end of file diff --git a/Result9/Src/Logics/report_factory.py b/Result9/Src/Logics/report_factory.py deleted file mode 100644 index e0e2914..0000000 --- a/Result9/Src/Logics/report_factory.py +++ /dev/null @@ -1,91 +0,0 @@ -from Src.Logics.reporting import reporting -from Src.Logics .markdown_reporting import markdown_reporting -from Src.Logics.csv_reporting import csv_reporting -from Src.Logics.json_reporting import json_reporting -from Src.exceptions import exception_proxy, argument_exception, operation_exception - -# -# Фабрика для отчетов -# -class report_factory: - __maps = {} - - # Формат данных для экспорт в Web сервер - __mimetype: str - - def __init__(self) -> None: - self.__build_structure() - - def __build_structure(self): - """ - Сформировать структуру - """ - self.__maps["csv"] = csv_reporting - self.__maps["markdown"] = markdown_reporting - self.__maps["json"] = json_reporting - - @property - def mimetype(self): - """ - Формат данных для экспорт в Web сервер - Returns: - _type_: _description_ - """ - return self.__mimetype - - def create(self, format: str, data:dict) -> reporting: - """ - Сформировать объект для построения отчетности - Args: - format (str): Тип формта - data (_type_): Словарь с данными - - Returns: - reporting: _description_ - """ - exception_proxy.validate(format, str) - exception_proxy.validate(data, dict) - - if len(data) == 0: - raise argument_exception("Пустые данные") - - if format not in self.__maps.keys(): - raise operation_exception(f"Для {format} нет обработчика") - - # Получаем тип связанный с форматом - report_type = self.__maps[format] - # Получаем объект - result = report_type(data) - self.__mimetype = result.mimetype() - - return result - - def create_response(self, format: str, data:dict, storage_key: str, app): - """ - Сформировать отчет и вывести его в формате response_class для Web сервера - Args: - format (str): тип формата: csv, markdown, json - data (dict): исходные данные - storage_key (str): ключ для отбора данных в storage - app (_type_): Flask приложение - Returns: - response_class: _description_ - """ - if app is None: - raise argument_exception("Некорректно переданы параметры!") - exception_proxy.validate(storage_key, str) - - # Получаем нужный отчет - report = self.create(format, data) - # Формируем данные - info = report.create(storage_key) - - # Подготовить ответ - result = app.response_class( - response = f"{info}", - status = 200, - mimetype = self.mimetype - ) - - return result - \ No newline at end of file diff --git a/Result9/Src/Logics/reporting.py b/Result9/Src/Logics/reporting.py deleted file mode 100644 index 2089f7f..0000000 --- a/Result9/Src/Logics/reporting.py +++ /dev/null @@ -1,96 +0,0 @@ -import abc -from Src.settings import settings -from Src.exceptions import exception_proxy, operation_exception -from Src.reference import reference - - -# -# Абстрактный класс для реализации отчетности -# -class reporting(abc.ABC): - # Набор данных - __data = {} - # Список полей - __fields = [] - - - def __init__(self, _data): - """ - - Args: - _data (_type_): Словарь с данными - """ - - exception_proxy.validate(_data, dict) - self.__data = _data - - - @abc.abstractmethod - def create(self, storage_key: str): - """ - Сформировать отчет - Args: - storage_key (str): Ключ для отбора данных - """ - exception_proxy.validate(storage_key, str) - self.__fields = self.build(storage_key, self.__data) - - return "" - - def mimetype(self) -> str: - """ - Тип данных для формирования ответа Web сервера - Returns: - str: _description_ - """ - return "application/text" - - @staticmethod - def build( storage_key: str, data: dict) -> list: - """ - Предобработка. Получить набор полей - Args: - storage_key (str): ключ в словаре_ - data (dict): Данные - словарь - - Returns: - list: список - """ - - exception_proxy.validate(storage_key, str) - if data is None: - raise operation_exception("Набор данных не определен!") - - if len(data) == 0: - raise operation_exception("Набор данных пуст!") - - item = data[storage_key][0] - result = reference.create_fields( item ) - return result - - def _build(self, storage_key: str) -> list: - """ - Предобработка данных. Возвращает набор полей класса typeKey - Args: - storage_key (str): ключ для выборки данных - Returns: - list: список - """ - return reporting.build(storage_key, self.__data) - - - @property - def fields(self) -> list: - """ - Набор полей от исходного объекта на основании которого формируем отчет - """ - return self.__fields - - @property - def data(self) -> dict: - """ - - Returns: - dict: словарь с данными - """ - return self.__data \ No newline at end of file diff --git a/Result9/Src/Logics/start_factory.py b/Result9/Src/Logics/start_factory.py deleted file mode 100644 index c3546f3..0000000 --- a/Result9/Src/Logics/start_factory.py +++ /dev/null @@ -1,288 +0,0 @@ -# Модели -from Src.Models.group_model import group_model -from Src.Models.unit_model import unit_model -from Src.Models.nomenclature_model import nomenclature_model -from Src.reference import reference -from Src.Models.receipe_model import receipe_model -from Src.Models.storage_row_model import storage_row_model -from Src.Models.storage_model import storage_model - -# Системное -from Src.settings import settings -from Src.Storage.storage import storage -from Src.exceptions import exception_proxy, operation_exception, argument_exception - -# -# Класс для обработки данных. Начало работы приложения -# -class start_factory: - __oprions: settings = None - __storage: storage = None - - def __init__(self, _options: settings, - _storage: storage = None) -> None: - - exception_proxy.validate(_options, settings) - self.__oprions = _options - self.__storage = _storage - - - def __save(self, key:str, items: list): - """ - Сохранить данные - Args: - key (str): ключ доступ - items (list): список - """ - exception_proxy.validate(key, str) - - if self.__storage == None: - self.__storage = storage() - - self.__storage.data[ key ] = items - - @property - def storage(self): - """ - Ссылка на объект хранилище данных - Returns: - _type_: _description_ - """ - return self.__storage - - # Статические методы - - @staticmethod - def create_units() -> list: - """ - Сформировать список единиц измерения - Returns: - _type_: _description_ - """ - items = [] - items.append( unit_model.create_gram() ) - items.append( unit_model.create_killogram() ) - items.append( unit_model.create_liter() ) - items.append( unit_model.create_milliliter() ) - items.append( unit_model.create_ting() ) - - return items - - @staticmethod - def create_nomenclatures() -> list: - """ - Сформировать список номенклатуры - """ - - group = group_model.create_default_group() - items = [ {"Мука пшеничная": "киллограмм"}, - {"Сахар":"киллограмм"}, - {"Сливочное масло" : "киллограмм"}, - {"Яйца": "штука"}, - {"Ванилин": "грамм"}, - {"Куринное филе": "киллограмм"}, - {"Салат Романо": "грамм"}, - {"Сыр Пармезан" : "киллограмм"}, - {"Чеснок": "киллограмм"}, - {"Белый хлеб": "киллограмм"}, - {"Соль": "киллограмм"}, {"Черный перец": "грамм"}, - {"Оливковое масло": "литр"}, - {"Лимонный сок": "литр"}, - {"Горчица дижонская": "грамм"}, - {"Сахарная пудра": "грамм"}, - {"Ванилиин": "грамм"}, - {"Корица": "грамм"}, - {"Какао": "киллограмм"}] - - # Подготовим словарь со список единиц измерения - units = reference.create_dictionary(start_factory.create_units()) - - result = [] - for position in items: - # Получаем список кортежей и берем первое значение - _list = list(position.items()) - if len(_list) < 1: - raise operation_exception("Невозможно сформировать элементы номенклатуры! Некорректный список исходных элементов!") - - tuple = list(_list)[0] - - # Получаем неименование номенклатуры и единицы измерения - if len(tuple) < 2: - raise operation_exception("Невозможно сформировать элемент номенклатуры. Длина кортежа не корректна!") - - name = tuple[0] - unit_name = tuple[1] - - if not unit_name in units.keys(): - raise operation_exception(f"Невозможно найти в списке указанную единицу измерения {unit_name}!") - - # Создаем объект - номенклатура - item = nomenclature_model( name, group, units[unit_name]) - result.append(item) - - return result - - @staticmethod - def create_groups() -> list: - """ - Сформировать список групп номенклатуры - Returns: - _type_: _description_ - """ - items = [] - items.append( group_model.create_default_group()) - return items - - @staticmethod - def create_receipts(_data: list = None) -> list: - """ - Сформировать список рецептов - Args: - _data (list, optional): Список номенклатуры. Defaults to None. - - Raises: - argument_exception: _description_ - - Returns: - _type_: Массив объектов receipe_model - """ - result = [] - if _data is None: - data = start_factory.create_nomenclatures() - else: - data = _data - - if len(data) == 0: - raise argument_exception("Некорректно переданы параметры! Список номенклатуры пуст.") - - # Вафли хрустящие в вафильнице - items = [ ("Мука пшеничная", 100), ("Сахар", 80), ("Сливочное масло", 70), - ("Яйца", 1) - ] - item = receipe_model.create_receipt("Вафли хрустящие в вафильнице", "", items, data) - - # Шаги приготовления - item.instructions.extend([ - "Масло положите в сотейник с толстым дном. Растопите его на маленьком огне на плите, на водяной бане либо в микроволновке.", - "Добавьте в теплое масло сахар. Перемешайте венчиком до полного растворения сахара. От тепла сахар довольно быстро растает.", - "Добавьте в масло яйцо. Предварительно все-таки проверьте масло, не горячее ли оно, иначе яйцо может свариться. Перемешайте яйцо с маслом до однородности.", - "Всыпьте муку, добавьте ванилин.", - "Перемешайте массу венчиком до состояния гладкого однородного теста."]) - - item.comments = "Время приготовления: 20 мин. 8 порций" - result.append( item ) - - # Цезарь с курицей - items = [ ("Куринное филе", 200), ("Салат Романо", 50), ("Сыр Пармезан", 50), - ("Чеснок", 10) , ("Белый хлеб", 30 ), ("Соль", 5), ("Черный перец", 2), - ("Оливковое масло", 10), ("Лимонный сок", 5), ("Горчица дижонская", 5), - ("Яйца", 2) - ] - item = receipe_model.create_receipt("Цезарь с курицей", "", items, data) - result.append(item) - - # Безе - items = [ ("Яйца", 3), ("Сахарная пудра", 180), ("Ванилиин" , 5), ("Корица", 5) ,("Какао", 20) ] - result.append( receipe_model.create_receipt("Безе", "", items, data)) - return result - - @staticmethod - def create_storage_transactions(data: dict) -> list: - """ - Сформировать список складских транзакций - Returns: - _type_: Массив объектов storage_row_model - """ - result = [] - default_storage = storage_model.create_default() - - if len(data.keys()) == 0: - raise operation_exception("Набор данных пуст. Невозможно сформировать список транзакций!") - - items = [ ( "Мука пшеничная", 1, "киллограмм") , - ( "Черный перец", 50, "грамм" ), - ( "Сахар" , 0.5, "киллограмм"), - ( "Яйца", 6,"штука" ), - ( "Оливковое масло", 0.2, "литр" ), - ( "Куринное филе" ,0.5, "киллограмм"), - ( "Салат Романо", 1, "штука" ), - ( "Белый хлеб" , 3, "штука" ), - ( "Сыр Пармезан", 0.2, "киллограмм") , - ( "Горчица дижонская" ,0.1, "литр" ), - ( "Черный перец", 10, "грамм" ), - ( "Лимонный сок", 1, "литр" ), - ( "Какао" ,1,"киллограмм"), - ( "Сливочное масло" ,0.5, "киллограмм" ), - ( "Сыр Пармезан", 0.3, "киллограмм") , - ( "Ванилиин", 100, "грамм" ), - ( "Соль", 1, "киллограмм" ) - ] - - for element in items: - if len(element) < 3: - raise operation_exception("Некорректно сформирован список для генерации рецептов!") - - nomenclature_name = element[0] - quantity = element[1] - unit_name = element[2] - - row = storage_row_model.create_credit_row(nomenclature_name, quantity, unit_name , data, default_storage) - result.append(row) - - return result - - - # Основной метод - def create(self) -> bool: - """ - В зависимости от настроек, сформировать или загрузить набор данных - Returns: - _type_: _description_ - """ - - if self.__storage == None: - self.__storage = storage() - - if self.__oprions.is_first_start == True: - - try: - # 1. Формируем и зпоминаем номеклатуру - nomenclatures = start_factory.create_nomenclatures() - self.__save( storage.nomenclature_key(), nomenclatures ) - - # 2. Формируем и запоминаем рецепты - items = start_factory.create_receipts(nomenclatures) - self.__save( storage.receipt_key(), items) - - # 3. Формируем и запоминаем единицы измерения - items = start_factory.create_units() - self.__save( storage.unit_key(), items) - - # 4. Формируем и запоминаем группы номенклатуры - items = start_factory.create_groups() - self.__save( storage.group_key(), items) - - # 5. Формируем типовые складские проводки - items = start_factory.create_storage_transactions( self.storage.data ) - self.__save( storage.storage_transaction_key(), items) - - except Exception as ex: - raise operation_exception(f"Ошибка при формировании шаблонных данных!\n{ex}") - - else: - # Другой вариант. Загрузка из источника данных - try: - self.__storage.load() - except Exception as ex: - raise operation_exception(f"Ошибка при формировании шаблонных данных!\n{ex}") - - - - - - - - - - - \ No newline at end of file diff --git a/Result9/Src/Logics/storage_prototype.py b/Result9/Src/Logics/storage_prototype.py deleted file mode 100644 index 35fe3f8..0000000 --- a/Result9/Src/Logics/storage_prototype.py +++ /dev/null @@ -1,98 +0,0 @@ -from Src.exceptions import argument_exception, exception_proxy -from Src.errors import error_proxy -from datetime import datetime -from Src.Models.nomenclature_model import nomenclature_model - -# -# Прототип для обработки складских транзакций -# -class storage_prototype(error_proxy): - __data = [] - - def __init__(self, data: list) -> None: - if len(data) <= 0: - self.error = "Набор данных пуст!" - - exception_proxy.validate(data, list) - self.__data = data - self.clear() - - # Методы фильтрации - - def filter_by_period( self,start_period: datetime, stop_period: datetime ): - """ - Отфильтровать по периоду - Args: - start_period (datetime): начало - stop_period (datetime): окончание - - Returns: - storage_prototype: _description_ - """ - self.clear() - - exception_proxy.validate(start_period, datetime) - exception_proxy.validate(stop_period, datetime) - if len(self.__data) <= 0: - self.error = "Некорректно переданы параметры!" - - if start_period > stop_period: - self.error = "Некорректный период!" - - - if not self.is_empty: - return self.__data - - result = [] - for item in self.__data: - if item.period > start_period and item.period <= stop_period: - result.append(item) - - return storage_prototype( result ) - - - def filter_by_nomenclature(self, nomenclature: nomenclature_model): - """ - Отфильтровать по номенклатуре - Args: - nomenclature (nomenclature_model): _description_ - - Returns: - storage_prototype: _description_ - """ - self.clear() - - exception_proxy.validate(nomenclature, nomenclature_model) - - result = [] - for item in self.__data: - if item.nomenclature.id == nomenclature.id: - result.append(item) - - return storage_prototype( result ) - - # Методы фильтрации - - @property - def data(self): - """ - Полученные данные - Returns: - _type_: _description_ - """ - return self.__data - - @data.setter - def data(self, value: list): - """ - Исходные данные - Args: - value (list): _description_ - """ - exception_proxy.validate(value, list) - self.__data = value - - - - - \ No newline at end of file diff --git a/Result9/Src/Models/event_type.py b/Result9/Src/Models/event_type.py deleted file mode 100644 index d8da4e8..0000000 --- a/Result9/Src/Models/event_type.py +++ /dev/null @@ -1,22 +0,0 @@ -from Src.reference import reference - - -""" Типы событий """ - -class event_type(reference): - - - @staticmethod - def changed_block_period() -> str: - - """ - Событие изменения даты блокировки - """ - return "changed_block_period" - - @staticmethod - def nomenclature_deleted() -> str: - """ - Событие удаления номенклатуры из рецептов - """ - return "nomenclature_deleted" \ No newline at end of file diff --git a/Result9/Src/Models/event_value.py b/Result9/Src/Models/event_value.py deleted file mode 100644 index 517da27..0000000 --- a/Result9/Src/Models/event_value.py +++ /dev/null @@ -1,10 +0,0 @@ -from Src.reference import reference - - -class event_value(reference): - - @property - def event_source(self) -> str: - pass - - \ No newline at end of file diff --git a/Result9/Src/Models/group_model.py b/Result9/Src/Models/group_model.py deleted file mode 100644 index 4f2e84b..0000000 --- a/Result9/Src/Models/group_model.py +++ /dev/null @@ -1,16 +0,0 @@ -from Src.reference import reference - -# -# Модель группу номенклатуры -# -class group_model(reference): - def create_default_group(): - """ - Фабричный метод. Создать группу по умолчанию - - Returns: - _type_: _description_ - """ - item = group_model("Ингредиенты") - return item - \ No newline at end of file diff --git a/Result9/Src/Models/nomenclature_model.py b/Result9/Src/Models/nomenclature_model.py deleted file mode 100644 index 4e5e115..0000000 --- a/Result9/Src/Models/nomenclature_model.py +++ /dev/null @@ -1,95 +0,0 @@ -from Src.reference import reference -from Src.exceptions import exception_proxy, operation_exception -from Src.Models.unit_model import unit_model -from Src.Models.group_model import group_model - - -class nomenclature_model(reference): - " Группа номенклатуры " - _group = None - " Единица измерения " - _unit = None - - - def __init__(self, name:str = None, group: reference = None, unit: reference = None): - """_summary_ - - Args: - name (str): Наименование - group (reference): Группа - unit (reference): Единица измерения - """ - - if not group is None: - exception_proxy.validate(group, reference) - self._group = group - - if not unit is None: - exception_proxy.validate(unit, reference) - self._unit = unit - - super().__init__(name) - - @property - def group(self) -> group_model: - " Группа номенклатуры " - return self._group - - @group.setter - def group(self, value: reference): - " Группа номенклатуры " - exception_proxy.validate(value, reference) - self._group = value - - @property - def unit(self) -> unit_model: - " Единица измерения " - return self._unit - - @unit.setter - def unit(self, value: reference): - " Единица измерения " - exception_proxy.validate(value, reference) - self._unit = value - - def load(self, source: dict): - """ - Загрузить данные - Args: - source (dict): исходный словарь - """ - super().load(source) - if source is None: - return None - - source_fields = ["unit", "group"] - if set(source_fields).issubset(list(source.keys())) == False: - raise operation_exception(f"Невозможно загрузить данные в объект {self}!") - - self._group = group_model().load(source["group"]) - self._unit = unit_model().load(source["unit"]) - - return self - - # Фабричные методы - - @staticmethod - def get(nomenclature_name: str, nomenclatures: dict): - """ - Получить значение элемента номенклатуры из словаря - Args: - nomenclature_name (str): наименование - nomenclatures (dict): исходный словарь storage.data - - Returns: - nomenclature_model: _description_ - """ - exception_proxy.validate(nomenclature_name, str) - - keys = list(filter(lambda x: x == nomenclature_name, nomenclatures.keys() )) - if len(keys) == 0: - raise operation_exception(f"Некоректно передан список. Не найдена номенклатура {nomenclature_name}!") - - return nomenclatures[keys[0]] - - \ No newline at end of file diff --git a/Result9/Src/Models/receipe_model.py b/Result9/Src/Models/receipe_model.py deleted file mode 100644 index 1fb50d8..0000000 --- a/Result9/Src/Models/receipe_model.py +++ /dev/null @@ -1,214 +0,0 @@ -from Src.reference import reference -from Src.Models.receipe_row_model import receipe_row_model -from Src.exceptions import exception_proxy , operation_exception, argument_exception -from Src.Models.nomenclature_model import nomenclature_model - -# -# Класс описание рецепта приготовления блюда -# -class receipe_model(reference): - # Вес брутто - _brutto: int = 0 - - # Вес нетто - _netto: int = 0 - - # Состав рецепта - _rows = {} - - # Инструкции - _instructions = list() - - # Описание - _comments: str = "" - - def __init__(self, name = None): - super().__init__(name) - self._rows = {} - self._instructions = [] - self._brutto = 0 - - def add(self, row: receipe_row_model): - """ - Добавить/ изменить состав блюда - Args: - row (receipe_row_model): _description_ - """ - exception_proxy.validate(row, receipe_row_model) - self._rows[row.name] = row - self.__calc_brutto() - - def delete(self, row: receipe_row_model): - """ - Удалить из состава блюда - Args: - row (receipe_row_model): _description_ - """ - exception_proxy.validate(row, receipe_row_model) - - if row.name in self._rows.keys(): - self._rows.pop(row.name) - - self.__calc_brutto() - - def __calc_brutto(self): - """ - Перерасчет брутто - """ - self._brutto = 0 - for position in self._rows: - # Получаем свойство size - self._brutto += self._rows[position].size - - @property - def brutto(self) -> int: - """ - Вес брутто - Returns: - int : _description_ - """ - return self._brutto - - @brutto.setter - def brutto(self, value: int): - exception_proxy.validate(value, int) - self._brutto = value - - @property - def netto(self) -> int: - return self._netto - - @netto.setter - def netto(self, value: int): - """ - Вес нетто - Args: - value (int): _description_ - """ - exception_proxy.validate(value, int) - - self._netto = value - - @property - def instructions(self) -> list: - """ - Инструкции для приготовления - Returns: - _type_: _description_ - """ - return self._instructions - - @property - def comments(self) -> str: - return self._comments - - - @comments.setter - def comments(self, value: str): - """ - Описание блюда - Args: - value (str): _description_ - """ - exception_proxy.validate(value, str) - self._comments = value - - @property - def consist(self) -> list: - """ - Состав рецепта - Returns: - _type_: _description_ - """ - return self._rows - - def rows(self) -> list: - """ - Получить состав рецепта (read only) - Returns: - _type_: _description_ - """ - result = [] - for key in self._rows.keys(): - result.append( self._rows[key] ) - - return result - - def load(self, source: dict): - """ - Загрузить данные из словаря - Args: - source (dict): исходный словарь - - """ - super().load(source) - if source is None: - return None - - source_fields = ["comments", "consist", "instructions","netto", "brutto"] - if set(source_fields).issubset(list(source.keys())) == False: - raise operation_exception(f"Невозможно загрузить данные в объект {source}!") - - self._netto = source["netto"] - self._brutto = source["brutto"] - - # Загрузим состав - for item in source["consist"].items(): - row = item[1] - if row is not None: - value = receipe_row_model().load(row) - self.add(value) - - # Загрузим инструкции - self._instructions = source["instructions"] - return self - - - - @staticmethod - def create_receipt(name: str, comments: str, items: list, data: list): - """ - Фабричный метод. Сформировать рецепт - Args: - name (str): Наименование рецепта - comments (str): Приготовление - items (list): Состав рецепта - data (list): Список номенклатуры - - Returns: - receipe_model: _description_ - """ - exception_proxy.validate(name, str) - if len(items) == 0: - raise argument_exception(f"Некорректно передан параметр {items}. Список пуст!") - - - # Подготовим словарь со списком номенклатуры - nomenclatures = reference.create_dictionary(data) - receipt = receipe_model(name) - if comments != "": - receipt.comments = comments - - for position in items: - - if len(position) < 2: - raise operation_exception("Невозможно сформировать элементы рецепта! Некорректный список исходных элементов!") - - nomenclature_name = position[0] - size = position[1] - nomenclature = nomenclature_model.get( nomenclature_name, nomenclatures ) - - # Определяем единицу измерения - if nomenclature.unit.base_unit is None: - unit = nomenclature.unit - else: - unit = nomenclature.unit.base_unit - - # Создаем запись в рецепте - row = receipe_row_model() - row.nomenclature = nomenclature - row.size = size - row.unit = unit - receipt.add(row) - - return receipt \ No newline at end of file diff --git a/Result9/Src/Models/receipe_row_model.py b/Result9/Src/Models/receipe_row_model.py deleted file mode 100644 index 8630c3b..0000000 --- a/Result9/Src/Models/receipe_row_model.py +++ /dev/null @@ -1,113 +0,0 @@ -from Src.reference import reference -from Src.Models.nomenclature_model import nomenclature_model -from Src.Models.unit_model import unit_model -from Src.exceptions import exception_proxy, operation_exception -from Src.Models.storage_row_model import storage_row_model -from Src.Models.storage_model import storage_model - -from datetime import datetime - -# -# Класс описание одной строки рецепта -# -class receipe_row_model(reference): - __nomenclature: nomenclature_model = None - __size: int = 0 - __unit: unit_model = None - - def __init__(self): - super().__init__() - - @property - def nomenclature(self): - """ - Номенклатура - Returns: - _type_: _description_ - """ - return self.__nomenclature - - @nomenclature.setter - def nomenclature(self, value: nomenclature_model): - exception_proxy.validate(value, nomenclature_model) - self._name = f"{value.name}" - self.__nomenclature = value - - - @property - def size(self) -> float: - """ - Размер - - Returns: - _type_: _description_ - """ - return self.__size - - - @size.setter - def size(self, value ): - exception_proxy.validate(value, (float, int)) - self.__size = value - - - @property - def unit(self) -> unit_model: - """ - Единица измерения - - Returns: - _type_: _description_ - """ - return self.__unit - - @unit.setter - def unit(self, value: unit_model): - exception_proxy.validate(value, unit_model) - self.__unit = value - - def load(self, source: dict): - """ - Загрузить из словаря - Args: - source (dict): словарь - """ - super().load(source) - if source is None: - return None - - source_fields = ["unit", "size", "nomenclature"] - if set(source_fields).issubset(list(source.keys())) == False: - raise operation_exception(f"Невозможно загрузить данные в объект {source}!") - - self.__size = source["size"] - self.__nomenclature = nomenclature_model().load( source[ "nomenclature"]) - self.__unit = unit_model().load(source["unit"]) - - return self - - @staticmethod - def create_debit_transaction( row, period : datetime, storage: storage_model ) -> storage_row_model: - """ - Сформировать транзакцию списания - Args: - row (receipe_row_model): исходная запись рецепта - period (datetime): период - storage (storage_model): склад - - Returns: - storage_row_model: _description_ - """ - exception_proxy.validate(period , datetime) - exception_proxy.validate(storage, storage_model) - - item = storage_row_model(f"debit transaction") - item.nomenclature = row.nomenclature - item.period = period - item.storage = storage - item.storage_type = False - item.value = row.size - item.unit = row.unit - - return item - diff --git a/Result9/Src/Models/storage_model.py b/Result9/Src/Models/storage_model.py deleted file mode 100644 index 48d68ac..0000000 --- a/Result9/Src/Models/storage_model.py +++ /dev/null @@ -1,61 +0,0 @@ -from Src.reference import reference -from Src.exceptions import exception_proxy, operation_exception - -# -# Модель склада -# -class storage_model(reference): - _address: str = "" - - @property - def address(self) -> str: - """ - Адрес - - Returns: - _type_: _description_ - """ - return self._address - - @address.setter - def address(self, value:str): - """ - Адрес - Args: - value (str): _description_ - """ - exception_proxy.validate(value, str) - self._address = value - - - def load(self, source: dict): - """ - Десериализовать свойства - Args: - source (dict): исходный слова - """ - if source is None: - return None - super().load(source) - - source_fields = ["address"] - if set(source_fields).issubset(list(source.keys())) == False: - raise operation_exception(f"Невозможно загрузить данные в объект {source}!") - - self._address = source["address"] - return self - - # Фабричные методы - - @staticmethod - def create_default() -> reference: - """ - Сформировать склад по умолчанию - Returns: - reference: _description_ - """ - storage = storage_model("default") - storage.address = "г. Москва. ул. Академика Королева, 10" - - return storage - \ No newline at end of file diff --git a/Result9/Src/Models/storage_row_model.py b/Result9/Src/Models/storage_row_model.py deleted file mode 100644 index b923384..0000000 --- a/Result9/Src/Models/storage_row_model.py +++ /dev/null @@ -1,252 +0,0 @@ -from random import randrange - -from Src.exceptions import argument_exception, exception_proxy, operation_exception -from Src.reference import reference -from Src.Models.storage_model import storage_model -from Src.Models.storage_row_turn_model import storage_row_turn_model -from Src.Storage.storage import storage -from datetime import datetime, timedelta -from Src.Models.nomenclature_model import nomenclature_model -from Src.Models.unit_model import unit_model - -# -# Модель складской проводки -# -class storage_row_model(reference): - # Тип складской проводки - _storage_type: bool = False - # Период - _period : datetime - # Номенклатура - _nomenclature: nomenclature_model = None - # Склад - _storage: storage_model = None - # Единица измерений - _unit: unit_model = None - # Значение - _value: float = 0 - - - @property - def value(self) -> float: - """ - Значение - Returns: - float: _description_ - """ - return self._value - - @value.setter - def value(self, value: float) -> float: - """ - Значение - Args: - value (float): _description_ - - Raises: - argument_exception: _description_ - - Returns: - float: _description_ - """ - exception_proxy.validate(value, (float, int)) - if value <= 0: - raise argument_exception("Некорректно переданы параметры!") - - self._value = value - - @property - def nomenclature(self) -> nomenclature_model: - """ - Номенклатура - Returns: - nomenclature_model: _description_ - """ - return self._nomenclature - - @nomenclature.setter - def nomenclature(self, value: nomenclature_model) -> nomenclature_model: - """ - Номенклатура - Args: - value (nomenclature_model): _description_ - """ - exception_proxy.validate(value, nomenclature_model) - self._nomenclature = value - - - @property - def unit(self) -> unit_model: - """ - Единица измерения - Returns: - unit_model: _description_ - """ - return self._unit - - @unit.setter - def unit(self, value: unit_model) -> unit_model: - """ - Единица измерения - Args: - value (unit_model): _description_ - - Returns: - unit_model: _description_ - """ - exception_proxy.validate(value, unit_model) - self._unit = value - - @property - def storage(self) -> storage_model: - """ - Склад - Returns: - storage_model: _description_ - """ - return self._storage - - @storage.setter - def storage(self, value: storage_model) -> storage_model: - """ - Склад - Args: - value (storage_model): _description_ - - Returns: - storage_model: _description_ - """ - exception_proxy.validate(value, storage_model) - self._storage = value - - @property - def storage_type(self) -> bool: - """ - Тип складской проводки (True - приход, False - расход) - Returns: - bool: _description_ - """ - return self._storage_type - - @storage_type.setter - def storage_type(self, value) -> bool: - """ - Тип складской проводки (True - приход, False - расход) - Args: - value (_type_): _description_ - - Raises: - argument_exception: _description_ - - Returns: - bool: _description_ - """ - if isinstance(value, int): - self._storage_type = True if value > 0 else False - - elif isinstance(value, bool): - self._storage_type = value - - else: - raise argument_exception("Некорректно переданы параметры!") - - @property - def period(self) -> datetime: - """ - Дата транзакции - Returns: - datetime: _description_ - """ - return self._period - - @period.setter - def period(self, value: datetime) -> datetime: - """ - Дата транзакции - """ - exception_proxy.validate(value, datetime) - self._period = value - - - def load(self, source: dict): - """ - Десериализовать свойства - Args: - source (dict): исходный слова - """ - if source is None: - return None - super().load(source) - - source_fields = ["period", "storage_type", "nomenclature", "value", "unit", "storage" ] - if set(source_fields).issubset(list(source.keys())) == False: - raise operation_exception(f"Невозможно загрузить данные в объект {source}!") - - self._value = source["value"] - self._period = datetime.strptime(source["period"], "%Y-%m-%d") - self._nomenclature = nomenclature_model().load( source["nomenclature"]) - self._storage = storage_model().load( source["storage"] ) - self._unit = unit_model().load( source["unit"]) - self._storage_type = source["storage_type"] - - return self - - # Фабричные методы - - @staticmethod - def create_credit_row(nomenclature_name: str, quantity, unit_name: str, data: dict, _storage: storage_model) -> reference: - """ - Фабричный метод для создания транзакции на поступление - Используется в start_factory - Args: - nomenclature_name (str): Наименование номенклатуры - quantity(int, float): Количество - unit_name (str): Наименование единицы измерения - data (dict): исходный набор данных - storage(storage_model): склад - Returns: - reference: _description_ - """ - exception_proxy.validate(nomenclature_name, str) - exception_proxy.validate(_storage, storage_model) - exception_proxy.validate(quantity, (int, float)) - exception_proxy.validate(unit_name, str) - - - # Определим номенклатуру - items = data[ storage.nomenclature_key() ] - nomenclatures = reference.create_dictionary(items) - nomenclature = nomenclature_model.get( nomenclature_name, nomenclatures) - - # Определяем единицу измерения - items = data[ storage.unit_key()] - units = reference.create_dictionary(items) - unit = unit_model.get(unit_name, units ) - - start_date = datetime.strptime("2024-01-01", "%Y-%m-%d") - stop_date = datetime.strptime("2024-02-01", "%Y-%m-%d") - - # Создаем транзакцию - item = storage_row_model("sample_credit_transaction") - item.nomenclature = nomenclature - item.unit = unit - item.storage_type = True - item.value = quantity - item.storage = _storage - item.period = storage_row_model.random_date(start_date, stop_date) - - return item - - # Источник https://stackoverflow.com/questions/553303/generate-a-random-date-between-two-other-dates - @staticmethod - def random_date(start, end): - delta = end - start - int_delta = (delta.days * 24 * 60 * 60) + delta.seconds - random_second = randrange(int_delta) - return start + timedelta(seconds=random_second) - - - - - - diff --git a/Result9/Src/Models/storage_row_turn_model.py b/Result9/Src/Models/storage_row_turn_model.py deleted file mode 100644 index 359a57a..0000000 --- a/Result9/Src/Models/storage_row_turn_model.py +++ /dev/null @@ -1,125 +0,0 @@ -from Src.reference import reference -from Src.exceptions import exception_proxy, argument_exception -from Src.Models.nomenclature_model import nomenclature_model -from Src.Models.storage_model import storage_model -from Src.Models.unit_model import unit_model - -# -# Модель складского оборота -# -class storage_row_turn_model(reference): - # Номенклатура - _nomenclature: nomenclature_model = None - # Склад - _storage: storage_model = None - # Единица измерений - _unit: unit_model = None - # Значение - _value: float = 0 - - - @property - def value(self) -> float: - """ - Значение - Returns: - float: _description_ - """ - return self._value - - @value.setter - def value(self, value: float) -> float: - """ - Значение - Args: - value (float): _description_ - - Raises: - argument_exception: _description_ - - Returns: - float: _description_ - """ - exception_proxy.validate(value, (float, int)) - self._value = value - - @property - def nomenclature(self) -> nomenclature_model: - """ - Номенклатура - Returns: - nomenclature_model: _description_ - """ - return self._nomenclature - - @nomenclature.setter - def nomenclature(self, value: nomenclature_model) -> nomenclature_model: - """ - Номенклатура - Args: - value (nomenclature_model): _description_ - """ - exception_proxy.validate(value, nomenclature_model) - self._nomenclature = value - - - @property - def unit(self) -> unit_model: - """ - Единица измерения - Returns: - unit_model: _description_ - """ - return self._unit - - def unit(self, value: unit_model) -> unit_model: - """ - Единица измерения - Args: - value (unit_model): _description_ - - Returns: - unit_model: _description_ - """ - exception_proxy.validate(value, unit_model) - self._unit = value - - - def storage(self) -> storage_model: - """ - Склад - Returns: - storage_model: _description_ - """ - return self._storage - - def storage(self, value: storage_model) -> storage_model: - """ - Склад - Args: - value (storage_model): _description_ - - Returns: - storage_model: _description_ - """ - exception_proxy.validate(value, storage_model) - self._storage = value - - @staticmethod - def create(nomenclature : nomenclature_model, storage: storage_model, unit: unit_model) -> reference: - """ - Фабричный метод для создания складского оборота - Args: - nomenclature (nomenclature_model): _description_ - storage (storage_model): _description_ - unit (unit_model): _description_ - - Returns: - storage_row_turn_model: _description_ - """ - row = storage_row_turn_model("-") - row.storage = storage - row.unit = unit - row.nomenclature = nomenclature - - return row \ No newline at end of file diff --git a/Result9/Src/Models/unit_model.py b/Result9/Src/Models/unit_model.py deleted file mode 100644 index 698cdbf..0000000 --- a/Result9/Src/Models/unit_model.py +++ /dev/null @@ -1,163 +0,0 @@ -from Src.reference import reference -from Src.exceptions import exception_proxy, argument_exception, operation_exception - - - -# -# Модель единицы измерения для номенклатуры -# -class unit_model(reference): - - # Базовая единица измерения - __base_unit: reference = None - - # Коэффициент пересчета к базовой единице измерения - __coefficient: int = 1 - - def __init__(self, name: str = None, base: reference = None, coeff: int = 1 ): - super().__init__(name) - - if base != None: - self.base_unit = base - - if coeff != 1: - self.coefficient = coeff - - - @property - def base_unit(self) -> reference: - """ - Базовая единица измерения - Returns: - _type_: _description_ - """ - return self.__base_unit - - - @base_unit.setter - def base_unit(self, value: reference ): - exception_proxy.validate(value, reference) - self.__base_unit = value - - - @property - def coefficient(self) -> int: - """ - Коэффициент пересчета - Returns: - _type_: _description_ - """ - return self.__coefficient - - @coefficient.setter - def coefficient(self, value:int): - exception_proxy.validate(value, int) - - if(value <= 0): - raise argument_exception("Значение коэффициента должно быть > 1!") - - self.__coefficient = value - - def load(self, source: dict): - """ - Загрузить данные - Args: - source (dict): исходный словарь - - """ - super().load(source) - if source is None: - return None - - source_fields = ["coefficient", "base_unit"] - if set(source_fields).issubset(list(source.keys())) == False: - raise operation_exception(f"Невозможно загрузить данные в объект {source}!") - - self.__coefficient = source["coefficient"] - self.__base_unit = unit_model().load(source["base_unit"]) - - return self - - - - # Фабричные методы - - @staticmethod - def create_gram(): - """ - Создать единицу измерения - грамм - - Returns: - _type_: _description_ - """ - item = unit_model("грамм", None, 1) - return item - - @staticmethod - def create_killogram(): - """ - Создать единицу измерения - киллограмм - Returns: - _type_: _description_ - """ - base = unit_model.create_gram() - item = unit_model("киллограмм", base, 1000) - return item - - @staticmethod - def create_ting(): - """ - Создать единицу изменения - штуки - Returns: - _type_: _description_ - """ - return unit_model("штука") - - def create_milliliter(): - """ - Создать единицу измерения - миллилитр - Returns: - _type_: _description_ - """ - return unit_model("миллилитр") - - def create_liter(): - """ - Создать единицу измерения - литр - Returns: - _type_: _description_ - """ - base = unit_model.create_milliliter() - item = unit_model("литр", base, 1000) - return item - - - @staticmethod - def get(unit_name: str, units: dict): - """ - Получить значение элемента единицы измерения из словаря - Args: - nomenclature_name (str): наименование - nomenclatures (dict): исходный словарь storage.data - - Returns: - nomenclature_model: _description_ - """ - exception_proxy.validate(unit_name, str) - - keys = list(filter(lambda x: x == unit_name, units.keys() )) - if len(keys) == 0: - raise operation_exception(f"Некоректно передан список. Не найдена номенклатура {unit_name}!") - - return units[keys[0]] - - - - - - - - - - - \ No newline at end of file diff --git a/Result9/Src/Storage/storage.json b/Result9/Src/Storage/storage.json deleted file mode 100644 index 2b6918a..0000000 --- a/Result9/Src/Storage/storage.json +++ /dev/null @@ -1,2260 +0,0 @@ -{ - "group_model": [ - { - "description": "", - "id": "44298be97ad7482497f6fc8d6deb0808", - "is_error": false, - "name": "Ингредиенты" - } - ], - "nomenclature_model": [ - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "5c16c9c869c944ca81571f6cf70a790d", - "is_error": false, - "name": "Мука пшеничная", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "e787ff8a837d40418f9121f1272616ab", - "is_error": false, - "name": "Сахар", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "998f959f17704d85b0fad15936283032", - "is_error": false, - "name": "Сливочное масло", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "8beba2f92e754eeda776859bc197af39", - "is_error": false, - "name": "Яйца", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "f1a87af30e0c4faca9c3184050b3e370", - "is_error": false, - "name": "штука" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "11456b73dc1c4620b9387016648509b9", - "is_error": false, - "name": "Ванилин", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "6a448c9b996646888ace90ed26a7547b", - "is_error": false, - "name": "Куринное филе", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "5b5936b9384a4fe59b24e3fd127377d0", - "is_error": false, - "name": "Салат Романо", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "1696620e15b74a70b7fbd3c28076255b", - "is_error": false, - "name": "Сыр Пармезан", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "87ae9afa51d74a94af801f7037d59d8d", - "is_error": false, - "name": "Чеснок", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "3c8d6792212a4feba49e48770995ee55", - "is_error": false, - "name": "Белый хлеб", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "fb5741984d24441789ecd2a2d0c6637a", - "is_error": false, - "name": "Соль", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "6ea66e6494ad447a996ad93fdb38697e", - "is_error": false, - "name": "Черный перец", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "e8bc8e612eab4fecb1ba20b685267cc6", - "is_error": false, - "name": "Оливковое масло", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "11be8eb6981a4f49bbe0fe93f136a00e", - "is_error": false, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "ce323ec5feb840c9843d4380a8630fcf", - "is_error": false, - "name": "литр" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "cdbbecb3fa594a799e5b4c6a14058c42", - "is_error": false, - "name": "Лимонный сок", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "11be8eb6981a4f49bbe0fe93f136a00e", - "is_error": false, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "ce323ec5feb840c9843d4380a8630fcf", - "is_error": false, - "name": "литр" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "af0af126ae0b41c38ea53544777721b9", - "is_error": false, - "name": "Горчица дижонская", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "ffa7503c9ef64bdcb51b762fa3595649", - "is_error": false, - "name": "Сахарная пудра", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "0be1452f2ae649e88008a595ba309aba", - "is_error": false, - "name": "Ванилиин", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "f658b569df3d46709cce01b5a7383cd5", - "is_error": false, - "name": "Корица", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "bf459ed1953f437fa0a1a7f2f3c87f46", - "is_error": false, - "name": "Какао", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - } - ], - "receipe_model": [ - { - "brutto": 251, - "comments": "Время приготовления: 20 мин. 8 порций", - "consist": { - "Мука пшеничная": { - "description": "", - "id": "7c92cf932de44581951ca05738464de2", - "is_error": false, - "name": "Мука пшеничная", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "5c16c9c869c944ca81571f6cf70a790d", - "is_error": false, - "name": "Мука пшеничная", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - "size": 100, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - } - }, - "Сахар": { - "description": "", - "id": "4f50b78ffb3f43848bdb08279ff95823", - "is_error": false, - "name": "Сахар", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "e787ff8a837d40418f9121f1272616ab", - "is_error": false, - "name": "Сахар", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - "size": 80, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - } - }, - "Сливочное масло": { - "description": "", - "id": "244833ce4dad4f21bbdddeb0bce73ec2", - "is_error": false, - "name": "Сливочное масло", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "998f959f17704d85b0fad15936283032", - "is_error": false, - "name": "Сливочное масло", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - "size": 70, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - } - }, - "Яйца": { - "description": "", - "id": "843cebf7329d41de9dff0ae4bdbb43cb", - "is_error": false, - "name": "Яйца", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "8beba2f92e754eeda776859bc197af39", - "is_error": false, - "name": "Яйца", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "f1a87af30e0c4faca9c3184050b3e370", - "is_error": false, - "name": "штука" - } - }, - "size": 1, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "f1a87af30e0c4faca9c3184050b3e370", - "is_error": false, - "name": "штука" - } - } - }, - "description": "", - "id": "89a9226cf02643d8a9e856fbc0ca4351", - "instructions": [ - { - "instructions": "Масло положите в сотейник с толстым дном. Растопите его на маленьком огне на плите, на водяной бане либо в микроволновке." - }, - { - "instructions": "Добавьте в теплое масло сахар. Перемешайте венчиком до полного растворения сахара. От тепла сахар довольно быстро растает." - }, - { - "instructions": "Добавьте в масло яйцо. Предварительно все-таки проверьте масло, не горячее ли оно, иначе яйцо может свариться. Перемешайте яйцо с маслом до однородности." - }, - { - "instructions": "Всыпьте муку, добавьте ванилин." - }, - { - "instructions": "Перемешайте массу венчиком до состояния гладкого однородного теста." - } - ], - "is_error": false, - "name": "Вафли хрустящие в вафильнице", - "netto": 0 - }, - { - "brutto": 369, - "comments": "", - "consist": { - "Белый хлеб": { - "description": "", - "id": "fe7a448350184766b6c57a1cea5df43e", - "is_error": false, - "name": "Белый хлеб", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "3c8d6792212a4feba49e48770995ee55", - "is_error": false, - "name": "Белый хлеб", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - "size": 30, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - } - }, - "Горчица дижонская": { - "description": "", - "id": "a5221530e2574658bb08fd2d55ef4a97", - "is_error": false, - "name": "Горчица дижонская", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "af0af126ae0b41c38ea53544777721b9", - "is_error": false, - "name": "Горчица дижонская", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - "size": 5, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - "Куринное филе": { - "description": "", - "id": "8814e19f162e4b3db2a8233524c50955", - "is_error": false, - "name": "Куринное филе", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "6a448c9b996646888ace90ed26a7547b", - "is_error": false, - "name": "Куринное филе", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - "size": 200, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - } - }, - "Лимонный сок": { - "description": "", - "id": "2ca49b7384144e4eb07a3a8072de0d09", - "is_error": false, - "name": "Лимонный сок", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "cdbbecb3fa594a799e5b4c6a14058c42", - "is_error": false, - "name": "Лимонный сок", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "11be8eb6981a4f49bbe0fe93f136a00e", - "is_error": false, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "ce323ec5feb840c9843d4380a8630fcf", - "is_error": false, - "name": "литр" - } - }, - "size": 5, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "11be8eb6981a4f49bbe0fe93f136a00e", - "is_error": false, - "name": "миллилитр" - } - }, - "Оливковое масло": { - "description": "", - "id": "b9999b177e644de5b52f2acaab0a1d58", - "is_error": false, - "name": "Оливковое масло", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "e8bc8e612eab4fecb1ba20b685267cc6", - "is_error": false, - "name": "Оливковое масло", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "11be8eb6981a4f49bbe0fe93f136a00e", - "is_error": false, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "ce323ec5feb840c9843d4380a8630fcf", - "is_error": false, - "name": "литр" - } - }, - "size": 10, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "11be8eb6981a4f49bbe0fe93f136a00e", - "is_error": false, - "name": "миллилитр" - } - }, - "Салат Романо": { - "description": "", - "id": "2fccc854834a49ffb0f5a4f83801d2e5", - "is_error": false, - "name": "Салат Романо", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "5b5936b9384a4fe59b24e3fd127377d0", - "is_error": false, - "name": "Салат Романо", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - "size": 50, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - "Соль": { - "description": "", - "id": "a36a115edf094b9184a550f994242eb5", - "is_error": false, - "name": "Соль", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "fb5741984d24441789ecd2a2d0c6637a", - "is_error": false, - "name": "Соль", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - "size": 5, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - } - }, - "Сыр Пармезан": { - "description": "", - "id": "a36abd4567394c7396c0fccc15dfaaa8", - "is_error": false, - "name": "Сыр Пармезан", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "1696620e15b74a70b7fbd3c28076255b", - "is_error": false, - "name": "Сыр Пармезан", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - "size": 50, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - } - }, - "Черный перец": { - "description": "", - "id": "1d51f6245b1d4f6db47670e91613a892", - "is_error": false, - "name": "Черный перец", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "6ea66e6494ad447a996ad93fdb38697e", - "is_error": false, - "name": "Черный перец", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - "size": 2, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - "Чеснок": { - "description": "", - "id": "20f1bb98774f498080cb7dc59a0be7fd", - "is_error": false, - "name": "Чеснок", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "87ae9afa51d74a94af801f7037d59d8d", - "is_error": false, - "name": "Чеснок", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - "size": 10, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - } - }, - "Яйца": { - "description": "", - "id": "2ccd5d4a20a54eb0beebe0ee2fb87b41", - "is_error": false, - "name": "Яйца", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "8beba2f92e754eeda776859bc197af39", - "is_error": false, - "name": "Яйца", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "f1a87af30e0c4faca9c3184050b3e370", - "is_error": false, - "name": "штука" - } - }, - "size": 2, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "f1a87af30e0c4faca9c3184050b3e370", - "is_error": false, - "name": "штука" - } - } - }, - "description": "", - "id": "c01913436f1a4d26b31359285fceb10f", - "instructions": [], - "is_error": false, - "name": "Цезарь с курицей", - "netto": 0 - }, - { - "brutto": 213, - "comments": "", - "consist": { - "Ванилиин": { - "description": "", - "id": "f58171de9ca34eecb26717784ed32133", - "is_error": false, - "name": "Ванилиин", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "0be1452f2ae649e88008a595ba309aba", - "is_error": false, - "name": "Ванилиин", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - "size": 5, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - "Какао": { - "description": "", - "id": "e8e68575186d444e8c6d5c41918fba65", - "is_error": false, - "name": "Какао", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "bf459ed1953f437fa0a1a7f2f3c87f46", - "is_error": false, - "name": "Какао", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - "size": 20, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - } - }, - "Корица": { - "description": "", - "id": "dd8b376d6cd545589e2bf9937a8e44cd", - "is_error": false, - "name": "Корица", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "f658b569df3d46709cce01b5a7383cd5", - "is_error": false, - "name": "Корица", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - "size": 5, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - "Сахарная пудра": { - "description": "", - "id": "b6c28c224bb44bf9a4b74bedc348af8c", - "is_error": false, - "name": "Сахарная пудра", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "ffa7503c9ef64bdcb51b762fa3595649", - "is_error": false, - "name": "Сахарная пудра", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - "size": 180, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - "Яйца": { - "description": "", - "id": "44a484b273994bb3973beaa793576b3a", - "is_error": false, - "name": "Яйца", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "8beba2f92e754eeda776859bc197af39", - "is_error": false, - "name": "Яйца", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "f1a87af30e0c4faca9c3184050b3e370", - "is_error": false, - "name": "штука" - } - }, - "size": 3, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "f1a87af30e0c4faca9c3184050b3e370", - "is_error": false, - "name": "штука" - } - } - }, - "description": "", - "id": "9c858076aacf425685bfbf60a0fd4267", - "instructions": [], - "is_error": false, - "name": "Безе", - "netto": 0 - } - ], - "storage_row_model": [ - { - "description": "", - "id": "a66dd566f8764eee9ca29f6dc5245eb1", - "is_error": false, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "5c16c9c869c944ca81571f6cf70a790d", - "is_error": false, - "name": "Мука пшеничная", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - "period": "2024-01-27", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": false, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": false, - "name": "киллограмм" - }, - "value": 1 - }, - { - "description": "", - "id": "b643d8e77fdf4459a6871281e8f10eac", - "is_error": false, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "6ea66e6494ad447a996ad93fdb38697e", - "is_error": false, - "name": "Черный перец", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - "period": "2024-01-15", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": false, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "8d5f31aa6ca84177a6bd5761ab50f6fc", - "is_error": false, - "name": "грамм" - }, - "value": 50 - }, - { - "description": "", - "id": "da263ab918c34833a00b386b88781710", - "is_error": false, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "e787ff8a837d40418f9121f1272616ab", - "is_error": false, - "name": "Сахар", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - "period": "2024-01-28", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": false, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": false, - "name": "киллограмм" - }, - "value": 0.5 - }, - { - "description": "", - "id": "9bd6f1ce38574a268820d59aaf9a32ce", - "is_error": false, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "8beba2f92e754eeda776859bc197af39", - "is_error": false, - "name": "Яйца", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "f1a87af30e0c4faca9c3184050b3e370", - "is_error": false, - "name": "штука" - } - }, - "period": "2024-01-27", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": false, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "aaa9f8244b2a4f1ca199b0b376967213", - "is_error": false, - "name": "штука" - }, - "value": 6 - }, - { - "description": "", - "id": "bf0a45ae55e048899dbce60f1ed7ea03", - "is_error": false, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "e8bc8e612eab4fecb1ba20b685267cc6", - "is_error": false, - "name": "Оливковое масло", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "11be8eb6981a4f49bbe0fe93f136a00e", - "is_error": false, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "ce323ec5feb840c9843d4380a8630fcf", - "is_error": false, - "name": "литр" - } - }, - "period": "2024-01-03", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": false, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d113c297f698464a85cff3e803f36ce7", - "is_error": false, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "d486cc7e786f4b828933d5ace911a875", - "is_error": false, - "name": "литр" - }, - "value": 0.2 - }, - { - "description": "", - "id": "fb17787652844ae2b4cd276c81a588a1", - "is_error": false, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "6a448c9b996646888ace90ed26a7547b", - "is_error": false, - "name": "Куринное филе", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - "period": "2024-01-31", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": false, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": false, - "name": "киллограмм" - }, - "value": 0.5 - }, - { - "description": "", - "id": "30168c6837f04bd4b212d65d8ffd1eb0", - "is_error": false, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "5b5936b9384a4fe59b24e3fd127377d0", - "is_error": false, - "name": "Салат Романо", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - "period": "2024-01-21", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": false, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "aaa9f8244b2a4f1ca199b0b376967213", - "is_error": false, - "name": "штука" - }, - "value": 1 - }, - { - "description": "", - "id": "9f2ecf8cbf64420882ee07e2024cfee3", - "is_error": false, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "3c8d6792212a4feba49e48770995ee55", - "is_error": false, - "name": "Белый хлеб", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - "period": "2024-01-09", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": false, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "aaa9f8244b2a4f1ca199b0b376967213", - "is_error": false, - "name": "штука" - }, - "value": 3 - }, - { - "description": "", - "id": "5c18cef4d4584b9a80077766225eaaed", - "is_error": false, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "1696620e15b74a70b7fbd3c28076255b", - "is_error": false, - "name": "Сыр Пармезан", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - "period": "2024-01-27", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": false, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": false, - "name": "киллограмм" - }, - "value": 0.2 - }, - { - "description": "", - "id": "0cf3414a8d114f3c89db9741aa9acadf", - "is_error": false, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "af0af126ae0b41c38ea53544777721b9", - "is_error": false, - "name": "Горчица дижонская", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - "period": "2024-01-06", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": false, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d113c297f698464a85cff3e803f36ce7", - "is_error": false, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "d486cc7e786f4b828933d5ace911a875", - "is_error": false, - "name": "литр" - }, - "value": 0.1 - }, - { - "description": "", - "id": "2f2bede132d1485e9562ca13326ab5ac", - "is_error": false, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "6ea66e6494ad447a996ad93fdb38697e", - "is_error": false, - "name": "Черный перец", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - "period": "2024-01-17", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": false, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "8d5f31aa6ca84177a6bd5761ab50f6fc", - "is_error": false, - "name": "грамм" - }, - "value": 10 - }, - { - "description": "", - "id": "15e085acf2224edab862af3d79dfeab5", - "is_error": false, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "cdbbecb3fa594a799e5b4c6a14058c42", - "is_error": false, - "name": "Лимонный сок", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "11be8eb6981a4f49bbe0fe93f136a00e", - "is_error": false, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "ce323ec5feb840c9843d4380a8630fcf", - "is_error": false, - "name": "литр" - } - }, - "period": "2024-01-23", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": false, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d113c297f698464a85cff3e803f36ce7", - "is_error": false, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "d486cc7e786f4b828933d5ace911a875", - "is_error": false, - "name": "литр" - }, - "value": 1 - }, - { - "description": "", - "id": "a77598bd2dc944cc9c9ed05579c0caa4", - "is_error": false, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "bf459ed1953f437fa0a1a7f2f3c87f46", - "is_error": false, - "name": "Какао", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - "period": "2024-01-12", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": false, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": false, - "name": "киллограмм" - }, - "value": 1 - }, - { - "description": "", - "id": "22df33c9d3a543d980c9cda91fe7685c", - "is_error": false, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "998f959f17704d85b0fad15936283032", - "is_error": false, - "name": "Сливочное масло", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - "period": "2024-01-12", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": false, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": false, - "name": "киллограмм" - }, - "value": 0.5 - }, - { - "description": "", - "id": "1a35d9478dcd49ecae266acf21c50da3", - "is_error": false, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "1696620e15b74a70b7fbd3c28076255b", - "is_error": false, - "name": "Сыр Пармезан", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - "period": "2024-01-24", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": false, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": false, - "name": "киллограмм" - }, - "value": 0.3 - }, - { - "description": "", - "id": "26f95d36c7d149d9bd42e36a185ea846", - "is_error": false, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "0be1452f2ae649e88008a595ba309aba", - "is_error": false, - "name": "Ванилиин", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": false, - "name": "грамм" - } - }, - "period": "2024-01-18", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": false, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "8d5f31aa6ca84177a6bd5761ab50f6fc", - "is_error": false, - "name": "грамм" - }, - "value": 100 - }, - { - "description": "", - "id": "9c495297d49540e68eb0f08ff6a9e9a8", - "is_error": false, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "Ингредиенты" - }, - "id": "fb5741984d24441789ecd2a2d0c6637a", - "is_error": false, - "name": "Соль", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "киллограмм" - } - }, - "period": "2024-01-01", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": false, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": false, - "name": "киллограмм" - }, - "value": 1 - } - ], - "unit_model": [ - { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "8d5f31aa6ca84177a6bd5761ab50f6fc", - "is_error": false, - "name": "грамм" - }, - { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": false, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": false, - "name": "киллограмм" - }, - { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d113c297f698464a85cff3e803f36ce7", - "is_error": false, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "d486cc7e786f4b828933d5ace911a875", - "is_error": false, - "name": "литр" - }, - { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "ab87512809eb4da6bf6cc026c6740983", - "is_error": false, - "name": "миллилитр" - }, - { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "aaa9f8244b2a4f1ca199b0b376967213", - "is_error": false, - "name": "штука" - } - ] -} \ No newline at end of file diff --git a/Result9/Src/Storage/storage.py b/Result9/Src/Storage/storage.py deleted file mode 100644 index 920b9d7..0000000 --- a/Result9/Src/Storage/storage.py +++ /dev/null @@ -1,196 +0,0 @@ -import json -import os - -from Src.exceptions import operation_exception, exception_proxy -from Src.Logics.convert_factory import convert_factory -from Src.reference import reference - - -# -# Класс хранилище данных -# -class storage(): - __data = {} - __storage_file = "storage.json" - __mapping = {} - - def __new__(cls): - if not hasattr(cls, 'instance'): - cls.instance = super(storage, cls).__new__(cls) - - return cls.instance - - def __init__(self) -> None: - # Связка для всех моделей - for inheritor in reference.__subclasses__(): - self.__mapping[inheritor.__name__] = inheritor - - - @property - def data(self) -> dict: - """ - Данные по моделям - - Returns: - _type_: _description_ - """ - return self.__data - - def load(self): - """ - Загрузить данные из хранилища - Raises: - operation_exception: _description_ - """ - try: - file_path = os.path.split(__file__) - data_file = "%s/%s" % (file_path[0], self.__storage_file) - if not os.path.exists(data_file): - raise operation_exception(f"Невозможно загрузить данные! Не найден файл {data_file}") - - with open(data_file, "r") as read_file: - source = json.load(read_file) - - self.__data = {} - for key in storage.storage_keys(storage): - if key in source.keys(): - source_data = source[key] - self.__data[key] = [] - - for item in source_data: - object = self.__mapping[key] - instance = object().load(item) - self.__data[key].append(instance) - - except Exception as ex: - raise operation_exception(f"Ошибка при чтении данных. Файл {self.__storage_file}\n{ex}") - - - def save(self): - """ - Сохранить данные в хранилище - Raises: - operation_exception: _description_ - """ - try: - factory = convert_factory() - with open(self.__storage_file, "w") as write_file: - data = factory.serialize( self.data ) - json_text = json.dumps(data, sort_keys = True, indent = 4, ensure_ascii = False) - write_file.write(json_text) - - return True - except Exception as ex: - raise operation_exception(f"Ошибка при записи файла {self.__storage_file}\n{ex}") - - return False - - - def save_blocked_turns(self, turns:list): - """ - Сохранить новый список заблокированных оборотов - """ - exception_proxy.validate(turns, list) - if len(turns) > 0: - self.__data[ storage.blocked_turns_key() ] = turns - - - @staticmethod - def save_blocked_turns(turns:list): - """ - Сохранить новый список заблокированных оборотов (статический вызов) - """ - object = storage() - object.save_blocked_turns(turns) - - @staticmethod - def blocked_turns_key(): - """ - Ключ для хранения заблокированных оборотов - Returns: - _type_: _description_ - """ - return "storage_row_turn_model" - - @staticmethod - def nomenclature_key(): - """ - Ключ для хранения номенклатуры - Returns: - _type_: _description_ - """ - return "nomenclature_model" - - - @staticmethod - def group_key(): - """ - Списк номенклатурных групп - Returns: - _type_: _description_ - """ - return "group_model" - - - @staticmethod - def storage_transaction_key(): - """ - Список складских проводок - Returns: - _type_: _description_ - """ - return "storage_row_model" - - - @staticmethod - def unit_key(): - """ - Список единиц измерения - Returns: - _type_: _description_ - """ - return "unit_model" - - @staticmethod - def receipt_key(): - """ - Список рецептов - Returns: - _type_: _description_ - """ - return "receipe_model" - - # Код взят: https://github.com/UpTechCompany/GitExample/blob/6665bc70c4933da12f07c0a0d7a4fc638c157c40/storage/storage.py#L30 - - @staticmethod - def storage_keys(cls): - """ - Получить список ключей - Returns: - _type_: _description_ - """ - keys = [] - methods = [getattr(cls, method) for method in dir(cls) if callable(getattr(cls, method))] - for method in methods: - if method.__name__.endswith("_key") and callable(method): - keys.append(method()) - return keys - - - def Ok( app): - """" - Сформировать данные для сервера - """ - if app is None: - raise operation_exception("Некорректно переданы параметры!") - - json_text = json.dumps({"status" : "ok"}, sort_keys = True, indent = 4, ensure_ascii = False) - - # Подготовить ответ - result = app.response_class( - response = f"{json_text}", - status = 200, - mimetype = "application/json; charset=utf-8" - ) - - return result \ No newline at end of file diff --git a/Result9/Src/errors.py b/Result9/Src/errors.py deleted file mode 100644 index cb29788..0000000 --- a/Result9/Src/errors.py +++ /dev/null @@ -1,94 +0,0 @@ -import json - -# -# Класс для обработки и хранения текстовой информации об ошибке -# -class error_proxy: - " Текст с описание ошибки " - _error_text = "" - - def __init__(self, exception: Exception = None): - if exception is not None: - self.set_error(exception) - - @property - def error(self): - """ - Получить текстовое описание ошибки - Returns: - str: _description_ - """ - return self._error_text - - @error.setter - def error(self, value: str): - if value == "": - raise Exception("Некорректно переданы параметры!") - - self._error_text = value - - @classmethod - def set_error(self, exception: Exception): - """ - Сохранить текстовое описание ошибки из исключения - Args: - exception (Exception): входящее исключение - """ - - if exception is None: - self._error_text = "" - return - - self._error_text = "Ошибка! " + str(exception) - - @property - def is_empty(self) -> bool: - """ - Флаг. Есть ошибка - Returns: - bool: _description_ - """ - if len(self._error_text) != 0: - return False - else: - return True - - def clear(self): - """ - Очистить - """ - self._error_text = "" - - @staticmethod - def create_error_response( app, message: str, http_code: int = 0): - """ - Сформировать структуру response_class для описания ошибки - Args: - app (_type_): Flask - message (str): Сообщение - http_code(int): Код возврата - - Returns: - response_class: _description_ - """ - - if app is None: - raise Exception("Некорректно переданы параметры!") - - if http_code == 0: - code = 500 - else: - code = http_code - - # Формируем описание - json_text = json.dumps({"details" : message}, sort_keys = True, indent = 4, ensure_ascii = False) - - # Формируем результат - result = app.response_class( - response = f"{json_text}", - status = code, - mimetype = "application/json; charset=utf-8" - ) - - return result - \ No newline at end of file diff --git a/Result9/Src/exceptions.py b/Result9/Src/exceptions.py deleted file mode 100644 index 552326c..0000000 --- a/Result9/Src/exceptions.py +++ /dev/null @@ -1,72 +0,0 @@ -from Src.errors import error_proxy - -# Набор классов для генерации собственных исключений - -# -# Абстрактный класс для наследования -# -class exception_proxy(Exception): - _error : error_proxy = error_proxy() - - def __init__(self, *args: object) -> None: - super().__init__(*args) - self._error.set_error(self) - - @property - def error(self): - """ - Информация об ошибке - Returns: - _type_: _description_ - """ - return self._error - - # -> Источник: https://github.com/zhbr112/Restaurant-automation/blob/b2db73872c4c126155ad52b82db79223943aca29/src/abstract_reference.py#L16 - - @staticmethod - def validate( value, type_, len_= None): - """ - Валидация аргумента по типу и длине - Args: - value (any): Аргумент - type_ (object): Ожидаемый тип - len_ (int): Максимальная длина - Raises: - arguent_exception: Некорректный тип - arguent_exception: Неулевая длина - arguent_exception: Некорректная длина аргумента - Returns: - True или Exception - """ - - if value is None: - raise argument_exception("Пустой аргумент") - - # Проверка типа - if not isinstance(value, type_): - raise argument_exception("Некорректный тип") - - # Проверка аргумента - if len(str(value).strip()) == 0: - raise argument_exception("Пустой аргумент") - - if len_ is not None and len(str(value).strip()) >= len_: - raise argument_exception("Некорректная длина аргумента") - - return True - - - - -# -# Исключение при проверки аргументов -# -class argument_exception(exception_proxy): - pass - -# -# Исключение при выполнении операции -# -class operation_exception(exception_proxy): - pass - \ No newline at end of file diff --git a/Result9/Src/reference.py b/Result9/Src/reference.py deleted file mode 100644 index aed3313..0000000 --- a/Result9/Src/reference.py +++ /dev/null @@ -1,146 +0,0 @@ -import uuid -from abc import ABC -from Src.errors import error_proxy -from Src.exceptions import exception_proxy, argument_exception, operation_exception - -# -# Абстрактный класс для наследования -# -class reference(ABC): - " Readonly: Уникальный код " - _id = None - " Краткое наименование " - _name = "" - " Описание " - _description = "" - " Информация об ошибке " - _error = error_proxy() - - def __init__(self, name = None): - self._id = uuid.uuid4() - self._name = name - - @property - def name(self): - "Краткое наименование" - return self._name - - @name.setter - def name(self, value: str): - "Краткое наименование" - exception_proxy.validate( value.strip(), str, 50) - self._name = value.strip() - - @property - def description(self): - " Полное наименование " - return self._description - - @description.setter - def description(self, value: str): - " Полное наименование " - exception_proxy.validate( value.strip(), str) - self._description = value.strip() - - @property - def id(self): - " Уникальный код записи " - return str(self._id.hex) - - @id.setter - def id(self, value:uuid.UUID ): - " Уникальный код записи " - exception_proxy.validate(value, uuid.UUID) - self._id = value - - @property - def is_error(self): - " Флаг. Есть ошибка " - return self._error.error != "" - - def load(self, source: dict): - """ - Десериализовать свойства - Args: - source (dict): исходный слова - """ - if source is None: - return None - - if len(source) == 0: - raise argument_exception("Некорректно переданы параметры!") - - source_fields = ["id", "name", "description"] - if set(source_fields).issubset(list(source.keys())) == False: - raise operation_exception(f"Невозможно загрузить данные в объект. {source}!") - - self._id = uuid.UUID( source["id"]) - self._name = source["name"] - self._description = source["description"] - - return self - - - @staticmethod - def create_dictionary(items: list): - """ - Сформировать словарь из списка элементов reference - Args: - items (list): _description_ - """ - exception_proxy.validate(items, list) - - result = {} - for position in items: - result[ position.name ] = position - - return result - - @staticmethod - def create_fields(source) -> list: - """ - Сформировать список полей от объекта типа reference - Args: - source (_type_): _description_ - - Returns: - list: _description_ - """ - - if source is None: - raise argument_exception("Некорректно переданы параметры!") - - items = list(filter(lambda x: not x.startswith("_") and not x.startswith("create_") , dir(source))) - result = [] - - for item in items: - attribute = getattr(source.__class__, item) - if isinstance(attribute, property): - result.append(item) - - return result - - def __str__(self) -> str: - """ - Изменим строковое представление класса - Returns: - str: _description_ - """ - return self.id - - def __hash__(self) -> int: - """ - Формирование хеш по коду - Returns: - int: _description_ - """ - return hash(self.id) - - - - - - - - - \ No newline at end of file diff --git a/Result9/Src/settings.json b/Result9/Src/settings.json deleted file mode 100644 index f62e706..0000000 --- a/Result9/Src/settings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "block_period": "2021-01-01", - "inn": 3800020344, - "is_first_start": false, - "report_mode": "json", - "short_name": "ООО Ромашка" -} \ No newline at end of file diff --git a/Result9/Src/settings.py b/Result9/Src/settings.py deleted file mode 100644 index e774fe9..0000000 --- a/Result9/Src/settings.py +++ /dev/null @@ -1,99 +0,0 @@ -from Src.exceptions import exception_proxy, argument_exception -from datetime import datetime -from Src.Logics.Services.storage_observer import storage_observer -from Src.Models.event_type import event_type - -# -# Класс для описания настроек -# -class settings(): - _inn = 0 - _short_name = "" - _first_start = True - _mode = "csv" - _block_period = datetime.now - - - @property - def inn(self): - """ - ИНН - Returns: - int: - """ - return self._inn - - @inn.setter - def inn(self, value: int): - exception_proxy.validate(value, int) - self._inn = value - - @property - def short_name(self): - """ - Короткое наименование организации - Returns: - str: - """ - return self._short_name - - @short_name.setter - def short_name(self, value:str): - exception_proxy.validate(value, str) - self._short_name = value - - - @property - def is_first_start(self): - """ - Флаг Первый старт - """ - return self._first_start - - @is_first_start.setter - def is_first_start(self, value: bool): - self._first_start = value - - @property - def report_mode(self): - """ - Режим построения отчетности - Returns: - _type_: _description_ - """ - return self._mode - - - @report_mode.setter - def report_mode(self, value: str): - exception_proxy.validate(value, str) - - self._mode = value - - - @property - def block_period(self): - """ - Дата блокировки периода - """ - return self._block_period - - @block_period.setter - def block_period(self, value): - if isinstance(value, datetime): - self._block_period = value - return - - legacy_period = self._block_period - - if isinstance(value, str): - try: - self._block_period = datetime.strptime(value, "%Y-%m-%d") - except Exception as ex: - raise argument_exception(f"Невозможно сконвертировать сроку в дату! {ex}") - else: - raise argument_exception("Некорректно переданы параметры!") - - if legacy_period == self._block_period: - storage_observer.raise_event( event_type.changed_block_period() ) - diff --git a/Result9/Src/settings_manager.py b/Result9/Src/settings_manager.py deleted file mode 100644 index 12514cd..0000000 --- a/Result9/Src/settings_manager.py +++ /dev/null @@ -1,143 +0,0 @@ -import os -import json -import uuid - -from Src.settings import settings -from Src.errors import error_proxy -from Src.exceptions import exception_proxy, operation_exception -from Src.Logics.convert_factory import convert_factory - -# -# Менеджер настроек -# -class settings_manager(object): - # Наименование файла по умолчанию - _settings_file_name = "settings.json" - # Словарь с исходными данными - _data = None - # Внутренний уникальный номер - _uniqueNumber = None - # Данные с настройками - _settings = None - # Описание ошибок - _error = error_proxy() - - def __new__(cls): - if not hasattr(cls, 'instance'): - cls.instance = super(settings_manager, cls).__new__(cls) - return cls.instance - - - def __init__(self): - if self._uniqueNumber is None: - self._uniqueNumber = uuid.uuid4() - self.open(self._settings_file_name) - - # После загрузки создаем объект класса settings - self._settings = settings() - self.__load() - - - def __open(self): - """ - Открыть файл с настройками - """ - file_path = os.path.split(__file__) - settings_file = "%s/%s" % (file_path[0], self._settings_file_name) - if not os.path.exists(settings_file): - self._error.set_error( Exception("ERROR: Невозможно загрузить настройки! Не найден файл %s", settings_file)) - - try: - with open(settings_file, "r") as read_file: - self._data = json.load(read_file) - except: - self._error.set_error( Exception("ERROR: Невозможно загрузить настройки! Не найден файл %s", settings_file)) - - def open(self, file_name: str): - """ - Открыть файл с настройками - Args: - file_name (str): - """ - exception_proxy.validate( file_name, str) - - legacy_file_name = self._settings_file_name - self._settings_file_name = file_name - self.__open() - self.__load() - - # Восстанавливаем старое наименование файлв - self._settings_file_name = legacy_file_name - - - def __load(self): - """ - Private: Загрузить словарь в объект - """ - - if len(self._data) == 0: - return - - # Список полей от типа назначения - fields = list(filter(lambda x: not x.startswith("_"), dir(self._settings.__class__))) - - # Заполняем свойства - for field in fields: - keys = list(filter(lambda x: x == field, self._data.keys())) - if len(keys) != 0: - value = self._data[field] - - # Если обычное свойство - заполняем. - if not isinstance(value, list) and not isinstance(value, dict): - setattr(self._settings, field, value) - - def save(self) -> bool: - """ - Сохранить настройки - """ - try: - factory = convert_factory() - - file_path = os.path.split(__file__) - settings_file = "%s/%s" % (file_path[0], self._settings_file_name) - - with open(settings_file, "w") as write_file: - data = factory.serialize( self._settings ) - json_text = json.dumps(data, sort_keys = True, indent = 4, ensure_ascii = False) - write_file.write(json_text) - - return True - except Exception as ex: - raise operation_exception(f"Ошибка при записи файла {self.__storage_file}\n{ex}") - - - - @property - def settings(self) -> settings: - """ - Текущие настройки в приложении - Returns: - settings: _ - """ - return self._settings - - @property - def data(self): - """ - Словарь, который содержит данные из настроек - Returns: - dict: - """ - return self._data - - @property - def error(self) -> error_proxy: - """ - Текущая информация об ошибке - Returns: - error_proxy: - """ - return self._error - - - \ No newline at end of file diff --git a/Result9/Src/test.json b/Result9/Src/test.json deleted file mode 100644 index f62e706..0000000 --- a/Result9/Src/test.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "block_period": "2021-01-01", - "inn": 3800020344, - "is_first_start": false, - "report_mode": "json", - "short_name": "ООО Ромашка" -} \ No newline at end of file diff --git a/Result9/Tst/convert_test.py b/Result9/Tst/convert_test.py deleted file mode 100644 index 5663437..0000000 --- a/Result9/Tst/convert_test.py +++ /dev/null @@ -1,162 +0,0 @@ -from Src.Logics.start_factory import start_factory -from Src.Logics.convert_factory import convert_factory -from Src.Models.nomenclature_model import nomenclature_model -from Src.Models.receipe_model import receipe_model -from Src.Storage.storage import storage -from Src.settings_manager import settings_manager - -import unittest -import json - -class convert_test(unittest.TestCase): - - # - # Проверить загрузку одного элемента номенклатуры в объект - # - def test_check_load_nomenclature(self): - try: - with open("nomenclature_deserialize.json", "r") as read_file: - # Подготовка - source = json.load(read_file) - nomenclature = nomenclature_model() - - # Действие - result = nomenclature.load(source) - - # Проверки - assert result is not None - assert result.id == "8446fdc4ce4441d8b1dcaeedb6a676c4" - - except Exception as ex: - raise Exception(f"Ошибка: {ex}") - - # - # Проверить загрузку элемента рецепта в объект - # - def test_check_load_receipt(self): - try: - with open("receipt_deserialize.json", "r") as read_file: - # Подготовка - source = json.load(read_file) - receipt = receipe_model() - - # Действие - result = receipt.load(source) - - # Проверки - assert result is not None - assert len(result.consist) > 0 - - except Exception as ex: - raise Exception(f"Ошибка: {ex}") - - # - # Проверить формирование словаря и преобразование в json номенклатуры - # - def test_check_serialize_nomenclature(self): - # Подготовка - items = start_factory.create_nomenclatures() - factory = convert_factory() - if len(items) == 0: - raise Exception("Список номенклатуры пуст!") - - item = items[0] - - # Действие - result = factory.serialize(item) - - # Проверки - assert result is not None - json_text = json.dumps(result, sort_keys = True, indent = 4) - - file = open("nomenclature.json", "w") - file.write(json_text) - file.close() - - # - # Проверить формирование словаря по списку номенклатуры и конвертацию в json - # - def test_check_serialize_nomenctalures(self): - # Подготовка - items = start_factory.create_nomenclatures() - factory = convert_factory() - - # Действие - result = factory.serialize(items) - - # Проверки - assert result is not None - json_text = json.dumps(result, sort_keys = True, indent = 4) - - file = open("nomenclatures.json", "w") - file.write(json_text) - file.close() - - # - # Проверить формирование словаря по списку рецептов и конвертация в json - # - def test_check_serialize_receipts(self): - # Подготовка - items = start_factory.create_receipts() - factory = convert_factory() - - # Действие - result = factory.serialize(items) - - # Проверки - assert result is not None - json_text = json.dumps(result, sort_keys = True, indent = 4) - - file = open("receipts.json", "w") - file.write(json_text) - file.close() - - # - # Выгрузить один рецепт - # - def test_check_serialize_receipt(self): - # Подготовка - options = settings_manager() - start = start_factory( options.settings ) - start.create() - factory = convert_factory() - items = start.storage.data[ storage.receipt_key() ] - if len(items) == 0: - raise Exception("Набор рецептов пуст!") - item = items[0] - - # Действие - result = factory.serialize(item) - - # Проверки - assert result is not None - json_text = json.dumps(result, sort_keys = True, indent = 4) - - file = open("receipt_deserialize.json", "w") - file.write(json_text) - file.close() - - # - # Выгрузить одну транзакцию - # - def test_check_serialize_transaction(self): - # Подготовка - options = settings_manager() - start = start_factory( options.settings ) - start.create() - factory = convert_factory() - items = start.storage.data[ storage.storage_transaction_key() ] - if len(items) == 0: - raise Exception("Набор транзакций пуст!") - item = items[0] - - # Действие - result = factory.serialize(item) - - # Проверки - assert result is not None - json_text = json.dumps(result, sort_keys = True, indent = 4) - - file = open("transaction_deserialize.json", "w") - file.write(json_text) - file.close() diff --git a/Result9/Tst/error_test.py b/Result9/Tst/error_test.py deleted file mode 100644 index 1f106b9..0000000 --- a/Result9/Tst/error_test.py +++ /dev/null @@ -1,37 +0,0 @@ -from Src.errors import error_proxy -import unittest - -# -# Набор автотестов для проверки работы класса error_proxy -# -class error_proxy_test(unittest.TestCase): - - # - # Проверить простой сбособ создания объекта с ошибкой - # - def test_create_error_proxy(self): - # Подготовка - proxy = error_proxy() - - # Действие - proxy.error = "test" - - # Проверка - assert proxy.error == "test" - - def test_create_exception_error_proxy(self): - # Подготовка - proxy = error_proxy() - - try: - # Действие - proxy.error = "" - except Exception as ex: - proxy.set_error(ex) - - # Проверка - print(proxy.error) - assert proxy.error != "" - - - \ No newline at end of file diff --git a/Result9/Tst/factory_test.py b/Result9/Tst/factory_test.py deleted file mode 100644 index daf539e..0000000 --- a/Result9/Tst/factory_test.py +++ /dev/null @@ -1,121 +0,0 @@ -from Src.Logics.start_factory import start_factory -from Src.settings_manager import settings_manager -from Src.Storage.storage import storage -from Src.Logics.report_factory import report_factory - -import unittest - -# -# Набор автотестов для проверки работы фабричного метода -# -class factory_test(unittest.TestCase): - - # - # Проверить метод storage_keys в хранилище - # - def test_check_method_storage_keys(self): - # Подготовка - manager = settings_manager() - start = start_factory( manager.settings ) - start.create() - - # Действия - result = start.storage.storage_keys( start.storage ) - - # Проверки - assert result is not None - assert len(result) > 0 - - # - # Проверка работы фабрики для построения отчетности - # - def test_check_report_factory_create(self): - # Подготовка - manager = settings_manager() - start = start_factory( manager.settings ) - start.create() - factory = report_factory() - key = storage.unit_key() - - # Действие - report = factory.create( - manager.settings.report_mode, - start.storage.data) - - # Проверки - assert report is not None - print ( report.create(key) ) - - # - # Проверка создания начальных рецептов - # - def test_check_create_receipts(self): - # Подготовка - items = start_factory.create_receipts() - - # Действие - - # Проверки - assert len(items) > 0 - - # - # Проверка создание начальной номенклатуры - # - def test_check_create_nomenclatures(self): - # Подготовка - items = start_factory.create_nomenclatures() - - # действие - - # Прверки - assert len(items) > 0 - - - # - # Проверка создание списка единиц измерения - # - def test_check_create_units(self): - # Подготовка - items = start_factory.create_units() - - # Действие - - # Проверки - assert len(items) > 0 - - # - # Проверка создания списка групп - # - def test_check_create_groups(self): - # Подготовка - items = start_factory.create_groups() - - # Действие - - # Проверки - assert len(items) > 0 - - - # - # Проверка работы класса start_factory. Метод create - # - def test_check_factory_create(self): - # Подготовка - manager = settings_manager() - factory = start_factory( manager.settings ) - - - # Действие - factory.create() - - # Проверка - assert not factory.storage is None - assert storage.nomenclature_key() in factory.storage.data - assert storage.receipt_key() in factory.storage.data - assert storage.group_key() in factory.storage.data - assert storage.unit_key() in factory.storage.data - assert storage.storage_transaction_key() in factory.storage.data - - - - diff --git a/Result9/Tst/nomenclature_test.py b/Result9/Tst/nomenclature_test.py deleted file mode 100644 index 4e54778..0000000 --- a/Result9/Tst/nomenclature_test.py +++ /dev/null @@ -1,53 +0,0 @@ -from Src.Models.group_model import group_model -from Src.Models.nomenclature_model import nomenclature_model -from Src.Models.unit_model import unit_model -from Src.exceptions import argument_exception - -import unittest - -# -# Набор автотестов для проверки работы моделей связанных с номенклатурой -# -class nomenclature_test(unittest.TestCase): - - # - # Проверить создание новой карточки номенклатуры - # - def test_create_nomenclature(self): - # Подготовка - group = group_model("test group") - item = nomenclature_model("test") - unit = unit_model("test unit") - - # Действие - item.description = "test description" - item.group = group - item.unit = unit - - # Проверка - print(item.unit) - assert item is not None - - # - # Проверить создание новой карточки номенклатуры с ошибкой - # - def test_create_nomenclature_fail_name(self): - # Подготовка - group = group_model("test group") - item = nomenclature_model("test nomenclature") - unit = unit_model("test unit") - item.description = "test description" - item.group = group - item.unit = unit - - # Действие - with self.assertRaises(argument_exception) as context: - item.name = "11111111111111111111111111111111111111111111111111111111111111111111111111" - - # Проверка - self.assertTrue(f'Есть нужное исключение - {context.exception}') - - - -if __name__ == '__main__': - unittest.main() \ No newline at end of file diff --git a/Result9/Tst/processing_test.py b/Result9/Tst/processing_test.py deleted file mode 100644 index ab2aa56..0000000 --- a/Result9/Tst/processing_test.py +++ /dev/null @@ -1,99 +0,0 @@ -import unittest -from Src.Logics.start_factory import start_factory -from Src.settings_manager import settings_manager -from Src.Logics.process_factory import process_factory -from Src.Storage.storage import storage -from Src.Logics.Processings.processing import processing -from Src.Models.storage_row_turn_model import storage_row_turn_model -from Src.exceptions import operation_exception -from Src.Models.storage_model import storage_model -from Src.Models.unit_model import unit_model - -# -# Набор содульных тестов для проверки процессов обработки данных -# -class processing_test(unittest.TestCase): - - # - # Проверить работу фабрики процессов - # Запустить расчет складских оборотов - # - def test_check_process_factory(self): - # Подготовка - manager = settings_manager() - start = start_factory(manager.settings) - start.create() - factory = process_factory() - - # Действие - result = factory.create( process_factory.turn_key() ) - - # Проверка - assert result is not None - - - # - # Проверить работу процесса расчета оборотов - # - def test_check_process_turns(self): - # Подготовка - manager = settings_manager() - start = start_factory(manager.settings) - start.create() - factory = process_factory() - key = storage.storage_transaction_key() - transactions = start.storage.data[ key ] - processing = factory.create( process_factory.turn_key() ) - - # Действие - result = processing().process(transactions) - - # Проверка - assert result is not None - assert len(result) > 0 - turn = list(filter(lambda x: x.nomenclature.name == "Сыр Пармезан", result )) - assert turn[0].value == 0.5 - - # - # Проверить работы агрегирования оборотов. - # В сохраненных данных есть оборот которого нет в рассчитанный - # - def test_check_aggregate_turns(self): - # Подготовка - default_storage = storage_model.create_default() - manager = settings_manager() - start = start_factory(manager.settings) - start.create() - nomenclatures = start.storage.data[ storage.nomenclature_key()] - if len(nomenclatures) == 0: - raise operation_exception("Список номенклатуры пуст!") - - # Создаем тестовый оборот и добавляем его в хранилище - turn = storage_row_turn_model() - turn.nomenclature = nomenclatures[0] - turn.storage = default_storage - turn.unit = unit_model.create_killogram() - turn.value = 1 - - start.storage.data[ storage.blocked_turns_key() ] = [] - start.storage.data[ storage.blocked_turns_key() ].append( turn) - - # Получаем процессы агрегации и расчета оборотов - factory = process_factory() - aggregate_processing = factory.create( process_factory.aggregate_key() ) - turn_processing = factory.create( process_factory.turn_key() ) - calculated_turns = turn_processing().process( start.storage.data[ storage.storage_transaction_key() ] ) - calculated_len = len(calculated_turns) - - # Действие - result = aggregate_processing().process( calculated_turns ) - - # Проверки - assert result is not None - assert calculated_len + 1 == len(result) - - - - - - \ No newline at end of file diff --git a/Result9/Tst/prototype_test.py b/Result9/Tst/prototype_test.py deleted file mode 100644 index 0d98732..0000000 --- a/Result9/Tst/prototype_test.py +++ /dev/null @@ -1,61 +0,0 @@ -from Src.Logics.storage_prototype import storage_prototype -from Src.Logics.start_factory import start_factory -from Src.settings_manager import settings_manager -from Src.Storage.storage import storage -from Src.exceptions import operation_exception - -from datetime import datetime -import unittest - -class prototype_test(unittest.TestCase): - - # - # Проверить метод filter_by_period - # - def test_check_filter_by_period(self): - # Подготовка - manager = settings_manager() - start = start_factory(manager.settings) - start.create() - key = storage.storage_transaction_key() - data = start.storage.data[ key ] - - start_date = datetime.strptime("2024-01-01", "%Y-%m-%d") - stop_date = datetime.strptime("2024-01-10", "%Y-%m-%d") - prototype = storage_prototype( data) - - - # Дейтсвие - result = prototype.filter_by_period( start_date, stop_date ) - - # Проверка - assert isinstance(result, storage_prototype) - assert prototype.is_empty == True - assert len(result.data) > 0 - - # - # Проверить метод filter_by_nomenclature - # - def test_check_filter_by_nomenclature(self): - # Подготовка - manager = settings_manager() - start = start_factory(manager.settings) - start.create() - key = storage.storage_transaction_key() - data = start.storage.data[ key ] - if len(data) == 0: - raise operation_exception("Данные не подготовлены!") - - element = data[0] - nomenclature = element.nomenclature - prototype = storage_prototype( data) - - # Действие - result = prototype.filter_by_nomenclature( nomenclature ) - - # Проверка - assert isinstance(result, storage_prototype) - assert prototype.is_empty == True - assert len(result.data) > 0 - - \ No newline at end of file diff --git a/Result9/Tst/reporting_test.py b/Result9/Tst/reporting_test.py deleted file mode 100644 index 7a2a245..0000000 --- a/Result9/Tst/reporting_test.py +++ /dev/null @@ -1,166 +0,0 @@ -import unittest -from Src.Logics.reporting import reporting -from Src.Models.unit_model import unit_model -from Src.Storage.storage import storage -from Src.Logics.csv_reporting import csv_reporting -from Src.Models.nomenclature_model import nomenclature_model -from Src.Models.group_model import group_model -from Src.Logics.markdown_reporting import markdown_reporting -from Src.Logics.json_reporting import json_reporting -from Src.Logics.start_factory import start_factory -from Src.settings import settings - - -class reporting_test(unittest.TestCase): - - - def test_check_json_reporting_build(self): - # Подготовка - data = {} - list = [] - item = unit_model.create_gram() - list.append(item) - key = storage.unit_key() - data[ key ] = list - report = json_reporting( data ) - - # Действие - result = report.create( key ) - - # Проверки - assert result is not None - assert len(result) > 0 - - # - # Проверить формирование рецептов в формате csv - # - def test_check_csv_create_receipe_key(self): - # Подготовка - optiins = settings() - start = start_factory( optiins ) - start.create() - key = storage.receipt_key() - report = csv_reporting( start.storage.data ) - - # Действие - result = report.create( key ) - - # Проверки - assert result is not None - assert len(result) > 0 - - # - # Проверить формирование рецептов в формате Markdown - # - def test_check_markdown_create_receipt_key(self): - # Подготовка - optiins = settings() - start = start_factory( optiins ) - start.create() - key = storage.receipt_key() - report = markdown_reporting( start.storage.data ) - - # Действие - result = report.create( key ) - - # Проверки - assert result is not None - assert len(result) > 0 - - - - # - # Проверить статический метод build класса reporting - # - def test_check_reporting_build(self): - # Подготовка - data = {} - list = [] - item = unit_model.create_gram() - list.append(item) - data[ storage.unit_key() ] = list - - # Дейстие - result = reporting.build( storage.unit_key(), data ) - - # Проверки - assert result is not None - assert len(result) > 0 - - - # - # Проверить формированеи отчета в csv формате по единицам измерения - # - def test_check_csv_create_unit_key(self): - # Подготовка - data = {} - list = [] - item = unit_model.create_gram() - list.append(item) - key = storage.unit_key() - data[ key ] = list - report = csv_reporting( data ) - - # Действие - result = report.create( key ) - - # Проверки - assert result is not None - assert len(result) > 0 - - - # - # Проверить формирование отчета в csv формате по номенклатуре - # - def test_check_csv_create_nomenclature_key(self): - # Подготовка - data = {} - list = [] - - unit = unit_model.create_killogram() - group = group_model.create_default_group() - item = nomenclature_model("Тушка бройлера", group, unit ) - item.description = "Ингредиент для салата" - list.append(item) - - key = storage.nomenclature_key() - - data[ key ] = list - report = csv_reporting( data ) - - # Действие - result = report.create( key ) - - # Проверки - assert result is not None - assert len(result) > 0 - - file = open("csv_report.csv", "w") - file.write(result) - file.close() - - - # - # Проверить формитирование отчета в markdown формате по ед / измерениям - # - def test_check_markdown_create_unit_key(self): - # Подготовка - data = {} - list = [] - item = unit_model.create_gram() - list.append(item) - key = storage.unit_key() - data[ key ] = list - report = markdown_reporting( data ) - - # Действие - result = report.create( key ) - - # Проверки - assert result is not None - assert len(result) > 0 - - file = open("markdown_report.md", "w") - file.write(result) - file.close() - \ No newline at end of file diff --git a/Result9/Tst/service_test.py b/Result9/Tst/service_test.py deleted file mode 100644 index 20ed6ce..0000000 --- a/Result9/Tst/service_test.py +++ /dev/null @@ -1,273 +0,0 @@ -from Src.Logics.Services.storage_service import storage_service -from Src.Logics.start_factory import start_factory -from Src.settings_manager import settings_manager -from Src.Storage.storage import storage -from Src.exceptions import operation_exception -from Src.Logics.Services.reference_service import reference_service -from Src.Logics.convert_factory import convert_factory -from Src.Models.nomenclature_model import nomenclature_model -from Src.Logics.Services.storage_observer import storage_observer -from Src.Models.event_type import event_type -from Src.Logics.Services.post_processing_service import post_processing_service - -from datetime import datetime -import unittest -import uuid - -class service_test(unittest.TestCase): - # - # Проверить добавление reference (номенклатура) - # - def test_check_add_item_reference(self): - # Подготовка - manager = settings_manager() - start = start_factory(manager.settings) - start.create() - key = storage.nomenclature_key() - data = start.storage.data[ key ] - convert = convert_factory() - - if len(data) == 0: - raise operation_exception("Некорректно сформирован набор данных!") - - # Создаем новый элемент номенклатуры - dict = convert.serialize( data[0] ) - item = nomenclature_model().load(dict) - item.id = uuid.uuid4() - - service = reference_service(data) - start_len = len(data) - - # Действие - result = service.add( item ) - - # Проверка - assert result == True - assert len(data) - 1 == start_len - - # - # Проверить изменение reference (номенклатуры) - # - def test_check_change_item_reference(self): - # Подготовка - manager = settings_manager() - start = start_factory(manager.settings) - start.create() - key = storage.nomenclature_key() - data = start.storage.data[ key ] - convert = convert_factory() - - if len(data) == 0: - raise operation_exception("Некорректно сформирован набор данных!") - - # Создаем новый элемент номенклатуры - dict = convert.serialize( data[0] ) - item = nomenclature_model().load(dict) - item.name = "test" - - service = reference_service(data) - start_len = len(data) - - # Действие - result = service.change( item ) - - # Проверка - assert result == True - assert len(data) == start_len - - - # - # Проверить работу метода create_turns - # - def test_check_create_turns(self): - # Подготовка - manager = settings_manager() - start = start_factory(manager.settings) - start.create() - key = storage.storage_transaction_key() - data = start.storage.data[ key ] - service = storage_service(data) - start_date = datetime.strptime("2024-01-01", "%Y-%m-%d") - stop_date = datetime.strptime("2024-01-10", "%Y-%m-%d") - - # Действие - result = service.create_turns(start_date, stop_date) - - # Проверки - assert len(result) > 0 - - # - # Проверить метод create_turns_by_nomenclature - # - def test_check_create_turns_by_nomenclature(self): - # Подготовка - manager = settings_manager() - start = start_factory(manager.settings) - start.create() - key = storage.storage_transaction_key() - data = start.storage.data[ key ] - service = storage_service(data) - start_date = datetime.strptime("2024-01-01", "%Y-%m-%d") - stop_date = datetime.strptime("2024-01-30", "%Y-%m-%d") - - if len(data) == 0: - raise operation_exception("Набор данных пуст!") - - nomenclature = data[0].nomenclature - - # Действие - result = service.create_turns_by_nomenclature(start_date, stop_date, nomenclature ) - - # Проверки - assert len(result) == 1 - - # - # Проверить метод turns_only_nomenclature - # - def test_check_create_turns_only_nomenclature(self): - # Подготовка - manager = settings_manager() - start = start_factory(manager.settings) - start.create() - key = storage.storage_transaction_key() - data = start.storage.data[ key ] - service = storage_service(data) - - if len(data) == 0: - raise operation_exception("Набор данных пуст!") - - nomenclature = data[0].nomenclature - - # Действие - result = service.create_turns_only_nomenclature( nomenclature ) - - # Проверки - assert len(result) > 0 - - # - # Проверить работу метода create_turns_by_receipt - # - def test_check_create_turns_by_receipt(self): - # Подготовка - manager = settings_manager() - start = start_factory(manager.settings) - start.create() - key = storage.storage_transaction_key() - transactions_data = start.storage.data[ key ] - service = storage_service(transactions_data) - - if len(transactions_data) == 0: - raise operation_exception("Набор данных пуст!") - - key = storage.receipt_key() - receipts_data = start.storage.data[ key ] - - if len(receipts_data) == 0: - raise operation_exception("Набор данных пуст!") - - receipt = receipts_data[0] - - # Действие - result = service.create_turns_by_receipt(receipt) - - # Проверки - assert len(result) > 0 - - # - # Проверить метод build_debits_by_receipt. Ошибочный сценарий. - # - def test_check_build_debits_by_receipt_fail(self): - # Подготовка - manager = settings_manager() - start = start_factory(manager.settings) - start.create() - key = storage.storage_transaction_key() - transactions_data = start.storage.data[ key ] - service = storage_service(transactions_data) - - if len(transactions_data) == 0: - raise operation_exception("Набор данных пуст!") - - key = storage.receipt_key() - receipts_data = start.storage.data[ key ] - - if len(receipts_data) == 0: - raise operation_exception("Набор данных пуст!") - - # -> Цезарь с курицей - receipt = receipts_data[1] - - # Действие и проверка - with self.assertRaises(operation_exception): - service.build_debits_by_receipt( receipt ) - - - # - # Проверить метод build_debits_by_receipt. Корректный сценарий - # - def test_check_build_debits_by_receipt_pass(self): - # Подготовка - manager = settings_manager() - start = start_factory(manager.settings) - start.create() - key = storage.storage_transaction_key() - transactions_data = start.storage.data[ key ] - start_len_transaction = len(transactions_data) - service = storage_service(transactions_data) - - if len(transactions_data) == 0: - raise operation_exception("Набор данных пуст!") - - key = storage.receipt_key() - receipts_data = start.storage.data[ key ] - - if len(receipts_data) == 0: - raise operation_exception("Набор данных пуст!") - - # -> Вафли хрустящие в вафильнице - receipt = receipts_data[0] - - # Действие и проверка - service.build_debits_by_receipt( receipt ) - stop_len_transaction = len(start.storage.data[ storage.storage_transaction_key() ]) - - # Проверка (транзакций должно быть больше) - assert start_len_transaction < stop_len_transaction - - - def test_check_observer_blocked_period(self): - # Подготовка - manager = settings_manager() - start = start_factory(manager.settings) - start.create() - key = storage.storage_transaction_key() - transactions_data = start.storage.data[ key ] - service = storage_service(transactions_data) - - # Действие - try: - storage_observer.raise_event( event_type.changed_block_period() ) - pass - except Exception as ex: - print(f"ex") - - - def test_check_observer_nomenclature_deleted(self): - # Подготовка - manager = settings_manager() - start = start_factory(manager.settings) - start.create() - nomenclature = nomenclature_model() - service = reference_service(nomenclature) - - # Действие - try: - storage_observer.raise_event( event_type.nomenclature_deleted() ) - service.delete(nomenclature) - pass - except Exception as ex: - print(f"{ex}") - - # Проверка - assert nomenclature is None - \ No newline at end of file diff --git a/Result9/Tst/settings_test.py b/Result9/Tst/settings_test.py deleted file mode 100644 index 9a687af..0000000 --- a/Result9/Tst/settings_test.py +++ /dev/null @@ -1,106 +0,0 @@ -from Src.settings_manager import settings_manager -import unittest -from datetime import datetime - -from Src.Logics.Services.storage_service import storage_service -from Src.Logics.start_factory import start_factory -from Src.Storage.storage import storage -from Src.exceptions import operation_exception -from Src.Logics.Services.reference_service import reference_service -from Src.Logics.convert_factory import convert_factory -from Src.Logics.Services.storage_observer import storage_observer -from Src.Models.event_type import event_type - - -# -# Набор автотестов для проверки работы модуmeля настроек -# -class settings_test(unittest.TestCase): - - # - # Проверить действие наблюдателя при изменениях даты блокировки - # - def test_check_changed_block_period(self): - - # Подготовка - manager = settings_manager() - start = start_factory(manager.settings) - start.create() - key = storage.storage_transaction_key() - transactions_data = start.storage.data[ key ] - service = storage_service(transactions_data) - - # Действие - try: - manager.settings.block_period = datetime.strptime("2021-01-01", "%Y-%m-%d") - pass - except Exception as ex: - print(f"{ex}") - - # - # Проверить сохранение настроек - # - def test_save_settings(self): - # Подготовка - manager = settings_manager() - manager.settings.block_period = datetime.strptime("2021-01-01", "%Y-%m-%d") - - # Действие - result = manager.save() - - # Проверки - assert result == True - - - # - # Проверить на корректность создания и загрузки файла с настройками - # - def test_create_app_settings(self): - # Подготовка - manager = settings_manager() - - # Действие - result = manager.data - - # Проверки - print(manager.data) - print(type(manager.data)) - assert result is not None - assert manager.settings.inn > 0 - assert manager.settings.short_name != "" - - # - # Проверить тип создания объекта как singletone - # - def test_double_create_app_setting(self): - # Подготовка - manager1 = settings_manager() - manager2 = settings_manager() - - # Действие - - # Проверки - print(manager1._uniqueNumber) - print(manager2._uniqueNumber) - assert manager1._uniqueNumber == manager2._uniqueNumber - - # - # Проверить работу менеджера загрузки настроек при не корректном файле настроек - # - def test_fail_open_settings(self): - # Подготовка - manager = settings_manager() - - # Действие - manager.open("test.json") - - # Проверки - assert manager.error.is_empty == False - - - - - - - - diff --git a/Result9/Tst/storage_test.py b/Result9/Tst/storage_test.py deleted file mode 100644 index 0dde890..0000000 --- a/Result9/Tst/storage_test.py +++ /dev/null @@ -1,41 +0,0 @@ -import unittest -from Src.settings_manager import settings_manager -from Src.Logics.start_factory import start_factory -from Src.exceptions import operation_exception - -class storage_test(unittest.TestCase): - - # - # Проверить сохранение данных - # - def test_check_save(self): - # Подготовка - options = settings_manager() - start = start_factory(options.settings) - start.create() - storage = start.storage - - # Действие - result = storage.save() - - # Проверки - assert result == True - - # - # Проверить загрузку данных - # - def test_check_load(self): - # Подготовка - options = settings_manager() - start = start_factory(options.settings) - start.create() - storage = start.storage - - - # Действие - storage.load() - - # Проверки - assert len(storage.data) > 0 - - diff --git a/Result9/csv_report.csv b/Result9/csv_report.csv deleted file mode 100644 index c0a6851..0000000 --- a/Result9/csv_report.csv +++ /dev/null @@ -1,2 +0,0 @@ -description;group;id;is_error;name;unit -Ингредиент для салата;1c9b78ecc0cf4465ac07a682a518ad86;0d6674afc728471b99b30ced559b87bb;True;Тушка бройлера;c9161d641ac44f1f80d784cc105de86f diff --git a/Result9/main.py b/Result9/main.py deleted file mode 100644 index 331da6c..0000000 --- a/Result9/main.py +++ /dev/null @@ -1,194 +0,0 @@ -from flask import Flask, request -from Src.settings_manager import settings_manager -from Src.Storage.storage import storage -from Src.errors import error_proxy -from Src.Logics.report_factory import report_factory -from Src.Logics.start_factory import start_factory -from datetime import datetime -from Src.Logics.Services.storage_service import storage_service -from Src.Models.nomenclature_model import nomenclature_model -from Src.Logics.Services.service import service -from Src.Logics.Services.reference_service import reference_service - - - -app = Flask(__name__) -app.config['JSON_AS_ASCII'] = False - -# Сформировать начальный набор данных -options = settings_manager() -start = start_factory(options.settings) -start.create() - - -# Отчетность - -@app.route("/api/report/", methods = ["GET"]) -def get_report(storage_key: str): - """ - Сформировать отчет - """ - - keys = storage.storage_keys( start.storage ) - if storage_key == "" or storage_key not in keys: - return error_proxy.create_error_response(app, f"Некорректный передан запрос! Необходимо передать: /api/report/. Список ключей (storage_key): {keys}.", 400) - - # Создаем фабрику - report = report_factory() - data = start.storage.data - - # Формируем результат - try: - result = report.create_response( options.settings.report_mode, data, storage_key, app ) - return result - except Exception as ex: - return error_proxy.create_error_response(app, f"Ошибка при формировании отчета {ex}", 500) - -# Отчетность - -# Складские операции - -@app.route("/api/storage/turns", methods = ["GET"] ) -def get_turns(): - """ - Получить обороты за период - """ - - # Получить параметры - args = request.args - if "start_period" not in args.keys(): - return error_proxy.create_error_response(app, "Необходимо передать параметры: start_period, stop_period!") - - if "stop_period" not in args.keys(): - return error_proxy.create_error_response(app, "Необходимо передать параметры: start_period, stop_period!") - - start_date = datetime.strptime(args["start_period"], "%Y-%m-%d") - stop_date = datetime.strptime(args["stop_period"], "%Y-%m-%d") - - source_data = start.storage.data[ storage.storage_transaction_key() ] - data = storage_service( source_data ).create_turns( start_date, stop_date ) - result = service.create_response( app, data ) - return result - -@app.route("/api/storage//turns", methods = ["GET"] ) -def get_turns_nomenclature(nomenclature_id): - """ - Получить обороты за период и по коду номенклатукры - """ - - # Получить параметры - args = request.args - if "start_period" not in args.keys(): - return error_proxy.create_error_response(app, "Необходимо передать параметры: start_period, stop_period!", 400) - - if "stop_period" not in args.keys(): - return error_proxy.create_error_response(app, "Необходимо передать параметры: start_period, stop_period!", 400) - - try: - start_date = datetime.strptime(args["start_period"], "%Y-%m-%d") - stop_date = datetime.strptime(args["stop_period"], "%Y-%m-%d") - except: - return error_proxy.create_error_response(app, "Некорректно перпеданы параметры: start_period, stop_period", 400) - - transactions_data = start.storage.data[ storage.storage_transaction_key() ] - nomenclature_data = start.storage.data[ storage.nomenclature_key() ] - - nomenclatures = nomenclature_model.create_dictionary( nomenclature_data ) - ids = [item.id for item in nomenclatures.values()] - if nomenclature_id not in ids: - return error_proxy.create_error_response(app, "Некорректно передан код номенклатуры!", 400) - - nomenclature = nomenclatures[nomenclature_id] - - data = storage_service( transactions_data ).create_turns_by_nomenclature( start_date, stop_date, nomenclature ) - result = service.create_response( data, app ) - return result - -# Складские операции - -# Номерклатура -@app.route("/api/nomenclature", methods = ["PUT"]) -def add_nomenclature(): - """ - Добавить номерклатуру - """ - try: - data = request.get_json() - item = nomenclature_model().load(data) - source_data = start.storage.data[ storage.nomenclature_key() ] - result = reference_service( source_data ).add( item ) - return service.create_response( {"result": result} ) - except Exception as ex: - return error_proxy.create_error_response(app, f"Ошибка при добавлении данных!\n {ex}") - - -@app.route("/api/nomenclature", methods = ["DELETE"]) -def delete_nomenclature(): - """ - Удалить номенклатуру - """ - try: - data = request.get_json() - item = nomenclature_model().load(data) - source_data = start.storage.data[ storage.nomenclature_key() ] - result = reference_service( source_data ).delete( item ) - return service.create_response( {"result": result} ) - except Exception as ex: - return error_proxy.create_error_response(app, f"Ошибка при удалении данных!\n {ex}") - - -@app.route("/api/nomenclature", methods = ["PATH"]) -def change_nomenclature(): - """ - Изменить номенклатуру - """ - try: - data = request.get_json() - item = nomenclature_model().load(data) - source_data = start.storage.data[ storage.nomenclature_key() ] - result = reference_service( source_data ).change( item ) - return service.create_response( {"result": result} ) - except Exception as ex: - return error_proxy.create_error_response(app, f"Ошибка при изменении данных!\n {ex}") - -@app.route("/api/nomenclature", methods = ["GET"]) -def get_nomenclature(): - """ - Получить список номенклатуры - """ - args = request.args - if "id" not in args.keys(): - # Вывод всех элементов - source_data = start.storage.data[ storage.nomenclature_key() ] - result = reference_service(source_data ).get() - return service.create_response(app, result) - else: - # Вывод конкретного элемента - try: - source_data = start.storage.data[ storage.nomenclature_key() ] - result = reference_service(source_data ).get_item(args["id"]) - return service.create_response(app, result) - except Exception as ex: - return error_proxy.create_error_response(app, f"Ошибка при получении данных!\n {ex}") - - -# Номенклатура - -@app.route("/api/block_period", methods=["GET"]) -def get_block_period(): - args = request.args - if "period" in args.keys(): - - try: - period = datetime.strptime(args["period"], "%Y-%m-%d") - options.settings.block_period = period - options.save() - except: - return error_proxy.create_error_response(app, "Некорректно перпеданы параметры: period", 400) - - result = [options.settings.block_period.strftime('%Y-%m-%d')] - return service.create_response(app, result) - - -if __name__ == "__main__": - app.run(debug = True) \ No newline at end of file diff --git a/Result9/markdown_report.md b/Result9/markdown_report.md deleted file mode 100644 index ffc2722..0000000 --- a/Result9/markdown_report.md +++ /dev/null @@ -1,4 +0,0 @@ -# unit_model -|base_unit|coefficient|description|id|is_error|name| -|--|--|--|--|--|--| -||1||7ac7371cc3f44255bdd2b26406ff6a10|True|грамм| \ No newline at end of file diff --git a/Result9/nomenclature.json b/Result9/nomenclature.json deleted file mode 100644 index 4385441..0000000 --- a/Result9/nomenclature.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "description": "", - "group": { - "description": "", - "id": "d949c361b783415aae902a113f9f1a9f", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "68f056781b9c4b84a807da07d1a25837", - "is_error": false, - "name": "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "153ab681b116494196f28813a5ab3c74", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "ea40ac391066490cbd1ff03d51fce03d", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } -} \ No newline at end of file diff --git a/Result9/nomenclature_deserialize.json b/Result9/nomenclature_deserialize.json deleted file mode 100644 index cc7b04e..0000000 --- a/Result9/nomenclature_deserialize.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "description": "", - "group": { - "description": "", - "id": "aae8e9c8c1874374a57e80caab3724f3", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "8446fdc4ce4441d8b1dcaeedb6a676c4", - "is_error": false, - "name": "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "9a75a9005f00497c9bebf2e26e1c0b50", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "4296954f7854499a969a7aa73e0ee06f", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } -} \ No newline at end of file diff --git a/Result9/nomenclatures.json b/Result9/nomenclatures.json deleted file mode 100644 index 9f0e6a8..0000000 --- a/Result9/nomenclatures.json +++ /dev/null @@ -1,459 +0,0 @@ -[ - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "6ce4120463424c70a6cd64ea4f5b0744", - "is_error": false, - "name": "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "e5145fa9464e4f219504e9d0c0b7286c", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "69a80144d0774bfe84a3c44519a214b9", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "008efbfd935046179fa57b6cbcf8c4cf", - "is_error": false, - "name": "\u0421\u0430\u0445\u0430\u0440", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "e5145fa9464e4f219504e9d0c0b7286c", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "69a80144d0774bfe84a3c44519a214b9", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "7573e196bb144633b20b7539bddc1125", - "is_error": false, - "name": "\u0421\u043b\u0438\u0432\u043e\u0447\u043d\u043e\u0435 \u043c\u0430\u0441\u043b\u043e", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "e5145fa9464e4f219504e9d0c0b7286c", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "69a80144d0774bfe84a3c44519a214b9", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "f52f3015d17446539539466806a82866", - "is_error": false, - "name": "\u042f\u0439\u0446\u0430", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "3954d1c883864df58de4ba2d7448aa8e", - "is_error": false, - "name": "\u0448\u0442\u0443\u043a\u0430" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "25bac2aaee504df2a582e46f41b67a90", - "is_error": false, - "name": "\u0412\u0430\u043d\u0438\u043b\u0438\u043d", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "18ac789bf337459aac21b1ea03a0a8f6", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "bf9d231c73454fb28e4a73f0fe211088", - "is_error": false, - "name": "\u041a\u0443\u0440\u0438\u043d\u043d\u043e\u0435 \u0444\u0438\u043b\u0435", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "e5145fa9464e4f219504e9d0c0b7286c", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "69a80144d0774bfe84a3c44519a214b9", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "071f1e2b3b154f17a9248663d4928e58", - "is_error": false, - "name": "\u0421\u0430\u043b\u0430\u0442 \u0420\u043e\u043c\u0430\u043d\u043e", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "18ac789bf337459aac21b1ea03a0a8f6", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "8e4075a9e7ea4b379deb0f7be2ff47e5", - "is_error": false, - "name": "\u0421\u044b\u0440 \u041f\u0430\u0440\u043c\u0435\u0437\u0430\u043d", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "e5145fa9464e4f219504e9d0c0b7286c", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "69a80144d0774bfe84a3c44519a214b9", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "67ad382c5e94419cb793b40b5f01d95e", - "is_error": false, - "name": "\u0427\u0435\u0441\u043d\u043e\u043a", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "e5145fa9464e4f219504e9d0c0b7286c", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "69a80144d0774bfe84a3c44519a214b9", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "f769b3a2bb3d4467b7c39675ffa787be", - "is_error": false, - "name": "\u0411\u0435\u043b\u044b\u0439 \u0445\u043b\u0435\u0431", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "e5145fa9464e4f219504e9d0c0b7286c", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "69a80144d0774bfe84a3c44519a214b9", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "798e5e7488fe4a229556fbf2ee6ad728", - "is_error": false, - "name": "\u0421\u043e\u043b\u044c", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "e5145fa9464e4f219504e9d0c0b7286c", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "69a80144d0774bfe84a3c44519a214b9", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "1b976bcdbe914a3f81fb6ad6ba510200", - "is_error": false, - "name": "\u0427\u0435\u0440\u043d\u044b\u0439 \u043f\u0435\u0440\u0435\u0446", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "18ac789bf337459aac21b1ea03a0a8f6", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "1f3d5f94b829448697f24b911a3c3a9b", - "is_error": false, - "name": "\u041e\u043b\u0438\u0432\u043a\u043e\u0432\u043e\u0435 \u043c\u0430\u0441\u043b\u043e", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "0ef5d7fdedaa4dbeba902116f6d83e59", - "is_error": false, - "name": "\u043c\u0438\u043b\u043b\u0438\u043b\u0438\u0442\u0440" - }, - "coefficient": 1000, - "description": "", - "id": "be61d5f9a0d64d1e9664acab55f33cac", - "is_error": false, - "name": "\u043b\u0438\u0442\u0440" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "3d560f3d72d74e85b6df878ead757733", - "is_error": false, - "name": "\u041b\u0438\u043c\u043e\u043d\u043d\u044b\u0439 \u0441\u043e\u043a", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "0ef5d7fdedaa4dbeba902116f6d83e59", - "is_error": false, - "name": "\u043c\u0438\u043b\u043b\u0438\u043b\u0438\u0442\u0440" - }, - "coefficient": 1000, - "description": "", - "id": "be61d5f9a0d64d1e9664acab55f33cac", - "is_error": false, - "name": "\u043b\u0438\u0442\u0440" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "1e3975f4600c4078afc3a56130f4fcea", - "is_error": false, - "name": "\u0413\u043e\u0440\u0447\u0438\u0446\u0430 \u0434\u0438\u0436\u043e\u043d\u0441\u043a\u0430\u044f", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "18ac789bf337459aac21b1ea03a0a8f6", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "6068a0365d2944439e1560f7d6cb6d73", - "is_error": false, - "name": "\u0421\u0430\u0445\u0430\u0440\u043d\u0430\u044f \u043f\u0443\u0434\u0440\u0430", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "18ac789bf337459aac21b1ea03a0a8f6", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "d1e715d6f7a04a0bb7a564f77dfb236f", - "is_error": false, - "name": "\u0412\u0430\u043d\u0438\u043b\u0438\u0438\u043d", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "18ac789bf337459aac21b1ea03a0a8f6", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "5ca1f43a0a50477da8460e9f316a501d", - "is_error": false, - "name": "\u041a\u043e\u0440\u0438\u0446\u0430", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "18ac789bf337459aac21b1ea03a0a8f6", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "52ba9b32e5be4bfa998750ed48ef2f8a", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "64159be615094a028c4cf26071f08a8d", - "is_error": false, - "name": "\u041a\u0430\u043a\u0430\u043e", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "e5145fa9464e4f219504e9d0c0b7286c", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "69a80144d0774bfe84a3c44519a214b9", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - } -] \ No newline at end of file diff --git a/Result9/readme.md b/Result9/readme.md deleted file mode 100644 index dedc6ed..0000000 --- a/Result9/readme.md +++ /dev/null @@ -1,47 +0,0 @@ -# Промежуточный результат -Редакция: **2024-04-14** - -Исходный код по курсу `Шаблоны проектирования` после проведения двух секций занятий. -Разобраны следующие темы: -- Шаблон `Одиночка` -- ООП : `Наследование`, `Инкапсуляция` -- Git : Основы работы -- Json: Основы работы -- Разработаны основные модели данных -- Созданы `Фабричные методы` для генерации данных -- Разработан `абстрактный` класс для формирования отчетности -- Разработаны наборы класссов для выгрузки данных в разныее форматы - * Json - * Csv - * Markdown -- Шаблон `Фабрика` для создания нужного объекта для построение -отчетности. -- Подключена библиотека `Flask` -- Добавлена `Фабрика` для реализации процессов обработки транзакций -- Добавлен расчет оборотов. -- Добавлен универсальный сервис для реализации [CRUD](https://ru.wikipedia.org/wiki/CRUD) операций - * на примере работы с Номенклатурой - * с использованием `Postman` -- Добавлена система [десериализации](https://ru.wikipedia.org/wiki/%D0%A1%D0%B5%D1%80%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F) данных - * на примере класса `storage` - * на примере класса `settings_manager` -- Продемонстрированы варианты работы с интеграционным тестированием -- Реализован механизм хранения и учета предподготовленных данных с - использованием свойства `blocked_period` - -## Структура - -| Файл | Назначение | -|------------------------------|----------------------------------| -| settings_manager.py | Менеджер управления настройками | -| settings.py | Модель настроек | -| reference.py | Абстрактный класс для наследования справочников | -| errors.py | Файл с классами для хранения и обработки ошибок | -| exceptions.py | Файл с классами для генерации собственных исключений | -| .\Models | Набор моделей | -| .\Tst | Модульные тесты `nunit` | -| .\Src\Logics | Набор классов для инкапсуляции бизнес логики | -| .\Src\Data | Наборы классов для хранения данных | -| .\Src\Storage | Каталог для хранения данных | - - diff --git a/Result9/receipt_deserialize.json b/Result9/receipt_deserialize.json deleted file mode 100644 index 6597808..0000000 --- a/Result9/receipt_deserialize.json +++ /dev/null @@ -1,199 +0,0 @@ -{ - "brutto": 251, - "comments": "", - "consist": { - "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f": { - "description": "", - "id": "7c92cf932de44581951ca05738464de2", - "is_error": false, - "name": "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "5c16c9c869c944ca81571f6cf70a790d", - "is_error": false, - "name": "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - "size": 100, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "\u0421\u0430\u0445\u0430\u0440": { - "description": "", - "id": "4f50b78ffb3f43848bdb08279ff95823", - "is_error": false, - "name": "\u0421\u0430\u0445\u0430\u0440", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "e787ff8a837d40418f9121f1272616ab", - "is_error": false, - "name": "\u0421\u0430\u0445\u0430\u0440", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - "size": 80, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "\u0421\u043b\u0438\u0432\u043e\u0447\u043d\u043e\u0435 \u043c\u0430\u0441\u043b\u043e": { - "description": "", - "id": "244833ce4dad4f21bbdddeb0bce73ec2", - "is_error": false, - "name": "\u0421\u043b\u0438\u0432\u043e\u0447\u043d\u043e\u0435 \u043c\u0430\u0441\u043b\u043e", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "998f959f17704d85b0fad15936283032", - "is_error": false, - "name": "\u0421\u043b\u0438\u0432\u043e\u0447\u043d\u043e\u0435 \u043c\u0430\u0441\u043b\u043e", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - "size": 70, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "\u042f\u0439\u0446\u0430": { - "description": "", - "id": "843cebf7329d41de9dff0ae4bdbb43cb", - "is_error": false, - "name": "\u042f\u0439\u0446\u0430", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "8beba2f92e754eeda776859bc197af39", - "is_error": false, - "name": "\u042f\u0439\u0446\u0430", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "f1a87af30e0c4faca9c3184050b3e370", - "is_error": false, - "name": "\u0448\u0442\u0443\u043a\u0430" - } - }, - "size": 1, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "f1a87af30e0c4faca9c3184050b3e370", - "is_error": false, - "name": "\u0448\u0442\u0443\u043a\u0430" - } - } - }, - "description": "", - "id": "89a9226cf02643d8a9e856fbc0ca4351", - "instructions": [ - { - "instructions": { - "instructions": "\u041c\u0430\u0441\u043b\u043e \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u0435 \u0432 \u0441\u043e\u0442\u0435\u0439\u043d\u0438\u043a \u0441 \u0442\u043e\u043b\u0441\u0442\u044b\u043c \u0434\u043d\u043e\u043c. \u0420\u0430\u0441\u0442\u043e\u043f\u0438\u0442\u0435 \u0435\u0433\u043e \u043d\u0430 \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u043e\u043c \u043e\u0433\u043d\u0435 \u043d\u0430 \u043f\u043b\u0438\u0442\u0435, \u043d\u0430 \u0432\u043e\u0434\u044f\u043d\u043e\u0439 \u0431\u0430\u043d\u0435 \u043b\u0438\u0431\u043e \u0432 \u043c\u0438\u043a\u0440\u043e\u0432\u043e\u043b\u043d\u043e\u0432\u043a\u0435." - } - }, - { - "instructions": { - "instructions": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432 \u0442\u0435\u043f\u043b\u043e\u0435 \u043c\u0430\u0441\u043b\u043e \u0441\u0430\u0445\u0430\u0440. \u041f\u0435\u0440\u0435\u043c\u0435\u0448\u0430\u0439\u0442\u0435 \u0432\u0435\u043d\u0447\u0438\u043a\u043e\u043c \u0434\u043e \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u0440\u0430\u0441\u0442\u0432\u043e\u0440\u0435\u043d\u0438\u044f \u0441\u0430\u0445\u0430\u0440\u0430. \u041e\u0442 \u0442\u0435\u043f\u043b\u0430 \u0441\u0430\u0445\u0430\u0440 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u043e \u0440\u0430\u0441\u0442\u0430\u0435\u0442." - } - }, - { - "instructions": { - "instructions": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432 \u043c\u0430\u0441\u043b\u043e \u044f\u0439\u0446\u043e. \u041f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u0441\u0435-\u0442\u0430\u043a\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043c\u0430\u0441\u043b\u043e, \u043d\u0435 \u0433\u043e\u0440\u044f\u0447\u0435\u0435 \u043b\u0438 \u043e\u043d\u043e, \u0438\u043d\u0430\u0447\u0435 \u044f\u0439\u0446\u043e \u043c\u043e\u0436\u0435\u0442 \u0441\u0432\u0430\u0440\u0438\u0442\u044c\u0441\u044f. \u041f\u0435\u0440\u0435\u043c\u0435\u0448\u0430\u0439\u0442\u0435 \u044f\u0439\u0446\u043e \u0441 \u043c\u0430\u0441\u043b\u043e\u043c \u0434\u043e \u043e\u0434\u043d\u043e\u0440\u043e\u0434\u043d\u043e\u0441\u0442\u0438." - } - }, - { - "instructions": { - "instructions": "\u0412\u0441\u044b\u043f\u044c\u0442\u0435 \u043c\u0443\u043a\u0443, \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432\u0430\u043d\u0438\u043b\u0438\u043d." - } - }, - { - "instructions": { - "instructions": "\u041f\u0435\u0440\u0435\u043c\u0435\u0448\u0430\u0439\u0442\u0435 \u043c\u0430\u0441\u0441\u0443 \u0432\u0435\u043d\u0447\u0438\u043a\u043e\u043c \u0434\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0433\u043b\u0430\u0434\u043a\u043e\u0433\u043e \u043e\u0434\u043d\u043e\u0440\u043e\u0434\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430." - } - } - ], - "is_error": false, - "name": "\u0412\u0430\u0444\u043b\u0438 \u0445\u0440\u0443\u0441\u0442\u044f\u0449\u0438\u0435 \u0432 \u0432\u0430\u0444\u0438\u043b\u044c\u043d\u0438\u0446\u0435", - "netto": 0 -} \ No newline at end of file diff --git a/Result9/receipts.json b/Result9/receipts.json deleted file mode 100644 index 321cb79..0000000 --- a/Result9/receipts.json +++ /dev/null @@ -1,831 +0,0 @@ -[ - { - "brutto": 251, - "comments": "\u0412\u0440\u0435\u043c\u044f \u043f\u0440\u0438\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d\u0438\u044f: 20 \u043c\u0438\u043d. 8 \u043f\u043e\u0440\u0446\u0438\u0439", - "consist": { - "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f": { - "description": "", - "id": "37d4d81cb939457a8a485f21c957ea56", - "is_error": false, - "name": "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "b9bfcf89fe834f17906e3f093d13b414", - "is_error": false, - "name": "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6b2f766223044776944a8a7078c1e36b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "4f780e91e4f544cb90ab579f19fc6463", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - "size": 100, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6b2f766223044776944a8a7078c1e36b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "\u0421\u0430\u0445\u0430\u0440": { - "description": "", - "id": "070644f547824814bebbbc67cfbf696b", - "is_error": false, - "name": "\u0421\u0430\u0445\u0430\u0440", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "dd24bb55e7704b2fbe27dd0488fa6266", - "is_error": false, - "name": "\u0421\u0430\u0445\u0430\u0440", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6b2f766223044776944a8a7078c1e36b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "4f780e91e4f544cb90ab579f19fc6463", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - "size": 80, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6b2f766223044776944a8a7078c1e36b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "\u0421\u043b\u0438\u0432\u043e\u0447\u043d\u043e\u0435 \u043c\u0430\u0441\u043b\u043e": { - "description": "", - "id": "3df6ad6a7a3b4febbd09b8c9881e43f0", - "is_error": false, - "name": "\u0421\u043b\u0438\u0432\u043e\u0447\u043d\u043e\u0435 \u043c\u0430\u0441\u043b\u043e", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "46d0ce6a74594431b63c33b6d5965af0", - "is_error": false, - "name": "\u0421\u043b\u0438\u0432\u043e\u0447\u043d\u043e\u0435 \u043c\u0430\u0441\u043b\u043e", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6b2f766223044776944a8a7078c1e36b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "4f780e91e4f544cb90ab579f19fc6463", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - "size": 70, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6b2f766223044776944a8a7078c1e36b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "\u042f\u0439\u0446\u0430": { - "description": "", - "id": "25fbacf28a944bf3833727d92556e27a", - "is_error": false, - "name": "\u042f\u0439\u0446\u0430", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "201c716dab504bd3b5901d197ab7ae06", - "is_error": false, - "name": "\u042f\u0439\u0446\u0430", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "cc4c8224e80a437d86c0e4b5c481906d", - "is_error": false, - "name": "\u0448\u0442\u0443\u043a\u0430" - } - }, - "size": 1, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "cc4c8224e80a437d86c0e4b5c481906d", - "is_error": false, - "name": "\u0448\u0442\u0443\u043a\u0430" - } - } - }, - "description": "", - "id": "37519e71920d4f54870d62cf93bff852", - "instructions": [ - { - "instructions": "\u041c\u0430\u0441\u043b\u043e \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u0435 \u0432 \u0441\u043e\u0442\u0435\u0439\u043d\u0438\u043a \u0441 \u0442\u043e\u043b\u0441\u0442\u044b\u043c \u0434\u043d\u043e\u043c. \u0420\u0430\u0441\u0442\u043e\u043f\u0438\u0442\u0435 \u0435\u0433\u043e \u043d\u0430 \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u043e\u043c \u043e\u0433\u043d\u0435 \u043d\u0430 \u043f\u043b\u0438\u0442\u0435, \u043d\u0430 \u0432\u043e\u0434\u044f\u043d\u043e\u0439 \u0431\u0430\u043d\u0435 \u043b\u0438\u0431\u043e \u0432 \u043c\u0438\u043a\u0440\u043e\u0432\u043e\u043b\u043d\u043e\u0432\u043a\u0435." - }, - { - "instructions": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432 \u0442\u0435\u043f\u043b\u043e\u0435 \u043c\u0430\u0441\u043b\u043e \u0441\u0430\u0445\u0430\u0440. \u041f\u0435\u0440\u0435\u043c\u0435\u0448\u0430\u0439\u0442\u0435 \u0432\u0435\u043d\u0447\u0438\u043a\u043e\u043c \u0434\u043e \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u0440\u0430\u0441\u0442\u0432\u043e\u0440\u0435\u043d\u0438\u044f \u0441\u0430\u0445\u0430\u0440\u0430. \u041e\u0442 \u0442\u0435\u043f\u043b\u0430 \u0441\u0430\u0445\u0430\u0440 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u043e \u0440\u0430\u0441\u0442\u0430\u0435\u0442." - }, - { - "instructions": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432 \u043c\u0430\u0441\u043b\u043e \u044f\u0439\u0446\u043e. \u041f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u0441\u0435-\u0442\u0430\u043a\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043c\u0430\u0441\u043b\u043e, \u043d\u0435 \u0433\u043e\u0440\u044f\u0447\u0435\u0435 \u043b\u0438 \u043e\u043d\u043e, \u0438\u043d\u0430\u0447\u0435 \u044f\u0439\u0446\u043e \u043c\u043e\u0436\u0435\u0442 \u0441\u0432\u0430\u0440\u0438\u0442\u044c\u0441\u044f. \u041f\u0435\u0440\u0435\u043c\u0435\u0448\u0430\u0439\u0442\u0435 \u044f\u0439\u0446\u043e \u0441 \u043c\u0430\u0441\u043b\u043e\u043c \u0434\u043e \u043e\u0434\u043d\u043e\u0440\u043e\u0434\u043d\u043e\u0441\u0442\u0438." - }, - { - "instructions": "\u0412\u0441\u044b\u043f\u044c\u0442\u0435 \u043c\u0443\u043a\u0443, \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432\u0430\u043d\u0438\u043b\u0438\u043d." - }, - { - "instructions": "\u041f\u0435\u0440\u0435\u043c\u0435\u0448\u0430\u0439\u0442\u0435 \u043c\u0430\u0441\u0441\u0443 \u0432\u0435\u043d\u0447\u0438\u043a\u043e\u043c \u0434\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0433\u043b\u0430\u0434\u043a\u043e\u0433\u043e \u043e\u0434\u043d\u043e\u0440\u043e\u0434\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430." - } - ], - "is_error": false, - "name": "\u0412\u0430\u0444\u043b\u0438 \u0445\u0440\u0443\u0441\u0442\u044f\u0449\u0438\u0435 \u0432 \u0432\u0430\u0444\u0438\u043b\u044c\u043d\u0438\u0446\u0435", - "netto": 0 - }, - { - "brutto": 369, - "comments": "", - "consist": { - "\u0411\u0435\u043b\u044b\u0439 \u0445\u043b\u0435\u0431": { - "description": "", - "id": "15aecc37e6964f88ac538c581b22408f", - "is_error": false, - "name": "\u0411\u0435\u043b\u044b\u0439 \u0445\u043b\u0435\u0431", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "53b72f78aa0f4b8fa4e046443f0a07f4", - "is_error": false, - "name": "\u0411\u0435\u043b\u044b\u0439 \u0445\u043b\u0435\u0431", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6b2f766223044776944a8a7078c1e36b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "4f780e91e4f544cb90ab579f19fc6463", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - "size": 30, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6b2f766223044776944a8a7078c1e36b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "\u0413\u043e\u0440\u0447\u0438\u0446\u0430 \u0434\u0438\u0436\u043e\u043d\u0441\u043a\u0430\u044f": { - "description": "", - "id": "4590bd0ed696489e8a4343134d95b859", - "is_error": false, - "name": "\u0413\u043e\u0440\u0447\u0438\u0446\u0430 \u0434\u0438\u0436\u043e\u043d\u0441\u043a\u0430\u044f", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "02d251e8ec6a41a181dd6659a5fc6fcc", - "is_error": false, - "name": "\u0413\u043e\u0440\u0447\u0438\u0446\u0430 \u0434\u0438\u0436\u043e\u043d\u0441\u043a\u0430\u044f", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "size": 5, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "\u041a\u0443\u0440\u0438\u043d\u043d\u043e\u0435 \u0444\u0438\u043b\u0435": { - "description": "", - "id": "84e43acba92142539d570d9615d77692", - "is_error": false, - "name": "\u041a\u0443\u0440\u0438\u043d\u043d\u043e\u0435 \u0444\u0438\u043b\u0435", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "69da6c4defb341a8b17d90f688cc4747", - "is_error": false, - "name": "\u041a\u0443\u0440\u0438\u043d\u043d\u043e\u0435 \u0444\u0438\u043b\u0435", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6b2f766223044776944a8a7078c1e36b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "4f780e91e4f544cb90ab579f19fc6463", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - "size": 200, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6b2f766223044776944a8a7078c1e36b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "\u041b\u0438\u043c\u043e\u043d\u043d\u044b\u0439 \u0441\u043e\u043a": { - "description": "", - "id": "956f5f02ad464379af83bc62e0ee2787", - "is_error": false, - "name": "\u041b\u0438\u043c\u043e\u043d\u043d\u044b\u0439 \u0441\u043e\u043a", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "37c6f449d5254611ae354f1b65bb2d1d", - "is_error": false, - "name": "\u041b\u0438\u043c\u043e\u043d\u043d\u044b\u0439 \u0441\u043e\u043a", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5addd6db1a5e437bbf89c7fe49ef24b0", - "is_error": false, - "name": "\u043c\u0438\u043b\u043b\u0438\u043b\u0438\u0442\u0440" - }, - "coefficient": 1000, - "description": "", - "id": "9d69adc76ab84bd58f02e4471c092179", - "is_error": false, - "name": "\u043b\u0438\u0442\u0440" - } - }, - "size": 5, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5addd6db1a5e437bbf89c7fe49ef24b0", - "is_error": false, - "name": "\u043c\u0438\u043b\u043b\u0438\u043b\u0438\u0442\u0440" - } - }, - "\u041e\u043b\u0438\u0432\u043a\u043e\u0432\u043e\u0435 \u043c\u0430\u0441\u043b\u043e": { - "description": "", - "id": "39c528c94f814f5c8f5274d14c48f853", - "is_error": false, - "name": "\u041e\u043b\u0438\u0432\u043a\u043e\u0432\u043e\u0435 \u043c\u0430\u0441\u043b\u043e", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "973f1fd272634eabb7e0ed9439b09023", - "is_error": false, - "name": "\u041e\u043b\u0438\u0432\u043a\u043e\u0432\u043e\u0435 \u043c\u0430\u0441\u043b\u043e", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5addd6db1a5e437bbf89c7fe49ef24b0", - "is_error": false, - "name": "\u043c\u0438\u043b\u043b\u0438\u043b\u0438\u0442\u0440" - }, - "coefficient": 1000, - "description": "", - "id": "9d69adc76ab84bd58f02e4471c092179", - "is_error": false, - "name": "\u043b\u0438\u0442\u0440" - } - }, - "size": 10, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5addd6db1a5e437bbf89c7fe49ef24b0", - "is_error": false, - "name": "\u043c\u0438\u043b\u043b\u0438\u043b\u0438\u0442\u0440" - } - }, - "\u0421\u0430\u043b\u0430\u0442 \u0420\u043e\u043c\u0430\u043d\u043e": { - "description": "", - "id": "29ed510765524089837986278a00a3a6", - "is_error": false, - "name": "\u0421\u0430\u043b\u0430\u0442 \u0420\u043e\u043c\u0430\u043d\u043e", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "ac6af82bdd6348febf6070debc57ff59", - "is_error": false, - "name": "\u0421\u0430\u043b\u0430\u0442 \u0420\u043e\u043c\u0430\u043d\u043e", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "size": 50, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "\u0421\u043e\u043b\u044c": { - "description": "", - "id": "edc00bb4b7384b3d9e748b862ff1f1fd", - "is_error": false, - "name": "\u0421\u043e\u043b\u044c", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "2aa0ba1ba94c44e19f2b249b82c6c0a8", - "is_error": false, - "name": "\u0421\u043e\u043b\u044c", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6b2f766223044776944a8a7078c1e36b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "4f780e91e4f544cb90ab579f19fc6463", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - "size": 5, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6b2f766223044776944a8a7078c1e36b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "\u0421\u044b\u0440 \u041f\u0430\u0440\u043c\u0435\u0437\u0430\u043d": { - "description": "", - "id": "4c01cc31745f48aea8ffb585280f8c58", - "is_error": false, - "name": "\u0421\u044b\u0440 \u041f\u0430\u0440\u043c\u0435\u0437\u0430\u043d", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "b3f122f0ed4a46fe8848d16238633186", - "is_error": false, - "name": "\u0421\u044b\u0440 \u041f\u0430\u0440\u043c\u0435\u0437\u0430\u043d", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6b2f766223044776944a8a7078c1e36b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "4f780e91e4f544cb90ab579f19fc6463", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - "size": 50, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6b2f766223044776944a8a7078c1e36b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "\u0427\u0435\u0440\u043d\u044b\u0439 \u043f\u0435\u0440\u0435\u0446": { - "description": "", - "id": "749fe9b908074b288b52ac5bedf4d3c3", - "is_error": false, - "name": "\u0427\u0435\u0440\u043d\u044b\u0439 \u043f\u0435\u0440\u0435\u0446", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "0303a20c8e0443168cc296faa3592b99", - "is_error": false, - "name": "\u0427\u0435\u0440\u043d\u044b\u0439 \u043f\u0435\u0440\u0435\u0446", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "size": 2, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "\u0427\u0435\u0441\u043d\u043e\u043a": { - "description": "", - "id": "e23a5cece6214bacb2f0d5a3575eb1a6", - "is_error": false, - "name": "\u0427\u0435\u0441\u043d\u043e\u043a", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "d87de5c794d14deba04e492255e10fb8", - "is_error": false, - "name": "\u0427\u0435\u0441\u043d\u043e\u043a", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6b2f766223044776944a8a7078c1e36b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "4f780e91e4f544cb90ab579f19fc6463", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - "size": 10, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6b2f766223044776944a8a7078c1e36b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "\u042f\u0439\u0446\u0430": { - "description": "", - "id": "8b6dc84ec2184b08be26e797b0f128a2", - "is_error": false, - "name": "\u042f\u0439\u0446\u0430", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "201c716dab504bd3b5901d197ab7ae06", - "is_error": false, - "name": "\u042f\u0439\u0446\u0430", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "cc4c8224e80a437d86c0e4b5c481906d", - "is_error": false, - "name": "\u0448\u0442\u0443\u043a\u0430" - } - }, - "size": 2, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "cc4c8224e80a437d86c0e4b5c481906d", - "is_error": false, - "name": "\u0448\u0442\u0443\u043a\u0430" - } - } - }, - "description": "", - "id": "05aba11d24cc4d56957e5d6930b329b7", - "instructions": [], - "is_error": false, - "name": "\u0426\u0435\u0437\u0430\u0440\u044c \u0441 \u043a\u0443\u0440\u0438\u0446\u0435\u0439", - "netto": 0 - }, - { - "brutto": 213, - "comments": "", - "consist": { - "\u0412\u0430\u043d\u0438\u043b\u0438\u0438\u043d": { - "description": "", - "id": "fd81f9b466ad4382a8e595619a1db5cf", - "is_error": false, - "name": "\u0412\u0430\u043d\u0438\u043b\u0438\u0438\u043d", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "a18090224c4e4551884c0d2fe52e2c91", - "is_error": false, - "name": "\u0412\u0430\u043d\u0438\u043b\u0438\u0438\u043d", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "size": 5, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "\u041a\u0430\u043a\u0430\u043e": { - "description": "", - "id": "e72614fc7c2d4f0abef0d8db070ef048", - "is_error": false, - "name": "\u041a\u0430\u043a\u0430\u043e", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "57be2d4763f143789aced9133d90e351", - "is_error": false, - "name": "\u041a\u0430\u043a\u0430\u043e", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6b2f766223044776944a8a7078c1e36b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "4f780e91e4f544cb90ab579f19fc6463", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - "size": 20, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6b2f766223044776944a8a7078c1e36b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "\u041a\u043e\u0440\u0438\u0446\u0430": { - "description": "", - "id": "9a9a0e800c5844b6991ec0160c373ad7", - "is_error": false, - "name": "\u041a\u043e\u0440\u0438\u0446\u0430", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "63df5a9ec4cd44889743c09197b297a6", - "is_error": false, - "name": "\u041a\u043e\u0440\u0438\u0446\u0430", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "size": 5, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "\u0421\u0430\u0445\u0430\u0440\u043d\u0430\u044f \u043f\u0443\u0434\u0440\u0430": { - "description": "", - "id": "ebf7ce33a8604e7f8f2aa4ba19171152", - "is_error": false, - "name": "\u0421\u0430\u0445\u0430\u0440\u043d\u0430\u044f \u043f\u0443\u0434\u0440\u0430", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "00869dd3ce6744b2ae97a7f54d86c1bb", - "is_error": false, - "name": "\u0421\u0430\u0445\u0430\u0440\u043d\u0430\u044f \u043f\u0443\u0434\u0440\u0430", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "size": 180, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "6ab9c3fe9f5f4ed1b2b2d646352effd5", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - } - }, - "\u042f\u0439\u0446\u0430": { - "description": "", - "id": "5a14fa59332046f9ba142ed4e41fd3a4", - "is_error": false, - "name": "\u042f\u0439\u0446\u0430", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "b98ad33f66b64a149ce4c3be34bbc116", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "201c716dab504bd3b5901d197ab7ae06", - "is_error": false, - "name": "\u042f\u0439\u0446\u0430", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "cc4c8224e80a437d86c0e4b5c481906d", - "is_error": false, - "name": "\u0448\u0442\u0443\u043a\u0430" - } - }, - "size": 3, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "cc4c8224e80a437d86c0e4b5c481906d", - "is_error": false, - "name": "\u0448\u0442\u0443\u043a\u0430" - } - } - }, - "description": "", - "id": "da5fb9eb29824f3cade8151a520a5950", - "instructions": [], - "is_error": false, - "name": "\u0411\u0435\u0437\u0435", - "netto": 0 - } -] \ No newline at end of file diff --git a/Result9/storage.json b/Result9/storage.json deleted file mode 100644 index 7e4f43c..0000000 --- a/Result9/storage.json +++ /dev/null @@ -1,2270 +0,0 @@ -{ - "group_model": [ - { - "description": "", - "id": "44298be97ad7482497f6fc8d6deb0808", - "is_error": true, - "name": "Ингредиенты" - } - ], - "nomenclature_model": [ - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "5c16c9c869c944ca81571f6cf70a790d", - "is_error": true, - "name": "Мука пшеничная", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "e787ff8a837d40418f9121f1272616ab", - "is_error": true, - "name": "Сахар", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "998f959f17704d85b0fad15936283032", - "is_error": true, - "name": "Сливочное масло", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "8beba2f92e754eeda776859bc197af39", - "is_error": true, - "name": "Яйца", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "f1a87af30e0c4faca9c3184050b3e370", - "is_error": true, - "name": "штука" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "11456b73dc1c4620b9387016648509b9", - "is_error": true, - "name": "Ванилин", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "6a448c9b996646888ace90ed26a7547b", - "is_error": true, - "name": "Куринное филе", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "5b5936b9384a4fe59b24e3fd127377d0", - "is_error": true, - "name": "Салат Романо", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "1696620e15b74a70b7fbd3c28076255b", - "is_error": true, - "name": "Сыр Пармезан", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "87ae9afa51d74a94af801f7037d59d8d", - "is_error": true, - "name": "Чеснок", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "3c8d6792212a4feba49e48770995ee55", - "is_error": true, - "name": "Белый хлеб", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "fb5741984d24441789ecd2a2d0c6637a", - "is_error": true, - "name": "Соль", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "6ea66e6494ad447a996ad93fdb38697e", - "is_error": true, - "name": "Черный перец", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "e8bc8e612eab4fecb1ba20b685267cc6", - "is_error": true, - "name": "Оливковое масло", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "11be8eb6981a4f49bbe0fe93f136a00e", - "is_error": true, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "ce323ec5feb840c9843d4380a8630fcf", - "is_error": true, - "name": "литр" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "cdbbecb3fa594a799e5b4c6a14058c42", - "is_error": true, - "name": "Лимонный сок", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "11be8eb6981a4f49bbe0fe93f136a00e", - "is_error": true, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "ce323ec5feb840c9843d4380a8630fcf", - "is_error": true, - "name": "литр" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "af0af126ae0b41c38ea53544777721b9", - "is_error": true, - "name": "Горчица дижонская", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "ffa7503c9ef64bdcb51b762fa3595649", - "is_error": true, - "name": "Сахарная пудра", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "0be1452f2ae649e88008a595ba309aba", - "is_error": true, - "name": "Ванилиин", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "f658b569df3d46709cce01b5a7383cd5", - "is_error": true, - "name": "Корица", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "bf459ed1953f437fa0a1a7f2f3c87f46", - "is_error": true, - "name": "Какао", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - } - ], - "receipe_model": [ - { - "brutto": 251, - "comments": "", - "consist": { - "Мука пшеничная": { - "description": "", - "id": "7c92cf932de44581951ca05738464de2", - "is_error": true, - "name": "Мука пшеничная", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "5c16c9c869c944ca81571f6cf70a790d", - "is_error": true, - "name": "Мука пшеничная", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - "size": 100, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - } - }, - "Сахар": { - "description": "", - "id": "4f50b78ffb3f43848bdb08279ff95823", - "is_error": true, - "name": "Сахар", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "e787ff8a837d40418f9121f1272616ab", - "is_error": true, - "name": "Сахар", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - "size": 80, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - } - }, - "Сливочное масло": { - "description": "", - "id": "244833ce4dad4f21bbdddeb0bce73ec2", - "is_error": true, - "name": "Сливочное масло", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "998f959f17704d85b0fad15936283032", - "is_error": true, - "name": "Сливочное масло", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - "size": 70, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - } - }, - "Яйца": { - "description": "", - "id": "843cebf7329d41de9dff0ae4bdbb43cb", - "is_error": true, - "name": "Яйца", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "8beba2f92e754eeda776859bc197af39", - "is_error": true, - "name": "Яйца", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "f1a87af30e0c4faca9c3184050b3e370", - "is_error": true, - "name": "штука" - } - }, - "size": 1, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "f1a87af30e0c4faca9c3184050b3e370", - "is_error": true, - "name": "штука" - } - } - }, - "description": "", - "id": "89a9226cf02643d8a9e856fbc0ca4351", - "instructions": [ - { - "instructions": { - "instructions": "Масло положите в сотейник с толстым дном. Растопите его на маленьком огне на плите, на водяной бане либо в микроволновке." - } - }, - { - "instructions": { - "instructions": "Добавьте в теплое масло сахар. Перемешайте венчиком до полного растворения сахара. От тепла сахар довольно быстро растает." - } - }, - { - "instructions": { - "instructions": "Добавьте в масло яйцо. Предварительно все-таки проверьте масло, не горячее ли оно, иначе яйцо может свариться. Перемешайте яйцо с маслом до однородности." - } - }, - { - "instructions": { - "instructions": "Всыпьте муку, добавьте ванилин." - } - }, - { - "instructions": { - "instructions": "Перемешайте массу венчиком до состояния гладкого однородного теста." - } - } - ], - "is_error": true, - "name": "Вафли хрустящие в вафильнице", - "netto": 0 - }, - { - "brutto": 369, - "comments": "", - "consist": { - "Белый хлеб": { - "description": "", - "id": "fe7a448350184766b6c57a1cea5df43e", - "is_error": true, - "name": "Белый хлеб", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "3c8d6792212a4feba49e48770995ee55", - "is_error": true, - "name": "Белый хлеб", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - "size": 30, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - } - }, - "Горчица дижонская": { - "description": "", - "id": "a5221530e2574658bb08fd2d55ef4a97", - "is_error": true, - "name": "Горчица дижонская", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "af0af126ae0b41c38ea53544777721b9", - "is_error": true, - "name": "Горчица дижонская", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - "size": 5, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - "Куринное филе": { - "description": "", - "id": "8814e19f162e4b3db2a8233524c50955", - "is_error": true, - "name": "Куринное филе", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "6a448c9b996646888ace90ed26a7547b", - "is_error": true, - "name": "Куринное филе", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - "size": 200, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - } - }, - "Лимонный сок": { - "description": "", - "id": "2ca49b7384144e4eb07a3a8072de0d09", - "is_error": true, - "name": "Лимонный сок", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "cdbbecb3fa594a799e5b4c6a14058c42", - "is_error": true, - "name": "Лимонный сок", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "11be8eb6981a4f49bbe0fe93f136a00e", - "is_error": true, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "ce323ec5feb840c9843d4380a8630fcf", - "is_error": true, - "name": "литр" - } - }, - "size": 5, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "11be8eb6981a4f49bbe0fe93f136a00e", - "is_error": true, - "name": "миллилитр" - } - }, - "Оливковое масло": { - "description": "", - "id": "b9999b177e644de5b52f2acaab0a1d58", - "is_error": true, - "name": "Оливковое масло", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "e8bc8e612eab4fecb1ba20b685267cc6", - "is_error": true, - "name": "Оливковое масло", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "11be8eb6981a4f49bbe0fe93f136a00e", - "is_error": true, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "ce323ec5feb840c9843d4380a8630fcf", - "is_error": true, - "name": "литр" - } - }, - "size": 10, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "11be8eb6981a4f49bbe0fe93f136a00e", - "is_error": true, - "name": "миллилитр" - } - }, - "Салат Романо": { - "description": "", - "id": "2fccc854834a49ffb0f5a4f83801d2e5", - "is_error": true, - "name": "Салат Романо", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "5b5936b9384a4fe59b24e3fd127377d0", - "is_error": true, - "name": "Салат Романо", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - "size": 50, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - "Соль": { - "description": "", - "id": "a36a115edf094b9184a550f994242eb5", - "is_error": true, - "name": "Соль", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "fb5741984d24441789ecd2a2d0c6637a", - "is_error": true, - "name": "Соль", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - "size": 5, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - } - }, - "Сыр Пармезан": { - "description": "", - "id": "a36abd4567394c7396c0fccc15dfaaa8", - "is_error": true, - "name": "Сыр Пармезан", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "1696620e15b74a70b7fbd3c28076255b", - "is_error": true, - "name": "Сыр Пармезан", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - "size": 50, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - } - }, - "Черный перец": { - "description": "", - "id": "1d51f6245b1d4f6db47670e91613a892", - "is_error": true, - "name": "Черный перец", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "6ea66e6494ad447a996ad93fdb38697e", - "is_error": true, - "name": "Черный перец", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - "size": 2, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - "Чеснок": { - "description": "", - "id": "20f1bb98774f498080cb7dc59a0be7fd", - "is_error": true, - "name": "Чеснок", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "87ae9afa51d74a94af801f7037d59d8d", - "is_error": true, - "name": "Чеснок", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - "size": 10, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - } - }, - "Яйца": { - "description": "", - "id": "2ccd5d4a20a54eb0beebe0ee2fb87b41", - "is_error": true, - "name": "Яйца", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "8beba2f92e754eeda776859bc197af39", - "is_error": true, - "name": "Яйца", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "f1a87af30e0c4faca9c3184050b3e370", - "is_error": true, - "name": "штука" - } - }, - "size": 2, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "f1a87af30e0c4faca9c3184050b3e370", - "is_error": true, - "name": "штука" - } - } - }, - "description": "", - "id": "c01913436f1a4d26b31359285fceb10f", - "instructions": [], - "is_error": true, - "name": "Цезарь с курицей", - "netto": 0 - }, - { - "brutto": 213, - "comments": "", - "consist": { - "Ванилиин": { - "description": "", - "id": "f58171de9ca34eecb26717784ed32133", - "is_error": true, - "name": "Ванилиин", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "0be1452f2ae649e88008a595ba309aba", - "is_error": true, - "name": "Ванилиин", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - "size": 5, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - "Какао": { - "description": "", - "id": "e8e68575186d444e8c6d5c41918fba65", - "is_error": true, - "name": "Какао", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "bf459ed1953f437fa0a1a7f2f3c87f46", - "is_error": true, - "name": "Какао", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - "size": 20, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - } - }, - "Корица": { - "description": "", - "id": "dd8b376d6cd545589e2bf9937a8e44cd", - "is_error": true, - "name": "Корица", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "f658b569df3d46709cce01b5a7383cd5", - "is_error": true, - "name": "Корица", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - "size": 5, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - "Сахарная пудра": { - "description": "", - "id": "b6c28c224bb44bf9a4b74bedc348af8c", - "is_error": true, - "name": "Сахарная пудра", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "ffa7503c9ef64bdcb51b762fa3595649", - "is_error": true, - "name": "Сахарная пудра", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - "size": 180, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - "Яйца": { - "description": "", - "id": "44a484b273994bb3973beaa793576b3a", - "is_error": true, - "name": "Яйца", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "8beba2f92e754eeda776859bc197af39", - "is_error": true, - "name": "Яйца", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "f1a87af30e0c4faca9c3184050b3e370", - "is_error": true, - "name": "штука" - } - }, - "size": 3, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "f1a87af30e0c4faca9c3184050b3e370", - "is_error": true, - "name": "штука" - } - } - }, - "description": "", - "id": "9c858076aacf425685bfbf60a0fd4267", - "instructions": [], - "is_error": true, - "name": "Безе", - "netto": 0 - } - ], - "storage_row_model": [ - { - "description": "", - "id": "a66dd566f8764eee9ca29f6dc5245eb1", - "is_error": true, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "5c16c9c869c944ca81571f6cf70a790d", - "is_error": true, - "name": "Мука пшеничная", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - "period": "2024-01-27", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": true, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": true, - "name": "киллограмм" - }, - "value": 1 - }, - { - "description": "", - "id": "b643d8e77fdf4459a6871281e8f10eac", - "is_error": true, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "6ea66e6494ad447a996ad93fdb38697e", - "is_error": true, - "name": "Черный перец", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - "period": "2024-01-15", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": true, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "8d5f31aa6ca84177a6bd5761ab50f6fc", - "is_error": true, - "name": "грамм" - }, - "value": 50 - }, - { - "description": "", - "id": "da263ab918c34833a00b386b88781710", - "is_error": true, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "e787ff8a837d40418f9121f1272616ab", - "is_error": true, - "name": "Сахар", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - "period": "2024-01-28", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": true, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": true, - "name": "киллограмм" - }, - "value": 0.5 - }, - { - "description": "", - "id": "9bd6f1ce38574a268820d59aaf9a32ce", - "is_error": true, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "8beba2f92e754eeda776859bc197af39", - "is_error": true, - "name": "Яйца", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "f1a87af30e0c4faca9c3184050b3e370", - "is_error": true, - "name": "штука" - } - }, - "period": "2024-01-27", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": true, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "aaa9f8244b2a4f1ca199b0b376967213", - "is_error": true, - "name": "штука" - }, - "value": 6 - }, - { - "description": "", - "id": "bf0a45ae55e048899dbce60f1ed7ea03", - "is_error": true, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "e8bc8e612eab4fecb1ba20b685267cc6", - "is_error": true, - "name": "Оливковое масло", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "11be8eb6981a4f49bbe0fe93f136a00e", - "is_error": true, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "ce323ec5feb840c9843d4380a8630fcf", - "is_error": true, - "name": "литр" - } - }, - "period": "2024-01-03", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": true, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d113c297f698464a85cff3e803f36ce7", - "is_error": true, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "d486cc7e786f4b828933d5ace911a875", - "is_error": true, - "name": "литр" - }, - "value": 0.2 - }, - { - "description": "", - "id": "fb17787652844ae2b4cd276c81a588a1", - "is_error": true, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "6a448c9b996646888ace90ed26a7547b", - "is_error": true, - "name": "Куринное филе", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - "period": "2024-01-31", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": true, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": true, - "name": "киллограмм" - }, - "value": 0.5 - }, - { - "description": "", - "id": "30168c6837f04bd4b212d65d8ffd1eb0", - "is_error": true, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "5b5936b9384a4fe59b24e3fd127377d0", - "is_error": true, - "name": "Салат Романо", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - "period": "2024-01-21", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": true, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "aaa9f8244b2a4f1ca199b0b376967213", - "is_error": true, - "name": "штука" - }, - "value": 1 - }, - { - "description": "", - "id": "9f2ecf8cbf64420882ee07e2024cfee3", - "is_error": true, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "3c8d6792212a4feba49e48770995ee55", - "is_error": true, - "name": "Белый хлеб", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - "period": "2024-01-09", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": true, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "aaa9f8244b2a4f1ca199b0b376967213", - "is_error": true, - "name": "штука" - }, - "value": 3 - }, - { - "description": "", - "id": "5c18cef4d4584b9a80077766225eaaed", - "is_error": true, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "1696620e15b74a70b7fbd3c28076255b", - "is_error": true, - "name": "Сыр Пармезан", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - "period": "2024-01-27", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": true, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": true, - "name": "киллограмм" - }, - "value": 0.2 - }, - { - "description": "", - "id": "0cf3414a8d114f3c89db9741aa9acadf", - "is_error": true, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "af0af126ae0b41c38ea53544777721b9", - "is_error": true, - "name": "Горчица дижонская", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - "period": "2024-01-06", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": true, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d113c297f698464a85cff3e803f36ce7", - "is_error": true, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "d486cc7e786f4b828933d5ace911a875", - "is_error": true, - "name": "литр" - }, - "value": 0.1 - }, - { - "description": "", - "id": "2f2bede132d1485e9562ca13326ab5ac", - "is_error": true, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "6ea66e6494ad447a996ad93fdb38697e", - "is_error": true, - "name": "Черный перец", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - "period": "2024-01-17", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": true, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "8d5f31aa6ca84177a6bd5761ab50f6fc", - "is_error": true, - "name": "грамм" - }, - "value": 10 - }, - { - "description": "", - "id": "15e085acf2224edab862af3d79dfeab5", - "is_error": true, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "cdbbecb3fa594a799e5b4c6a14058c42", - "is_error": true, - "name": "Лимонный сок", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "11be8eb6981a4f49bbe0fe93f136a00e", - "is_error": true, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "ce323ec5feb840c9843d4380a8630fcf", - "is_error": true, - "name": "литр" - } - }, - "period": "2024-01-23", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": true, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d113c297f698464a85cff3e803f36ce7", - "is_error": true, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "d486cc7e786f4b828933d5ace911a875", - "is_error": true, - "name": "литр" - }, - "value": 1 - }, - { - "description": "", - "id": "a77598bd2dc944cc9c9ed05579c0caa4", - "is_error": true, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "bf459ed1953f437fa0a1a7f2f3c87f46", - "is_error": true, - "name": "Какао", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - "period": "2024-01-12", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": true, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": true, - "name": "киллограмм" - }, - "value": 1 - }, - { - "description": "", - "id": "22df33c9d3a543d980c9cda91fe7685c", - "is_error": true, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "998f959f17704d85b0fad15936283032", - "is_error": true, - "name": "Сливочное масло", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - "period": "2024-01-12", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": true, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": true, - "name": "киллограмм" - }, - "value": 0.5 - }, - { - "description": "", - "id": "1a35d9478dcd49ecae266acf21c50da3", - "is_error": true, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "1696620e15b74a70b7fbd3c28076255b", - "is_error": true, - "name": "Сыр Пармезан", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - "period": "2024-01-24", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": true, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": true, - "name": "киллограмм" - }, - "value": 0.3 - }, - { - "description": "", - "id": "26f95d36c7d149d9bd42e36a185ea846", - "is_error": true, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "0be1452f2ae649e88008a595ba309aba", - "is_error": true, - "name": "Ванилиин", - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "4e2440552fd04af4b7d3df1031b2aedd", - "is_error": true, - "name": "грамм" - } - }, - "period": "2024-01-18", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": true, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "8d5f31aa6ca84177a6bd5761ab50f6fc", - "is_error": true, - "name": "грамм" - }, - "value": 100 - }, - { - "description": "", - "id": "9c495297d49540e68eb0f08ff6a9e9a8", - "is_error": true, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": true, - "name": "Ингредиенты" - }, - "id": "fb5741984d24441789ecd2a2d0c6637a", - "is_error": true, - "name": "Соль", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": true, - "name": "киллограмм" - } - }, - "period": "2024-01-01", - "storage": { - "address": "г. Москва. ул. Академика Королева, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": true, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": true, - "name": "киллограмм" - }, - "value": 1 - } - ], - "unit_model": [ - { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "8d5f31aa6ca84177a6bd5761ab50f6fc", - "is_error": true, - "name": "грамм" - }, - { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": true, - "name": "грамм" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": true, - "name": "киллограмм" - }, - { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d113c297f698464a85cff3e803f36ce7", - "is_error": true, - "name": "миллилитр" - }, - "coefficient": 1000, - "description": "", - "id": "d486cc7e786f4b828933d5ace911a875", - "is_error": true, - "name": "литр" - }, - { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "ab87512809eb4da6bf6cc026c6740983", - "is_error": true, - "name": "миллилитр" - }, - { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "aaa9f8244b2a4f1ca199b0b376967213", - "is_error": true, - "name": "штука" - } - ] -} \ No newline at end of file diff --git a/Result9/storage_journal.md b/Result9/storage_journal.md deleted file mode 100644 index fd817fd..0000000 --- a/Result9/storage_journal.md +++ /dev/null @@ -1,72 +0,0 @@ - -# Журнал складский операци - -[Источник](https://github.com/KROKODILCHIK234/gitExample1/pull/2/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5) - -### Таблица покупок номенклатуры - -| Наименование | Количество | Цена | Единица измерения | -|---------------------|------------|--------|-------------------| -| Мука | 1 | 50 | кг | -| Сахар | 0.5 | 30 | кг | -| Яйца | 6 | 5 | шт | -| Масло | 0.2 | 80 | л | -| Куринное филе | 0.5 | 120 | кг | -| Салат Римский | 1 | 25 | шт | -| Хлеб | 0.3 | 20 | шт | -| Сыр Пармезан | 0.2 | 200 | кг | -| Майонез | 0.1 | 50 | л | -| Перец черный молотый| 1 | 15 | г | -| Лимон | 1 | 30 | шт | -| Молоко | 0.3 | 40 | л | -| Какао-порошок | 0.1 | 70 | кг | - -### Таблица затрат номенклатуры для приготовления рецептов - -#### Вафли хрустящие в вафильнице - -| Наименование | Количество | Единица измерения | -|--------------------|------------|-------------------| -| Мука | 0.2 | кг | -| Сахар | 0.1 | кг | -| Яйца | 2 | шт | -| Масло | 0.1 | л | - -#### Цезарь с курицей - -| Наименование | Количество | Единица измерения | -|---------------------|------------|-------------------| -| Куринное филе | 0.3 | кг | -| Салат Римский | 1 | шт | -| Хлеб | 0.1 | шт | -| Сыр Пармезан | 0.1 | кг | -| Майонез | 0.05 | л | -| Перец черный молотый| 0.3 | г | -| Лимон | 0.25 | шт | - -#### Безе - -| Наименование | Количество | Единица измерения | -|--------------------|------------|-------------------| -| Яйца | 4 | шт | -| Сахар | 0.2 | кг | -| Молоко | 0.15 | л | -| Какао-порошок | 0.05 | кг | - -### Таблица с остатками номенклатуры после приготовления - -| Наименование | Количество | Единица измерения | -|---------------------|------------|-------------------| -| Мука | 0.6 | кг | -| Сахар | 0.2 | кг | -| Яйца | 0 | шт | -| Масло | 0.1 | л | -| Куринное филе | 0.2 | кг | -| Салат Римский | 0 | шт | -| Хлеб | 0.1 | шт | -| Сыр Пармезан | 0.1 | кг | -| Майонез | 0.05 | л | -| Перец черный молотый| 0.7 | г | -| Лимон | 0.75 | шт | -| Молоко | 0.15 | л | -| Какао-порошок | 0.05 | кг | \ No newline at end of file diff --git a/Result9/transaction_deserialize.json b/Result9/transaction_deserialize.json deleted file mode 100644 index 02ddbfb..0000000 --- a/Result9/transaction_deserialize.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "description": "", - "id": "a66dd566f8764eee9ca29f6dc5245eb1", - "is_error": false, - "name": "sample_credit_transaction", - "nomenclature": { - "description": "", - "group": { - "description": "", - "id": "18277c4705764bf0893dbd49f2930c34", - "is_error": false, - "name": "\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b" - }, - "id": "5c16c9c869c944ca81571f6cf70a790d", - "is_error": false, - "name": "\u041c\u0443\u043a\u0430 \u043f\u0448\u0435\u043d\u0438\u0447\u043d\u0430\u044f", - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "5f73f137f82f4d7eb2d529a83b54b982", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "1bed8c4830a64ab987692730fb43e84c", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - } - }, - "period": "2024-01-27", - "storage": { - "address": "\u0433. \u041c\u043e\u0441\u043a\u0432\u0430. \u0443\u043b. \u0410\u043a\u0430\u0434\u0435\u043c\u0438\u043a\u0430 \u041a\u043e\u0440\u043e\u043b\u0435\u0432\u0430, 10", - "description": "", - "id": "1fbbced64e3e474eb27e0f464161c61b", - "is_error": false, - "name": "default" - }, - "storage_type": true, - "unit": { - "base_unit": { - "base_unit": null, - "coefficient": 1, - "description": "", - "id": "d8e5cf980f724a0b97b6a5ef65af6b1b", - "is_error": false, - "name": "\u0433\u0440\u0430\u043c\u043c" - }, - "coefficient": 1000, - "description": "", - "id": "51fa5f0d487d4989bc4439e025a42255", - "is_error": false, - "name": "\u043a\u0438\u043b\u043b\u043e\u0433\u0440\u0430\u043c\u043c" - }, - "value": 1 -} \ No newline at end of file