backend_users_equipment_interventions
This commit is contained in:
+20
-1
@@ -25,5 +25,24 @@ $ git commit -m "conf_initial"
|
||||
|
||||
$ docker compose up -d
|
||||
|
||||
$ docker compose exec web python3.13 manage.py createsuperuser
|
||||
docker compose exec web python manage.py migrate
|
||||
|
||||
$ docker compose exec web python manage.py createsuperuser
|
||||
|
||||
docker compose exec web python manage.py startapp users
|
||||
docker compose exec web python manage.py startapp equipment
|
||||
docker compose exec web python manage.py startapp interventions
|
||||
|
||||
sudo chown -R $USER: .
|
||||
|
||||
docker compose exec web python manage.py makemigrations users
|
||||
docker compose exec web python manage.py migrate
|
||||
|
||||
docker compose exec web python -m pip install django-simple-history
|
||||
|
||||
docker compose exec web python manage.py makemigrations
|
||||
docker compose exec web python manage.py migrate
|
||||
|
||||
docker compose exec web python manage.py makemigrations equipment
|
||||
docker compose exec web python manage.py migrate
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
from django.contrib import admin
|
||||
from simple_history.admin import SimpleHistoryAdmin
|
||||
from .models import (
|
||||
Computer,
|
||||
Monitor,
|
||||
Peripheral,
|
||||
DigitalCertificate,
|
||||
MaintenanceRecord,
|
||||
DecommissionRecord,
|
||||
)
|
||||
|
||||
|
||||
# --- Inlines for Computer ---
|
||||
class MaintenanceInlineComputer(admin.TabularInline):
|
||||
model = MaintenanceRecord
|
||||
fk_name = "computer"
|
||||
extra = 0
|
||||
|
||||
|
||||
class DecommissionInlineComputer(admin.StackedInline):
|
||||
model = DecommissionRecord
|
||||
fk_name = "computer"
|
||||
extra = 0
|
||||
max_num = 1
|
||||
|
||||
|
||||
# --- Inlines for Monitor ---
|
||||
class MaintenanceInlineMonitor(admin.TabularInline):
|
||||
model = MaintenanceRecord
|
||||
fk_name = "monitor"
|
||||
extra = 0
|
||||
|
||||
|
||||
class DecommissionInlineMonitor(admin.StackedInline):
|
||||
model = DecommissionRecord
|
||||
fk_name = "monitor"
|
||||
extra = 0
|
||||
max_num = 1
|
||||
|
||||
|
||||
# --- Inlines for Peripheral ---
|
||||
class MaintenanceInlinePeripheral(admin.TabularInline):
|
||||
model = MaintenanceRecord
|
||||
fk_name = "peripheral"
|
||||
extra = 0
|
||||
|
||||
|
||||
class DecommissionInlinePeripheral(admin.StackedInline):
|
||||
model = DecommissionRecord
|
||||
fk_name = "peripheral"
|
||||
extra = 0
|
||||
max_num = 1
|
||||
|
||||
|
||||
@admin.register(Computer)
|
||||
class ComputerAdmin(SimpleHistoryAdmin):
|
||||
list_display = ("equipment_code", "oem_name", "status", "assigned_to", "entry_date")
|
||||
list_filter = ("status", "entry_date", "assigned_to")
|
||||
search_fields = ("equipment_code", "oem_name", "mac_address", "observations")
|
||||
inlines = [MaintenanceInlineComputer, DecommissionInlineComputer]
|
||||
|
||||
|
||||
@admin.register(Monitor)
|
||||
class MonitorAdmin(SimpleHistoryAdmin):
|
||||
list_display = ("equipment_code", "name", "status", "assigned_to", "entry_date")
|
||||
list_filter = ("status", "entry_date", "assigned_to")
|
||||
search_fields = ("equipment_code", "name", "serial_number")
|
||||
inlines = [MaintenanceInlineMonitor, DecommissionInlineMonitor]
|
||||
|
||||
|
||||
@admin.register(Peripheral)
|
||||
class PeripheralAdmin(SimpleHistoryAdmin):
|
||||
list_display = (
|
||||
"equipment_code",
|
||||
"name",
|
||||
"peripheral_type",
|
||||
"status",
|
||||
"assigned_to",
|
||||
)
|
||||
list_filter = ("status", "peripheral_type", "entry_date", "assigned_to")
|
||||
search_fields = ("equipment_code", "name", "serial_number")
|
||||
inlines = [MaintenanceInlinePeripheral, DecommissionInlinePeripheral]
|
||||
|
||||
|
||||
@admin.register(DigitalCertificate)
|
||||
class DigitalCertificateAdmin(SimpleHistoryAdmin):
|
||||
list_display = (
|
||||
"get_certificate_type_display",
|
||||
"assigned_to",
|
||||
"issue_date",
|
||||
"expiration_date",
|
||||
"is_canceled",
|
||||
)
|
||||
list_filter = ("certificate_type", "is_canceled", "issue_date")
|
||||
search_fields = ("assigned_to__name", "assigned_to__username")
|
||||
|
||||
|
||||
# Optionally register the records independently so they can be searched globally
|
||||
@admin.register(MaintenanceRecord)
|
||||
class MaintenanceRecordAdmin(SimpleHistoryAdmin):
|
||||
list_display = ("date", "operation_type", "performed_by")
|
||||
list_filter = ("date", "operation_type")
|
||||
search_fields = ("description", "performed_by")
|
||||
|
||||
|
||||
@admin.register(DecommissionRecord)
|
||||
class DecommissionRecordAdmin(SimpleHistoryAdmin):
|
||||
list_display = ("document_number", "date", "reason")
|
||||
list_filter = ("date",)
|
||||
search_fields = ("document_number", "reason")
|
||||
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class EquipmentConfig(AppConfig):
|
||||
name = 'equipment'
|
||||
@@ -0,0 +1,196 @@
|
||||
# Generated by Django 6.1 on 2026-08-18 12:20
|
||||
|
||||
import datetime
|
||||
import django.db.models.deletion
|
||||
import simple_history.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('users', '0002_historicalcompartiment_historicaldirectie_and_more'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Computer',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('equipment_code', models.CharField(blank=True, max_length=50, unique=True, verbose_name='Cod Echipament')),
|
||||
('entry_date', models.DateField(default=datetime.date.today, verbose_name='Data intrării')),
|
||||
('oem_name', models.CharField(max_length=255, verbose_name='Producător (OEM)')),
|
||||
('motherboard', models.CharField(max_length=255, verbose_name='Placă de bază')),
|
||||
('cpu', models.CharField(max_length=255, verbose_name='Procesor (CPU)')),
|
||||
('ram', models.CharField(max_length=100, verbose_name='Memorie RAM')),
|
||||
('storage', models.CharField(max_length=100, verbose_name='Stocare (HDD/SSD)')),
|
||||
('optical_drive', models.CharField(blank=True, max_length=100, null=True, verbose_name='Unitate optică')),
|
||||
('gpu', models.CharField(max_length=255, verbose_name='Placă video (GPU)')),
|
||||
('mac_address', models.CharField(max_length=50, verbose_name='Adresă MAC')),
|
||||
('os_license', models.CharField(max_length=255, verbose_name='Licență OS')),
|
||||
('additional_licenses', models.TextField(blank=True, null=True, verbose_name='Licențe adiționale')),
|
||||
('observations', models.TextField(blank=True, null=True, verbose_name='Observații')),
|
||||
('assigned_to', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='computers', to='users.employee', verbose_name='Atribuit către')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Computer / Laptop',
|
||||
'verbose_name_plural': 'Computere / Laptopuri',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DigitalCertificate',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('certificate_type', models.CharField(choices=[('cloud', 'Cloud'), ('usb', 'USB Token')], max_length=50, verbose_name='Tip Certificat')),
|
||||
('issue_date', models.DateField(verbose_name='Data emiterii')),
|
||||
('expiration_date', models.DateField(verbose_name='Data expirării')),
|
||||
('is_canceled', models.BooleanField(default=False, verbose_name='Anulat?')),
|
||||
('assigned_to', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='digital_certificates', to='users.employee', verbose_name='Titular Certificat')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Certificat Digital',
|
||||
'verbose_name_plural': 'Certificate Digitale',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HistoricalComputer',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('equipment_code', models.CharField(blank=True, db_index=True, max_length=50, verbose_name='Cod Echipament')),
|
||||
('entry_date', models.DateField(default=datetime.date.today, verbose_name='Data intrării')),
|
||||
('oem_name', models.CharField(max_length=255, verbose_name='Producător (OEM)')),
|
||||
('motherboard', models.CharField(max_length=255, verbose_name='Placă de bază')),
|
||||
('cpu', models.CharField(max_length=255, verbose_name='Procesor (CPU)')),
|
||||
('ram', models.CharField(max_length=100, verbose_name='Memorie RAM')),
|
||||
('storage', models.CharField(max_length=100, verbose_name='Stocare (HDD/SSD)')),
|
||||
('optical_drive', models.CharField(blank=True, max_length=100, null=True, verbose_name='Unitate optică')),
|
||||
('gpu', models.CharField(max_length=255, verbose_name='Placă video (GPU)')),
|
||||
('mac_address', models.CharField(max_length=50, verbose_name='Adresă MAC')),
|
||||
('os_license', models.CharField(max_length=255, verbose_name='Licență OS')),
|
||||
('additional_licenses', models.TextField(blank=True, null=True, verbose_name='Licențe adiționale')),
|
||||
('observations', models.TextField(blank=True, null=True, verbose_name='Observații')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('assigned_to', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='users.employee', verbose_name='Atribuit către')),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical Computer / Laptop',
|
||||
'verbose_name_plural': 'historical Computere / Laptopuri',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HistoricalDigitalCertificate',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('certificate_type', models.CharField(choices=[('cloud', 'Cloud'), ('usb', 'USB Token')], max_length=50, verbose_name='Tip Certificat')),
|
||||
('issue_date', models.DateField(verbose_name='Data emiterii')),
|
||||
('expiration_date', models.DateField(verbose_name='Data expirării')),
|
||||
('is_canceled', models.BooleanField(default=False, verbose_name='Anulat?')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('assigned_to', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='users.employee', verbose_name='Titular Certificat')),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical Certificat Digital',
|
||||
'verbose_name_plural': 'historical Certificate Digitale',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HistoricalMonitor',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('equipment_code', models.CharField(blank=True, db_index=True, max_length=50, verbose_name='Cod Echipament')),
|
||||
('entry_date', models.DateField(default=datetime.date.today, verbose_name='Data intrării')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Nume Monitor')),
|
||||
('serial_number', models.CharField(max_length=100, verbose_name='Număr Serial')),
|
||||
('screen_diagonal', models.CharField(max_length=50, verbose_name='Diagonală (inch)')),
|
||||
('observations', models.TextField(blank=True, null=True, verbose_name='Observații')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('assigned_to', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='users.employee', verbose_name='Atribuit către')),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical Monitor',
|
||||
'verbose_name_plural': 'historical Monitoare',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HistoricalPeripheral',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('equipment_code', models.CharField(blank=True, db_index=True, max_length=50, verbose_name='Cod Echipament')),
|
||||
('entry_date', models.DateField(default=datetime.date.today, verbose_name='Data intrării')),
|
||||
('peripheral_type', models.CharField(choices=[('printer', 'Imprimantă'), ('ups', 'UPS'), ('scanner', 'Scanner'), ('copier', 'Copiator'), ('ethernet', 'Adaptor Ethernet'), ('audio', 'Boxe Audio'), ('hdd_ext', 'HDD Extern'), ('webcam', 'Cameră Web'), ('other', 'Altul')], max_length=50, verbose_name='Tip Periferic')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Nume Echipament')),
|
||||
('serial_number', models.CharField(max_length=100, verbose_name='Număr Serial')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('assigned_to', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='users.employee', verbose_name='Atribuit către')),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical Periferic',
|
||||
'verbose_name_plural': 'historical Periferice',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Monitor',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('equipment_code', models.CharField(blank=True, max_length=50, unique=True, verbose_name='Cod Echipament')),
|
||||
('entry_date', models.DateField(default=datetime.date.today, verbose_name='Data intrării')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Nume Monitor')),
|
||||
('serial_number', models.CharField(max_length=100, verbose_name='Număr Serial')),
|
||||
('screen_diagonal', models.CharField(max_length=50, verbose_name='Diagonală (inch)')),
|
||||
('observations', models.TextField(blank=True, null=True, verbose_name='Observații')),
|
||||
('assigned_to', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='monitors', to='users.employee', verbose_name='Atribuit către')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Monitor',
|
||||
'verbose_name_plural': 'Monitoare',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Peripheral',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('equipment_code', models.CharField(blank=True, max_length=50, unique=True, verbose_name='Cod Echipament')),
|
||||
('entry_date', models.DateField(default=datetime.date.today, verbose_name='Data intrării')),
|
||||
('peripheral_type', models.CharField(choices=[('printer', 'Imprimantă'), ('ups', 'UPS'), ('scanner', 'Scanner'), ('copier', 'Copiator'), ('ethernet', 'Adaptor Ethernet'), ('audio', 'Boxe Audio'), ('hdd_ext', 'HDD Extern'), ('webcam', 'Cameră Web'), ('other', 'Altul')], max_length=50, verbose_name='Tip Periferic')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Nume Echipament')),
|
||||
('serial_number', models.CharField(max_length=100, verbose_name='Număr Serial')),
|
||||
('assigned_to', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='peripherals', to='users.employee', verbose_name='Atribuit către')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Periferic',
|
||||
'verbose_name_plural': 'Periferice',
|
||||
},
|
||||
),
|
||||
]
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
# Generated by Django 6.1 on 2026-08-18 12:45
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('equipment', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='computer',
|
||||
name='equipment_code',
|
||||
field=models.CharField(max_length=50, unique=True, verbose_name='Cod Echipament'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='computer',
|
||||
name='gpu',
|
||||
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Placă video (GPU)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='computer',
|
||||
name='oem_name',
|
||||
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Producător (OEM)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='historicalcomputer',
|
||||
name='equipment_code',
|
||||
field=models.CharField(db_index=True, max_length=50, verbose_name='Cod Echipament'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='historicalcomputer',
|
||||
name='gpu',
|
||||
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Placă video (GPU)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='historicalcomputer',
|
||||
name='oem_name',
|
||||
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Producător (OEM)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='historicalmonitor',
|
||||
name='equipment_code',
|
||||
field=models.CharField(db_index=True, max_length=50, verbose_name='Cod Echipament'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='historicalperipheral',
|
||||
name='equipment_code',
|
||||
field=models.CharField(db_index=True, max_length=50, verbose_name='Cod Echipament'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='monitor',
|
||||
name='equipment_code',
|
||||
field=models.CharField(max_length=50, unique=True, verbose_name='Cod Echipament'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='peripheral',
|
||||
name='equipment_code',
|
||||
field=models.CharField(max_length=50, unique=True, verbose_name='Cod Echipament'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,130 @@
|
||||
# Generated by Django 6.1 on 2026-08-18 12:55
|
||||
|
||||
import datetime
|
||||
import django.db.models.deletion
|
||||
import simple_history.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('equipment', '0002_alter_computer_equipment_code_alter_computer_gpu_and_more'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='computer',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('activ', 'Activ'), ('casat', 'Casat (Decommissioned)')], default='activ', max_length=20, verbose_name='Stare'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalcomputer',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('activ', 'Activ'), ('casat', 'Casat (Decommissioned)')], default='activ', max_length=20, verbose_name='Stare'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalmonitor',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('activ', 'Activ'), ('casat', 'Casat (Decommissioned)')], default='activ', max_length=20, verbose_name='Stare'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalperipheral',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('activ', 'Activ'), ('casat', 'Casat (Decommissioned)')], default='activ', max_length=20, verbose_name='Stare'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='monitor',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('activ', 'Activ'), ('casat', 'Casat (Decommissioned)')], default='activ', max_length=20, verbose_name='Stare'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='peripheral',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('activ', 'Activ'), ('casat', 'Casat (Decommissioned)')], default='activ', max_length=20, verbose_name='Stare'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DecommissionRecord',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('date', models.DateField(default=datetime.date.today, verbose_name='Data casării')),
|
||||
('document_number', models.CharField(max_length=100, verbose_name='Număr document (Proces Verbal)')),
|
||||
('reason', models.TextField(verbose_name='Motivul casării (Defect, Uzat moral, etc.)')),
|
||||
('computer', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='decommission_log', to='equipment.computer')),
|
||||
('monitor', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='decommission_log', to='equipment.monitor')),
|
||||
('peripheral', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='decommission_log', to='equipment.peripheral')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Înregistrare Casare',
|
||||
'verbose_name_plural': 'Înregistrări Casare',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HistoricalDecommissionRecord',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('date', models.DateField(default=datetime.date.today, verbose_name='Data casării')),
|
||||
('document_number', models.CharField(max_length=100, verbose_name='Număr document (Proces Verbal)')),
|
||||
('reason', models.TextField(verbose_name='Motivul casării (Defect, Uzat moral, etc.)')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('computer', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='equipment.computer')),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
('monitor', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='equipment.monitor')),
|
||||
('peripheral', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='equipment.peripheral')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical Înregistrare Casare',
|
||||
'verbose_name_plural': 'historical Înregistrări Casare',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HistoricalMaintenanceRecord',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('date', models.DateField(default=datetime.date.today, verbose_name='Data intervenției')),
|
||||
('operation_type', models.CharField(max_length=150, verbose_name='Tip operațiune (Curățare, Upgrade, etc.)')),
|
||||
('description', models.TextField(verbose_name='Descriere detaliată')),
|
||||
('performed_by', models.CharField(max_length=100, verbose_name='Efectuat de')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('computer', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='equipment.computer')),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
('monitor', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='equipment.monitor')),
|
||||
('peripheral', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='equipment.peripheral')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical Foaie de Mentenanță',
|
||||
'verbose_name_plural': 'historical Foi de Mentenanță',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MaintenanceRecord',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('date', models.DateField(default=datetime.date.today, verbose_name='Data intervenției')),
|
||||
('operation_type', models.CharField(max_length=150, verbose_name='Tip operațiune (Curățare, Upgrade, etc.)')),
|
||||
('description', models.TextField(verbose_name='Descriere detaliată')),
|
||||
('performed_by', models.CharField(max_length=100, verbose_name='Efectuat de')),
|
||||
('computer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='maintenance_logs', to='equipment.computer')),
|
||||
('monitor', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='maintenance_logs', to='equipment.monitor')),
|
||||
('peripheral', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='maintenance_logs', to='equipment.peripheral')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Foaie de Mentenanță',
|
||||
'verbose_name_plural': 'Foi de Mentenanță',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,291 @@
|
||||
from django.db import models
|
||||
from datetime import date
|
||||
from simple_history.models import HistoricalRecords
|
||||
from users.models import Employee
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
EQUIPMENT_STATUS = [
|
||||
("activ", "Activ"),
|
||||
("casat", "Casat (Decommissioned)"),
|
||||
]
|
||||
|
||||
|
||||
class Computer(models.Model):
|
||||
# Unique ID and Entry
|
||||
equipment_code = models.CharField(
|
||||
max_length=50, unique=True, verbose_name="Cod Echipament"
|
||||
)
|
||||
entry_date = models.DateField(default=date.today, verbose_name="Data intrării")
|
||||
|
||||
# Hardware Specs
|
||||
oem_name = models.CharField(
|
||||
max_length=255, null=True, blank=True, verbose_name="Producător (OEM)"
|
||||
)
|
||||
motherboard = models.CharField(max_length=255, verbose_name="Placă de bază")
|
||||
cpu = models.CharField(max_length=255, verbose_name="Procesor (CPU)")
|
||||
ram = models.CharField(max_length=100, verbose_name="Memorie RAM")
|
||||
storage = models.CharField(max_length=100, verbose_name="Stocare (HDD/SSD)")
|
||||
optical_drive = models.CharField(
|
||||
max_length=100, blank=True, null=True, verbose_name="Unitate optică"
|
||||
)
|
||||
gpu = models.CharField(
|
||||
max_length=255, null=True, blank=True, verbose_name="Placă video (GPU)"
|
||||
)
|
||||
mac_address = models.CharField(max_length=50, verbose_name="Adresă MAC")
|
||||
|
||||
# Software & Misc
|
||||
os_license = models.CharField(max_length=255, verbose_name="Licență OS")
|
||||
additional_licenses = models.TextField(
|
||||
blank=True, null=True, verbose_name="Licențe adiționale"
|
||||
)
|
||||
observations = models.TextField(blank=True, null=True, verbose_name="Observații")
|
||||
|
||||
# Custody Assignment
|
||||
assigned_to = models.ForeignKey(
|
||||
Employee,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="computers",
|
||||
verbose_name="Atribuit către",
|
||||
)
|
||||
status = models.CharField(
|
||||
max_length=20, choices=EQUIPMENT_STATUS, default="activ", verbose_name="Stare"
|
||||
)
|
||||
|
||||
history = HistoricalRecords()
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.equipment_code} - {self.oem_name or 'Sistem Asamblat'}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Computer / Laptop"
|
||||
verbose_name_plural = "Computere / Laptopuri"
|
||||
|
||||
|
||||
class Monitor(models.Model):
|
||||
equipment_code = models.CharField(
|
||||
max_length=50, unique=True, verbose_name="Cod Echipament"
|
||||
)
|
||||
entry_date = models.DateField(default=date.today, verbose_name="Data intrării")
|
||||
|
||||
name = models.CharField(max_length=255, verbose_name="Nume Monitor")
|
||||
serial_number = models.CharField(max_length=100, verbose_name="Număr Serial")
|
||||
screen_diagonal = models.CharField(max_length=50, verbose_name="Diagonală (inch)")
|
||||
observations = models.TextField(blank=True, null=True, verbose_name="Observații")
|
||||
|
||||
assigned_to = models.ForeignKey(
|
||||
Employee,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="monitors",
|
||||
verbose_name="Atribuit către",
|
||||
)
|
||||
status = models.CharField(
|
||||
max_length=20, choices=EQUIPMENT_STATUS, default="activ", verbose_name="Stare"
|
||||
)
|
||||
|
||||
history = HistoricalRecords()
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.equipment_code} - {self.name}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Monitor"
|
||||
verbose_name_plural = "Monitoare"
|
||||
|
||||
|
||||
class Peripheral(models.Model):
|
||||
PERIPHERAL_TYPES = [
|
||||
("printer", "Imprimantă"),
|
||||
("ups", "UPS"),
|
||||
("scanner", "Scanner"),
|
||||
("copier", "Copiator"),
|
||||
("ethernet", "Adaptor Ethernet"),
|
||||
("audio", "Boxe Audio"),
|
||||
("hdd_ext", "HDD Extern"),
|
||||
("webcam", "Cameră Web"),
|
||||
("other", "Altul"),
|
||||
]
|
||||
|
||||
equipment_code = models.CharField(
|
||||
max_length=50, unique=True, verbose_name="Cod Echipament"
|
||||
)
|
||||
entry_date = models.DateField(default=date.today, verbose_name="Data intrării")
|
||||
|
||||
peripheral_type = models.CharField(
|
||||
max_length=50, choices=PERIPHERAL_TYPES, verbose_name="Tip Periferic"
|
||||
)
|
||||
name = models.CharField(max_length=255, verbose_name="Nume Echipament")
|
||||
serial_number = models.CharField(max_length=100, verbose_name="Număr Serial")
|
||||
|
||||
assigned_to = models.ForeignKey(
|
||||
Employee,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="peripherals",
|
||||
verbose_name="Atribuit către",
|
||||
)
|
||||
status = models.CharField(
|
||||
max_length=20, choices=EQUIPMENT_STATUS, default="activ", verbose_name="Stare"
|
||||
)
|
||||
|
||||
history = HistoricalRecords()
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.equipment_code} - {self.name} ({self.get_peripheral_type_display()})"
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Periferic"
|
||||
verbose_name_plural = "Periferice"
|
||||
|
||||
|
||||
class DigitalCertificate(models.Model):
|
||||
CERT_TYPES = [
|
||||
("cloud", "Cloud"),
|
||||
("usb", "USB Token"),
|
||||
]
|
||||
|
||||
certificate_type = models.CharField(
|
||||
max_length=50, choices=CERT_TYPES, verbose_name="Tip Certificat"
|
||||
)
|
||||
issue_date = models.DateField(verbose_name="Data emiterii")
|
||||
expiration_date = models.DateField(verbose_name="Data expirării")
|
||||
is_canceled = models.BooleanField(default=False, verbose_name="Anulat?")
|
||||
|
||||
# Links directly to the employee
|
||||
assigned_to = models.ForeignKey(
|
||||
Employee,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="digital_certificates",
|
||||
verbose_name="Titular Certificat",
|
||||
)
|
||||
|
||||
history = HistoricalRecords()
|
||||
|
||||
def __str__(self):
|
||||
return f"Certificat {self.get_certificate_type_display()} - {self.assigned_to.name}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Certificat Digital"
|
||||
verbose_name_plural = "Certificate Digitale"
|
||||
|
||||
|
||||
class MaintenanceRecord(models.Model):
|
||||
date = models.DateField(default=date.today, verbose_name="Data intervenției")
|
||||
operation_type = models.CharField(
|
||||
max_length=150, verbose_name="Tip operațiune (Curățare, Upgrade, etc.)"
|
||||
)
|
||||
description = models.TextField(verbose_name="Descriere detaliată")
|
||||
performed_by = models.CharField(max_length=100, verbose_name="Efectuat de")
|
||||
|
||||
# Links to equipment (only one should be populated)
|
||||
computer = models.ForeignKey(
|
||||
Computer,
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="maintenance_logs",
|
||||
)
|
||||
monitor = models.ForeignKey(
|
||||
Monitor,
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="maintenance_logs",
|
||||
)
|
||||
peripheral = models.ForeignKey(
|
||||
Peripheral,
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="maintenance_logs",
|
||||
)
|
||||
|
||||
history = HistoricalRecords()
|
||||
|
||||
def clean(self):
|
||||
equipment_count = sum(
|
||||
[bool(self.computer), bool(self.monitor), bool(self.peripheral)]
|
||||
)
|
||||
if equipment_count != 1:
|
||||
raise ValidationError(
|
||||
"O înregistrare de mentenanță trebuie să fie legată de exact un singur echipament."
|
||||
)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.clean()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.date} - {self.operation_type}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Foaie de Mentenanță"
|
||||
verbose_name_plural = "Foi de Mentenanță"
|
||||
|
||||
|
||||
class DecommissionRecord(models.Model):
|
||||
date = models.DateField(default=date.today, verbose_name="Data casării")
|
||||
document_number = models.CharField(
|
||||
max_length=100, verbose_name="Număr document (Proces Verbal)"
|
||||
)
|
||||
reason = models.TextField(verbose_name="Motivul casării (Defect, Uzat moral, etc.)")
|
||||
|
||||
# Links to equipment
|
||||
computer = models.OneToOneField(
|
||||
Computer,
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="decommission_log",
|
||||
)
|
||||
monitor = models.OneToOneField(
|
||||
Monitor,
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="decommission_log",
|
||||
)
|
||||
peripheral = models.OneToOneField(
|
||||
Peripheral,
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="decommission_log",
|
||||
)
|
||||
|
||||
history = HistoricalRecords()
|
||||
|
||||
def clean(self):
|
||||
equipment_count = sum(
|
||||
[bool(self.computer), bool(self.monitor), bool(self.peripheral)]
|
||||
)
|
||||
if equipment_count != 1:
|
||||
raise ValidationError(
|
||||
"Un proces verbal de casare trebuie să fie legat de exact un singur echipament."
|
||||
)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.clean()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
# Automatically update the status of the linked equipment to 'casat'
|
||||
if self.computer:
|
||||
self.computer.status = "casat"
|
||||
self.computer.save()
|
||||
elif self.monitor:
|
||||
self.monitor.status = "casat"
|
||||
self.monitor.save()
|
||||
elif self.peripheral:
|
||||
self.peripheral.status = "casat"
|
||||
self.peripheral.save()
|
||||
|
||||
def __str__(self):
|
||||
return f"Casare {self.document_number} din {self.date}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Înregistrare Casare"
|
||||
verbose_name_plural = "Înregistrări Casare"
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
@@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/6.1/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
@@ -20,62 +21,74 @@ BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
# See https://docs.djangoproject.com/en/6.1/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-z(p$%wh6x^_9!1cgt%!s$w8k39(fox-^!iujxai42_wncja4%s'
|
||||
SECRET_KEY = "django-insecure-z(p$%wh6x^_9!1cgt%!s$w8k39(fox-^!iujxai42_wncja4%s"
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
ALLOWED_HOSTS = ["localhost", "127.0.0.1", "0.0.0.0"]
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
# third-party apps:
|
||||
"simple_history",
|
||||
# custom apps
|
||||
"users",
|
||||
"equipment",
|
||||
"interventions",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
# Add this line to track the user making the changes:
|
||||
"simple_history.middleware.HistoryRequestMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'eumi__v4_project.urls'
|
||||
ROOT_URLCONF = "eumi__v4_project.urls"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'eumi__v4_project.wsgi.application'
|
||||
WSGI_APPLICATION = "eumi__v4_project.wsgi.application"
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/6.1/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.postgresql",
|
||||
"NAME": os.environ.get("DB_NAME", "djangodb"),
|
||||
"USER": os.environ.get("DB_USER", "djangouser"),
|
||||
"PASSWORD": os.environ.get("DB_PASSWORD", "djangopass"),
|
||||
"HOST": os.environ.get("DB_HOST", "db"),
|
||||
"PORT": os.environ.get("DB_PORT", "5432"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,16 +98,16 @@ DATABASES = {
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
||||
},
|
||||
]
|
||||
|
||||
@@ -102,9 +115,9 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/6.1/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
LANGUAGE_CODE = "ro"
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
TIME_ZONE = "Europe/Bucharest"
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
@@ -114,14 +127,14 @@ USE_TZ = True
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/6.1/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
STATIC_URL = "static/"
|
||||
|
||||
|
||||
# Email
|
||||
# https://docs.djangoproject.com/en/6.1/topics/email/#topic-email-configuration
|
||||
|
||||
MAILERS = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.mail.backends.console.EmailBackend',
|
||||
"default": {
|
||||
"BACKEND": "django.core.mail.backends.console.EmailBackend",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
from django.contrib import admin
|
||||
from simple_history.admin import SimpleHistoryAdmin
|
||||
from .models import Intervention
|
||||
|
||||
|
||||
@admin.register(Intervention)
|
||||
class InterventionAdmin(SimpleHistoryAdmin):
|
||||
list_display = (
|
||||
"id",
|
||||
"title",
|
||||
"status",
|
||||
"priority",
|
||||
"reported_by",
|
||||
"assigned_to_it",
|
||||
"created_at",
|
||||
)
|
||||
list_filter = ("status", "priority", "created_at", "assigned_to_it")
|
||||
search_fields = ("title", "description", "reported_by__name", "assigned_to_it")
|
||||
readonly_fields = ("created_at", "updated_at")
|
||||
|
||||
fieldsets = (
|
||||
(
|
||||
"Detalii Tichet",
|
||||
{"fields": ("title", "description", "reported_by", "priority", "status")},
|
||||
),
|
||||
(
|
||||
"Asignare și Rezolvare",
|
||||
{"fields": ("assigned_to_it", "resolution_notes", "resolved_at")},
|
||||
),
|
||||
("Timpi", {"fields": ("created_at", "updated_at"), "classes": ("collapse",)}),
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class InterventionsConfig(AppConfig):
|
||||
name = 'interventions'
|
||||
@@ -0,0 +1,68 @@
|
||||
# Generated by Django 6.1 on 2026-08-18 13:10
|
||||
|
||||
import django.db.models.deletion
|
||||
import simple_history.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('users', '0002_historicalcompartiment_historicaldirectie_and_more'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='HistoricalIntervention',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=255, verbose_name='Titlu scurt / Subiect')),
|
||||
('description', models.TextField(verbose_name='Descrierea problemei')),
|
||||
('created_at', models.DateTimeField(blank=True, editable=False, verbose_name='Data raportării')),
|
||||
('updated_at', models.DateTimeField(blank=True, editable=False, verbose_name='Ultima actualizare')),
|
||||
('resolved_at', models.DateTimeField(blank=True, null=True, verbose_name='Data soluționării')),
|
||||
('status', models.CharField(choices=[('nou', 'Nou (Deschis)'), ('in_lucru', 'În lucru'), ('in_asteptare', 'În așteptare'), ('solutionat', 'Soluționat'), ('inchis', 'Închis')], default='nou', max_length=20, verbose_name='Stare')),
|
||||
('priority', models.CharField(choices=[('scazuta', 'Scăzută'), ('medie', 'Medie'), ('ridicata', 'Ridicată'), ('critica', 'Critică (Urgență)')], default='medie', max_length=20, verbose_name='Prioritate')),
|
||||
('assigned_to_it', models.CharField(blank=True, max_length=150, null=True, verbose_name='Atribuit către (IT)')),
|
||||
('resolution_notes', models.TextField(blank=True, null=True, verbose_name='Note de soluționare')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
('reported_by', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='users.employee', verbose_name='Raportat de (Utilizator)')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical Intervenție Tehnică',
|
||||
'verbose_name_plural': 'historical Intervenții Tehnice',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Intervention',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=255, verbose_name='Titlu scurt / Subiect')),
|
||||
('description', models.TextField(verbose_name='Descrierea problemei')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Data raportării')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Ultima actualizare')),
|
||||
('resolved_at', models.DateTimeField(blank=True, null=True, verbose_name='Data soluționării')),
|
||||
('status', models.CharField(choices=[('nou', 'Nou (Deschis)'), ('in_lucru', 'În lucru'), ('in_asteptare', 'În așteptare'), ('solutionat', 'Soluționat'), ('inchis', 'Închis')], default='nou', max_length=20, verbose_name='Stare')),
|
||||
('priority', models.CharField(choices=[('scazuta', 'Scăzută'), ('medie', 'Medie'), ('ridicata', 'Ridicată'), ('critica', 'Critică (Urgență)')], default='medie', max_length=20, verbose_name='Prioritate')),
|
||||
('assigned_to_it', models.CharField(blank=True, max_length=150, null=True, verbose_name='Atribuit către (IT)')),
|
||||
('resolution_notes', models.TextField(blank=True, null=True, verbose_name='Note de soluționare')),
|
||||
('reported_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reported_interventions', to='users.employee', verbose_name='Raportat de (Utilizator)')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Intervenție Tehnică',
|
||||
'verbose_name_plural': 'Intervenții Tehnice',
|
||||
'ordering': ['-created_at'],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,65 @@
|
||||
from django.db import models
|
||||
from simple_history.models import HistoricalRecords
|
||||
from users.models import Employee
|
||||
|
||||
|
||||
class Intervention(models.Model):
|
||||
STATUS_CHOICES = [
|
||||
("nou", "Nou (Deschis)"),
|
||||
("in_lucru", "În lucru"),
|
||||
("in_asteptare", "În așteptare"),
|
||||
("solutionat", "Soluționat"),
|
||||
("inchis", "Închis"),
|
||||
]
|
||||
|
||||
PRIORITY_CHOICES = [
|
||||
("scazuta", "Scăzută"),
|
||||
("medie", "Medie"),
|
||||
("ridicata", "Ridicată"),
|
||||
("critica", "Critică (Urgență)"),
|
||||
]
|
||||
|
||||
title = models.CharField(max_length=255, verbose_name="Titlu scurt / Subiect")
|
||||
description = models.TextField(verbose_name="Descrierea problemei")
|
||||
|
||||
# Tracking the timeline
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Data raportării")
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name="Ultima actualizare")
|
||||
resolved_at = models.DateTimeField(
|
||||
null=True, blank=True, verbose_name="Data soluționării"
|
||||
)
|
||||
|
||||
status = models.CharField(
|
||||
max_length=20, choices=STATUS_CHOICES, default="nou", verbose_name="Stare"
|
||||
)
|
||||
priority = models.CharField(
|
||||
max_length=20,
|
||||
choices=PRIORITY_CHOICES,
|
||||
default="medie",
|
||||
verbose_name="Prioritate",
|
||||
)
|
||||
|
||||
# People involved
|
||||
reported_by = models.ForeignKey(
|
||||
Employee,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="reported_interventions",
|
||||
verbose_name="Raportat de (Utilizator)",
|
||||
)
|
||||
assigned_to_it = models.CharField(
|
||||
max_length=150, blank=True, null=True, verbose_name="Atribuit către (IT)"
|
||||
)
|
||||
|
||||
resolution_notes = models.TextField(
|
||||
blank=True, null=True, verbose_name="Note de soluționare"
|
||||
)
|
||||
|
||||
history = HistoricalRecords()
|
||||
|
||||
def __str__(self):
|
||||
return f"Ticket #{self.id} - {self.title} ({self.get_status_display()})"
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Intervenție Tehnică"
|
||||
verbose_name_plural = "Intervenții Tehnice"
|
||||
ordering = ["-created_at"]
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
@@ -2,3 +2,5 @@ asgiref==3.12.1
|
||||
Django==6.1
|
||||
psycopg2-binary==2.9.12
|
||||
sqlparse==0.6.0
|
||||
django-simple-history
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
from django.contrib import admin
|
||||
from simple_history.admin import SimpleHistoryAdmin
|
||||
from .models import Directie, Serviciu, Compartiment, Employee
|
||||
|
||||
|
||||
@admin.register(Directie)
|
||||
class DirectieAdmin(SimpleHistoryAdmin):
|
||||
list_display = ("name",)
|
||||
search_fields = ("name",)
|
||||
|
||||
|
||||
@admin.register(Serviciu)
|
||||
class ServiciuAdmin(SimpleHistoryAdmin):
|
||||
list_display = ("name", "directie")
|
||||
list_filter = ("directie",)
|
||||
search_fields = ("name",)
|
||||
|
||||
|
||||
@admin.register(Compartiment)
|
||||
class CompartimentAdmin(SimpleHistoryAdmin):
|
||||
list_display = ("name", "directie", "serviciu")
|
||||
list_filter = ("directie", "serviciu")
|
||||
search_fields = ("name",)
|
||||
|
||||
|
||||
@admin.register(Employee)
|
||||
class EmployeeAdmin(SimpleHistoryAdmin):
|
||||
list_display = (
|
||||
"name",
|
||||
"username",
|
||||
"role",
|
||||
"email",
|
||||
"directie",
|
||||
"serviciu",
|
||||
"compartiment",
|
||||
)
|
||||
list_filter = ("directie", "serviciu", "compartiment")
|
||||
search_fields = ("name", "username", "email")
|
||||
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class UsersConfig(AppConfig):
|
||||
name = 'users'
|
||||
@@ -0,0 +1,74 @@
|
||||
# Generated by Django 6.1 on 2026-08-18 11:35
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Directie',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Nume Direcție')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Direcție',
|
||||
'verbose_name_plural': 'Direcții',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Compartiment',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Nume Compartiment')),
|
||||
('directie', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='compartimente_directe', to='users.directie', verbose_name='Direcție (Direct)')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Compartiment',
|
||||
'verbose_name_plural': 'Compartimente',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Serviciu',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Nume Serviciu')),
|
||||
('directie', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='servicii', to='users.directie', verbose_name='Direcție')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Serviciu',
|
||||
'verbose_name_plural': 'Servicii',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Employee',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Name')),
|
||||
('username', models.CharField(max_length=150, unique=True, verbose_name='User name')),
|
||||
('role', models.CharField(max_length=255, verbose_name='Function/role')),
|
||||
('office_number', models.CharField(max_length=50, verbose_name='Office number')),
|
||||
('email', models.EmailField(max_length=254, verbose_name='Email address')),
|
||||
('observations', models.TextField(blank=True, null=True, verbose_name='Observations')),
|
||||
('compartiment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='employees', to='users.compartiment', verbose_name='Compartiment')),
|
||||
('directie', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='employees', to='users.directie', verbose_name='Direcție')),
|
||||
('serviciu', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='employees', to='users.serviciu', verbose_name='Serviciu')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Angajat / Utilizator',
|
||||
'verbose_name_plural': 'Angajați / Utilizatori',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='compartiment',
|
||||
name='serviciu',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='compartimente_subordonate', to='users.serviciu', verbose_name='Serviciu'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,104 @@
|
||||
# Generated by Django 6.1 on 2026-08-18 11:43
|
||||
|
||||
import django.db.models.deletion
|
||||
import simple_history.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='HistoricalCompartiment',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Nume Compartiment')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('directie', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='users.directie', verbose_name='Direcție (Direct)')),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
('serviciu', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='users.serviciu', verbose_name='Serviciu')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical Compartiment',
|
||||
'verbose_name_plural': 'historical Compartimente',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HistoricalDirectie',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Nume Direcție')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical Direcție',
|
||||
'verbose_name_plural': 'historical Direcții',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HistoricalEmployee',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Name')),
|
||||
('username', models.CharField(db_index=True, max_length=150, verbose_name='User name')),
|
||||
('role', models.CharField(max_length=255, verbose_name='Function/role')),
|
||||
('office_number', models.CharField(max_length=50, verbose_name='Office number')),
|
||||
('email', models.EmailField(max_length=254, verbose_name='Email address')),
|
||||
('observations', models.TextField(blank=True, null=True, verbose_name='Observations')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('compartiment', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='users.compartiment', verbose_name='Compartiment')),
|
||||
('directie', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='users.directie', verbose_name='Direcție')),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
('serviciu', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='users.serviciu', verbose_name='Serviciu')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical Angajat / Utilizator',
|
||||
'verbose_name_plural': 'historical Angajați / Utilizatori',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HistoricalServiciu',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Nume Serviciu')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('directie', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='users.directie', verbose_name='Direcție')),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical Serviciu',
|
||||
'verbose_name_plural': 'historical Servicii',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
]
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
from django.db import models
|
||||
from django.core.exceptions import ValidationError
|
||||
from simple_history.models import HistoricalRecords
|
||||
|
||||
|
||||
class Directie(models.Model):
|
||||
name = models.CharField(max_length=255, verbose_name="Nume Direcție")
|
||||
history = HistoricalRecords()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Direcție"
|
||||
verbose_name_plural = "Direcții"
|
||||
|
||||
|
||||
class Serviciu(models.Model):
|
||||
name = models.CharField(max_length=255, verbose_name="Nume Serviciu")
|
||||
# A Serviciu is directly assigned to a Direcție
|
||||
directie = models.ForeignKey(
|
||||
Directie,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="servicii",
|
||||
verbose_name="Direcție",
|
||||
)
|
||||
history = HistoricalRecords()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Serviciu"
|
||||
verbose_name_plural = "Servicii"
|
||||
|
||||
|
||||
class Compartiment(models.Model):
|
||||
name = models.CharField(max_length=255, verbose_name="Nume Compartiment")
|
||||
# A Compartiment can be part of a Serviciu or directly included in a Direcție
|
||||
directie = models.ForeignKey(
|
||||
Directie,
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="compartimente_directe",
|
||||
verbose_name="Direcție (Direct)",
|
||||
)
|
||||
serviciu = models.ForeignKey(
|
||||
Serviciu,
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="compartimente_subordonate",
|
||||
verbose_name="Serviciu",
|
||||
)
|
||||
history = HistoricalRecords()
|
||||
|
||||
def clean(self):
|
||||
# Validation to ensure the Compartiment belongs to either a Direcție OR a Serviciu, but not both or neither.
|
||||
if not self.directie and not self.serviciu:
|
||||
raise ValidationError(
|
||||
"Un compartiment trebuie să aparțină de o Direcție sau de un Serviciu."
|
||||
)
|
||||
if self.directie and self.serviciu:
|
||||
raise ValidationError(
|
||||
"Un compartiment nu poate aparține simultan și de o Direcție și de un Serviciu."
|
||||
)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.clean()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Compartiment"
|
||||
verbose_name_plural = "Compartimente"
|
||||
|
||||
|
||||
class Employee(models.Model):
|
||||
"""
|
||||
Represents the 'Users' from the project specification who will hold equipment.
|
||||
Named 'Employee' to avoid conflicts with Django's built-in auth.User model.
|
||||
"""
|
||||
|
||||
name = models.CharField(max_length=255, verbose_name="Name")
|
||||
username = models.CharField(max_length=150, unique=True, verbose_name="User name")
|
||||
role = models.CharField(max_length=255, verbose_name="Function/role")
|
||||
office_number = models.CharField(max_length=50, verbose_name="Office number")
|
||||
email = models.EmailField(verbose_name="Email address")
|
||||
observations = models.TextField(null=True, blank=True, verbose_name="Observations")
|
||||
|
||||
# Organizational Placement - Employees can be placed at any of the three levels
|
||||
directie = models.ForeignKey(
|
||||
Directie,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="employees",
|
||||
verbose_name="Direcție",
|
||||
)
|
||||
serviciu = models.ForeignKey(
|
||||
Serviciu,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="employees",
|
||||
verbose_name="Serviciu",
|
||||
)
|
||||
compartiment = models.ForeignKey(
|
||||
Compartiment,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="employees",
|
||||
verbose_name="Compartiment",
|
||||
)
|
||||
history = HistoricalRecords()
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} ({self.username})"
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Angajat / Utilizator"
|
||||
verbose_name_plural = "Angajați / Utilizatori"
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
Reference in New Issue
Block a user