diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..85f944e8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,64 @@ +name: Django CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + + services: + mysql: + image: mysql:8.0 + env: + MYSQL_ROOT_PASSWORD: password + MYSQL_DATABASE: imaps_test + ports: + - 3306:3306 + options: >- + --health-cmd="mysqladmin ping -h localhost -ppassword" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v4 + with: + python-version: 3.12 + + - name: Install system deps for MySQL client + run: | + sudo apt-get update + sudo apt-get install -y default-libmysqlclient-dev build-essential + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Run migrations + env: + DB_HOST: 127.0.0.1 + DB_PORT: 3306 + DB_NAME: imaps + DB_USER: root + DB_PASS: password + run: | + python manage.py migrate --noinput + + - name: Run tests + env: + DB_HOST: 127.0.0.1 + DB_PORT: 3306 + DB_NAME: imaps_test + DB_USER: root + DB_PASS: password + run: | + python manage.py test --verbosity=2 diff --git a/IMAPS/IMAPS/.gitignore b/IMAPS/IMAPS/.gitignore new file mode 100644 index 00000000..42ebefc0 --- /dev/null +++ b/IMAPS/IMAPS/.gitignore @@ -0,0 +1,6 @@ +# Ignore OS-generated files +.DS_Store +Thumbs.db +.env +# Ignore dependencies +venv/ diff --git a/IMAPS/IMAPS/IMAPS/__init__.py b/IMAPS/IMAPS/IMAPS/__init__.py index e69de29b..063cd2cc 100644 --- a/IMAPS/IMAPS/IMAPS/__init__.py +++ b/IMAPS/IMAPS/IMAPS/__init__.py @@ -0,0 +1,2 @@ +import pymysql +pymysql.install_as_MySQLdb() diff --git a/IMAPS/IMAPS/IMAPS/__pycache__/__init__.cpython-311.pyc b/IMAPS/IMAPS/IMAPS/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index 7c8657ef..00000000 Binary files a/IMAPS/IMAPS/IMAPS/__pycache__/__init__.cpython-311.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS/__pycache__/__init__.cpython-312.pyc b/IMAPS/IMAPS/IMAPS/__pycache__/__init__.cpython-312.pyc index 750d5d14..c60c8c89 100644 Binary files a/IMAPS/IMAPS/IMAPS/__pycache__/__init__.cpython-312.pyc and b/IMAPS/IMAPS/IMAPS/__pycache__/__init__.cpython-312.pyc differ diff --git a/IMAPS/IMAPS/IMAPS/__pycache__/__init__.cpython-39.pyc b/IMAPS/IMAPS/IMAPS/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 8d54f18c..00000000 Binary files a/IMAPS/IMAPS/IMAPS/__pycache__/__init__.cpython-39.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS/__pycache__/settings.cpython-311.pyc b/IMAPS/IMAPS/IMAPS/__pycache__/settings.cpython-311.pyc deleted file mode 100644 index 0705ba52..00000000 Binary files a/IMAPS/IMAPS/IMAPS/__pycache__/settings.cpython-311.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS/__pycache__/settings.cpython-312.pyc b/IMAPS/IMAPS/IMAPS/__pycache__/settings.cpython-312.pyc index c58a28d6..ccf6e391 100644 Binary files a/IMAPS/IMAPS/IMAPS/__pycache__/settings.cpython-312.pyc and b/IMAPS/IMAPS/IMAPS/__pycache__/settings.cpython-312.pyc differ diff --git a/IMAPS/IMAPS/IMAPS/__pycache__/settings.cpython-39.pyc b/IMAPS/IMAPS/IMAPS/__pycache__/settings.cpython-39.pyc deleted file mode 100644 index d6999e80..00000000 Binary files a/IMAPS/IMAPS/IMAPS/__pycache__/settings.cpython-39.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS/__pycache__/urls.cpython-311.pyc b/IMAPS/IMAPS/IMAPS/__pycache__/urls.cpython-311.pyc deleted file mode 100644 index 8c17b8f8..00000000 Binary files a/IMAPS/IMAPS/IMAPS/__pycache__/urls.cpython-311.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS/__pycache__/urls.cpython-312.pyc b/IMAPS/IMAPS/IMAPS/__pycache__/urls.cpython-312.pyc index 82c5ef9b..2f0b211d 100644 Binary files a/IMAPS/IMAPS/IMAPS/__pycache__/urls.cpython-312.pyc and b/IMAPS/IMAPS/IMAPS/__pycache__/urls.cpython-312.pyc differ diff --git a/IMAPS/IMAPS/IMAPS/__pycache__/urls.cpython-39.pyc b/IMAPS/IMAPS/IMAPS/__pycache__/urls.cpython-39.pyc deleted file mode 100644 index bfdfb6ca..00000000 Binary files a/IMAPS/IMAPS/IMAPS/__pycache__/urls.cpython-39.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS/__pycache__/wsgi.cpython-311.pyc b/IMAPS/IMAPS/IMAPS/__pycache__/wsgi.cpython-311.pyc deleted file mode 100644 index 9863c083..00000000 Binary files a/IMAPS/IMAPS/IMAPS/__pycache__/wsgi.cpython-311.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS/__pycache__/wsgi.cpython-312.pyc b/IMAPS/IMAPS/IMAPS/__pycache__/wsgi.cpython-312.pyc index a661e9b0..e77681cf 100644 Binary files a/IMAPS/IMAPS/IMAPS/__pycache__/wsgi.cpython-312.pyc and b/IMAPS/IMAPS/IMAPS/__pycache__/wsgi.cpython-312.pyc differ diff --git a/IMAPS/IMAPS/IMAPS/__pycache__/wsgi.cpython-39.pyc b/IMAPS/IMAPS/IMAPS/__pycache__/wsgi.cpython-39.pyc deleted file mode 100644 index 984a56df..00000000 Binary files a/IMAPS/IMAPS/IMAPS/__pycache__/wsgi.cpython-39.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS/settings.py b/IMAPS/IMAPS/IMAPS/settings.py index 3b0f8e18..15956b33 100644 --- a/IMAPS/IMAPS/IMAPS/settings.py +++ b/IMAPS/IMAPS/IMAPS/settings.py @@ -11,7 +11,8 @@ """ from pathlib import Path - +import os +from dotenv import load_dotenv # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -75,20 +76,19 @@ # https://docs.djangoproject.com/en/5.1/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': 'imaps', # Your DB name - 'USER': 'root', # Your MySQL username - 'PASSWORD': '1234', # Your MySQL password - 'HOST': '127.0.0.1', # Use '127.0.0.1' instead of 'localhost' - 'PORT': '3306', # Use 3306 if running a standard MySQL server - 'OPTIONS': { - 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'" - } + "default": { + "ENGINE": os.getenv("DB_ENGINE", "django.db.backends.sqlite3"), + "NAME": os.getenv("DB_NAME", BASE_DIR / "db.sqlite3"), + "USER": os.getenv("DB_USER", ""), + "PASSWORD": os.getenv("DB_PASS", ""), + "HOST": os.getenv("DB_HOST", ""), + "PORT": os.getenv("DB_PORT", ""), + "OPTIONS": {"init_command": "SET sql_mode='STRICT_TRANS_TABLES'"}, } } + # Password validation # https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators diff --git a/IMAPS/IMAPS/IMAPS_app/__pycache__/forms.cpython-312.pyc b/IMAPS/IMAPS/IMAPS_app/__pycache__/forms.cpython-312.pyc index 652c0f86..538cffab 100644 Binary files a/IMAPS/IMAPS/IMAPS_app/__pycache__/forms.cpython-312.pyc and b/IMAPS/IMAPS/IMAPS_app/__pycache__/forms.cpython-312.pyc differ diff --git a/IMAPS/IMAPS/IMAPS_app/__pycache__/models.cpython-312.pyc b/IMAPS/IMAPS/IMAPS_app/__pycache__/models.cpython-312.pyc index 23146b24..21767875 100644 Binary files a/IMAPS/IMAPS/IMAPS_app/__pycache__/models.cpython-312.pyc and b/IMAPS/IMAPS/IMAPS_app/__pycache__/models.cpython-312.pyc differ diff --git a/IMAPS/IMAPS/IMAPS_app/__pycache__/views.cpython-312.pyc b/IMAPS/IMAPS/IMAPS_app/__pycache__/views.cpython-312.pyc index ef7d4901..20e67c3e 100644 Binary files a/IMAPS/IMAPS/IMAPS_app/__pycache__/views.cpython-312.pyc and b/IMAPS/IMAPS/IMAPS_app/__pycache__/views.cpython-312.pyc differ diff --git a/IMAPS/IMAPS/IMAPS_app/forms.py b/IMAPS/IMAPS/IMAPS_app/forms.py index a16e7124..47b553d9 100644 --- a/IMAPS/IMAPS/IMAPS_app/forms.py +++ b/IMAPS/IMAPS/IMAPS_app/forms.py @@ -35,7 +35,7 @@ class SupplierForm(RequiredFieldsAsteriskMixin, forms.ModelForm): class Meta: model = Supplier - fields = '__all__' + exclude = ['change_status'] class IngredientsRawMaterialsForm(RequiredFieldsAsteriskMixin, forms.ModelForm): UseCategory = forms.ChoiceField( diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/0003_ingredientsrawmaterials_change_status_and_more.py b/IMAPS/IMAPS/IMAPS_app/migrations/0003_ingredientsrawmaterials_change_status_and_more.py new file mode 100644 index 00000000..db2662c8 --- /dev/null +++ b/IMAPS/IMAPS/IMAPS_app/migrations/0003_ingredientsrawmaterials_change_status_and_more.py @@ -0,0 +1,43 @@ +# Generated by Django 5.1.6 on 2025-05-08 11:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('IMAPS_app', '0002_alter_ingredientsrawmaterials_cost_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='ingredientsrawmaterials', + name='change_status', + field=models.CharField(choices=[('active', 'Active'), ('old_modified', 'Old Modified'), ('new_modified', 'New Modified'), ('deleted', 'Deleted')], default='active', max_length=15), + ), + migrations.AddField( + model_name='packagingrawmaterials', + name='change_status', + field=models.CharField(choices=[('active', 'Active'), ('old_modified', 'Old Modified'), ('new_modified', 'New Modified'), ('deleted', 'Deleted')], default='active', max_length=15), + ), + migrations.AddField( + model_name='supplier', + name='change_status', + field=models.CharField(choices=[('active', 'Active'), ('old_modified', 'Old Modified'), ('new_modified', 'New Modified'), ('deleted', 'Deleted')], default='active', max_length=15), + ), + migrations.AddField( + model_name='usedingredient', + name='change_status', + field=models.CharField(choices=[('active', 'Active'), ('old_modified', 'Old Modified'), ('new_modified', 'New Modified'), ('deleted', 'Deleted')], default='active', max_length=15), + ), + migrations.AddField( + model_name='usedpackaging', + name='change_status', + field=models.CharField(choices=[('active', 'Active'), ('old_modified', 'Old Modified'), ('new_modified', 'New Modified'), ('deleted', 'Deleted')], default='active', max_length=15), + ), + migrations.AlterField( + model_name='supplier', + name='SupplierCode', + field=models.CharField(max_length=50, primary_key=True, serialize=False), + ), + ] diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0001_initial.cpython-311.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0001_initial.cpython-311.pyc deleted file mode 100644 index f7025d1c..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0001_initial.cpython-311.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0001_initial.cpython-312.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0001_initial.cpython-312.pyc index 225a8b8b..289e10eb 100644 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0001_initial.cpython-312.pyc and b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0001_initial.cpython-312.pyc differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0001_initial.cpython-39.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0001_initial.cpython-39.pyc deleted file mode 100644 index 0951ab43..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0001_initial.cpython-39.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_alter_ingredientsrawmaterials_cost_and_more.cpython-311.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_alter_ingredientsrawmaterials_cost_and_more.cpython-311.pyc deleted file mode 100644 index ebb7dcd9..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_alter_ingredientsrawmaterials_cost_and_more.cpython-311.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_alter_ingredientsrawmaterials_cost_and_more.cpython-312.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_alter_ingredientsrawmaterials_cost_and_more.cpython-312.pyc index 0782dbd3..ba9ac272 100644 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_alter_ingredientsrawmaterials_cost_and_more.cpython-312.pyc and b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_alter_ingredientsrawmaterials_cost_and_more.cpython-312.pyc differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_remove_packagingrawmaterials_batch_code_and_more.cpython-311.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_remove_packagingrawmaterials_batch_code_and_more.cpython-311.pyc deleted file mode 100644 index ea9705e1..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_remove_packagingrawmaterials_batch_code_and_more.cpython-311.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_remove_packagingrawmaterials_batch_code_and_more.cpython-312.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_remove_packagingrawmaterials_batch_code_and_more.cpython-312.pyc deleted file mode 100644 index a41ef526..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_remove_packagingrawmaterials_batch_code_and_more.cpython-312.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_remove_supplier_contact_email_and_more.cpython-312.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_remove_supplier_contact_email_and_more.cpython-312.pyc deleted file mode 100644 index f640a76e..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_remove_supplier_contact_email_and_more.cpython-312.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_rename_quanityused_usedpackaging_quantityused.cpython-311.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_rename_quanityused_usedpackaging_quantityused.cpython-311.pyc deleted file mode 100644 index a31da738..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0002_rename_quanityused_usedpackaging_quantityused.cpython-311.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0003_alter_packagingmaterial_batch_code_and_more.cpython-312.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0003_alter_packagingmaterial_batch_code_and_more.cpython-312.pyc deleted file mode 100644 index 64e93634..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0003_alter_packagingmaterial_batch_code_and_more.cpython-312.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0003_ingredientsrawmaterials_change_status_and_more.cpython-312.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0003_ingredientsrawmaterials_change_status_and_more.cpython-312.pyc new file mode 100644 index 00000000..3660ca6c Binary files /dev/null and b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0003_ingredientsrawmaterials_change_status_and_more.cpython-312.pyc differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0003_packagingrawmaterials_name_and_more.cpython-311.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0003_packagingrawmaterials_name_and_more.cpython-311.pyc deleted file mode 100644 index a9e716ae..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0003_packagingrawmaterials_name_and_more.cpython-311.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0003_packagingrawmaterials_name_and_more.cpython-312.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0003_packagingrawmaterials_name_and_more.cpython-312.pyc deleted file mode 100644 index b2011f36..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0003_packagingrawmaterials_name_and_more.cpython-312.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0004_alter_packagingmaterial_supplier.cpython-312.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0004_alter_packagingmaterial_supplier.cpython-312.pyc deleted file mode 100644 index 4499e993..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0004_alter_packagingmaterial_supplier.cpython-312.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0004_remove_packagingrawmaterials_name.cpython-311.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0004_remove_packagingrawmaterials_name.cpython-311.pyc deleted file mode 100644 index affd4304..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0004_remove_packagingrawmaterials_name.cpython-311.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0004_remove_packagingrawmaterials_name.cpython-312.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0004_remove_packagingrawmaterials_name.cpython-312.pyc deleted file mode 100644 index e37a3bba..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0004_remove_packagingrawmaterials_name.cpython-312.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0005_alter_ingredientsrawmaterials_rawmaterialbatchcode_and_more.cpython-312.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0005_alter_ingredientsrawmaterials_rawmaterialbatchcode_and_more.cpython-312.pyc deleted file mode 100644 index 5397c1ee..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0005_alter_ingredientsrawmaterials_rawmaterialbatchcode_and_more.cpython-312.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0005_remove_supplier_category_and_more.cpython-312.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0005_remove_supplier_category_and_more.cpython-312.pyc deleted file mode 100644 index af93f880..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0005_remove_supplier_category_and_more.cpython-312.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0006_alter_supplier_suppliercode_and_more.cpython-312.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0006_alter_supplier_suppliercode_and_more.cpython-312.pyc deleted file mode 100644 index 611cae83..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0006_alter_supplier_suppliercode_and_more.cpython-312.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0007_alter_supplier_suppliercode_and_more.cpython-312.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0007_alter_supplier_suppliercode_and_more.cpython-312.pyc deleted file mode 100644 index fe9a349b..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/0007_alter_supplier_suppliercode_and_more.cpython-312.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/__init__.cpython-311.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index e26f636a..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/__init__.cpython-311.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/__init__.cpython-312.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/__init__.cpython-312.pyc index 8ef14a52..2c91c8a5 100644 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/__init__.cpython-312.pyc and b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/__init__.cpython-312.pyc differ diff --git a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/__init__.cpython-39.pyc b/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 1a777d80..00000000 Binary files a/IMAPS/IMAPS/IMAPS_app/migrations/__pycache__/__init__.cpython-39.pyc and /dev/null differ diff --git a/IMAPS/IMAPS/IMAPS_app/models.py b/IMAPS/IMAPS/IMAPS_app/models.py index d42d0f38..dabaa740 100644 --- a/IMAPS/IMAPS/IMAPS_app/models.py +++ b/IMAPS/IMAPS/IMAPS_app/models.py @@ -1,15 +1,19 @@ +# Updated models.py with change_status logic import random import string from datetime import date, timedelta from django.db import models +CHANGE_STATUS_CHOICES = [ + ("active", "Active"), + ("old_modified", "Old Modified"), + ("new_modified", "New Modified"), + ("deleted", "Deleted"), +] + # -------------------- Supplier -------------------- class Supplier(models.Model): - SupplierCode = models.CharField( - max_length=50, - primary_key=True, - help_text="Unique identifier for the supplier (manually given)." - ) + SupplierCode = models.CharField(max_length=50, primary_key=True) SupplierName = models.CharField(max_length=255) CATEGORY_CHOICES = [ ('Ingredient', 'Ingredient'), @@ -21,6 +25,7 @@ class Supplier(models.Model): EmailAddress = models.CharField(max_length=320, blank=True, null=True) ContactNumber = models.CharField(max_length=18, blank=True, null=True) PointPerson = models.CharField(max_length=255, blank=True, null=True) + change_status = models.CharField(max_length=15, choices=CHANGE_STATUS_CHOICES, default="active") def __str__(self): return f"{self.SupplierName} ({self.SupplierCode})" @@ -30,27 +35,19 @@ def __str__(self): class IngredientsRawMaterials(models.Model): id = models.AutoField(primary_key=True) RawMaterialBatchCode = models.CharField(max_length=50, unique=True, blank=True) - SupplierCode = models.ForeignKey( - Supplier, - on_delete=models.CASCADE, - db_column='SupplierCode' - ) + SupplierCode = models.ForeignKey(Supplier, on_delete=models.CASCADE, db_column='SupplierCode') RawMaterialName = models.CharField(max_length=255) DateDelivered = models.DateField() QuantityBought = models.FloatField(default=0) QuantityLeft = models.FloatField(default=0) - USECATEGORY_CHOICES = [ - ('WBC', 'WBC'), - ('GGB', 'GGB'), - ('Both', 'Both'), - ] + USECATEGORY_CHOICES = [('WBC', 'WBC'), ('GGB', 'GGB'), ('Both', 'Both')] UseCategory = models.CharField(max_length=10, choices=USECATEGORY_CHOICES, default="GGB") ExpirationDate = models.DateField() Status = models.CharField(max_length=15, blank=True) Cost = models.FloatField(default=0, blank=True, null=True) + change_status = models.CharField(max_length=15, choices=CHANGE_STATUS_CHOICES, default="active") def save(self, *args, **kwargs): - # Determine if we need to regenerate batch code regenerate = False if not self.pk: regenerate = True @@ -68,7 +65,6 @@ def save(self, *args, **kwargs): if self.QuantityLeft == 0: self.QuantityLeft = self.QuantityBought - # Auto-compute Status today = date.today() if self.ExpirationDate and self.ExpirationDate <= today + timedelta(days=30): self.Status = "Expiring" @@ -87,24 +83,17 @@ def __str__(self): class PackagingRawMaterials(models.Model): id = models.AutoField(primary_key=True) PackagingBatchCode = models.CharField(max_length=50, unique=True, blank=True) - SupplierCode = models.ForeignKey( - Supplier, - on_delete=models.CASCADE, - db_column='SupplierCode' - ) + SupplierCode = models.ForeignKey(Supplier, on_delete=models.CASCADE, db_column='SupplierCode') RawMaterialName = models.CharField(max_length=255) QuantityBought = models.IntegerField(default=0) QuantityLeft = models.IntegerField(default=0) - USECATEGORY_CHOICES = [ - ('WBC', 'WBC'), - ('GGB', 'GGB'), - ('Both', 'Both'), - ] + USECATEGORY_CHOICES = [('WBC', 'WBC'), ('GGB', 'GGB'), ('Both', 'Both')] UseCategory = models.CharField(max_length=10, choices=USECATEGORY_CHOICES) Status = models.CharField(max_length=15, blank=True) Cost = models.FloatField(default=0, blank=True, null=True) ContainerSize = models.CharField(max_length=50, blank=True, null=True) DateDelivered = models.DateField() + change_status = models.CharField(max_length=15, choices=CHANGE_STATUS_CHOICES, default="active") def save(self, *args, **kwargs): regenerate = False @@ -139,20 +128,13 @@ def __str__(self): # -------------------- UsedIngredient -------------------- class UsedIngredient(models.Model): UsedIngredientBatchCode = models.CharField(max_length=50, primary_key=True, blank=True) - IngredientRawMaterialBatchCode = models.ForeignKey( - IngredientsRawMaterials, - on_delete=models.CASCADE, - db_column='RawMaterialBatchCode' - ) + IngredientRawMaterialBatchCode = models.ForeignKey(IngredientsRawMaterials, on_delete=models.CASCADE, db_column='RawMaterialBatchCode') RawMaterialName = models.CharField(max_length=255) QuantityUsed = models.IntegerField() - USECATEGORY_CHOICES = [ - ('WBC', 'WBC'), - ('GGB', 'GGB'), - ('Both', 'Both'), - ] + USECATEGORY_CHOICES = [('WBC', 'WBC'), ('GGB', 'GGB'), ('Both', 'Both')] UseCategory = models.CharField(max_length=10, choices=USECATEGORY_CHOICES) DateUsed = models.DateField() + change_status = models.CharField(max_length=15, choices=CHANGE_STATUS_CHOICES, default="active") def save(self, *args, **kwargs): if not self.UsedIngredientBatchCode: @@ -170,20 +152,13 @@ def __str__(self): # -------------------- UsedPackaging -------------------- class UsedPackaging(models.Model): USEDPackagingBatchCode = models.CharField(max_length=50, primary_key=True, blank=True) - PackagingRawMaterialBatchCode = models.ForeignKey( - PackagingRawMaterials, - on_delete=models.CASCADE, - db_column='PackagingBatchCode' - ) + PackagingRawMaterialBatchCode = models.ForeignKey(PackagingRawMaterials, on_delete=models.CASCADE, db_column='PackagingBatchCode') RawMaterialName = models.CharField(max_length=255) QuantityUsed = models.IntegerField() - USECATEGORY_CHOICES = [ - ('WBC', 'WBC'), - ('GGB', 'GGB'), - ('Both', 'Both'), - ] + USECATEGORY_CHOICES = [('WBC', 'WBC'), ('GGB', 'GGB'), ('Both', 'Both')] UseCategory = models.CharField(max_length=10, choices=USECATEGORY_CHOICES) DateUsed = models.DateField() + change_status = models.CharField(max_length=15, choices=CHANGE_STATUS_CHOICES, default="active") def save(self, *args, **kwargs): if not self.USEDPackagingBatchCode: diff --git a/IMAPS/IMAPS/IMAPS_app/static/IMAPS_app/css/styles.css b/IMAPS/IMAPS/IMAPS_app/static/IMAPS_app/css/styles.css index 91ec6f76..334dd54e 100644 --- a/IMAPS/IMAPS/IMAPS_app/static/IMAPS_app/css/styles.css +++ b/IMAPS/IMAPS/IMAPS_app/static/IMAPS_app/css/styles.css @@ -154,6 +154,7 @@ button:hover { /* Table Styles */ .table-container { margin-top: 20px; + overflow: visible !important; } table { @@ -163,7 +164,7 @@ table { margin-top: 20px; background: #fff; border-radius: 20px; - overflow: hidden; + overflow: visible; table-layout: fixed; box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1); } @@ -823,4 +824,100 @@ td a:hover { max-height: calc(100vh - 150px); /* leave room for header/footer */ overflow-y: auto; /* scroll if it gets too tall */ } - \ No newline at end of file + + .multi-cell { position: relative; } +.toggle-dropdown { cursor: pointer; margin-left: 4px; } +.dropdown-list { + display: none; + position: absolute; + top: 100%; left: 0; + background: #fff; + border: 1px solid #ccc; + list-style: none; margin: 4px 0; padding: 0; + z-index: 10; +} +.dropdown-list li { padding: 4px 8px; } +.multi-cell:hover .dropdown-list, +.multi-cell:focus-within .dropdown-list { display: block; } + + +.multi-cell { + position: relative; /* establish a containing block */ + overflow: visible !important; /* let children overflow this cell */ +} + +.dropdown-list { + display: none; + position: absolute; + top: 70%; /* just under the cell’s bottom edge */ + left: 0; + background: #fff; + border: 1px solid #ccc; + list-style: none; + margin: 4px 0 0; + padding: 0; + z-index: 9999; /* higher than any surrounding container */ + box-shadow: 0 2px 6px rgba(0,0,0,0.2); +} + +.dropdown-list li { + padding: 4px 8px; + white-space: nowrap; +} + + +/* Show on hover */ +.multi-cell:hover .dropdown-list { + display: block; +} + +/* Show when the cell has the “open” class (toggled by JS) */ +.multi-cell.open .dropdown-list { + display: block; +} + +/* Make every input in the update form span the full content width */ +#updateSupplierForm input, +#updateSupplierForm select { + display: block; /* ensure it’s a block-level box */ + width: 100% !important; /* stretch to the container’s full width */ + box-sizing: border-box; /* include padding/border in that width */ +} + +#updateSupplierForm .fields-wrap .field-row:not(:last-child) { + margin-bottom: 0.5rem; +} + + +.dropdown-wrapper { + display: block; + text-align: center; + position: relative; + margin-top: 0.5em; +} +.dropdown-wrapper .toggle-dropdown { + cursor: pointer; + display: inline-block; +} +.dropdown-wrapper .dropdown-list { + display: none; + position: absolute; + top: 100%; + left: 50%; + transform: translateX(-50%); + background: #fff; + border: 1px solid #ccc; + list-style: none; + margin: 0.25em 0 0; + padding: 0; + z-index: 9999; + box-shadow: 0 2px 6px rgba(0,0,0,0.2); +} +.dropdown-wrapper:hover .dropdown-list, +.dropdown-wrapper.open .dropdown-list { + display: block; +} +.dropdown-wrapper .dropdown-list li { + padding: 0.5em 1em; + white-space: nowrap; +} diff --git a/IMAPS/IMAPS/IMAPS_app/templates/packaging.html b/IMAPS/IMAPS/IMAPS_app/templates/packaging.html index 3fdb3835..071d315a 100644 --- a/IMAPS/IMAPS/IMAPS_app/templates/packaging.html +++ b/IMAPS/IMAPS/IMAPS_app/templates/packaging.html @@ -503,8 +503,8 @@