From f72424f3691e3d1d52000ce65a3334cd19e140b3 Mon Sep 17 00:00:00 2001 From: Maxim Olifirenko Date: Fri, 7 Nov 2025 19:52:03 +0800 Subject: [PATCH] Reworked method of calculating turnovers using cache dict --- src/logics/tbs_calculator.py | 45 +++++++++++-------------- src/logics/tbs_line.py | 60 ++++++++++++++++++++++----------- src/singletons/repository.py | 17 +++++++++- src/singletons/start_service.py | 11 ++++++ 4 files changed, 86 insertions(+), 47 deletions(-) diff --git a/src/logics/tbs_calculator.py b/src/logics/tbs_calculator.py index a492060..72771f8 100644 --- a/src/logics/tbs_calculator.py +++ b/src/logics/tbs_calculator.py @@ -1,10 +1,8 @@ -from typing import List, Dict +from typing import List from datetime import date, datetime from src.core.validator import Validator as vld from src.logics.tbs_line import TbsLine from src.models.storage_model import StorageModel -from src.models.transaction_model import TransactionModel -from src.singletons.repository import Repository from src.singletons.start_service import StartService @@ -23,28 +21,23 @@ def calculate( start = datetime(start.year, start.month, start.day) end = datetime(end.year, end.month, end.day, 23, 59, 59) - transactions = StartService().transactions - data: Dict[str, TbsLine] = dict() - for transaction in transactions.values(): - if transaction.storage == storage and transaction.datetime <= end: - code = transaction.nomenclature.unique_code - if code not in data: - data[code] = TbsLine(transaction) - line = data[code] - line.add(transaction, start, end) - tbs_keys = data.keys() - all_keys = StartService().data[Repository.nomenclatures_key].keys() - other_keys = set(all_keys) - set(tbs_keys) - for key in other_keys: - nomenclature = StartService().repository.get(unique_code=key) - if nomenclature is None: - continue - data[key] = TbsLine(TransactionModel( - nomenclature=nomenclature, - storage=storage, - count=0, - measure_unit=nomenclature.measure_unit - )) + result = list() + data = StartService().repository.transactions_data - return list(data.values()) + for nom_key, stor_dict in data.items(): + nomenclature = StartService().repository.get(unique_code=nom_key) + line = TbsLine(nomenclature=nomenclature) + list_ = stor_dict.get(storage.unique_code, list()) + for dt, count in list_: + if dt < start: + line.start_count += count + elif dt <= end: + if count > 0: + line.income += count + else: + line.outgo += count + + result += [line] + + return result diff --git a/src/logics/tbs_line.py b/src/logics/tbs_line.py index 0c25a57..0396a78 100644 --- a/src/logics/tbs_line.py +++ b/src/logics/tbs_line.py @@ -1,4 +1,4 @@ -from typing import List +from typing import Optional, Union from datetime import datetime from src.core.validator import Validator as vld from src.core.exceptions import OperationException @@ -15,24 +15,33 @@ class TbsLine: # Единица измерения (базовая для номенклатуры) __measure_unit: MeasureUnitModel - # Значения транзакций до начальной даты - __counts_before_start: List[float] + # Начальный остаток + __start_count: float = 0.0 - # Значения транзакций в промежутке между начальной и конечной датами - __counts_before_end: List[float] + # Приход + __income: float = 0.0 - # Приход (вычисляемое поле) - - # Расход (вычисляемое поле) + # Расход + __outgo: float = 0.0 # Остаток на дату окончания (вычисляемое поле) - def __init__(self, transaction: TransactionModel): - self.nomenclature = transaction.nomenclature + def __init__( + self, + nomenclature: NomenclatureModel, + start_count: Optional[float] = None, + income: Optional[float] = None, + outgo: Optional[float] = None, + ): + self.nomenclature = nomenclature base_unit, _ = self.nomenclature.measure_unit.get_base_unit() self.measure_unit = base_unit - self.__counts_before_start = list() - self.__counts_before_end = list() + if start_count is not None: + self.start_count = start_count + if income is not None: + self.income = income + if outgo is not None: + self.outgo = outgo """Поле номенклатуры""" @property @@ -57,26 +66,37 @@ def measure_unit(self, value: MeasureUnitModel): """Поле начального остатка""" @property def start_count(self) -> float: - return sum(self.__counts_before_start) + return self.__start_count + + @start_count.setter + def start_count(self, value: Union[int, float]): + vld.is_number(value, "start_count") + self.__start_count = value """Поле прихода""" @property def income(self) -> float: - return sum([value - for value in self.__counts_before_end - if value > 0]) + return self.__income + + @income.setter + def income(self, value: Union[int, float]): + vld.is_number(value, "income") + self.__income = value """Поле расхода""" @property def outgo(self) -> float: - return sum([value - for value in self.__counts_before_end - if value < 0]) + return self.__outgo + + @outgo.setter + def outgo(self, value: Union[int, float]): + vld.is_number(value, "outgo") + self.__outgo = value """Поле конечного остатка""" @property def end_count(self) -> float: - return self.start_count + self.income + self.outgo + return self.__start_count + self.__income + self.__outgo """ Метод, добавляющий значение транзакции к соответствующему полю __count diff --git a/src/singletons/repository.py b/src/singletons/repository.py index a3ecc32..e35edc2 100644 --- a/src/singletons/repository.py +++ b/src/singletons/repository.py @@ -1,4 +1,5 @@ -from typing import List, Any, Optional +from typing import List, Any, Optional, Dict, Tuple +from datetime import datetime from src.core.validator import Validator as vld from src.core.exceptions import ParamException from src.utils import get_properties @@ -12,6 +13,9 @@ class Repository: # Словарь наименований моделей __data = dict() + # Вложенный словарь с данными о транзакциях + __transactions_data: Dict[str, Dict[str, List[Tuple[datetime, float]]]] + # Ключ для единиц измерения measure_unit_key: str = "measure_units" @@ -46,12 +50,23 @@ def keys() -> List[str]: return [getattr(Repository, f) for f in get_properties(Repository) if f.endswith("_key")] + """Данные о транзакциях""" + @property + def transactions_data(self) -> Dict[str, Dict[str, List[Tuple[datetime, float]]]]: + return self.__transactions_data + + @transactions_data.setter + def transactions_data(self, value: Dict): + vld.is_dict(value, "transactions_data") + self.__transactions_data = value + """Инициализация списков в словаре данных""" def initalize(self): # Все ключи будут ссылаться на словари формата name: object # с соответствующими объектами for key in Repository.keys(): self.data[key] = dict() + self.transactions_data = dict() """Метод получения объекта в памяти по имени""" def get_by_name(self, name: str) -> Optional[Any]: diff --git a/src/singletons/start_service.py b/src/singletons/start_service.py index d1bb787..324d053 100644 --- a/src/singletons/start_service.py +++ b/src/singletons/start_service.py @@ -130,6 +130,17 @@ def __convert_models( model = model_type.from_dto(dto, self.__repository) self.__repository.data[repo_key][model.unique_code] = model + if model_type is TransactionModel: + nom_id = model.nomenclature.unique_code + stor_id = model.storage.unique_code + _, coef = model.measure_unit.get_base_unit() + stor_dict = self.repository.transactions_data.get(nom_id, + dict()) + list_ = stor_dict.get(stor_id, list()) + list_.append((model.datetime, coef * model.count)) + stor_dict[stor_id] = list_ + self.repository.transactions_data[nom_id] = stor_dict + return True """Метод конвертации объекта в модели групп номенклатур"""