From b4bdae6d893d0511617ba6b3ffd91c5b3f27b5ee Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 5 Feb 2026 09:02:25 +0000 Subject: [PATCH] 0.0.1 --- core/__pycache__/admin.cpython-311.pyc | Bin 212 -> 3350 bytes core/__pycache__/models.cpython-311.pyc | Bin 209 -> 6700 bytes core/__pycache__/views.cpython-311.pyc | Bin 1364 -> 2121 bytes core/admin.py | 44 ++- core/management/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 168 bytes core/management/commands/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 177 bytes .../__pycache__/seed_data.cpython-311.pyc | Bin 0 -> 4683 bytes core/management/commands/seed_data.py | 75 ++++++ core/migrations/0001_initial.py | 94 +++++++ .../__pycache__/0001_initial.cpython-311.pyc | Bin 0 -> 5514 bytes core/models.py | 86 +++++- core/templates/base.html | 179 ++++++++++-- core/templates/core/index.html | 255 +++++++++--------- core/views.py | 21 +- 16 files changed, 600 insertions(+), 154 deletions(-) create mode 100644 core/management/__init__.py create mode 100644 core/management/__pycache__/__init__.cpython-311.pyc create mode 100644 core/management/commands/__init__.py create mode 100644 core/management/commands/__pycache__/__init__.cpython-311.pyc create mode 100644 core/management/commands/__pycache__/seed_data.cpython-311.pyc create mode 100644 core/management/commands/seed_data.py create mode 100644 core/migrations/0001_initial.py create mode 100644 core/migrations/__pycache__/0001_initial.cpython-311.pyc diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index a5ed392d6714413db63120e4233d2e96cbadb5de..9cc4b9b91a68ead46095e2e967616cba01c37aca 100644 GIT binary patch literal 3350 zcmbVOOK;mo5Z>iOq7+Gf%6a=yKN=csoF0OrK+z&Cj37v?255^E>_MQ#jm?Cl+$Gh< zHy!c=bPLcQ;3CNH=-mc%F!mInr`%-3r<^*oBxRXm0XL#JqPW9v=bQOV*7*f6`AOxp@DlLdYxP5QloirxvBeQ5@A%eAQBY&C-0`(*1%}@QYT_H!Q<1 zStUvo!ss)JjyOy`poF}EGyW*#K5?{{#L>a)QhN751s^!$&jI|rN&gV5l0npR5DS1< zyf9)lgQ({q8h}{3Fk&r(Sja&v1LCC%BTi%xi#doDK&)OEaZ={K2G2~uGgE1T&*$En z&dh7%%sUAfQx~3hCWBbYL7WD}nF}M%I=n)KcNp90g)gMOjXJ4BN=-`H*YY@@T?38Kyh zd%;>AptSJ^@Cf(p7TajGIopsaxxKf~tMH688eD`|z;uVl+nGmTHBGf-|C;g|cs5LpPa+@+jpMU?22CA}oar)NT}@31E<*Ge4ev(x5gxHW zy5>iA4Mwt&L19!&cQh!DE-Hxx;_|36K%s`uJHmhhNtu|6yvuI_mPz?M{4~r$E`?bb z6PIMEp1NMdc$U-+aNuY#`5_{XlDdsKU+!)lPcL*I;t|tB|Bs|s8wxunsEOiUqIn&Z zgsTBW7DR5eFUnp!Y6cyDn{lAL43qLtzyRe{_-VY(r@VlE$U94Q3Gbo7(v4BQ2UMrm zx(_AaGmWADM^ddM3pXJe*9=*RLKxXmClr&O4Qt4^-5_Ft9kf_V(*!S$Qfcrtyv!)^ z7T!aHIvZp19Wq%(CO1b{3;O3XIo~Xp3VNiV$9Tm|p0%0>fd_qvYXFp^H{2nG4&8!z zLT4``ZfC>(3_Lg*EKz2CA6MqOo5z)EcN33TX6Tw9@gj`muOe31p;j?jOSNIgjB8L3 zl{WUzL{w*z;wu2iG3%lXwc*g%V6B;I>>;a>DDMmKX_!XtVmnhFMlOo3%vYFA!yTLp z4Qe4oDUU9>U$J_3uzIVo?j{xvYm43cc*Hru=kjxMzKXKtE7)0}t7u~4tcvNnD`OjO z%6y0dXJN;KZO4uGl{Ocp$n_cI!ruc?ehIVkufV|W@ZZ!kD!q#jp;?DBQ|`Ajo5<4~ z?JDTnFzce`JhOwH_U%?Xh`77WRk$w-Qdl8}H$cMiwT~Ck7-+B`b9{~CQiBF(JFqw5 zHo1jrl!W8WMYeF+@?B_XOm+1nk@=83wcNM(s+{B?vK?g1Z+ujB3T8~!Z}P&h*m zUOfjYrSzCA9-jXmlP8D&|NE9sbn7qYd-@Tb>(jY`qEK@{#!au@pISP>fm1XmDp4*E z{SBf_N)+RWUhUJX1BKF?12S%UoBi396C5~2gDBUA{)H5!cto%C>6L+^(!~K8H@&<4 zndK84I7NdfFl*vpPEkrn6tJ%$$_k=nP4DM^eHBqw5M>2Xt`Gf(C?m1VBTCi`W0^;k z`O#SB2W5rUFqv7?>+~9PLY8pg1WF{~rXjd6de#(x1l(6Xlh delta 151 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 a96&KfATE|=n7l?plKTRK3=kEu0fhh~{vb5~ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 2a36fd69370b38a98d8b01bf8eb9817c42f16ed6..a09b86d679488e5534d4683c2f366eba1a38f2a3 100644 GIT binary patch literal 2121 zcmbVM&1)M+6ra`ir=^Irv88saIE7e>EyZzg2`PnAQezUQ#tj7aP<6dKvDVq$QD;Z~ z5HjeH9tt`5P@0PmKDjCGACO~@K1yOiV!;sT$u~jBC8xewTauOJ&}1}v``(-PX6C(l zzxQW69zift(i82#htR)V=nu~s^Xg|{o*@;ff{vuquOJB^dvuTCm3#u%d3C=LkOD66 z(}PAx3IXp|1A5qqND(6{MU9vgGvZR*7?Z|~gp`2w;1eVzRpCdJ3!U)e?&kz2x`tV4 z15-;QCWrxwO%+q%LQFGojhOgE0MlH+@h|JL$(-OaC2Cb+PI%GWz$PPf+X*hoN?9|p z6MjhO;~yyO;6xTptAZ5<`zm~g{s;l%h5xJHA>?PMj`sU2Bi{p%4hNe3^E(W*qc%H2 zT~LL`ZX&5i^_YU{-SII0z#OgfbKUWlrK{{ZrSILQ6~%E$O{hjaOe54A&14UF!u2G!87o{52h8r}=+_|!<< z!#*D<(OdcdVz{zC$mpm|-CGyxJ~g@*-0`z3L;E^XV|D)lRK?+-G5{R$+U$ypf-EcJ z?46b6tSVdORU%V0yQ#5q)~XtYOt-TZlbL2QO|c5Km|UrBYPH&SK_P|{`nFPe$R#KI zy)HAItZ9l9)yz_bk~NAgi$jgOSFKuPh9eCGqjLX**B)RkR^eE)N(!k0A8zRQb+U;mjWNY zIs?}j7ea2drPXbkaYe`@dqk)~KjZe$ba{4z6#r6%j8l;-)0|9l!b`~sD#T=Xi&-4} zS$DjdDVB+Wi`7+ljcO5ZVWkRvugKGDRb&Mh-N-gHylE9G+m4T0(`(?*uWDTa05#Cd zZ!+v#E}J!_|Dk27D|ndwgEMmuw(mAPqW zZoX?GUbV$yN1Shq^Dm61INuWQ+2Xy9xY8C^TH*s+d~hsI+2Zw%c&jbmdNJJ+7i@9i z$a8ez$YYCld+}gA&_h5PcX}7mM811z@;E)!O6To#{y04ew`is3?DSlBJk$L|>|Xe^ zmk1@m14v`xbqd8&yH}f;>E~;$=)4`BZ~Eq6L4Hu~g5x0;y$Y-oIIS_t@p$@#$x}_i zDig{e@0R z@q&Bs!o|eY7_J_?>D60FI8D<3fy9`Yo;p*@MdO#eZ{B?Gy_q*Ze#}pT;ZLDZ5OBQz zjjc8RARL$O4whDfufa1NN)?b%25+y$Ym!Jg!yF&|+fq}k{>fTLXA|yg* zXe!<}X<@(b?@Kf?69H&yGat%OIq(56;`Rss$y5K%gW6Dg8KALQ9RPg*^?u-hcQq27 zor3os?A^(hkpmGiXagCx=|Ib=8*YstKYZgXCoFXl{X(1@>|shg-X6$ENvAxHyB{inOuBwZ@p06E>P{wCAAY(d13PUi1CZpd= literal 0 HcmV?d00001 diff --git a/core/management/commands/__init__.py b/core/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/management/commands/__pycache__/__init__.cpython-311.pyc b/core/management/commands/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2e0c69fa4a982434dc10070b90021ca996c09197 GIT binary patch literal 177 zcmZ3^%ge<81b-u2GC}lX5CH>>P{wCAAY(d13PUi1CZpdlIY~;;_lhPbtkwwJTx;8Va(um>)=dU}j`w{J;PsikN|70F^W>t^fc4 literal 0 HcmV?d00001 diff --git a/core/management/commands/__pycache__/seed_data.cpython-311.pyc b/core/management/commands/__pycache__/seed_data.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7219d50fb901f23882a2e4f56315eab0154cdaff GIT binary patch literal 4683 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 JuVCT!{U0f2dKCZw literal 0 HcmV?d00001 diff --git a/core/models.py b/core/models.py index 71a8362..6ed3c2b 100644 --- a/core/models.py +++ b/core/models.py @@ -1,3 +1,87 @@ from django.db import models +from django.contrib.auth.models import User -# Create your models here. +class Plant(models.Model): + name = models.CharField(max_length=255) + location = models.CharField(max_length=255, blank=True) + + def __str__(self): + return self.name + +class Category(models.Model): + name = models.CharField(max_length=100) + + class Meta: + verbose_name_plural = "Categories" + + def __str__(self): + return self.name + +class Product(models.Model): + name = models.CharField(max_length=255) + sku = models.CharField(max_length=100, unique=True) + category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True) + description = models.TextField(blank=True) + + def __str__(self): + return f"{self.name} ({self.sku})" + +class Inventory(models.Model): + plant = models.ForeignKey(Plant, on_delete=models.CASCADE, related_name='inventory') + product = models.ForeignKey(Product, on_delete=models.CASCADE) + quantity = models.DecimalField(max_digits=12, decimal_places=2, default=0) + lot_number = models.CharField(max_length=100, blank=True) + + class Meta: + verbose_name_plural = "Inventory" + unique_together = ('plant', 'product', 'lot_number') + + def __str__(self): + return f"{self.product.name} at {self.plant.name} - Lot: {self.lot_number or 'N/A'}" + +class Machine(models.Model): + STATUS_CHOICES = [ + ('active', 'Active'), + ('maintenance', 'Maintenance'), + ('broken', 'Broken'), + ] + name = models.CharField(max_length=255) + plant = models.ForeignKey(Plant, on_delete=models.CASCADE, related_name='machines') + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active') + last_maintenance = models.DateField(null=True, blank=True) + + def __str__(self): + return f"{self.name} ({self.plant.name})" + +class WorkOrder(models.Model): + STATUS_CHOICES = [ + ('planned', 'Planned'), + ('in_progress', 'In Progress'), + ('completed', 'Completed'), + ('cancelled', 'Cancelled'), + ] + order_number = models.CharField(max_length=50, unique=True) + plant = models.ForeignKey(Plant, on_delete=models.CASCADE) + product = models.ForeignKey(Product, on_delete=models.CASCADE) + quantity = models.PositiveIntegerField() + machine = models.ForeignKey(Machine, on_delete=models.SET_NULL, null=True, blank=True) + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='planned') + start_date = models.DateTimeField(null=True, blank=True) + end_date = models.DateTimeField(null=True, blank=True) + + def __str__(self): + return self.order_number + +class Inspection(models.Model): + RESULT_CHOICES = [ + ('pass', 'Pass'), + ('fail', 'Fail'), + ] + work_order = models.ForeignKey(WorkOrder, on_delete=models.CASCADE, related_name='inspections') + inspector = models.ForeignKey(User, on_delete=models.SET_NULL, null=True) + result = models.CharField(max_length=10, choices=RESULT_CHOICES) + notes = models.TextField(blank=True) + timestamp = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return f"Inspection for {self.work_order.order_number} - {self.result}" \ No newline at end of file diff --git a/core/templates/base.html b/core/templates/base.html index 1e7e5fb..1b89ad8 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -1,25 +1,166 @@ - - - {% block title %}Knowledge Base{% endblock %} - {% if project_description %} - - - - {% endif %} - {% if project_image_url %} - - - {% endif %} - {% load static %} - - {% block head %}{% endblock %} + + + {% block title %}Multi-Plant ERP{% endblock %} + {% load static %} + + + {% block head %}{% endblock %} - - {% block content %}{% endblock %} - + - +
+
+
+ {{ user.username }} +
+
+
+ +
+ {% block content %}{% endblock %} +
+
+ + \ No newline at end of file diff --git a/core/templates/core/index.html b/core/templates/core/index.html index faec813..2afffe5 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -1,145 +1,146 @@ {% extends "base.html" %} -{% block title %}{{ project_name }}{% endblock %} +{% block title %}Dashboard | Multi-Plant ERP{% endblock %} -{% block head %} - - - +{% block extra_css %} {% endblock %} {% block content %} -
-
-

