diff --git a/pingen/README.rst b/pingen/README.rst
new file mode 100644
index 00000000000..14a55b8ffa5
--- /dev/null
+++ b/pingen/README.rst
@@ -0,0 +1,108 @@
+.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
+ :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
+ :alt: License: AGPL-3
+
+===========================
+Integration with pingen.com
+===========================
+
+What is pingen.com
+==================
+
+Pingen.com is a paid online service.
+It sends uploaded documents by letter post.
+
+Scope of the integration
+========================
+
+One can decide, per document / attachment, if it should be pushed
+to pingen.com. The documents are pushed asynchronously.
+
+A second cron updates the informations of the documents from pingen.com, so we
+know which of them have been sent.
+
+Configuration
+=============
+
+The authentication token is configured on the company's view. You can also
+tick a checkbox if the staging environment (https://stage-api.pingen.com)
+should be used.
+
+The setup of the 2 crons can be changed as well:
+
+ * Run Pingen Document Push
+ * Run Pingen Document Update
+
+Usage
+=====
+
+On the attachment view, a new pingen.com tab has been added.
+You can tick a box to push the document to pingen.com.
+
+There is 3 additional options:
+
+ * Send: the document will not be only uploaded, but will be also be sent
+ * Speed: priority or economy
+ * Type of print: color or black and white
+
+Once the configuration is done and the attachment saved, a Pingen Document
+is created. You can directly access to the latter on the Link on the right on
+the attachment view.
+
+You can find them in `Settings > Customization > Low Level Objets > Pingen
+Documents` or in the more convenient `Documents` menu if you have installed the
+`document` module.
+
+Errors
+======
+
+Sometimes, pingen.com will refuse to send a document because it does not meet
+its requirements. In such case, the document's state becomes "Pingen Error"
+and you will need to manually handle the case, either from the pingen.com
+backend, or by changing the document on OpenERP and resolving the error on the
+Pingen Document.
+
+When a connection error occurs, the action will be retried on the next
+scheduler run.
+
+Dependencies
+============
+
+ * Require the Python library `requests `_
+ * The PDF files sent to pingen.com have to respect some `formatting rules
+ `_.
+ * The address must be in a format accepted by pingen.com: the last line
+ is the country in English or German.
+
+Bug Tracker
+===========
+
+Bugs are tracked on `GitHub Issues
+`_. In case of trouble, please
+check there if your issue has already been reported. If you spotted it first,
+help us smashing it by providing a detailed and welcomed feedback.
+
+Credits
+=======
+
+Contributors
+============
+
+* Yannick Vaucher
+* Guewen Baconnier
+* Anar Baghirli
+
+Maintainer
+==========
+
+.. image:: https://odoo-community.org/logo.png
+ :alt: Odoo Community Association
+ :target: https://odoo-community.org
+
+This module is maintained by the OCA.
+
+OCA, or the Odoo Community Association, is a nonprofit organization whose
+mission is to support the collaborative development of Odoo features and
+promote its widespread use.
+
+To contribute to this module, please visit https://odoo-community.org.
diff --git a/pingen/__init__.py b/pingen/__init__.py
index 4ff2feaecd5..e26b57004e1 100644
--- a/pingen/__init__.py
+++ b/pingen/__init__.py
@@ -1,25 +1,9 @@
# -*- coding: utf-8 -*-
-##############################################################################
-#
-# Author: Guewen Baconnier
-# Copyright 2012 Camptocamp SA
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
+# Author: Guewen Baconnier
+# Copyright 2012-2017 Camptocamp SA
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
-import ir_attachment
-import pingen
-import pingen_document
-import res_company
+from . import ir_attachment
+from . import pingen
+from . import pingen_document
+from . import res_company
diff --git a/pingen/__manifest__.py b/pingen/__manifest__.py
index 4ae5dc88634..98523bd5618 100644
--- a/pingen/__manifest__.py
+++ b/pingen/__manifest__.py
@@ -1,27 +1,11 @@
# -*- coding: utf-8 -*-
-##############################################################################
-#
-# Author: Guewen Baconnier
-# Copyright 2012 Camptocamp SA
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
+# Author: Guewen Baconnier
+# Copyright 2012-2017 Camptocamp SA
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
'name': 'pingen.com integration',
- 'version': '1.0',
+ 'version': '10.0.1.0.0',
'author': "Camptocamp,Odoo Community Association (OCA)",
'maintainer': 'Camptocamp',
'license': 'AGPL-3',
@@ -31,79 +15,6 @@
'external_dependencies': {
'python': ['requests'],
},
- 'description': """
-Integration with pingen.com
-===========================
-
-What is pingen.com
-------------------
-
-Pingen.com is a paid online service.
-It sends uploaded documents by letter post.
-
-Scope of the integration
-------------------------
-
-One can decide, per document / attachment, if it should be pushed
-to pingen.com. The documents are pushed asynchronously.
-
-A second cron updates the informations of the documents from pingen.com, so we
-know which of them have been sent.
-
-Configuration
--------------
-
-The authentication token is configured on the company's view. You can also
-tick a checkbox if the staging environment (https://stage-api.pingen.com)
-should be used.
-
-The setup of the 2 crons can be changed as well:
-
- * Run Pingen Document Push
- * Run Pingen Document Update
-
-Usage
------
-
-On the attachment view, a new pingen.com tab has been added.
-You can tick a box to push the document to pingen.com.
-
-There is 3 additional options:
-
- * Send: the document will not be only uploaded, but will be also be sent
- * Speed: priority or economy
- * Type of print: color or black and white
-
-Once the configuration is done and the attachment saved, a Pingen Document
-is created. You can directly access to the latter on the Link on the right on
-the attachment view.
-
-You can find them in `Settings > Customization > Low Level Objets > Pingen
-Documents` or in the more convenient `Documents` menu if you have installed the
-`document` module.
-
-Errors
-------
-
-Sometimes, pingen.com will refuse to send a document because it does not meet
-its requirements. In such case, the document's state becomes "Pingen Error" and
-you will need to manually handle the case, either from the pingen.com backend,
-or by changing the document on OpenERP and resolving the error on the Pingen
-Document.
-
-When a connection error occurs, the action will be retried on the next scheduler
-run.
-
-Dependencies
-------------
-
- * Require the Python library `requests `_
- * The PDF files sent to pingen.com have to respect some `formatting rules
- `_.
- * The address must be in a format accepted by pingen.com: the last line
- is the country in English or German.
-
-""",
'website': 'http://www.camptocamp.com',
'data': [
'ir_attachment_view.xml',
@@ -113,7 +24,7 @@
'security/ir.model.access.csv',
],
'tests': [],
- 'installable': False,
+ 'installable': True,
'auto_install': False,
'application': True,
}
diff --git a/pingen/ir_attachment.py b/pingen/ir_attachment.py
index ba00c47b7b2..423230cae45 100644
--- a/pingen/ir_attachment.py
+++ b/pingen/ir_attachment.py
@@ -1,123 +1,99 @@
# -*- coding: utf-8 -*-
-##############################################################################
-#
-# Author: Guewen Baconnier
-# Copyright 2012 Camptocamp SA
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
+# Author: Guewen Baconnier
+# Copyright 2012-2017 Camptocamp SA
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import requests
import base64
-from openerp.osv import osv, orm, fields
-from openerp.tools.translate import _
+from odoo import models, fields, _
+from odoo.exceptions import UserError
-class ir_attachment(orm.Model):
+class IrAttachment(models.Model):
_inherit = 'ir.attachment'
- _columns = {
- 'send_to_pingen': fields.boolean('Send to Pingen.com'),
- 'pingen_document_ids': fields.one2many(
- 'pingen.document', 'attachment_id',
- string='Pingen Document', readonly=True),
- 'pingen_send': fields.boolean(
- 'Send',
- help="Defines if a document is merely uploaded or also sent"),
- 'pingen_speed': fields.selection(
- [('1', 'Priority'), ('2', 'Economy')],
- 'Speed',
- help="Defines the sending speed if the document is automatically sent"),
- 'pingen_color': fields.selection([('0', 'B/W'), ('1', 'Color')], 'Type of print'),
- }
+ send_to_pingen = fields.Boolean("Send to Pingen.com")
+ pingen_document_ids = fields.One2many(
+ 'pingen.document', 'attachment_id',
+ string='Pingen Document', readonly=True)
+ pingen_send = fields.Boolean(
+ 'Send', help="Defines if a document is merely uploaded or also sent",
+ default=True)
+ pingen_speed = fields.Selection(
+ [('1', 'Priority'), ('2', 'Economy')],
+ 'Speed', default='2',
+ help='Defines the sending speed if ' +
+ 'the document is automatically sent')
+ pingen_color = fields.Selection([('0', 'B/W'), ('1', 'Color')],
+ 'Type of print',
+ default='0')
- _defaults = {
- 'pingen_send': True,
- 'pingen_color': '0',
- 'pingen_speed': '2',
- }
-
- def _prepare_pingen_document_vals(self, cr, uid, attachment, context=None):
- return {'attachment_id': attachment.id,
+ def _prepare_pingen_document_vals(self):
+ return {'attachment_id': self.id,
'config': 'created from attachment'}
- def _handle_pingen_document(self, cr, uid, attachment_id, context=None):
- """ Reponsible of the related ``pingen.document`` when the ``send_to_pingen``
+ def _handle_pingen_document(self):
+ """ Reponsible of the related ``pingen.document``
+ when the ``send_to_pingen``
field is modified.
-
Only one pingen document can be created per attachment.
-
When ``send_to_pingen`` is activated:
* Create a ``pingen.document`` if it does not already exist
- * Put the related ``pingen.document`` to ``pending`` if it already exist
+ * Put the related ``pingen.document`` to ``pending``
+ if it already exist
When it is deactivated:
* Do nothing if no related ``pingen.document`` exists
* Or cancel it
* If it has already been pushed to pingen.com, raises
an `osv.except_osv` exception
"""
- pingen_document_obj = self.pool.get('pingen.document')
- attachment = self.browse(cr, uid, attachment_id, context=context)
- document = attachment.pingen_document_ids[0] if attachment.pingen_document_ids else None
- if attachment.send_to_pingen:
+ pingen_document_obj = self.env['pingen.document']
+ document = self.pingen_document_ids[0] if \
+ self.pingen_document_ids else None
+ if self.send_to_pingen:
if document:
- document.write({'state': 'pending'}, context=context)
+ document.write({'state': 'pending'})
else:
pingen_document_obj.create(
- cr, uid,
- self._prepare_pingen_document_vals(
- cr, uid, attachment, context=context),
- context=context)
+ self._prepare_pingen_document_vals())
else:
if document:
if document.state == 'pushed':
- raise osv.except_osv(
- _('Error'),
- _('The attachment %s is already pushed to pingen.com.') %
- attachment.name)
- document.write({'state': 'canceled'}, context=context)
+ raise UserError(
+ _('Error. The attachment ' +
+ '%s is already pushed to pingen.com.') %
+ self.name)
+ document.write({'state': 'canceled'})
return
- def create(self, cr, uid, vals, context=None):
- attachment_id = super(ir_attachment, self).create(cr, uid, vals, context=context)
+ def create(self, vals):
+ attachment_id = super(IrAttachment, self).create(vals)
if 'send_to_pingen' in vals:
- self._handle_pingen_document(cr, uid, attachment_id, context=context)
+ attachment_id._handle_pingen_document()
return attachment_id
- def write(self, cr, uid, ids, vals, context=None):
- res = super(ir_attachment, self).write(cr, uid, ids, vals, context=context)
+ def write(self, vals):
+ res = super(IrAttachment, self).write(vals)
if 'send_to_pingen' in vals:
- for attachment_id in ids:
- self._handle_pingen_document(cr, uid, attachment_id, context=context)
+ for attachment in self:
+ attachment._handle_pingen_document()
return res
- def _decoded_content(self, cr, uid, attachment, context=None):
+ def _decoded_content(self):
""" Returns the decoded content of an attachment (stored or url)
-
Returns None if the type is 'url' and the url is not reachable.
"""
decoded_document = None
- if attachment.type == 'binary':
- decoded_document = base64.decodestring(attachment.datas)
- elif attachment.type == 'url':
- response = requests.get(attachment.url)
+ if self.type == 'binary':
+ decoded_document = base64.b64decode(self.datas)
+ elif self.type == 'url':
+ response = requests.get(self.url)
if response.ok:
decoded_document = requests.content
else:
- raise Exception(
- 'The type of attachment %s is not handled' % attachment.type)
+ raise UserError(
+ _('The type of attachment %s is not handled')
+ % self.type)
return decoded_document
diff --git a/pingen/ir_attachment_view.xml b/pingen/ir_attachment_view.xml
index d51a906f0f8..24e2335efcc 100644
--- a/pingen/ir_attachment_view.xml
+++ b/pingen/ir_attachment_view.xml
@@ -1,6 +1,6 @@
-
-
+
+
ir.attachment.pingen.view
@@ -8,14 +8,14 @@
form
-
-
+
+
-
-
+
+
@@ -28,4 +28,4 @@
src_model="ir.attachment"/>
-
+
diff --git a/pingen/pingen.py b/pingen/pingen.py
index 475407b39ee..3de6e200c67 100644
--- a/pingen/pingen.py
+++ b/pingen/pingen.py
@@ -1,29 +1,14 @@
# -*- coding: utf-8 -*-
-##############################################################################
-#
-# Author: Guewen Baconnier
-# Copyright 2012 Camptocamp SA
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
+# Author: Guewen Baconnier
+# Copyright 2012-2017 Camptocamp SA
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import requests
import logging
import urlparse
import json
import pytz
+import base64
from datetime import datetime
from requests.packages.urllib3.filepost import encode_multipart_formdata
@@ -34,7 +19,7 @@
100: 'Ready/Pending',
101: 'Processing',
102: 'Waiting for confirmation',
- 200: 'Sent',
+ 1: 'Sent',
300: 'Some error occured and object wasn\'t sent',
400: 'Sending cancelled',
}
@@ -90,14 +75,9 @@ def session(self):
""" Build a requests session """
if self._session is not None:
return self._session
- self._session = requests.Session(
- params={'token': self._token},
- # with safe_mode, requests catch errors and
- # returns a blank response with an error
- config={'safe_mode': True},
- # verify = False required for staging environment
- # because the SSL certificate is wrong
- verify=not self.staging)
+ self._session = requests.Session()
+ self._session.params = {'token': self._token}
+ self._session.verify = not self.staging
return self._session
def __enter__(self):
@@ -121,22 +101,36 @@ def _send(self, method, endpoint, **kwargs):
:param str endpoint: endpoint to call
:param kwargs: additional arguments forwarded to the requests method
"""
- complete_url = urlparse.urljoin(self.url, endpoint)
+
+ p_url = urlparse.urljoin(self.url, endpoint)
+
+ if endpoint == 'document/get':
+ complete_url = '{}{}{}{}{}'.format(p_url,
+ '/id/',
+ kwargs['params']['id'],
+ '/token/',
+ self._token)
+ else:
+ complete_url = '{}{}{}'.format(p_url,
+ '/token/',
+ self._token)
response = method(complete_url, **kwargs)
if not response.ok:
raise ConnectionError(
- "%s: %s" % (response.json['errorcode'],
- response.json['errormessage']))
+ "%s: %s" % (response.json()['errorcode'],
+ response.json()['errormessage']))
- if response.json['error']:
+ if response.json()['error']:
raise APIError(
- "%s: %s" % (response.json['errorcode'], response.json['errormessage']))
+ "%s: %s" % (response.json()['errorcode'],
+ response.json()['errormessage']))
return response
- def push_document(self, filename, filestream, send=None, speed=None, color=None):
+ def push_document(self, filename, filestream,
+ send=None, speed=None, color=None):
""" Upload a document to pingen.com and eventually ask to send it
:param str filename: name of the file to push
@@ -174,7 +168,7 @@ def push_document(self, filename, filestream, send=None, speed=None, color=None)
headers={'Content-Type': content_type},
data=multipart)
- rjson = response.json
+ rjson = response.json()
document_id = rjson['id']
if rjson.get('send'):
@@ -203,7 +197,7 @@ def send_document(self, document_id, speed=None, color=None):
params={'id': document_id},
data={'data': json.dumps(data)})
- return response.json['id']
+ return response.json()['id']
def post_infos(self, post_id):
""" Return the information of a post
@@ -213,10 +207,10 @@ def post_infos(self, post_id):
"""
response = self._send(
self.session.get,
- 'post/get',
+ 'document/get',
params={'id': post_id})
- return response.json['item']
+ return response.json()['item']
@staticmethod
def is_posted(post_infos):
@@ -224,4 +218,4 @@ def is_posted(post_infos):
:param dict post_infos: post infos returned by `post_infos`
"""
- return post_infos['status'] == 200
+ return post_infos['status'] == 1
diff --git a/pingen/pingen_data.xml b/pingen/pingen_data.xml
index 5227210c103..4b9cd38e13f 100644
--- a/pingen/pingen_data.xml
+++ b/pingen/pingen_data.xml
@@ -12,7 +12,7 @@
pingen.document
_push_and_send_to_pingen_cron
- (None,)
+ ()
@@ -25,7 +25,7 @@
pingen.document
_update_post_infos_cron
- (None,)
+ ()
diff --git a/pingen/pingen_document.py b/pingen/pingen_document.py
index ac59a028f67..eed4a3d402b 100644
--- a/pingen/pingen_document.py
+++ b/pingen/pingen_document.py
@@ -1,39 +1,23 @@
# -*- coding: utf-8 -*-
-##############################################################################
-#
-# Author: Guewen Baconnier
-# Copyright 2012 Camptocamp SA
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
+# Author: Guewen Baconnier
+# Copyright 2012-2017 Camptocamp SA
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import logging
from cStringIO import StringIO
-from contextlib import closing
-from openerp.osv import osv, orm, fields
-from openerp.tools.translate import _
-from openerp import pooler, tools
-from .pingen import APIError, ConnectionError, POST_SENDING_STATUS, \
- pingen_datetime_to_utc
+# from contextlib import closing
+from odoo import models, fields, _
+from odoo.exceptions import UserError
+import odoo
+from .pingen import APIError, ConnectionError, \
+ POST_SENDING_STATUS, pingen_datetime_to_utc
_logger = logging.getLogger(__name__)
-class pingen_document(orm.Model):
+class PingenDocument(models.Model):
""" A pingen document is the state of the synchronization of
an attachment with pingen.com
@@ -44,46 +28,38 @@ class pingen_document(orm.Model):
_name = 'pingen.document'
_inherits = {'ir.attachment': 'attachment_id'}
- _columns = {
- 'attachment_id': fields.many2one(
- 'ir.attachment', 'Document',
- required=True, readonly=True,
- ondelete='cascade'),
- 'state': fields.selection(
- [('pending', 'Pending'),
- ('pushed', 'Pushed'),
- ('sendcenter', 'In Sendcenter'),
- ('sent', 'Sent'),
- ('error', 'Connection Error'),
- ('pingen_error', 'Pingen Error'),
- ('canceled', 'Canceled')],
- string='State', readonly=True, required=True),
- 'push_date': fields.datetime('Push Date', readonly=True),
-
- # for `error` and `pingen_error` states when we push
- 'last_error_message': fields.text('Error Message', readonly=True),
-
- # pingen IDs
- 'pingen_id': fields.integer(
- 'Pingen ID', readonly=True,
- help="ID of the document in the Pingen Documents"),
- 'post_id': fields.integer(
- 'Pingen Post ID', readonly=True,
- help="ID of the document in the Pingen Sendcenter"),
-
- # sendcenter infos
- 'post_status': fields.char('Post Status', size=128, readonly=True),
- 'parsed_address': fields.text('Parsed Address', readonly=True),
- 'cost': fields.float('Cost', readonly=True),
- 'currency_id': fields.many2one('res.currency', 'Currency', readonly=True),
- 'country_id': fields.many2one('res.country', 'Country', readonly=True),
- 'send_date': fields.datetime('Date of sending', readonly=True),
- 'pages': fields.integer('Pages', readonly=True),
- }
-
- _defaults = {
- 'state': 'pending',
- }
+ attachment_id = fields.Many2one(
+ 'ir.attachment', 'Document',
+ required=True, readonly=True,
+ ondelete='cascade')
+ state = fields.Selection(
+ [('pending', 'Pending'),
+ ('pushed', 'Pushed'),
+ ('sendcenter', 'In Sendcenter'),
+ ('sent', 'Sent'),
+ ('error', 'Connection Error'),
+ ('pingen_error', 'Pingen Error'),
+ ('canceled', 'Canceled')],
+ string='State', readonly=True,
+ required=True, default='pending')
+ push_date = fields.Datetime('Push Date', readonly=True)
+ # for `error` and `pingen_error` states when we push
+ last_error_message = fields.Text('Error Message', readonly=True)
+ # pingen IDs
+ pingen_id = fields.Integer(
+ 'Pingen ID', readonly=True,
+ help="ID of the document in the Pingen Documents")
+ post_id = fields.Integer(
+ 'Pingen Post ID', readonly=True,
+ help="ID of the document in the Pingen Sendcenter")
+ # sendcenter infos
+ post_status = fields.Char('Post Status', size=128, readonly=True)
+ parsed_address = fields.Text('Parsed Address', readonly=True)
+ cost = fields.Float('Cost', readonly=True)
+ currency_id = fields.Many2one('res.currency', 'Currency', readonly=True)
+ country_id = fields.Many2one('res.country', 'Country', readonly=True)
+ send_date = fields.Datetime('Date of sending', readonly=True)
+ pages = fields.Integer('Pages', readonly=True)
_sql_constraints = [
('pingen_document_attachment_uniq',
@@ -91,43 +67,34 @@ class pingen_document(orm.Model):
'Only one Pingen document is allowed per attachment.'),
]
- def _get_pingen_session(self, cr, uid, context=None):
+ def _get_pingen_session(self):
""" Returns a pingen session for a user """
- company = self.pool.get('res.users').browse(
- cr, uid, uid, context=context).company_id
- return self.pool.get('res.company')._pingen(cr, uid, company, context=context)
+ return self.company_id._pingen()
- def _push_to_pingen(self, cr, uid, document, pingen=None, context=None):
+ def _push_to_pingen(self, pingen=None):
""" Push a document to pingen.com
-
:param Pingen pingen: optional pingen object to reuse session
"""
- attachment_obj = self.pool.get('ir.attachment')
-
- decoded_document = attachment_obj._decoded_content(
- cr, uid, document.attachment_id, context=context)
-
+ decoded_document = self.attachment_id._decoded_content()
if pingen is None:
- pingen = self._get_pingen_session(cr, uid, context=context)
+ pingen = self._get_pingen_session()
try:
doc_id, post_id, infos = pingen.push_document(
- document.datas_fname,
+ self.datas_fname,
StringIO(decoded_document),
- document.pingen_send,
- document.pingen_speed,
- document.pingen_color)
+ self.pingen_send,
+ self.pingen_speed,
+ self.pingen_color)
except ConnectionError:
_logger.exception(
'Connection Error when pushing Pingen Document %s to %s.' %
- (document.id, pingen.url))
+ (self.id, pingen.url))
raise
-
except APIError:
_logger.error(
'API Error when pushing Pingen Document %s to %s.' %
- (document.id, pingen.url))
+ (self.id, pingen.url))
raise
-
error = False
state = 'pushed'
if post_id:
@@ -135,291 +102,254 @@ def _push_to_pingen(self, cr, uid, document, pingen=None, context=None):
elif infos['requirement_failure']:
state = 'pingen_error'
error = _('The document does not meet the Pingen requirements.')
-
push_date = pingen_datetime_to_utc(infos['date'])
-
- document.write(
+ self.write(
{'last_error_message': error,
'state': state,
- 'push_date': push_date.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT),
+ 'push_date': fields.Datetime.to_string(push_date),
'pingen_id': doc_id,
- 'post_id': post_id},
- context=context)
- _logger.info('Pingen Document %s: pushed to %s' % (document.id, pingen.url))
+ 'post_id': post_id},)
+ _logger.info(
+ 'Pingen Document %s: pushed to %s' % (self.id, pingen.url))
- def push_to_pingen(self, cr, uid, ids, context=None):
+ def push_to_pingen(self):
""" Push a document to pingen.com
-
Convert errors to osv.except_osv to be handled by the client.
-
Wrapper method for multiple ids (when triggered from button for
instance) for public interface.
"""
- assert len(ids) == 1, "Only 1 id is allowed"
- with self._get_pingen_session(cr, uid, context=context) as session:
- for document in self.browse(cr, uid, ids, context=context):
- try:
- self._push_to_pingen(
- cr, uid, document, pingen=session, context=context)
- except ConnectionError as e:
- raise osv.except_osv(
- _('Pingen Connection Error'),
- _('Connection Error when asking for sending the document %s to Pingen') % document.name)
-
- except APIError as e:
- raise osv.except_osv(
- _('Pingen Error'),
- _('Error when asking Pingen to send the document %s: '
- '\n%s') % (document.name, e))
-
- except:
- _logger.exception(
- 'Unexcepted Error when updating the status of pingen.document %s: ' %
- document.id)
- raise osv.except_osv(
- _('Error'),
- _('Unexcepted Error when updating the status of Document %s') % document.name)
+ self.ensure_one()
+ for document in self:
+ try:
+ session = self._get_pingen_session()
+ document._push_to_pingen(pingen=session)
+ except ConnectionError as e:
+ raise UserError(
+ _('Connection Error when asking for ' +
+ 'sending the document %s to Pingen')
+ % document.name)
+ except APIError as e:
+ raise UserError(
+ _('Error when asking Pingen to send the document %s: '
+ '\n%s') % (document.name, e))
+ except Exception as e:
+ _logger.exception(
+ 'Unexcepted Error when updating ' +
+ 'the status of pingen.document %s: ' %
+ document.id)
+ raise UserError(
+ _('Unexcepted Error when updating the ' +
+ 'status of Document %s') % document.name)
return True
- def _push_and_send_to_pingen_cron(self, cr, uid, ids, context=None):
+ def _push_and_send_to_pingen_cron(self):
""" Push a document to pingen.com
-
Intended to be used in a cron.
-
Commit after each record
-
Instead of raising, store the error in the pingen.document
"""
- if not ids:
- ids = self.search(
- cr, uid,
- # do not retry pingen_error, they should be treated manually
- [('state', 'in', ['pending', 'pushed', 'error'])],
- limit=100,
- context=context)
-
- with closing(pooler.get_db(cr.dbname).cursor()) as loc_cr, \
- self._get_pingen_session(cr, uid, context=context) as session:
- for document in self.browse(loc_cr, uid, ids, context=context):
-
- if document.state == 'error':
- self._resolve_error(loc_cr, uid, document, context=context)
- document.refresh()
-
- try:
- if document.state == 'pending':
- self._push_to_pingen(
- loc_cr, uid, document, pingen=session, context=context)
-
- elif document.state == 'pushed':
- self._ask_pingen_send(
- loc_cr, uid, document, pingen=session, context=context)
- except ConnectionError as e:
- document.write({'last_error_message': e,
- 'state': 'error'},
- context=context)
- except APIError as e:
- document.write({'last_error_message': e,
- 'state': 'pingen_error'},
- context=context)
- except:
- _logger.error('Unexcepted error in pingen cron')
- loc_cr.rollback()
- raise
-
- else:
- loc_cr.commit()
-
+ with odoo.api.Environment.manage():
+ with odoo.registry(self.env.cr.dbname).cursor() as new_cr:
+ new_env = odoo.api.Environment(
+ new_cr, self.env.uid, self.env.context)
+ # Instead of raising, store the error in the pingen.document
+ self = self.with_env(new_env)
+ not_sent_docs = self.search([('state', '!=', 'sent')])
+ for document in not_sent_docs:
+ session = document._get_pingen_session()
+ if document.state == 'error':
+ document._resolve_error()
+ document.refresh()
+ try:
+ if document.state == 'pending':
+ document._push_to_pingen(pingen=session)
+ elif document.state == 'pushed':
+ document._ask_pingen_send(pingen=session)
+ except ConnectionError as e:
+ document.write({'last_error_message': e,
+ 'state': 'error'})
+ except APIError as e:
+ document.write({'last_error_message': e,
+ 'state': 'pingen_error'})
+ except BaseException as e:
+ _logger.error('Unexcepted error in pingen cron')
return True
- def _resolve_error(self, cr, uid, document, context=None):
+ def _resolve_error(self):
""" A document as resolved, put in the correct state """
- if document.post_id:
+ if self.post_id:
state = 'sendcenter'
- elif document.pingen_id:
+ elif self.pingen_id:
state = 'pushed'
else:
state = 'pending'
- document.write({'state': state}, context=context)
+ self.write({'state': state})
- def resolve_error(self, cr, uid, ids, context=None):
+ def resolve_error(self):
""" A document as resolved, put in the correct state """
- for document in self.browse(cr, uid, ids, context=context):
- self._resolve_error(cr, uid, document, context=context)
+ for document in self:
+ document._resolve_error()
return True
- def _ask_pingen_send(self, cr, uid, document, pingen, context=None):
+ def _ask_pingen_send(self, pingen):
""" For a document already pushed to pingen, ask to send it.
-
:param Pingen pingen: pingen object to reuse
"""
# sending has been explicitely asked so we change the option
# for consistency
- if not document.pingen_send:
- document.write({'pingen_send': True}, context=context)
+ if not self.pingen_send:
+ self.write({'pingen_send': True})
try:
post_id = pingen.send_document(
- document.pingen_id,
- document.pingen_speed,
- document.pingen_color)
+ self.pingen_id,
+ self.pingen_speed,
+ self.pingen_color)
except ConnectionError:
- _logger.exception('Connection Error when asking for sending Pingen Document %s to %s.' %
- (document.id, pingen.url))
+ _logger.exception(
+ 'Connection Error when asking for sending ' +
+ 'Pingen Document %s to %s.' %
+ (self.id, pingen.url))
raise
except APIError:
- _logger.exception('API Error when asking for sending Pingen Document %s to %s.' %
- (document.id, pingen.url))
+ _logger.exception(
+ 'API Error when asking for sending ' +
+ 'Pingen Document %s to %s.' %
+ (self.id, pingen.url))
raise
-
- document.write(
+ self.write(
{'last_error_message': False,
'state': 'sendcenter',
- 'post_id': post_id},
- context=context)
- _logger.info('Pingen Document %s: asked for sending to %s' % (document.id, pingen.url))
-
+ 'post_id': post_id})
+ _logger.info(
+ 'Pingen Document %s: asked for' +
+ 'sending to %s' % (self.id, pingen.url))
return True
- def ask_pingen_send(self, cr, uid, ids, context=None):
+ def ask_pingen_send(self):
""" For a document already pushed to pingen, ask to send it.
-
Wrapper method for multiple ids (when triggered from button for
instance) for public interface.
"""
- assert len(ids) == 1, "Only 1 id is allowed"
- with self._get_pingen_session(cr, uid, context=context) as session:
- for document in self.browse(cr, uid, ids, context=context):
- try:
- self._ask_pingen_send(cr, uid, document, pingen=session, context=context)
- except ConnectionError as e:
- raise osv.except_osv(
- _('Pingen Connection Error'),
- _('Connection Error when asking for '
- 'sending the document %s to Pingen') % document.name)
-
- except APIError as e:
- raise osv.except_osv(
- _('Pingen Error'),
- _('Error when asking Pingen to send the document %s: '
- '\n%s') % (document.name, e))
-
- except:
- _logger.exception(
- 'Unexcepted Error when updating the status of pingen.document %s: ' %
- document.id)
- raise osv.except_osv(
- _('Error'),
- _('Unexcepted Error when updating the status of Document %s') % document.name)
+ self.ensure_one()
+ for document in self:
+ try:
+ session = document._get_pingen_session()
+ document._ask_pingen_send(pingen=session)
+ except ConnectionError as e:
+ raise UserError(
+ _('Connection Error when asking for '
+ 'sending the document %s to Pingen') % document.name)
+
+ except APIError as e:
+ raise UserError(
+ _('Error when asking Pingen to send the document %s: '
+ '\n%s') % (document.name, e))
+
+ except BaseException as e:
+ _logger.exception(
+ 'Unexcepted Error when updating the ' +
+ 'status of pingen.document %s: ' %
+ document.id)
+ raise UserError(
+ _('Unexcepted Error when updating the ' +
+ 'status of Document %s') % document.name)
return True
- def _update_post_infos(self, cr, uid, document, pingen, context=None):
- """ Update the informations from pingen of a document in the Sendcenter
-
+ def _update_post_infos(self, pingen):
+ """ Update the informations from
+ pingen of a document in the Sendcenter
:param Pingen pingen: pingen object to reuse
"""
- if not document.post_id:
+ if not self.pingen_id:
return
-
try:
- post_infos = pingen.post_infos(document.post_id)
+ post_infos = pingen.post_infos(self.pingen_id)
except ConnectionError:
_logger.exception(
'Connection Error when asking for '
'sending Pingen Document %s to %s.' %
- (document.id, pingen.url))
+ (self.id, pingen.url))
raise
except APIError:
_logger.exception(
- 'API Error when asking for sending Pingen Document %s to %s.' %
- (document.id, pingen.url))
+ 'API Error when asking for sending ' +
+ 'Pingen Document %s to %s.' %
+ (self.id, pingen.url))
raise
-
- currency_ids = self.pool.get('res.currency').search(
- cr, uid, [('name', '=', post_infos['currency'])], context=context)
- country_ids = self.pool.get('res.country').search(
- cr, uid, [('code', '=', post_infos['country'])], context=context)
-
+ # currency_ids = self.env['res.currency'].search(
+ # [('name', '=', post_infos['currency'])])
+ country = self.env['res.country'].search(
+ [('code', '=', post_infos['country'])])
send_date = pingen_datetime_to_utc(post_infos['date'])
-
vals = {
- 'post_status': POST_SENDING_STATUS[post_infos['status']],
- 'cost': post_infos['cost'],
- 'currency_id': currency_ids[0] if currency_ids else False,
+ # 'post_status': POST_SENDING_STATUS[post_infos['status']],
+ # 'cost': post_infos['cost'],
+ # 'currency_id': currency_ids[0] if currency_ids else False,
'parsed_address': post_infos['address'],
- 'country_id': country_ids[0] if country_ids else False,
- 'send_date': send_date.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT),
- 'pages': post_infos['pages'],
+ 'country_id': country.id if country else False,
+ 'send_date': fields.Datetime.to_string(send_date),
+ # 'pages': post_infos['pages'],
'last_error_message': False,
}
if pingen.is_posted(post_infos):
vals['state'] = 'sent'
+ self.write(vals)
+ _logger.info('Pingen Document %s: status updated' % self.id)
- document.write(vals, context=context)
- _logger.info('Pingen Document %s: status updated' % document.id)
-
- def _update_post_infos_cron(self, cr, uid, ids, context=None):
- """ Update the informations from pingen of a document in the Sendcenter
-
+ def _update_post_infos_cron(self):
+ """ Update the informations from pingen of a
+ document in the Sendcenter
Intended to be used in a cron.
-
Commit after each record
-
- Do not raise errors, only skip the update of the record.
- """
- if not ids:
- ids = self.search(
- cr, uid,
- [('state', '=', 'sendcenter')],
- context=context)
-
- with closing(pooler.get_db(cr.dbname).cursor()) as loc_cr, \
- self._get_pingen_session(cr, uid, context=context) as session:
- for document in self.browse(loc_cr, uid, ids, context=context):
- try:
- self._update_post_infos(
- loc_cr, uid, document, pingen=session, context=context)
- except (ConnectionError, APIError):
- # will be retried the next time
- # In any case, the error has been logged by _update_post_infos
- loc_cr.rollback()
- except:
- _logger.error('Unexcepted error in pingen cron')
- loc_cr.rollback()
- raise
- else:
- loc_cr.commit()
+ Do not raise errors, only skip the update of the record."""
+ with odoo.api.Environment.manage():
+ with odoo.registry(self.env.cr.dbname).cursor() as new_cr:
+ new_env = odoo.api.Environment(
+ new_cr, self.env.uid, self.env.context)
+ # Instead of raising, store the error in the pingen.document
+ self = self.with_env(new_env)
+ pushed_docs = self.search([('state', '!=', 'sent')])
+ for document in pushed_docs:
+ session = document._get_pingen_session()
+ try:
+ document._update_post_infos(pingen=session)
+ except (ConnectionError, APIError):
+ # will be retried the next time
+ # In any case, the error has been
+ # logged by _update_post_infos
+ pass
+ except BaseException as e:
+ _logger.error('Unexcepted error in pingen cron: %', e)
+ raise
return True
- def update_post_infos(self, cr, uid, ids, context=None):
+ def update_post_infos(self):
""" Update the informations from pingen of a document in the Sendcenter
-
Wrapper method for multiple ids (when triggered from button for
instance) for public interface.
"""
- assert len(ids) == 1, "Only 1 id is allowed"
- with self._get_pingen_session(cr, uid, context=context) as session:
- for document in self.browse(cr, uid, ids, context=context):
- try:
- self._update_post_infos(
- cr, uid, document, pingen=session, context=context)
- except ConnectionError as e:
- raise osv.except_osv(
- _('Pingen Connection Error'),
- _('Connection Error when updating the status of Document %s'
- ' from Pingen') % document.name)
-
- except APIError as e:
- raise osv.except_osv(
- _('Pingen Error'),
- _('Error when updating the status of Document %s from Pingen: '
- '\n%s') % (document.name, e))
-
- except:
- _logger.exception(
- 'Unexcepted Error when updating the status of pingen.document %s: ' %
- document.id)
- raise osv.except_osv(
- _('Error'),
- _('Unexcepted Error when updating the status of Document %s') % document.name)
+ self.ensure_one()
+ for document in self:
+ try:
+ session = document._get_pingen_session()
+ document._update_post_infos(pingen=session)
+ except ConnectionError as e:
+ raise UserError(
+ _('Connection Error when updating ' +
+ 'the status of Document %s'
+ ' from Pingen') % document.name)
+ except APIError as e:
+ raise UserError(
+ _('Error when updating the status ' +
+ 'of Document %s from Pingen: '
+ '\n%s') % (document.name, e))
+ except BaseException as e:
+ _logger.exception(
+ 'Unexcepted Error when updating ' +
+ 'the status of pingen.document %s: ' %
+ document.id)
+ raise UserError(
+ _('Unexcepted Error when updating ' +
+ 'the status of Document %s') % document.name)
return True
diff --git a/pingen/pingen_document_view.xml b/pingen/pingen_document_view.xml
index 1686167654f..253e837d164 100644
--- a/pingen/pingen_document_view.xml
+++ b/pingen/pingen_document_view.xml
@@ -155,7 +155,7 @@
-
+
diff --git a/pingen/res_company.py b/pingen/res_company.py
index ad0c2c88771..1da79a12293 100644
--- a/pingen/res_company.py
+++ b/pingen/res_company.py
@@ -1,42 +1,20 @@
# -*- coding: utf-8 -*-
-##############################################################################
-#
-# Author: Guewen Baconnier
-# Copyright 2012 Camptocamp SA
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
+# Author: Guewen Baconnier
+# Copyright 2012-2017 Camptocamp SA
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
-from openerp.osv import orm, fields
-from openerp.osv.orm import browse_record
+from odoo import models, fields
from .pingen import Pingen
-class res_company(orm.Model):
+class ResCompany(models.Model):
_inherit = 'res.company'
- _columns = {
- 'pingen_token': fields.char('Pingen Token', size=32),
- 'pingen_staging': fields.boolean('Pingen Staging')
- }
+ pingen_token = fields.Char('Pingen Token', size=32)
+ pingen_staging = fields.Boolean('Pingen Staging')
- def _pingen(self, cr, uid, company, context=None):
+ def _pingen(self):
""" Return a Pingen instance to work on """
- assert isinstance(company, (int, long, browse_record)), \
- "one id or browse_record expected"
- if not isinstance(company, browse_record):
- company = self.browse(cr, uid, company, context=context)
- return Pingen(company.pingen_token, staging=company.pingen_staging)
+ self.ensure_one()
+ return Pingen(self.pingen_token, staging=self.pingen_staging)
diff --git a/pingen/res_company_view.xml b/pingen/res_company_view.xml
index d915f74e126..763556706df 100644
--- a/pingen/res_company_view.xml
+++ b/pingen/res_company_view.xml
@@ -1,20 +1,19 @@
-
-
res.company.form.inherit
res.company
form
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-