Files
he-database/sheets/models.py

215 lines
7.0 KiB
Python

from django.db import models
from django.utils import timezone
from django.core.validators import MinValueValidator, MaxValueValidator
class Institute(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Client(models.Model):
name = models.CharField(max_length=100)
address = models.TextField()
institute = models.ForeignKey(Institute, on_delete=models.CASCADE)
def __str__(self):
return f"{self.name} ({self.institute.name})"
class MonthlySheet(models.Model):
"""Represents one monthly page"""
year = models.IntegerField()
month = models.IntegerField() # 1-12
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
unique_together = ['year', 'month']
ordering = ['year', 'month']
def __str__(self):
return f"{self.year}-{self.month:02d}"
class Cell(models.Model):
"""A single cell in the spreadsheet"""
sheet = models.ForeignKey(MonthlySheet, on_delete=models.CASCADE, related_name='cells')
client = models.ForeignKey(Client, on_delete=models.CASCADE)
table_type = models.CharField(max_length=20, choices=[
('top_left', 'Top Left Table'),
('top_right', 'Top Right Table'),
('bottom_1', 'Bottom Table 1'),
('bottom_2', 'Bottom Table 2'),
('bottom_3', 'Bottom Table 3'),
])
row_index = models.IntegerField() # 0-23 for top tables, 0-9 for bottom
column_index = models.IntegerField() # Actually client index (0-5)
is_formula = models.BooleanField(default=False)
# Cell content
value = models.DecimalField(max_digits=15, decimal_places=6, null=True, blank=True)
formula = models.TextField(blank=True)
# Metadata
data_type = models.CharField(max_length=20, default='number', choices=[
('number', 'Number'),
('text', 'Text'),
('date', 'Date'),
])
class Meta:
unique_together = ['sheet', 'client', 'table_type', 'row_index', 'column_index']
indexes = [
models.Index(fields=['sheet', 'table_type', 'row_index']),
]
def __str__(self):
return f"{self.sheet} - {self.client.name} - {self.table_type}[{self.row_index}][{self.column_index}]"
class CellReference(models.Model):
"""Track dependencies between cells for calculations"""
source_cell = models.ForeignKey(Cell, on_delete=models.CASCADE, related_name='dependents')
target_cell = models.ForeignKey(Cell, on_delete=models.CASCADE, related_name='dependencies')
class Meta:
unique_together = ['source_cell', 'target_cell']
class Betriebskosten(models.Model):
KOSTENTYP_CHOICES = [
('sach', 'Sachkosten'),
('ln2', 'LN2'),
('helium', 'Helium'),
('inv', 'Inventar'),
]
buchungsdatum = models.DateField('Buchungsdatum')
rechnungsnummer = models.CharField('Rechnungsnummer', max_length=50)
kostentyp = models.CharField('Kostentyp', max_length=10, choices=KOSTENTYP_CHOICES)
gas_volume = models.DecimalField('Gasvolumen (Liter)', max_digits=10, decimal_places=2, null=True, blank=True)
betrag = models.DecimalField('Betrag (€)', max_digits=10, decimal_places=2)
beschreibung = models.TextField('Beschreibung', blank=True)
@property
def price_per_liter(self):
if self.kostentyp == 'helium' and self.gas_volume:
return self.betrag / self.gas_volume
return None
def __str__(self):
return f"{self.buchungsdatum} - {self.get_kostentyp_display()} - {self.betrag}"
class RowCalculation(models.Model):
"""Define calculations for specific rows"""
table_type = models.CharField(max_length=20, choices=[
('top_left', 'Top Left Table'),
('top_right', 'Top Right Table'),
('bottom_1', 'Bottom Table 1'),
('bottom_2', 'Bottom Table 2'),
('bottom_3', 'Bottom Table 3'),
])
row_index = models.IntegerField() # Which row has the formula
formula = models.TextField() # e.g., "row_10 + row_9"
description = models.CharField(max_length=200, blank=True)
class Meta:
unique_together = ['table_type', 'row_index']
def __str__(self):
return f"{self.table_type}[{self.row_index}]: {self.formula}"
# Or simpler: Just store row calculations in a JSONField
class TableConfig(models.Model):
"""Configuration for table calculations"""
table_type = models.CharField(max_length=20, unique=True, choices=[
('top_left', 'Top Left Table'),
('top_right', 'Top Right Table'),
('bottom_1', 'Bottom Table 1'),
('bottom_2', 'Bottom Table 2'),
('bottom_3', 'Bottom Table 3'),
])
calculations = models.JSONField(default=dict) # {11: "10 + 9", 15: "14 - 13"}
def __str__(self):
return f"{self.get_table_type_display()} Config"
class ExcelEntry(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE)
date = models.DateField(default=timezone.now)
pressure = models.DecimalField(
max_digits=10,
decimal_places=2,
validators=[MinValueValidator(0)],
default=0.00
)
purity = models.DecimalField(
max_digits=5,
decimal_places=2,
validators=[MinValueValidator(0), MaxValueValidator(100)],
default=0.00
)
notes = models.TextField(blank=True, null=True)
date_joined = models.DateField(auto_now_add=True)
# Manual input
lhe_zus = models.DecimalField(
max_digits=10,
decimal_places=3,
validators=[MinValueValidator(0)],
default=0.0
)
druckkorrektur = models.DecimalField(
max_digits=10,
decimal_places=3,
validators=[MinValueValidator(0)],
default=1.0
)
# Auto-calculated values (saved)
constant_300 = models.DecimalField(
max_digits=10,
decimal_places=3,
default=300.0
)
korrig_druck = models.DecimalField(
max_digits=12,
decimal_places=6,
default=0.0
)
nm3 = models.DecimalField(
max_digits=12,
decimal_places=6,
default=0.0
)
lhe = models.DecimalField(
max_digits=12,
decimal_places=6,
default=0.0
)
lhe_ges = models.DecimalField(
max_digits=12,
decimal_places=6,
default=0.0
)
def __str__(self):
return f"{self.client.name} - {self.date}"
class SecondTableEntry(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE)
date = models.DateField(default=timezone.now)
is_warm = models.BooleanField(default=False)
lhe_delivery = models.CharField(max_length=100, blank=True, null=True)
lhe_output = models.DecimalField(
max_digits=10,
decimal_places=2,
validators=[MinValueValidator(0)],
blank=True,
null=True
)
notes = models.TextField(blank=True, null=True)
date_joined = models.DateField(auto_now_add=True)
def __str__(self):
return f"{self.client.name} - {self.date}"