Analyzing your requirements and generating your app…

-
- Loading… +
+
+

Operational Dashboard

+

Real-time manufacturing insights across all facilities.

-

AppWizzy AI is collecting your requirements and applying the first changes.

-

This page will refresh automatically as the plan is implemented.

-

- Runtime: Django {{ django_version }} · Python {{ python_version }} - — UTC {{ current_time|date:"Y-m-d H:i:s" }} -

-
-
-
- Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC) -
+
+ + Add New Order +
+ + +
+
+
Active Facilities
+
{{ stats.plants_count }}
+
+
+
Total Stock Items
+
{{ stats.inventory_count }}
+
+
+
Production Machines
+
{{ stats.machines_count }}
+
+
+
Running Work Orders
+
{{ stats.active_work_orders }}
+
+
+ +
+
+

Recent Quality Inspections

+ View All +
+ + {% if stats.recent_inspections %} + + + + + + + + + + + + {% for inspection in stats.recent_inspections %} + + + + + + + + {% endfor %} + +
Work OrderFacilityResultInspectorDate
{{ inspection.work_order.order_number }}{{ inspection.work_order.plant.name }} + + {{ inspection.result|upper }} + + {{ inspection.inspector.username }}{{ inspection.timestamp|date:"M d, Y H:i" }}
+ {% else %} +

