feat: async email slanje s cache PDF fallbackom i admin logom
Prebaci slanje emaila putnog naloga u pozadinski task bez blokiranja UI-a i dodaj fallback kad queue nije dostupna. Kod greške slanja spremi generirane PDF-ove u cache i pošalji notifikaciju s download linkovima. Dodaj EmailDispatchLog model i Django admin pregled za praćenje kome je poslano, što je poslano, status i detalje privitaka. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -568,6 +568,67 @@ class VehicleNotification(BaseModel):
|
||||
return f"{self.title} -> {self.recipient} ({self.level})"
|
||||
|
||||
|
||||
class EmailDispatchLog(BaseModel):
|
||||
DISPATCH_TYPE_CHOICES = [
|
||||
('work_order', _("Putni nalog")),
|
||||
('service_record', _("Servisni zapis")),
|
||||
]
|
||||
STATUS_CHOICES = [
|
||||
('queued', _("U obradi")),
|
||||
('sent', _("Poslano")),
|
||||
('failed', _("Neuspješno")),
|
||||
]
|
||||
|
||||
dispatch_type = models.CharField(max_length=32, choices=DISPATCH_TYPE_CHOICES, db_index=True, verbose_name=_("Tip slanja"))
|
||||
requested_by = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='requested_email_dispatch_logs',
|
||||
verbose_name=_("Zatražio"),
|
||||
)
|
||||
work_order = models.ForeignKey(
|
||||
'fleet.WorkOrder',
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='email_dispatch_logs',
|
||||
verbose_name=_("Putni nalog"),
|
||||
)
|
||||
service_record = models.ForeignKey(
|
||||
'fleet.VehicleServiceRecord',
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='email_dispatch_logs',
|
||||
verbose_name=_("Servisni zapis"),
|
||||
)
|
||||
recipients = models.JSONField(default=list, blank=True, verbose_name=_("Primatelji"))
|
||||
subject = models.CharField(max_length=255, blank=True, verbose_name=_("Naslov"))
|
||||
message = models.TextField(blank=True, verbose_name=_("Poruka"))
|
||||
attachments = models.JSONField(default=list, blank=True, verbose_name=_("Privitci"))
|
||||
status = models.CharField(max_length=16, choices=STATUS_CHOICES, default='queued', db_index=True, verbose_name=_("Status"))
|
||||
error_message = models.TextField(blank=True, verbose_name=_("Greška"))
|
||||
metadata = models.JSONField(default=dict, blank=True, verbose_name=_("Metapodaci"))
|
||||
sent_at = models.DateTimeField(null=True, blank=True, verbose_name=_("Poslano u"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Log slanja emaila")
|
||||
verbose_name_plural = _("Logovi slanja emaila")
|
||||
ordering = ['-created_at']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.dispatch_type} -> {len(self.recipients or [])} ({self.status})"
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
if self.dispatch_type == 'work_order' and not self.work_order_id:
|
||||
raise ValidationError({'work_order': _("Putni nalog je obavezan za ovaj tip slanja.")})
|
||||
if self.dispatch_type == 'service_record' and not self.service_record_id:
|
||||
raise ValidationError({'service_record': _("Servisni zapis je obavezan za ovaj tip slanja.")})
|
||||
|
||||
|
||||
class ServiceContextNote(BaseModel):
|
||||
AUDIENCE_SOURCE_CHOICES = [
|
||||
('self', 'Samo autor'),
|
||||
|
||||
Reference in New Issue
Block a user