"""Seed demo data: roles, users, facilities/schedules/slots, a complete default workflow (with approval + payment + invoice + Odoo-sync steps), pricing rules, notification templates and a report definition. The workflow seeded here is demo DATA ONLY — the engine itself remains fully dynamic and knows nothing about pistachio processing. """ from decimal import Decimal from django.contrib.auth import get_user_model from django.core.management.base import BaseCommand from django.db import transaction from django.utils import timezone from apps.accounts.models import Customer from apps.notifications.models import NotificationTemplate from apps.pricing.models import PricingRule from apps.rbac.models import Permission, Role from apps.reports.models import ReportDefinition, ReportField from apps.reservations.models import Facility, FacilitySchedule from apps.reservations.slot_service import SlotService from apps.workflows.engine import WorkflowEngine from apps.workflows.models import Workflow, WorkflowStep, WorkflowTransition, WorkflowVersion User = get_user_model() PERMISSIONS = [ "reservation.view", "reservation.create", "reservation.update", "reservation.cancel", "reservation.approve", "slot.manage", "weighing.view", "weighing.create", "weighing.update", "workflow.view", "workflow.create", "workflow.update", "workflow.publish", "processing.view", "processing.start", "processing.pause", "processing.complete", "invoice.view", "invoice.create", "invoice.cancel", "report.view", "report.create", "user.view", "user.manage", "role.view", "role.manage", "settings.manage", ] ROLE_SPECS = { "administrator": ("Administrator", PERMISSIONS), "facility_manager": ("Facility Manager", [ "reservation.view", "reservation.update", "reservation.cancel", "reservation.approve", "slot.manage", "weighing.view", "weighing.create", "weighing.update", "processing.view", "processing.start", "processing.complete", "invoice.view", "report.view", "report.create", "user.view", ]), "operator": ("Operator", [ "reservation.view", "weighing.view", "weighing.create", "processing.view", "processing.start", "processing.pause", "processing.complete", ]), "customer": ("Customer", ["reservation.view", "reservation.create", "processing.view", "report.view"]), "accountant": ("Accountant", ["invoice.view", "invoice.create", "invoice.cancel", "report.view"]), "quality_controller": ("Quality Controller", ["processing.view", "processing.complete", "report.create"]), } # (key, name, flags) STEP_DEFS = [ ("reservation", "Reservation", {"is_start": True}), ("approval", "Approval", {}), ("weighing", "Weighing", {"required": True}), ("processing_selection", "Processing Selection", {"required": True}), ("customer_confirmation", "Customer Confirmation", {"requires_customer_confirmation": True}), ("payment", "Payment", {"requires_payment": True}), ("sorting", "Sorting", {}), ("washing", "Washing", {}), ("drying", "Drying", {}), ("quality_control", "Quality Control", {}), ("packaging", "Packaging", {}), ("final_weighing", "Final Weighing", {}), ("final_report", "Final Report", {}), ("invoice", "Invoice", {"triggers_invoice": True, "triggers_odoo_sync": True}), ("completed", "Completed", {"is_terminal": True}), ] REPORT_FIELDS = [ ("final_moisture", "Final Moisture", "decimal"), ("quality_grade", "Quality Grade", "choice"), ("broken_percentage", "Broken Percentage", "percentage"), ("category_a_weight", "Category A Weight", "weight"), ("category_b_weight", "Category B Weight", "weight"), ("category_c_weight", "Category C Weight", "weight"), ] class Command(BaseCommand): help = "Seed demo roles, users, facilities, a default workflow, pricing, templates and reports." @transaction.atomic def handle(self, *args, **options): perms = {} for code in PERMISSIONS: perms[code], _ = Permission.objects.get_or_create(code=code, defaults={"description": code}) roles = {} for slug, (name, codes) in ROLE_SPECS.items(): role, _ = Role.objects.get_or_create(slug=slug, defaults={"name": name}) role.permissions.set([perms[c] for c in codes]) roles[slug] = role def make_user(username, role_slug, **kw): user, _ = User.objects.get_or_create(username=username, defaults=kw) user.set_password("demo1234") user.save() if not user.user_roles.filter(role=roles[role_slug]).exists(): user.user_roles.create(role=roles[role_slug]) return user make_user("admin", "administrator", is_staff=True, is_superuser=True, email="admin@example.com") make_user("manager", "facility_manager") make_user("operator", "operator") make_user("accountant", "accountant") make_user("qc", "quality_controller") cust_user = make_user("customer", "customer", email="customer@example.com", phone="09120000000") Customer.objects.get_or_create(user=cust_user, defaults={"company_name": "Demo Pistachio Co."}) # Facility + schedule + slots facility, _ = Facility.objects.get_or_create(name="Main Facility") tomorrow = timezone.localdate() + timezone.timedelta(days=1) FacilitySchedule.objects.update_or_create( facility=facility, date=tomorrow, defaults={"is_open": True, "capacity": 3} ) SlotService().generate_slots(facility, tomorrow) # Default workflow workflow, _ = Workflow.objects.get_or_create( slug="pistachio-processing", defaults={"name": "Pistachio Processing", "description": "Default configurable workflow"}, ) version = WorkflowVersion.objects.create(workflow=workflow, version=workflow.versions.count() + 1) steps = {} for order, (key, name, flags) in enumerate(STEP_DEFS): steps[key] = WorkflowStep.objects.create( version=version, key=key, name=name, order=order, position_x=80 + (order % 5) * 200, position_y=40 + (order // 5) * 160, **flags, ) chain = [d[0] for d in STEP_DEFS] for a, b in zip(chain, chain[1:]): WorkflowTransition.objects.create(version=version, from_step=steps[a], to_step=steps[b]) # Pricing PricingRule.objects.get_or_create(step=steps["washing"], name="Washing per kg", rule_type="per_kg", amount=Decimal("5000")) PricingRule.objects.get_or_create(step=steps["drying"], name="Drying per kg", rule_type="per_kg", amount=Decimal("8000")) PricingRule.objects.get_or_create(step=steps["sorting"], name="Sorting per kg", rule_type="per_kg", amount=Decimal("12000")) # Report definition definition, _ = ReportDefinition.objects.get_or_create( workflow=workflow, name="Default Processing Report", defaults={"active": True} ) for order, (key, label, field_type) in enumerate(REPORT_FIELDS): ReportField.objects.get_or_create( definition=definition, key=key, defaults={"label": label, "field_type": field_type, "order": order}, ) # Notification templates NotificationTemplate.objects.get_or_create( event="reservation.approved", channel="sms", defaults={"body": "Cetana: reservation {{reservation_title}} on {{slot_date}} at {{slot_time}} is confirmed."}, ) NotificationTemplate.objects.get_or_create( event="order.completed", channel="sms", defaults={"body": "Cetana: order {{order_number}} is complete. Final report is ready."}, ) try: WorkflowEngine().publish(workflow, version) except Exception as exc: # pragma: no cover self.stdout.write(self.style.WARNING(f"Workflow publish skipped: {exc}")) # Pre-fill the Odoo server URL (credentials are entered in Settings). from apps.core.models import SystemSetting SystemSetting.objects.get_or_create(key="odoo.url", defaults={"value": "https://rayayazd.ir"}) self.stdout.write(self.style.SUCCESS("Seed data created. Logins use password 'demo1234'."))