Переехал с 1cpp. Ссылку публиковать не буду, форум умер :(
Приветствую коллеги!
Потихоньку разрабатываю замену платформы 1С 7.7 + 1cpp. Пока исключительно для себя и компании в которой работаю.
Успешно получалось долгое время обходить все болячки и технические долги во многом очень удачной платформы. Но в итоге уткнулся таки в ограничения. Переход на 8 - привел бы к таким-же последствиям.
Основным фактором, как это не странно, стал постепенный переход на Битрикс24. Пользователи активно начали работать с браузером. Понадобились синхронизации (выгрузки - загрузки), чтобы исключить двойную работу. Не считая установки двух мониторов, чтобы удобнее было работать в 1С и в Битрикс... Ну и массу других вещей...
Короче захотелось странного.
1. Нужен был оперативный конструктор, позволяющий редактировать структуру справочников и документов на лету.
2. Нужны были сложные структуры данных - типа деревьев, словарей (составных реквизитов), в том числе и в документах.
3. Нужны были документы с контролем ошибок, до того момента ,как это попадет в базу...
4. Все действия пользователя с данными - должны быть в истории, и это было свойством платформы на любых уровнях.
5. Чтобы кода поменьше.
6. Расширений и библиотек побольше.
7. JavaScript для управления формами.
8. И все это в браузере...
Короче.
Вроде бы концепт получился...
Разработка еще займет кучу времени.
Идей еще много, чтобы включить это в платформу, но пока могу дать первую версию, на которой уже можно сделать очень много.
Ссылка на проект тут
https://github.com/fobyphill/forTea
Недавно обновил проект.
Документация отстает - как обычно.
Что с многопользовательской работой? что с блокировками/транзакциями?
насколько я понимаю - установка неуспешно
ModuleNotFoundError: No module named 'distutils'
Launch unsuccessful. Exiting.
Для продолжения нажмите любую клавишу . . .
python
E:\00\venv
""
Creating venv in directory E:\00\venv using python "D:\Program Files\python.exe"
Install requirements to venv
activate
start pip
Unable to install requirements
exit code: 2
stdout:
Collecting asgiref==3.5.0 (from -r requirements.txt (line 1))
Using cached asgiref-3.5.0-py3-none-any.whl.metadata (9.2 kB)
Collecting backports.zoneinfo==0.2.1 (from -r requirements.txt (line 2))
Using cached backports.zoneinfo-0.2.1.tar.gz (74 kB)
Installing build dependencies: started
Installing build dependencies: finished with status 'done'
Getting requirements to build wheel: started
Getting requirements to build wheel: finished with status 'done'
Preparing metadata (pyproject.toml): started
Preparing metadata (pyproject.toml): finished with status 'done'
Collecting certifi==2021.10.8 (from -r requirements.txt (line 3))
Using cached certifi-2021.10.8-py2.py3-none-any.whl.metadata (3.0 kB)
Collecting charset-normalizer==2.0.12 (from -r requirements.txt (line 4))
Using cached charset_normalizer-2.0.12-py3-none-any.whl.metadata (11 kB)
Collecting contourpy==1.0.7 (from -r requirements.txt (line 5))
Using cached contourpy-1.0.7.tar.gz (13.4 MB)
Installing build dependencies: started
Installing build dependencies: finished with status 'done'
Getting requirements to build wheel: started
Getting requirements to build wheel: finished with status 'done'
Preparing metadata (pyproject.toml): started
Preparing metadata (pyproject.toml): finished with status 'done'
Collecting cycler==0.11.0 (from -r requirements.txt (line 6))
Using cached cycler-0.11.0-py3-none-any.whl.metadata (785 bytes)
Collecting DateTime==4.4 (from -r requirements.txt (line 7))
Using cached DateTime-4.4-py2.py3-none-any.whl.metadata (32 kB)
Collecting Django==4.0.2 (from -r requirements.txt (line 8))
Using cached Django-4.0.2-py3-none-any.whl.metadata (4.0 kB)
Collecting fonttools==4.39.4 (from -r requirements.txt (line 9))
Using cached fonttools-4.39.4-py3-none-any.whl.metadata (146 kB)
Collecting idna==3.3 (from -r requirements.txt (line 10))
Using cached idna-3.3-py3-none-any.whl.metadata (9.8 kB)
Collecting importlib-resources==5.12.0 (from -r requirements.txt (line 11))
Using cached importlib_resources-5.12.0-py3-none-any.whl.metadata (4.1 kB)
Collecting kiwisolver==1.4.4 (from -r requirements.txt (line 12))
Using cached kiwisolver-1.4.4.tar.gz (97 kB)
Installing build dependencies: started
Installing build dependencies: finished with status 'done'
Getting requirements to build wheel: started
Getting requirements to build wheel: finished with status 'done'
Preparing metadata (pyproject.toml): started
Preparing metadata (pyproject.toml): finished with status 'done'
Collecting matplotlib==3.7.1 (from -r requirements.txt (line 13))
Using cached matplotlib-3.7.1.tar.gz (38.0 MB)
Installing build dependencies: started
Installing build dependencies: finished with status 'done'
Getting requirements to build wheel: started
Getting requirements to build wheel: finished with status 'done'
Preparing metadata (pyproject.toml): started
Preparing metadata (pyproject.toml): finished with status 'done'
Collecting mysqlclient==2.1.0 (from -r requirements.txt (line 14))
Using cached mysqlclient-2.1.0.tar.gz (87 kB)
Installing build dependencies: started
Installing build dependencies: finished with status 'done'
Getting requirements to build wheel: started
Getting requirements to build wheel: finished with status 'done'
Preparing metadata (pyproject.toml): started
Preparing metadata (pyproject.toml): finished with status 'done'
Collecting networkx==3.1 (from -r requirements.txt (line 15))
Using cached networkx-3.1-py3-none-any.whl.metadata (5.3 kB)
Collecting numpy==1.22.4 (from -r requirements.txt (line 16))
Using cached numpy-1.22.4.zip (11.5 MB)
Installing build dependencies: started
Installing build dependencies: finished with status 'done'
Getting requirements to build wheel: started
Getting requirements to build wheel: finished with status 'done'
stderr:
[notice] A new release of pip is available: 24.3.1 -> 25.0.1
[notice] To update, run: python.exe -m pip install --upgrade pip
ERROR: Exception:
Traceback (most recent call last):
File "E:\00\venv\Lib\site-packages\pip\_internal\cli\base_command.py", line 105, in _run_wrapper
status = _inner_run()
File "E:\00\venv\Lib\site-packages\pip\_internal\cli\base_command.py", line 96, in _inner_run
return self.run(options, args)
~~~~~~~~^^^^^^^^^^^^^^^
File "E:\00\venv\Lib\site-packages\pip\_internal\cli\req_command.py", line 67, in wrapper
return func(self, options, args)
File "E:\00\venv\Lib\site-packages\pip\_internal\commands\install.py", line 379, in run
requirement_set = resolver.resolve(
reqs, check_supported_wheels=not options.target_dir
)
File "E:\00\venv\Lib\site-packages\pip\_internal\resolution\resolvelib\resolver.py", line 95, in resolve
result = self._result = resolver.resolve(
~~~~~~~~~~~~~~~~^
collected.requirements, max_rounds=limit_how_complex_resolution_can_be
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "E:\00\venv\Lib\site-packages\pip\_vendor\resolvelib\resolvers.py", line 546, in resolve
state = resolution.resolve(requirements, max_rounds=max_rounds)
File "E:\00\venv\Lib\site-packages\pip\_vendor\resolvelib\resolvers.py", line 397, in resolve
self._add_to_criteria(self.state.criteria, r, parent=None)
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\00\venv\Lib\site-packages\pip\_vendor\resolvelib\resolvers.py", line 173, in _add_to_criteria
if not criterion.candidates:
^^^^^^^^^^^^^^^^^^^^
File "E:\00\venv\Lib\site-packages\pip\_vendor\resolvelib\structs.py", line 156, in __bool__
return bool(self._sequence)
File "E:\00\venv\Lib\site-packages\pip\_internal\resolution\resolvelib\found_candidates.py", line 174, in __bool__
return any(self)
File "E:\00\venv\Lib\site-packages\pip\_internal\resolution\resolvelib\found_candidates.py", line 162, in <genexpr>
return (c for c in iterator if id(c) not in self._incompatible_ids)
^^^^^^^^
File "E:\00\venv\Lib\site-packages\pip\_internal\resolution\resolvelib\found_candidates.py", line 53, in _iter_built
candidate = func()
File "E:\00\venv\Lib\site-packages\pip\_internal\resolution\resolvelib\factory.py", line 187, in _make_candidate_from_link
base: Optional[BaseCandidate] = self._make_base_candidate_from_link(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
link, template, name, version
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "E:\00\venv\Lib\site-packages\pip\_internal\resolution\resolvelib\factory.py", line 233, in _make_base_candidate_from_link
self._link_candidate_cache[link] = LinkCandidate(
~~~~~~~~~~~~~^
link,
^^^^^
...<3 lines>...
version=version,
^^^^^^^^^^^^^^^^
)
^
File "E:\00\venv\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 304, in __init__
super().__init__(
~~~~~~~~~~~~~~~~^
link=link,
^^^^^^^^^^
...<4 lines>...
version=version,
^^^^^^^^^^^^^^^^
)
^
File "E:\00\venv\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 159, in __init__
self.dist = self._prepare()
~~~~~~~~~~~~~^^
File "E:\00\venv\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 236, in _prepare
dist = self._prepare_distribution()
File "E:\00\venv\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 315, in _prepare_distribution
return preparer.prepare_linked_requirement(self._ireq, parallel_builds=True)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\00\venv\Lib\site-packages\pip\_internal\operations\prepare.py", line 527, in prepare_linked_requirement
return self._prepare_linked_requirement(req, parallel_builds)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
File "E:\00\venv\Lib\site-packages\pip\_internal\operations\prepare.py", line 642, in _prepare_linked_requirement
dist = _get_prepared_distribution(
req,
...<3 lines>...
self.check_build_deps,
)
File "E:\00\venv\Lib\site-packages\pip\_internal\operations\prepare.py", line 72, in _get_prepared_distribution
abstract_dist.prepare_distribution_metadata(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
finder, build_isolation, check_build_deps
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "E:\00\venv\Lib\site-packages\pip\_internal\distributions\sdist.py", line 56, in prepare_distribution_metadata
self._install_build_reqs(finder)
~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^
File "E:\00\venv\Lib\site-packages\pip\_internal\distributions\sdist.py", line 126, in _install_build_reqs
build_reqs = self._get_build_requires_wheel()
File "E:\00\venv\Lib\site-packages\pip\_internal\distributions\sdist.py", line 103, in _get_build_requires_wheel
return backend.get_requires_for_build_wheel()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
File "E:\00\venv\Lib\site-packages\pip\_internal\utils\misc.py", line 701, in get_requires_for_build_wheel
return super().get_requires_for_build_wheel(config_settings=cs)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
File "E:\00\venv\Lib\site-packages\pip\_vendor\pyproject_hooks\_impl.py", line 166, in get_requires_for_build_wheel
return self._call_hook('get_requires_for_build_wheel', {
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
'config_settings': config_settings
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
})
^^
File "E:\00\venv\Lib\site-packages\pip\_vendor\pyproject_hooks\_impl.py", line 321, in _call_hook
raise BackendUnavailable(data.get('traceback', ''))
pip._vendor.pyproject_hooks._impl.BackendUnavailable: Traceback (most recent call last):
File "E:\00\venv\Lib\site-packages\pip\_vendor\pyproject_hooks\_in_process\_in_process.py", line 77, in _build_backend
obj = import_module(mod_path)
File "D:\Program Files\Lib\importlib\__init__.py", line 88, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
File "<frozen importlib._bootstrap>", line 1310, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 1026, in exec_module
File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
File "E:\TEMP\pip-build-env-kbzfw4bd\overlay\Lib\site-packages\setuptools\__init__.py", line 10, in <module>
import distutils.core
ModuleNotFoundError: No module named 'distutils'
Launch unsuccessful. Exiting.
Для продолжения нажмите любую клавишу . . .
Цитата: Злоп от 28 марта 2025, 00:32насколько я понимаю - установка неуспешно
ModuleNotFoundError: No module named 'distutils'
Launch unsuccessful. Exiting.
Для продолжения нажмите любую клавишу . . .
Спасибо.
Прошу меня простить.
Сегодня постараюсь поправить - добавил новый модуль, и забыл его в requirements.txt включить.
Цитата: Злоп от 28 марта 2025, 00:14Что с многопользовательской работой? что с блокировками/транзакциями?
Обычная "web-морда" с асинхронным поведением.
Поэтому многопользовательская работа в принципе нормальная.
Транзакции - на каждое изменение, плюс логирование всех изменений - как часть платформы.
Блокировки делать не стал :(. У меня на них многолетняя трудовая аллергия.
Выкрутился через очередь транзакций и маниакальное логирование каждого реквизита.
Смысл такой: При одновременной работе с одним и тем же объектом каждое изменение сохраняется в истории с временем и пользователем. Если два пользователя решили поменять наименование в справочнике - то оба их варианта сохранятся. "Правильность" определяется - кто последний - тот и "виновник". И каждое из изменений с помощью "ползунка таймлайна" - можно сделать актуальным.
А кто из- них прав в итоге - пусть пользователи выясняют друг с другом.
(Знаю что это неприятно (и как 1с-ник понимаю что "истина где-то рядом") - но система то, тут причем? Не знаю как вы - но если я получу сообщение - что "объект заблокирован" - меня это больше растраивает, чем гипотетическая правда о том что система "так защищает ваши данные". Имхо - Все данные - важны. Потеря данных при попытке их записать - это гораздо хуже. Опять же имхо. Не нарываюсь на холивар...)
Я могу наложить транзакцию, + включить блокировку, выполнить кучу действий с кучей объектов, зафиксировать транзакцию и только потом выключить блокировку. У меня есть варианты когда я даже не должен допускать грязное чтение кучи объектов...
?
Пока только на лицо хочу посмотреть как это выглядит.
Никике джавы учить не хочу.
Никаких джав - чесслово.
Скинул в личку ссылку на демку.
Цитата: Злоп от 28 марта 2025, 15:22Я могу наложить транзакцию, + включить блокировку, выполнить кучу действий с кучей объектов, зафиксировать транзакцию и только потом выключить блокировку. У меня есть варианты когда я даже не должен допускать грязное чтение кучи объектов...
?
У меня таких вариантов тоже есть "море". Но, пока обхожусь (обтекаю)... Спорить не буду. Блокировки бывают например нужны для исключения конкуренции за объект. Но и прямо сильной надобности не вижу.
Вот где прямо реально может пригодится - это вопросы (политики) безопасности (на чтение, на запись и т.д.). Но я до них в проекте еще не добрался... Двигаюсь от функциональности - к безопасности.
Почему только Злопу?
Я тоже глянуть хочу.
Йех.
(с закрытыми глазами давлю на кнопки)
База на сервере в теории - должна пересоздаваться раз в день. И это не факт.
Сервер тут: fortea2.ru
Админский доступ - дает доступ к конфигуратору и ко всему - что админ может делать (включая создание юзеров - через админку Django).
odmen
qweqwe123123
Юзер - ограничен в области видимости - может создавать только документы (в проекте - называются Контракты).
just_user
poiPOI098
Продвинутый юзер - может редактировать справочники и контракты.
power_user
sadhfjwei
Прошу ничего сильно не ломать.
А так - велкам.
Цитата: 1ex от 28 марта 2025, 15:41Блокировки бывают например нужны для исключения конкуренции за объект.
ПроцессN:
Стартанули. Точка А
прочитали кучу данных из кучи разных таблиц.
сделали кучу вычислений.
записали кучу данных в кучу разных таблиц
Финиш. Точка Б.
.
надо чтобы пока не отработает процесс1 от А до Б - чтобы аналогичный процесс2 (который будет использовать те же самые объекты от А до Б) ждал (или получил отлуп).
.
Реализуемо?
.
в клюшках просто выставлял логическую блокировку (если она еще не была выставлена, а если уже стояла - то ждал/отлуп), делал что надо, снимал блокировку.
Цитата: 1ex от 28 марта 2025, 15:33Никаких джав - чесслово.
Скинул в личку ссылку на демку.
Посмотрел бегло.
Жуть.
Фейс ни в дугу.
Как сырец-демо что в принципе что-то можно сделать - может быть... в принципе...
Фейс растянутый адски, похож на перспективный фейс 8-ки, от которого все шарахаются ;-)
Но я злоп, с годами стал требователен к красоте мира, поэтому мое мнение так себе...
Цитата: Злоп от 28 марта 2025, 23:47Цитата: 1ex от 28 марта 2025, 15:41Блокировки бывают например нужны для исключения конкуренции за объект.
ПроцессN:
Стартанули. Точка А
прочитали кучу данных из кучи разных таблиц.
сделали кучу вычислений.
записали кучу данных в кучу разных таблиц
Финиш. Точка Б.
.
надо чтобы пока не отработает процесс1 от А до Б - чтобы аналогичный процесс2 (который будет использовать те же самые объекты от А до Б) ждал (или получил отлуп).
.
Реализуемо?
.
в клюшках просто выставлял логическую блокировку (если она еще не была выставлена, а если уже стояла - то ждал/отлуп), делал что надо, снимал блокировку.
Сделал такое через "каскадные таски". Смысл в создании вложенных "умных" транзакций. Все как вы и описали. Умных - имею в виду, что это иерархия полноценных задача, с кодом условий (в основном шаблонированах - чтобы не писать код условий типа: Если таск такой-то - завершился, то стартуем другой.)
Я для разработки написал документик по Таскам. Вот - делюсь, можно кидать тапками: https://disk.yandex.ru/i/uakeZ9H7x6HnVw
Может не все еще работает - но таков путь :)
Цитата: Злоп от 28 марта 2025, 23:49Посмотрел бегло.
Жуть.
Фейс ни в дугу.
Как сырец-демо что в принципе что-то можно сделать - может быть... в принципе...
Фейс растянутый адски, похож на перспективный фейс 8-ки, от которого все шарахаются ;-)
Но я злоп, с годами стал требователен к красоте мира, поэтому мое мнение так себе...
Спасибо, что вообще посмотрели :)
(Ща, сочиню слезливую историю о том, почему мы такие убогие :) )
Начало анекдота...
Собрались как-то математик, 1с-овец (я), и бакендер (Фил).
Я, вместе с математиком, написал
Суперсложную Фигню стройную теорию. которую положил на хабр.
Если не получится осилить сотни две (три), формул - не ходите сюда:
https://habr.com/ru/articles/755158/
Бакендер сказал - какая
Суперсложная Фигня стройная теория, ща запилим.
Выкатил в итоге ядро, и спросил: -
А фронт вообще нужен? Я
почесал в затылке долго думал, и сказал: Давай сделаем шаблон из нескольких HTML страниц с каким-нибудь базовым CSS. Если вообще кому-нибудь кроме нас
Эта СФ этот хороший проект понадобится - то из-за открытых исходников могут подправить шаблоны страниц так, чтобы не вызывать резь в глазах. А нам "внутри" и так хватит. Я к сожалению для остальных - предпочитаю функциональность - красоте.
Зачем я это все: Фронтендера у меня пока нету - и когда появится - непонятно.
А в разработке у меня принцип: Устойчивость - Функциональность - Безопасность - Красивости и плюшки. Так-что доберусь когда-нибудь.
На гитхабе поправил проект (база - с простенькой демкой).
Старую версию - удалить.
Качаем, распаковываем, ничего вроде править не надо.
start.bat
ждем (надеюсь) вот такого :)
"Starting development server at http://127.0.0.1:8000/"
admin/admin