030bbc98fd
Шаблоны/ручное создание, access_projects, сид «Открытие аптеки». Плюс SILO/S3 для Django media. Co-authored-by: Cursor <cursoragent@cursor.com>
154 lines
5.4 KiB
Python
154 lines
5.4 KiB
Python
"""Сид шаблона «Открытие аптеки» с типовыми шагами по отделам.
|
|
|
|
python manage.py seed_project_templates
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from django.core.management.base import BaseCommand
|
|
from django.db import transaction
|
|
|
|
from apps.projects.models import (
|
|
ProjectTemplate, ProjectTemplateStep, ProjectTemplateStepItem,
|
|
)
|
|
from core.models.directories import Directory
|
|
|
|
|
|
# (step_title, dept_code_or_name_hints, offset_days, duration_days, [item titles])
|
|
PHARMACY_OPENING_STEPS = [
|
|
(
|
|
'Подготовка помещения и договора',
|
|
['юрист', 'legal', 'юридич'],
|
|
0, 21,
|
|
[
|
|
'Согласовать договор аренды',
|
|
'Проверить адрес и зону лицензирования',
|
|
'Получить технический паспорт / планы',
|
|
],
|
|
),
|
|
(
|
|
'Лицензирование',
|
|
['лиценз', 'фармацевт', 'качест'],
|
|
7, 30,
|
|
[
|
|
'Подготовить пакет документов на лицензию',
|
|
'Подать заявление в регулятор',
|
|
'Получить лицензию / внести изменения',
|
|
],
|
|
),
|
|
(
|
|
'IT и коммуникации',
|
|
['it', 'ит', 'информац'],
|
|
14, 21,
|
|
[
|
|
'Заказать канал связи / интернет',
|
|
'Установить РРО / ПРРО и ПО кассы',
|
|
'Настроить учётки AD / портал / почту',
|
|
'Проверить видеонаблюдение и сигнализацию',
|
|
],
|
|
),
|
|
(
|
|
'Закупки и оборудование',
|
|
['закуп', 'снабж', 'логист'],
|
|
14, 28,
|
|
[
|
|
'Заказать торговое оборудование',
|
|
'Закупить стартовый товарный запас',
|
|
'Организовать доставку и приёмку',
|
|
],
|
|
),
|
|
(
|
|
'HR и персонал',
|
|
['hr', 'кадр', 'персонал'],
|
|
21, 30,
|
|
[
|
|
'Утвердить штатное расписание',
|
|
'Нанять заведующего и фармацевтов',
|
|
'Оформить допуски и обучение',
|
|
],
|
|
),
|
|
(
|
|
'Маркетинг и открытие',
|
|
['маркет', 'реклам', 'pr'],
|
|
45, 21,
|
|
[
|
|
'Подготовить вывеску и оформление',
|
|
'Анонс открытия / акции',
|
|
'Чек-лист дня открытия',
|
|
],
|
|
),
|
|
]
|
|
|
|
|
|
def _find_department(hints: list[str]):
|
|
depts = list(
|
|
Directory.objects.filter(
|
|
directory_type__code='department', is_active=True,
|
|
)
|
|
)
|
|
for hint in hints:
|
|
h = hint.lower()
|
|
for d in depts:
|
|
blob = f'{d.code or ""} {d.name or ""}'.lower()
|
|
if h in blob:
|
|
return d
|
|
return None
|
|
|
|
|
|
class Command(BaseCommand):
|
|
help = 'Seed project template «Открытие аптеки».'
|
|
|
|
@transaction.atomic
|
|
def handle(self, *args, **options):
|
|
tpl, created = ProjectTemplate.objects.get_or_create(
|
|
name='Открытие аптеки',
|
|
defaults={
|
|
'description': (
|
|
'Типовой процесс открытия новой аптеки (1–3 месяца): '
|
|
'лицензии, IT, закупки, HR, маркетинг.'
|
|
),
|
|
'default_duration_days': 90,
|
|
'is_active': True,
|
|
},
|
|
)
|
|
if not created:
|
|
tpl.description = tpl.description or (
|
|
'Типовой процесс открытия новой аптеки (1–3 месяца).'
|
|
)
|
|
tpl.default_duration_days = tpl.default_duration_days or 90
|
|
tpl.is_active = True
|
|
tpl.save()
|
|
# Refresh steps only if empty
|
|
if tpl.steps.exists():
|
|
self.stdout.write(
|
|
self.style.WARNING(
|
|
f'Template «{tpl.name}» already has steps — skipped rewrite.'
|
|
)
|
|
)
|
|
return
|
|
|
|
for order, (title, hints, offset, duration, items) in enumerate(
|
|
PHARMACY_OPENING_STEPS,
|
|
):
|
|
dept = _find_department(hints)
|
|
step = ProjectTemplateStep.objects.create(
|
|
template=tpl,
|
|
title=title,
|
|
order=order,
|
|
department=dept,
|
|
offset_days=offset,
|
|
duration_days=duration,
|
|
)
|
|
for j, item_title in enumerate(items):
|
|
ProjectTemplateStepItem.objects.create(
|
|
step=step,
|
|
title=item_title,
|
|
order=j,
|
|
priority='medium',
|
|
)
|
|
dept_label = dept.name if dept else '— (отдел не найден, назначьте в админке)'
|
|
self.stdout.write(f' + {title} → {dept_label}')
|
|
|
|
self.stdout.write(self.style.SUCCESS(
|
|
f'Seeded template «{tpl.name}» with {tpl.steps.count()} steps.'
|
|
))
|