# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import re
from odoo import api, fields, models, _
from odoo.addons.iap import jsonrpc
DEFAULT_ENDPOINT = 'https://iap-services.odoo.com'
ESTIMATE_ENDPOINT = '/iap/snailmail/1/estimate'
PRINT_ENDPOINT = '/iap/snailmail/1/print'
class MultiComposer(models.TransientModel):
_name = 'multi.compose.message'
_inherit = 'multi.compose.message'
partner_id = fields.Many2one('res.partner', string='Partner')
snailmail_color = fields.Boolean(string='Color', default=lambda self: self.env.user.company_id.snailmail_color)
snailmail_duplex = fields.Boolean(string='Both side', default=lambda self: self.env.user.company_id.snailmail_duplex)
snailmail_send_by_letter = fields.Boolean('Post', default=lambda self: self.env.user.company_id.snailmail_send_by_letter)
snailmail_cost = fields.Float(string='Credits', compute='_snailmail_estimate', store=True, readonly=True)
currency_id = fields.Many2one('res.currency', default=lambda self: self.env.user.company_id.currency_id.id, string="Currency")
@api.multi
def _update_company(self):
super(MultiComposer,self)._update_company()
for rec in self:
if rec.company_id.snailmail_send_by_letter != rec.snailmail_send_by_letter:
rec.company_id.write({
'snailmail_send_by_letter': rec.snailmail_send_by_letter,
})
@api.multi
def _snailmail_create(self, route, batch=False):
"""
Create a dictionnary object to send to snailmail server.
:return: Dict in the form:
{
account_token: string, //IAP Account token of the user
documents: [{
pages: int,
pdf_bin: pdf file
res_id: int (client-side res_id),
res_model: char (client-side res_model),
address: {
name: char,
street: char,
street2: char (OPTIONAL),
zip: int,
city: char,
state: char (state code (OPTIONAL)),
country_code: char (country code)
}
return_address: {
name: char,
street: char,
street2: char (OPTIONAL),
zip: int,
city: char,
state: char (state code (OPTIONAL)),
country_code: char (country code)
}
}],
options: {
color: boolean (true if color, false if black-white),
duplex: boolean (true if duplex, false otherwise),
}
}
"""
account_token = self.env['iap.account'].get('snailmail').account_token
documents = []
pdf = self.print_attachment_id.datas
if pdf:
documents.append({
'pdf_bin': route == 'print' and pdf,
'pages': route == 'estimate' and self._count_pages_pdf(pdf),
'address': {
'name': self.partner_id.name,
'street': self.partner_id.street,
'street2': self.partner_id.street2,
'zip': self.partner_id.zip,
'state': self.partner_id.state_id.code if self.partner_id.state_id else False,
'city': self.partner_id.city,
'country_code': self.partner_id.country_id.code
},
'return_address': {
'name': self.company_id.partner_id.name,
'street': self.company_id.partner_id.street,
'street2': self.company_id.partner_id.street2,
'zip': self.company_id.partner_id.zip,
'state': self.company_id.partner_id.state_id.code if self.company_id.partner_id.state_id else False,
'city': self.company_id.partner_id.city,
'country_code': self.company_id.partner_id.country_id.code,
}
})
return {
'account_token': account_token,
'documents': documents,
'options': {
'color': self.snailmail_color,
'duplex': self.snailmail_duplex,
},
'batch': batch,
}
@api.multi
def action_snailmail_print(self):
"""
get response
{
'request_code': RESPONSE_OK, # 200 if good or fail
'total_cost': total_cost,
'credit_error': credit_error,
'request': {
'documents': documents,
'options': options
}
}
}
"""
self.ensure_one()
endpoint = self.env['ir.config_parameter'].sudo().get_param('snailmail.endpoint', DEFAULT_ENDPOINT)
params = self._snailmail_create('print', batch=True)
response = jsonrpc(endpoint + PRINT_ENDPOINT, params=params)
error = False
doc = response['request']['documents'][0]
record = self.env[self.model].browse(self.res_id)
if doc.get('sent') and response['request_code'] == 200:
if hasattr(record, '_message_log'):
message = _('The document was correctly sent by post.
The tracking id is %s' % doc['send_id'])
record._message_log(body=message)
else:
activity_data = {
'res_id': self.res_id,
'res_model_id': self.env['ir.model']._get(self.model).id,
'activity_type_id': self.env.ref('snailmail.mail_activity_data_snailmail').id,
'summary': _('Post letter: an error occured.'),
'note': _('An error occured when sending the document by post.
Error: %s' % \
self._get_error_message(doc['error'] if response['request_code'] == 200 else response['reason'])),
'user_id': self.env.user.id,
}
self.env['mail.activity'].create(activity_data)
error = True
if not error:
notification = _('Post letter: document sent.')
else:
notification = _('Post letter: an error occured. Please check the document for more information.')
# as we interact with the exterior world, we commit
self.env.cr.commit()
self._update_company()
return {
'error': error,
'message': notification,
}
@api.multi
@api.depends('print_attachment_id')
@api.onchange('snailmail_send_by_letter')
def _snailmail_estimate(self):
"""
Send a request to estimate the cost of sending all the documents with
the differents options.
The JSON object sent is the one generated from self._snailmail_create()
arguments sent:
{
"documents":[{
pages: int,
res_id: int (client_side, optional),
res_model: int (client_side, optional),
address: {
country_code: char (country name)
}
}],
'color': # color on the letter,
'duplex': # one/two side printing,
}
The answer of the server is the same JSON object with some additionnal fields:
{
"total_cost": integer, //The cost of sending ALL the documents
body: JSON object (same as body param except new param 'cost' in each documents)
}
"""
for composer in self:
if not composer.partner_id:
composer.partner_id = self.env[self.model].browse(self.res_id).partner_id
if composer.snailmail_send_by_letter and composer.print_attachment_id :# and not composer.is_estimated:
endpoint = self.env['ir.config_parameter'].sudo().get_param('snailmail.endpoint', DEFAULT_ENDPOINT)
params = composer._snailmail_create('estimate')
req = jsonrpc(endpoint + '/iap/snailmail/1/estimate', params=params)
composer.is_estimated = True
# The cost is sent in eurocents, we change it to euros and then convert it to the company currency
cost = int(req['total_cost'])/100.0
currency_eur = self.env.ref('base.EUR')
composer.snailmail_cost = currency_eur._convert(cost, self.currency_id, self.company_id, fields.Datetime.now())
# all the print stuff will go to the cron onsnailail print orders
def _count_pages_pdf(self, bin_pdf):
""" Count the number of pages of the given pdf file.
:param bin_pdf : binary content of the pdf file
"""
pages = 0
for match in re.compile(b"/Count\s+(\d+)").finditer(bin_pdf):
pages = int(match.group(1))
return pages
def _get_error_message(self, error):
if error == 'CREDIT_ERROR':
link = self.env['iap.account'].get_credits_url(service_name='snailmail')
return _('You don\'t have enough credits to perform this operation.
Please go to your iap account.' % link)
if error == 'NO_PRICE_AVAILABLE':
return _('The country of the partner is not covered by Snailmail.')
if error == 'MISSING_REQUIRED_FIELDS':
return _('One or more required fields are empty.')
if error == 'SOMETHING_IS_WRONG':
return _('An unknown error happened. Please contact the support.')
return error
@api.multi
def send_and_print_action(self):
res = super(MultiComposer,self).send_and_print_action()
if self.snailmail_send_by_letter:
self.action_snailmail_print()
return res
@api.multi
def send_action(self):
res = super(MultiComposer,self).send_action()
for wizard in self:
if wizard.snailmail_send_by_letter:
wizard.action_snailmail_print()
return res