diff --git a/Src/Core/abstract_model.py b/Src/Core/abstract_model.py index e0c056d..7626975 100644 --- a/Src/Core/abstract_model.py +++ b/Src/Core/abstract_model.py @@ -2,28 +2,26 @@ import uuid from Src.Core.validator import validator -class abstact_model(ABC): - __unique_code:str +class abstract_reference(ABC): + __name:str - def __init__(self) -> None: + def __init__(self,name:str) -> None: super().__init__() - self.__unique_code = uuid.uuid4().hex + self.name = name + + """ - Уникальный код + Уникальный имя """ @property - def unique_code(self) -> str: - return self.__unique_code + def name(self) -> str: + return self.__name - @unique_code.setter - def unique_code(self, value: str): - validator.validate(value, str) - self.__unique_code = value.strip() + @name.setter + def name(self, value: str): + validator.validate(value, str,50) + self.__name = value.strip() - """ - Перегрузка штатного варианта сравнения - """ - def __eq__(self, value: str) -> bool: - return self.__unique_code == value + diff --git a/Src/Core/validator.py b/Src/Core/validator.py index 8fc3ad3..19f47ee 100644 --- a/Src/Core/validator.py +++ b/Src/Core/validator.py @@ -11,6 +11,8 @@ class argument_exception(Exception): class operation_exception(Exception): pass +class error_proxy(Exception): + pass """ Набор проверок данных diff --git a/Src/Models/company_model.py b/Src/Models/company_model.py index 24e7487..ae126c6 100644 --- a/Src/Models/company_model.py +++ b/Src/Models/company_model.py @@ -1,32 +1,44 @@ from Src.Core.validator import validator -from Src.Core.abstract_model import abstact_model +from Src.Core.abstract_model import abstract_reference + + ############################################### # Модель организации -class company_model(abstact_model): - __name:str = "" +class company_model(abstract_reference): + """ + Модель компании/организации. + + Наследуется от abstract_reference и содержит реквизиты компании для финансовых операций. + Может инициализироваться данными из настроек приложения. + + Attributes: + name (str): Название компании (наследуется от abstract_reference) + inn (int): ИНН (Идентификационный номер налогоплательщика) - 12 символов + bic (int): БИК (Банковский идентификационный код) - 9 символов + corr_account (int): Корреспондентский счет - 11 символов + account (int): Расчетный счет - 11 символов + ownership (str): Вид собственности - 5 символов + """ + __inn:int = 0 __bic:int = 0 __corr_account:int = 0 __account:int = 0 __ownership:str = "" + def __init__(self, settings): - # ИНН : 12 симв - # Счет 11 симв - # Корреспондентский счет 11 симв - # БИК 9 симв - # Наименование - # Вид собственности 5 симв + if settings.company is not None and validator.validate(settings.company, company_model): + # Копируем все поля из настроек + super().__init__(settings.company.name) + #self.__name = settings.company.name + self.__inn = settings.company.inn + self.__bic = settings.company.bic + self.__corr_account = settings.company.corr_account + self.__account = settings.company.account + self.__ownership = settings.company.ownership - # Наименование - @property - def name(self) -> str: - return self.__name - @name.setter - def name(self, value:str): - validator.validate(value, str) - self.__name = value.strip() # ИНН @property @@ -76,7 +88,6 @@ def ownership(self, value:str): validator.validate(value, str, 5) self.__ownership = value.strip() - diff --git a/Src/Models/nomenclature_group_model.py b/Src/Models/nomenclature_group_model.py new file mode 100644 index 0000000..ecbfb45 --- /dev/null +++ b/Src/Models/nomenclature_group_model.py @@ -0,0 +1,28 @@ +from Src.Core.abstract_model import abstract_reference +from Src.Core.validator import validator + + +class nomenclature_group_model(abstract_reference): + """ + Модель группы номенклатуры. + + Наследуется от abstract_reference и добавляет описание для группы товаров/продуктов. + + Attributes: + name (str): Название группы номенклатуры (наследуется от abstract_reference) + description (str): Описание группы номенклатуры + + + """ + __description: str = "" + + + # description (str): Описание группы номенклатуры + @property + def description(self) -> str: + return self.__description + + @description.setter + def description(self, value: str): + validator.validate(value, str) + self.__description = value.strip() diff --git a/Src/Models/nomenclature_model.py b/Src/Models/nomenclature_model.py new file mode 100644 index 0000000..afe96dd --- /dev/null +++ b/Src/Models/nomenclature_model.py @@ -0,0 +1,68 @@ +from Src.Core.abstract_model import abstract_reference +from Src.Core.validator import validator, argument_exception +from Src.Models.nomenclature_group_model import nomenclature_group_model +from Src.Models.unit_model import unit_model + + +class nomenclature_model(abstract_reference): + """ + Модель номенклатуры товара/продукта. + + Наследуется от abstract_reference и содержит полную информацию о товаре, + включая артикул, группу и единицу измерения. + + Attributes: + name (str): Краткое название номенклатуры (наследуется от abstract_reference) + full_name (str): Полное наименование номенклатуры + article (str): Артикул товара + group (nomenclature_group_model): Группа номенклатуры + unit (unit_model): Единица измерения товара + + + """ + __full_name: str = "" + __article: str = "" + __group: nomenclature_group_model = None + __unit: unit_model = None + + #full_name (str): Полное наименование номенклатуры + @property + def full_name(self): + return self.__full_name + + @full_name.setter + def full_name(self, value:str): + validator.validate(value, str, 255) + self.__full_name = value + + #article (str): Артикул товара + @property + def article(self) -> str: + return self.__article + + @article.setter + def article(self, value: str): + validator.validate(value, str, 10) + self.__article = value.strip() + + #group (nomenclature_group_model): Группа номенклатуры + @property + def group(self) -> nomenclature_group_model: + return self.__group + + @group.setter + def group(self, value: nomenclature_group_model): + if value and not isinstance(value, nomenclature_group_model): + raise argument_exception("Группа должна быть объектом nomenclature_group_model") + self.__group = value + + #unit (unit_model): Единица измерения товара + @property + def unit(self) -> unit_model: + return self.__unit + + @unit.setter + def unit(self, value: unit_model): + if value and not isinstance(value, unit_model): + raise argument_exception("Единица измерения должна быть объектом unit_model") + self.__unit = value diff --git a/Src/Models/storage_model.py b/Src/Models/storage_model.py index 5af8e1a..a162b01 100644 --- a/Src/Models/storage_model.py +++ b/Src/Models/storage_model.py @@ -1,15 +1,43 @@ -from Src.Core.validator import validator -from Src.Core.abstract_model import abstact_model +from Src.Core.validator import validator, argument_exception +from Src.Core.abstract_model import abstract_reference +from Src.Models.company_model import company_model -class storage_model(abstact_model): - __name:str = "" - # Наименование +class storage_model(abstract_reference): + """ + Модель склада/хранилища. + + Наследуется от abstract_reference и добавляет информацию о местоположении + и принадлежности к компании. + + Attributes: + name (str): Название склада (наследуется от abstract_reference) + address (str): Физический адрес расположения склада + company (company_model): Компания-владелец склада + + """ + + __name: str = "" + __address: str = "" + __company: company_model = None + + + # address (str): Физический адрес расположения склада @property - def name(self) -> str: - return self.__name + def address(self) -> str: + return self.__address - @name.setter - def name(self, value:str): + @address.setter + def address(self, value: str): validator.validate(value, str) - self.__name = value.strip() + self.__address = value.strip() + + # company (company_model): Компания-владелец склада + @property + def company(self) -> company_model: + return self.__company + + @company.setter + def company(self, value: company_model): + validator.validate(value, company_model) + self.__company = value \ 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..9f15b33 --- /dev/null +++ b/Src/Models/unit_model.py @@ -0,0 +1,47 @@ +from __future__ import annotations +from Src.Core.abstract_model import abstract_reference +from Src.Core.validator import validator + + +class unit_model(abstract_reference): + """ + Модель единицы измерения для системы конвертации величин. + + Представляет собой узел в иерархической системе единиц измерения, + где каждая единица может быть связана с базовой через коэффициент преобразования. + + Attributes: + name (str): Наименование единицы измерения (наследуется от AbstractReference). + ratio (int): Коэффициент преобразования к базовой единице. По умолчанию 1. + base_model (UnitModel): Ссылка на базовую модель единицы измерения. + Если None, текущая единица считается базовой. + + """ + __ratio: int = 1 + __base_model: unit_model = None + + def __init__(self, name: str, ratio: int = 1, base_model: unit_model = None): + super().__init__(name) + # self.__name = name + self.__ratio = ratio + self.__base_model = base_model + + + #ratio (int): Коэффициент преобразования к базовой единице. + @property + def ratio(self) -> int: + return self.__ratio + + @ratio.setter + def ratio(self, value: int): + self.__ratio = value + + #base_model (UnitModel): Ссылка на базовую модель единицы измерения. + @property + def base_model(self) -> unit_model: + return self.__base_model + + @ratio.setter + def ratio(self, value:unit_model): + self.__base_model = value + diff --git a/Src/settings_manager.py b/Src/settings_manager.py index ec87dfe..1addf02 100644 --- a/Src/settings_manager.py +++ b/Src/settings_manager.py @@ -6,7 +6,7 @@ import os import json -#################################################### +####################################################3 # Менеджер настроек. # Предназначен для управления настройками и хранения параметров приложения class settings_manager: @@ -80,12 +80,13 @@ def convert(self, data: dict) -> bool: # Параметры настроек по умолчанию def set_default(self): - company = company_model() + self.__settings = settings_model() + + company = company_model(settings=settings_model()) company.name = "Рога и копыта" company.inn = -1 - - self.__settings = settings_model() self.__settings.company = company + diff --git a/Tst/settings.json b/Tst/settings.json index 9fce5d6..834a6f2 100644 --- a/Tst/settings.json +++ b/Tst/settings.json @@ -2,6 +2,6 @@ "company": { "name":"Рога и копыта", - "inn1":123456789 + "inn":123456789 } } diff --git a/Tst/test_models.py b/Tst/test_models.py index bccacf1..f65ca2a 100644 --- a/Tst/test_models.py +++ b/Tst/test_models.py @@ -1,3 +1,6 @@ +from Src.Models.nomenclature_group_model import nomenclature_group_model +from Src.Models.nomenclature_model import nomenclature_model +from Src.Models.unit_model import unit_model from Src.settings_manager import settings_manager from Src.Models.company_model import company_model import unittest @@ -67,7 +70,7 @@ def test_loadCombo_createmodel_companymodel(self): print(f"ИНН {manager1.settings.company.inn}") # Проверка на сравнение двух по значению одинаковых моделей - def text_equals_storage_model_create(self): + def test_text_equals_storage_model_create(self): # Подготовка id = uuid.uuid4().hex storage1 = storage_model() @@ -79,8 +82,50 @@ def text_equals_storage_model_create(self): # Проверки assert storage1 == storage2 + # тестирование создания модели группы номенклатуры + def test_nomenclature_group_model_create(self): + # Подготовка + nomenclature_group = nomenclature_group_model(name="test") + nomenclature_group.description="test desc" + + assert nomenclature_group.name == "test" and nomenclature_group.description=="test desc" + # тестирование создания модели unit и использование + def test_unit_model(self): + # Подготовка + gr=unit_model("gr") + kg=unit_model("kg",1000,gr) + + mass_g=150000 + mass_kg=150 + assert mass_kg*kg.ratio == mass_g + + # тестирование создания модели номенклатуры с всеми подклассами + def test_nomenclature_model_create(self): + # Подготовка + nomenclature_group = nomenclature_group_model(name="test") + + nomenclature_group.description = "test desc" + + gr = unit_model("gr") + kg = unit_model("kg", 1000, gr) + + nomenclature=nomenclature_model(name="test nomenclature") + nomenclature.group=nomenclature_group + nomenclature.unit=kg + + nomenclature.full_name="t" * 254 + nomenclature.description="test description" + nomenclature.article="cle" + + + assert nomenclature + # тестирование создания через фаил + def test_company_model_file_create(self): + manager = settings_manager() + manager.file_name="settings.json" + company = company_model(settings=manager.settings) + + assert company.inn ==123456789 - - if __name__ == '__main__': unittest.main() diff --git a/_legacy/index.html b/_legacy/index.html new file mode 100644 index 0000000..bbacbb4 --- /dev/null +++ b/_legacy/index.html @@ -0,0 +1,2 @@ + +
Styczen 2024
NdPnWtSrCzwPtSob
 123456
78910111213
14151617181920
21222324252627
28293031   
\ No newline at end of file diff --git a/_legacy/legacy.png b/_legacy/legacy.png new file mode 100644 index 0000000..4064afb Binary files /dev/null and b/_legacy/legacy.png differ diff --git a/_legacy/main.py b/_legacy/main.py new file mode 100644 index 0000000..7ed6eb4 --- /dev/null +++ b/_legacy/main.py @@ -0,0 +1,251 @@ +import time, math + + +cal_ID = 0 + +class MonthlyCalendar: + def __init__(self, year = None, month = None): + self.tFontFace = 'Arial, Helvetica' + self.tFontSize = 12 + self.tFontColor = '#FFFFFF' + self.tBGColor = '#304B90' + + self.hFontFace = 'Arial, Helvetica' + self.hFontSize = 10 + self.hFontColor = '#FFFFFF' + self.hBGColor = '#304B90' + + self.dFontFace = 'Arial, Helvetica' + self.dFontSize = 12 + self.dFontColor = '#000000' + self.dBGColor = '#FFFFFF' + + self.wFontFace = 'Arial, Helvetica' + self.wFontSize = 10 + self.wFontColor = '#FFFFFF' + self.wBGColor = '#304B90' + + self.saFontColor = '#0000D0' + self.saBGColor = '#F6F6FF' + + self.suFontColor = '#D00000' + self.suBGColor = '#FFF0F0' + + self.tdBorderColor = 'red' + + self.borderColor = '#304B90' + self.hilightColor = '#FFFF00' + + self.link = '' + self.offset = 1 + self.weekNumbers = 0 + + self.weekdays = ('Sob', 'Nd', 'Pn', 'Wt', 'Sr', 'Czw', 'Pt') + + self.months = ('Styczen', 'Luty', 'Marzec', 'Kwiecien', 'Maj', 'Czerwiec', + 'Lipiec', 'Sierpien', 'Wrzesien', 'Pazdziernik', 'Listopad', 'Grudzien') + + self.error = ('Year must be 1 - 3999!', 'Month must be 1 - 12!') + + if year is None and month is None: + year = time.localtime().tm_year + month = time.localtime().tm_mon + elif year is None and month is not None: year = time.localtime().tm_year + elif month is None: month = 1 + self.year = int(year) + self.month = int(month) + self.specDays = {} + + __size = 0 + __mDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + + def set_styles(self): + globals()['cal_ID'] += 1 + html = '' + return html + + def leap_year(self, year): + return not (year % 4) and (year < 1582 or year % 100 or not (year % 400)) + + def get_weekday(self, year, days): + a = days + if year: a += (year - 1) * 365 + for i in range(1, year): + if self.leap_year(i): a += 1 + if year > 1582 or (year == 1582 and days >= 277): a -= 10 + if a: a = (a - self.offset) % 7 + elif self.offset: a += 7 - self.offset + return a + + def get_week(self, year, days): + firstWDay = self.get_weekday(year, 0) + return int(math.floor((days + firstWDay) / 7) + (firstWDay <= 3)) + + def table_cell(self, content, cls, date = '', style = ''): + size = int(round(self.__size * 1.5)) + html = ' 0: + if self.specDays[content][0]: + style += 'background-color:' + self.specDays[content][0] + ';' + if self.specDays[content][1]: + html += ' title="' + self.specDays[content][1] + '"' + if self.specDays[content][2]: + link = self.specDays[content][2] + style += 'cursor:pointer' + ';' + else: + link='brak' + style += 'cursor:pointer' + ';' + + if link=='brak': + html += ' onMouseOver="this.className=\'cssHilight' + str(globals()['cal_ID']) + '\'"' + html += ' onMouseOut="this.className=\'' + cls + '\'"' + html += ' onClick="document.location.href=\'' + '?date=' + date + '\'"' + + + if link and link!='brak': + html += ' onMouseOver="this.className=\'cssHilight' + str(globals()['cal_ID']) + '\'"' + html += ' onMouseOut="this.className=\'' + cls + '\'"' + html += ' onClick="document.location.href=\'' + link + '?date=' + date + '\'"' + if style: html += ' style="' + style + '"' + html += '>' + content + '' + return html + + def table_head(self, content): + cols = self.weekNumbers and '8' or '7' + html = '' + \ + content + '' + for i in range(len(self.weekdays)): + ind = (i + self.offset) % 7 + wDay = self.weekdays[ind] + html += self.table_cell(wDay, 'cssHeading' + str(globals()['cal_ID'])) + if self.weekNumbers: html += self.table_cell(' ', 'cssHeading' + str(globals()['cal_ID'])) + html += '' + return html + + def viewEvent(self, start, end, color, title, link = ''): + if start > end: return + if start < 1 or start > 31: return + if end < 1 or end > 31: return + while start <= end: + self.specDays[str(start)] = [color, title, link] + start += 1 + + def create(self): + self.__size = (self.hFontSize > self.dFontSize) and self.hFontSize or self.dFontSize + if self.wFontSize > self.__size: self.__size = self.wFontSize + + date = time.strftime('%Y-%m-%d', time.localtime()) + (curYear, curMonth, curDay) = [int(v) for v in date.split('-')] + + if self.year < 1 or self.year > 3999: html = '' + self.error[0] + '' + elif self.month < 1 or self.month > 12: html = '' + self.error[1] + '' + else: + if self.leap_year(self.year): self.__mDays[1] = 29 + days = 0 + for i in range(self.month - 1): days += self.__mDays[i] + + start = self.get_weekday(self.year, days) + stop = self.__mDays[self.month-1] + + html = self.set_styles() + html += '' + html += '' + html += '
' + title = self.months[self.month-1] + ' ' + str(self.year) + html += self.table_head(title) + daycount = 1 + + if self.year == curYear and self.month == curMonth: inThisMonth = 1 + else: inThisMonth = 0 + + if self.weekNumbers: weekNr = self.get_week(self.year, days) + + while daycount <= stop: + html += '' + wdays = 0 + + for i in range(len(self.weekdays)): + ind = (i + self.offset) % 7 + if ind == 0: cls = 'cssSaturdays' + elif ind == 1: cls = 'cssSundays' + else: cls = 'cssDays' + + style = '' + date = "%s-%s-%s" % (self.year, self.month, daycount) + + if (daycount == 1 and i < start) or daycount > stop: content = ' ' + else: + content = str(daycount) + if inThisMonth and daycount == curDay: + style = 'padding:0px;border:3px solid ' + self.tdBorderColor + ';' + elif self.year == 1582 and self.month == 10 and daycount == 4: daycount = 14 + daycount += 1 + wdays += 1 + + html += self.table_cell(content, cls + str(globals()['cal_ID']), date, style) + + if self.weekNumbers: + if not weekNr: + if self.year == 1: content = ' ' + elif self.year == 1583: content = '52' + else: content = str(self.get_week(self.year - 1, 365)) + elif self.month == 12 and weekNr >= 52 and wdays < 4: content = '1' + else: content = str(weekNr) + + html += self.table_cell(content, 'cssWeeks' + str(globals()['cal_ID'])) + weekNr += 1 + + html += '' + html += '
' + return html + + +if __name__ == "__main__": + filepath = "calendar.html" + calendar = MonthlyCalendar() + body = calendar.create() + html = f"{body}" + file = open(filepath, "w") + file.write(html) + file.close() + diff --git a/_legacy/readme.md b/_legacy/readme.md new file mode 100644 index 0000000..75de7ac --- /dev/null +++ b/_legacy/readme.md @@ -0,0 +1,29 @@ +# Legacy +Первое задание + +Перед Вами программа которая формирует в формате **HTML** календарь на текущий месяц. +Получен следующий вариант: + +| | +|------| +|![legacy.png](legacy.png) +| | + +Заказчик просит внести изменения. + +> К сожалению, разработчик, которому ранее было поручено создать данный календарь не завершил работу. +> Увы, он сильно заболел и не доступен для консультаций. +> По мнению менеджмента, для Вас не должно составить никаких проблем. + +Вам необходимо: +- Дописать программу. Сделать выгрузку календаря в формат HTML, в файл. Наименование файла должен определять Заказчик (произвольное), но расширение - строго HTML +- Сделать календарь на русском языке по умолчанию. Так же, добавить разные языки: английский, немецкий. В перспективе, язык должен настраиваться. +- Календарь должен отображаться в зависимости от языка. Если русский, то начинаться с понедельника, если западный язык, то с воскресенья, как сейчас. +- Суббота и воскресенье должны отмечаться одним цветом. Фон светло красный, а цвет текста - ярко красный. Праздник (23 фебраля, 8-е марта и прочее) должен отображаться синим цветом и светло синим фоном. +Календарь должен формироваться за любой указанный месяц Заказчиком. + + +[Вот файл с программой который передал нам коллега](main.py)
+**Удачи, бро!** + +