fix: add editable work order travel expenses
Some checks failed
ERP CI/CD Pipeline / test (push) Has been cancelled
ERP CI/CD Pipeline / Deploy (server git pull + compose) (push) Has been cancelled

Introduce a dedicated travel-expenses table for work orders so Broj sati, Količina dnevnica, and Iznos dnevnice can be reviewed and edited before PDF generation. The PDF now uses the stored travel-expenses values, with default HR rate and cache invalidation on updates.
This commit is contained in:
mariomitte
2026-08-08 03:52:41 +02:00
parent a9ab83c202
commit 8577aaa01b
8 changed files with 697 additions and 35 deletions

View File

@@ -14,6 +14,7 @@ from .models import (
VehicleNotification,
WorkOrder,
WorkOrderAdditionalCostsTable,
WorkOrderTravelExpensesTable,
WorkOrderPhoto,
WorkOrderInvoice,
VehicleServiceRecord,
@@ -501,6 +502,105 @@ class WorkOrderAdditionalCostsTableSerializer(serializers.ModelSerializer):
return super().update(instance, validated_data)
class WorkOrderTravelExpensesTableSerializer(serializers.ModelSerializer):
RATE_DEFAULTS = {
'HR': Decimal('30.00'),
'BIH': Decimal('50.00'),
'SI': Decimal('80.00'),
'CG': Decimal('50.00'),
}
class Meta:
model = WorkOrderTravelExpensesTable
fields = [
'id',
'work_order',
'broj_sati',
'kolicina_dnevnica',
'iznos_dnevnica',
'daily_rate_country',
'total_for_payout',
'created_at',
'updated_at',
]
read_only_fields = ['id', 'total_for_payout', 'created_at', 'updated_at']
@staticmethod
def _parse_decimal(value):
if value in (None, ''):
return None
try:
normalized = str(value).strip().replace('', '').replace(' ', '').replace(',', '.')
return Decimal(normalized)
except (InvalidOperation, ValueError, TypeError):
raise serializers.ValidationError('Neispravna decimalna vrijednost.')
def validate_daily_rate_country(self, value):
normalized = str(value or '').strip().upper()
if not normalized:
return WorkOrderTravelExpensesTable.DEFAULT_RATE_COUNTRY
valid_choices = {choice for choice, _label in WorkOrderTravelExpensesTable.RATE_CHOICES}
if normalized not in valid_choices:
raise serializers.ValidationError('Neispravna država dnevnice.')
return normalized
def validate(self, attrs):
instance = getattr(self, 'instance', None)
country = attrs.get(
'daily_rate_country',
getattr(instance, 'daily_rate_country', WorkOrderTravelExpensesTable.DEFAULT_RATE_COUNTRY),
)
if 'iznos_dnevnica' not in attrs or attrs.get('iznos_dnevnica') in (None, ''):
attrs['iznos_dnevnica'] = self.RATE_DEFAULTS.get(
country,
WorkOrderTravelExpensesTable.DEFAULT_RATE_AMOUNT,
)
if 'broj_sati' in attrs:
attrs['broj_sati'] = self._parse_decimal(attrs['broj_sati'])
if 'kolicina_dnevnica' in attrs:
attrs['kolicina_dnevnica'] = self._parse_decimal(attrs['kolicina_dnevnica'])
if 'iznos_dnevnica' in attrs:
attrs['iznos_dnevnica'] = self._parse_decimal(attrs['iznos_dnevnica'])
attrs['broj_sati'] = attrs.get(
'broj_sati',
getattr(instance, 'broj_sati', Decimal('0.00')),
) or Decimal('0.00')
attrs['kolicina_dnevnica'] = attrs.get(
'kolicina_dnevnica',
getattr(instance, 'kolicina_dnevnica', Decimal('0.00')),
) or Decimal('0.00')
attrs['iznos_dnevnica'] = attrs.get(
'iznos_dnevnica',
getattr(instance, 'iznos_dnevnica', self.RATE_DEFAULTS.get(country, WorkOrderTravelExpensesTable.DEFAULT_RATE_AMOUNT)),
) or self.RATE_DEFAULTS.get(country, WorkOrderTravelExpensesTable.DEFAULT_RATE_AMOUNT)
attrs['daily_rate_country'] = country
return attrs
def _calculate_total_for_payout(self, validated_data):
quantity = validated_data.get('kolicina_dnevnica', Decimal('0.00'))
rate = validated_data.get('iznos_dnevnica', Decimal('0.00'))
if not isinstance(quantity, Decimal):
quantity = self._parse_decimal(quantity) or Decimal('0.00')
if not isinstance(rate, Decimal):
rate = self._parse_decimal(rate) or Decimal('0.00')
return (quantity * rate).quantize(Decimal('0.01'))
def create(self, validated_data):
validated_data['total_for_payout'] = self._calculate_total_for_payout(validated_data)
return super().create(validated_data)
def update(self, instance, validated_data):
merged = {
'kolicina_dnevnica': validated_data.get('kolicina_dnevnica', instance.kolicina_dnevnica),
'iznos_dnevnica': validated_data.get('iznos_dnevnica', instance.iznos_dnevnica),
}
validated_data['total_for_payout'] = self._calculate_total_for_payout(merged)
return super().update(instance, validated_data)
class WorkOrderPhotoSerializer(serializers.ModelSerializer):
class Meta:
model = WorkOrderPhoto