# coding: utf-8
"""
========
Models
========
Mail
====
.. autoclass:: Mail
"""
from copy import copy
from django.conf import settings
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.translation import ugettext_lazy as _
import poste
from jmb.core.db.models import DateModel, UserModel, StatusModel, OrderedModel, SEOModel
from jmb.i18n.models import TranslationModel, I18NModel
from jmb.core.db.fields import HTMLField, HTMLFieldTinyMCE
from jmb.address.models import Address
from jmb.core.middleware.thread_local import get_request
###################################
## MODEL #
###################################
# NOTA: i codici < 100 sono ufficiali Poste-On-Line per raccomandate
# Per le lettere danno solo descrizione
STATUS_CHOICES = (
# Codici ufficiali poste-on-line
('02', 'Giacenza'),
('00', 'Accettata online'),
('01', 'Consegnato'),
('03', 'In restituzione'),
('99', 'In lavorazione'),
# Codici nostri o ricavati da poste-on-line
('100', 'Non ancora inviata'),
('101', 'Fake send'),
('102', 'Presa in Carico Postel'), # Lol
('103', 'Annullato'),
('104', 'Prezzato'),
('105', 'Inviata al server'),
('106', 'Postalizzato'),
('500', 'Codice non noto'),
)
MAILING_TYPE = (
# ('tol', 'Telegramma'),
('lol', 'Lettera'),
('rol', 'Raccomandata'),
)
[docs]class XolAddress(Address):
"""
Classe astatta che derica da jmb.address.models.Address ed aggiunge un nominativo
"""
first_name = models.CharField(max_length=30, blank=True, null=True)
last_name = models.CharField(max_length=30, blank=True, null=True)
company_name = models.CharField(max_length=30, blank=True, null=True)
class Meta:
abstract = True
def who(self):
return (u"%s %s %s" % (self.first_name, self.last_name, self.company_name)).strip()
def where(self):
return (u"%s %s %s" % (self.name, self.number, self.locality)).strip()
[docs] def abstract(self):
return '%s - %s' % (self.who(), self.where())
[docs] def as_dict(self):
"Return a dict suitable for rol/lol from Address model"
return {
'nome' : self.first_name,
'cognome' : self.last_name,
'ragione_sociale' : self.company_name,
'citta' : self.locality,
'dug' : str(self.get_address_type_display()),
'toponimo' : self.name,
'cap' : self.zip_code,
'numero_civico' : ' '.join([self.number or '', self.crossed or '', self.scale or '', self.floor or '']),
'stato' : str(self.country),
'provincia' : self.province,
}
[docs] def validate(self, xol_client=None):
"""
Validate address if accepted by poste-on-line
"""
xol = xol_client or get_xol_client()
dest = poste.Destinatario(**self.as_dict())
return xol.valida_destinatari([dest])
[docs]class Mail(UserModel, DateModel):
"""
Classe degli invii fatti o pianificati
"""
guid = models.CharField(max_length=50, null=True, blank=True) # named id_richiesta in many contexts
guid_user = models.CharField(max_length=50, null=True, blank=True)
order_id = models.CharField(max_length=50, null=True, blank=True)
status = models.CharField(max_length=5, null=True, blank=True, choices=STATUS_CHOICES)
document = models.FileField(upload_to='rol')
cost = models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True)
type = models.CharField(max_length=3, choices=MAILING_TYPE)
[docs] def submit(self, xol_client=None):
"""
Send this invoice according to what has already been saved
"""
if settings.WEBPOSTE_FAKE_SEND:
self.status = 101
self.save()
request = get_request()
if request:
messages.warning(request, _('Document not sent since WEBPOSTE_FAKE_SEND is set'))
return
xol = xol_client or self.get_xol_client()
data = {'mittente' : poste.Mittente(**self.sender_addr.as_dict())}
data['destinatari'] = [poste.Destinatario(**dest.as_dict()) for dest in self.recipient_addrs.all()]
if hasattr(self, 'return_addr'):
data['ricevuta_ritorno'] = poste.Nominativo(self.return_addr.as_dict())
xol_submit = xol.submit(self.document.path, cliente="Jmb client", **data)
self.guid = xol_submit.guid
self.guid_user = xol_submit.guid_utente
self.status = 105
self.save()
self.update_recipients_ids(xol, xol_submit)
[docs] def update_recipients_ids(self, xol, xol_submit):
if self.type == 'lol':
dmap = xol.get_receipt_id(xol_submit.result.LOLResponse)
else:
dmap = xol.get_receipt_id(xol_submit.result.ROLResponse)
for rcp in self.recipient_addrs.all():
rcp.recipient_id = rcp.id
rcp.receipt_id = dmap[str(rcp.id)]
rcp.save()
[docs] def update_recipients_delivery_status(self, xol_client=None):
"""update delivery status for rol recipients
"""
xol = xol_client or self.get_xol_client()
if not self.type == 'rol':
return
resp = xol.get_stato_invii_per_id([self.guid])
dmap = resp[self.guid]
for rcp in self.recipient_addrs.all():
rcp.delivery_status = dmap[rcp.ref_number]['StatoType']
rcp.save()
[docs] def confirm(self, xol_client=None):
"""Confirm this order, parse result and update records
"""
xol = xol_client or self.get_xol_client()
response = xol.confirm(guid=self.guid)
self.order_id = response.IdOrdine
self.cost = response.Valorizzazione.Totale.ImportoTotale
if self.type == 'rol':
self.update_num_raccomandata(response)
self.save()
return response
[docs] def update_num_raccomandata(self, response):
"""return a dict mapping IdRicevuta (recipient service id) to NumeroRaccomandata
"""
refs = {}
for dest in result.DestinatariRaccomandata.ArrayOfDestinatarioRaccomandata:
refs[dest.IdRicevuta] = dest.NumeroRaccomandata
for rcp in self.recipient_addrs.all():
rcp.ref_number = refs[rcp.receipt_id]
rcp.save()
[docs] def update_status(self, xol_client=None):
"""update status querying the web service
"""
if not self.guid:
return
xol = xol_client or self.get_xol_client()
ret = xol.get_stato_richiesta(self.guid)
try:
self.status = choices2map(STATUS_CHOICES)[ret.StatoIdRichiesta]
except KeyError:
request = get_request()
if request:
messages.warning(request, _("No known status: '%s'" % ret.StatoIdRichiesta))
self.status = 500
self.save()
[docs] def get_xol_client(self):
if self.type == 'rol':
xol_client = poste.ROLClient
else:
xol_client = poste.LOLClient
xol = xol_client(
username=settings.WEBPOSTE_USERNAME,
password=settings.WEBPOSTE_PASSWORD,
service_level=settings.WEBPOSTE_SERVICE_LEVEL,
)
return xol
@classmethod
[docs] def validate(cls, xol_client=None, ids_list=None, q=None):
"""
Validate address if accepted by poste-on-line
Sarebbe piĆ¹ efficente ma poste-on-line non da informazione su *quale*
destinatario non valida...
"""
xol = xol_client or get_xol_client()
if ids_list:
q = cls.objects.filter(id__in=ids_list)
recipients = [poste.Destinatario(**dest.as_dict()) for dest in q]
return xol.valida_destinatari(recipients)
def __unicode__(self):
return u'%s ->%s' % (self.document, self.recipient_addrs.all()[0].who())
[docs]class SenderAddress(XolAddress):
mail = models.OneToOneField(Mail, related_name='sender_addr')
[docs]class RecipientAddress(XolAddress):
mail = models.ForeignKey(Mail, related_name='recipient_addrs')
guid = models.CharField(max_length=50, null=True, blank=True) # named id_richiesta in many contexts
delivery_status = models.CharField(max_length=30, null=True, blank=True, choices=STATUS_CHOICES)
recipient_id = models.CharField(max_length=30, null=True, blank=True)
receipt_id = models.CharField(max_length=30, null=True, blank=True)
ref_number = models.CharField(max_length=15, null=True, blank=True)
def as_dict(self):
dict = super(RecipientAddress, self).as_dict()
dict['id_destinatario'] = self.id
return dict
[docs] def update_status(self, xol_client=None):
"""update status querying the web service
In case of multi-recipient it's better to update from Mail object
"""
if not self.ref_number:
return
xol = xol_client or self.mail.get_xol_client()
ret = xol.get_stato_invii_per_numero(self.ref_number)
self.delivery_status = str(ret[self.ref_number].StatoType)
self.save()
[docs]class ReturnAddress(XolAddress):
mail = models.OneToOneField(Mail, related_name='return_addr')
[docs]class History(DateModel):
mail = models.ForeignKey(RecipientAddress, related_name='history')
delivery_status = models.CharField(max_length=30, null=True, blank=True, choices=STATUS_CHOICES)
delivery_update = models.DateField(null=True, blank=True)
[docs]def choices2map(my_tuple):
"""Return a dict mapping the values in choices
"""
dict = {}
for id, val in my_tuple:
dict[val] = id
return dict
[docs]def get_xol_client(type_='lol'):
"""Return a suitable xol client for type_
"""
if type_ == 'rol':
xol_client = poste.ROLClient
else:
xol_client = poste.LOLClient
xol = xol_client(
username=settings.WEBPOSTE_USERNAME,
password=settings.WEBPOSTE_PASSWORD,
service_level=settings.WEBPOSTE_SERVICE_LEVEL,
)
return xol