Source code for jmb.webposte.models

# 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