No recent inspections recorded.

+ {% endif %} +
{% endblock %} \ No newline at end of file diff --git a/core/views.py b/core/views.py index c9aed12..c24423b 100644 --- a/core/views.py +++ b/core/views.py @@ -4,22 +4,31 @@ import platform from django import get_version as django_version from django.shortcuts import render from django.utils import timezone - +from .models import Plant, Product, Inventory, Machine, WorkOrder, Inspection def home(request): - """Render the landing screen with loader and environment details.""" + """Render the ERP dashboard with summary statistics.""" host_name = request.get_host().lower() agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic" now = timezone.now() + # ERP Stats + stats = { + "plants_count": Plant.objects.count(), + "products_count": Product.objects.count(), + "inventory_count": Inventory.objects.count(), + "machines_count": Machine.objects.count(), + "active_work_orders": WorkOrder.objects.filter(status='in_progress').count(), + "recent_inspections": Inspection.objects.order_by('-timestamp')[:5], + } + context = { - "project_name": "New Style", + "project_name": "Multi-Plant ERP", "agent_brand": agent_brand, "django_version": django_version(), "python_version": platform.python_version(), "current_time": now, "host_name": host_name, - "project_description": os.getenv("PROJECT_DESCRIPTION", ""), - "project_image_url": os.getenv("PROJECT_IMAGE_URL", ""), + "stats": stats, } - return render(request, "core/index.html", context) + return render(request, "core/index.html", context) \ No newline at end of file