[ADD] Integration of the pingen.com online service in OpenERP

This commit is contained in:
Guewen Baconnier
2012-12-07 16:20:35 +01:00
committed by Stefan Rijnhart
21 changed files with 2214 additions and 0 deletions

26
pingen/__init__.py Normal file
View File

@@ -0,0 +1,26 @@
# -*- 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
import ir_attachment
import pingen
import pingen_document
import res_company

119
pingen/__openerp__.py Normal file
View File

@@ -0,0 +1,119 @@
# -*- 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'pingen.com integration',
'version': '1.0',
'author': 'Camptocamp',
'maintainer': 'Camptocamp',
'license': 'AGPL-3',
'category': 'Reporting',
'complexity': 'easy',
'depends': [],
'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 <http://docs.python-requests.org/>`_
* The PDF files sent to pingen.com have to respect some `formatting rules
<https://stage-app.pingen.com/resources/pingen_requirements_v1_en.pdf>`_.
* 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',
'pingen_document_view.xml',
'pingen_data.xml',
'res_company_view.xml',
'security/ir.model.access.csv',
],
'tests': [],
'installable': True,
'auto_install': False,
'application': True,
}

390
pingen/i18n/fr.po Normal file
View File

@@ -0,0 +1,390 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * pingen
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 6.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-11-26 10:54+0000\n"
"PO-Revision-Date: 2012-11-26 10:54+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: pingen
#: code:addons/pingen/pingen_document.py:177
#: code:addons/pingen/pingen_document.py:307
#: code:addons/pingen/pingen_document.py:414
#, python-format
msgid "Unexcepted Error when updating the status of Document %s"
msgstr "Erreur inattendue lors de la mise à jour du document %s"
#. module: pingen
#: field:pingen.document,push_date:0
msgid "Push Date"
msgstr "Date d'ajout"
#. module: pingen
#: view:pingen.document:0
msgid "Errors"
msgstr "Erreurs"
#. module: pingen
#: field:pingen.document,pingen_id:0
msgid "Pingen ID"
msgstr "ID Pingen"
#. module: pingen
#: constraint:res.company:0
msgid "Error! You can not create recursive companies."
msgstr "Error! You can not create recursive companies."
#. module: pingen
#: field:ir.attachment,pingen_send:0
msgid "Send"
msgstr "Envoyer"
#. module: pingen
#: code:addons/pingen/pingen_document.py:399
#, python-format
msgid "Connection Error when updating the status of Document %s from Pingen"
msgstr "Erreur de connexion lors de la mise à jour de l'état du document %s depuis Pingen"
#. module: pingen
#: field:pingen.document,state:0
msgid "State"
msgstr "État"
#. module: pingen
#: field:ir.attachment,pingen_color:0
msgid "Type of print"
msgstr "Type d'impression"
#. module: pingen
#: view:pingen.document:0
msgid "Attachment"
msgstr "Attachement"
#. module: pingen
#: code:addons/pingen/pingen_document.py:405
#, python-format
msgid "Error when updating the status of Document %s from Pingen: \n"
"%s"
msgstr "Erreur lors de la mise à jour de l'état du document %s depuis Pingen: \n"
"%s"
#. module: pingen
#: model:ir.actions.act_window,name:pingen.act_attachment_to_pingen_document
#: field:ir.attachment,pingen_document_ids:0
#: view:pingen.document:0
msgid "Pingen Document"
msgstr "Document Pingen"
#. module: pingen
#: field:pingen.document,attachment_id:0
msgid "Document"
msgstr "Document"
#. module: pingen
#: help:ir.attachment,pingen_send:0
msgid "Defines if a document is merely uploaded or also sent"
msgstr "Définit si un fichier est juste ajouté ou également envoyé"
#. module: pingen
#: view:pingen.document:0
#: selection:pingen.document,state:0
msgid "Pending"
msgstr "En attente"
#. module: pingen
#: selection:ir.attachment,pingen_speed:0
msgid "Economy"
msgstr "Économique"
#. module: pingen
#: view:pingen.document:0
msgid "Errors resolved"
msgstr "Erreurs résolues"
#. module: pingen
#: sql_constraint:pingen.document:0
msgid "Only one Pingen document is allowed per attachment."
msgstr "Uniquement un document Pingen est autorisé par attachement."
#. module: pingen
#: code:addons/pingen/pingen_document.py:168
#: code:addons/pingen/pingen_document.py:298
#, python-format
msgid "Error when asking Pingen to send the document %s: \n"
"%s"
msgstr "Erreurs lors de l'envoi du document par Pingen %s: \n"
"%s"
#. module: pingen
#: view:pingen.document:0
msgid "Update the letter's informations"
msgstr "Mettre à jour les informations de la lettre"
#. module: pingen
#: view:pingen.document:0
#: selection:pingen.document,state:0
msgid "Canceled"
msgstr "Annulé"
#. module: pingen
#: selection:pingen.document,state:0
msgid "Connection Error"
msgstr "Erreur de connexion"
#. module: pingen
#: model:ir.actions.act_window,name:pingen.action_pingen_document
#: model:ir.ui.menu,name:pingen.menu_pingen_document
msgid "Pingen Documents"
msgstr "Documents Pingen"
#. module: pingen
#: field:pingen.document,last_error_message:0
msgid "Error Message"
msgstr "Message d'erreur"
#. module: pingen
#: selection:ir.attachment,pingen_color:0
msgid "B/W"
msgstr "N/B"
#. module: pingen
#: field:res.company,pingen_staging:0
msgid "Pingen Staging"
msgstr "Staging Pingen"
#. module: pingen
#: model:ir.model,name:pingen.model_ir_attachment
msgid "ir.attachment"
msgstr "ir.attachment"
#. module: pingen
#: view:pingen.document:0
#: selection:pingen.document,state:0
msgid "In Sendcenter"
msgstr "Dans le Sendcenter"
#. module: pingen
#: view:pingen.document:0
#: selection:pingen.document,state:0
msgid "Sent"
msgstr "Envoyé"
#. module: pingen
#: field:pingen.document,post_status:0
msgid "Post Status"
msgstr "État de la lettre"
#. module: pingen
#: code:addons/pingen/pingen_document.py:163
#: code:addons/pingen/pingen_document.py:292
#, python-format
msgid "Connection Error when asking for sending the document %s to Pingen"
msgstr "Erreur de connexion avec Pingen lors de l'envoi de %s"
#. module: pingen
#: view:res.company:0
msgid "Configuration"
msgstr "Configuration"
#. module: pingen
#: view:pingen.document:0
msgid "Data"
msgstr "Données"
#. module: pingen
#: view:pingen.document:0
msgid "Options"
msgstr "Options"
#. module: pingen
#: field:res.company,pingen_token:0
msgid "Pingen Token"
msgstr "Token Pingen"
#. module: pingen
#: field:ir.attachment,send_to_pingen:0
msgid "Send to Pingen.com"
msgstr "Ajouter sur Pingen.com"
#. module: pingen
#: model:ir.model,name:pingen.model_pingen_document
msgid "pingen.document"
msgstr "pingen.document"
#. module: pingen
#: view:pingen.document:0
msgid "Dates"
msgstr "Dates"
#. module: pingen
#: view:pingen.document:0
msgid "Sendcenter"
msgstr "Sendcenter"
#. module: pingen
#: sql_constraint:res.company:0
msgid "The company name must be unique !"
msgstr "The company name must be unique !"
#. module: pingen
#: field:pingen.document,parsed_address:0
msgid "Parsed Address"
msgstr "Adresse analysée"
#. module: pingen
#: view:ir.attachment:0
#: view:pingen.document:0
#: view:res.company:0
msgid "Pingen.com"
msgstr "Pingen.com"
#. module: pingen
#: field:pingen.document,country_id:0
msgid "Country"
msgstr "Pays"
#. module: pingen
#: view:ir.attachment:0
msgid "Notes"
msgstr "Notes"
#. module: pingen
#: view:pingen.document:0
#: selection:pingen.document,state:0
msgid "Pushed"
msgstr "Ajouté"
#. module: pingen
#: model:ir.model,name:pingen.model_res_company
msgid "Companies"
msgstr "Compagnies"
#. module: pingen
#: code:addons/pingen/ir_attachment.py:90
#, python-format
msgid "The attachment %s is already pushed to pingen.com."
msgstr "L'attachement %s est déjà envoyé sur pingen.com."
#. module: pingen
#: view:pingen.document:0
msgid "Attached To"
msgstr "Attaché à "
#. module: pingen
#: code:addons/pingen/pingen_document.py:136
#, python-format
msgid "The document does not meet the Pingen requirements."
msgstr "Le document ne remplit pas les exigences de Pingen"
#. module: pingen
#: code:addons/pingen/ir_attachment.py:89
#: code:addons/pingen/pingen_document.py:176
#: code:addons/pingen/pingen_document.py:306
#: code:addons/pingen/pingen_document.py:413
#: view:pingen.document:0
#, python-format
msgid "Error"
msgstr "Erreur"
#. module: pingen
#: code:addons/pingen/pingen_document.py:162
#: code:addons/pingen/pingen_document.py:291
#: code:addons/pingen/pingen_document.py:398
#, python-format
msgid "Pingen Connection Error"
msgstr "Erreur de connexion avec Pingen"
#. module: pingen
#: field:pingen.document,send_date:0
msgid "Date of sending"
msgstr "Date d'envoi"
#. module: pingen
#: selection:ir.attachment,pingen_speed:0
msgid "Priority"
msgstr "Priorité"
#. module: pingen
#: selection:ir.attachment,pingen_color:0
msgid "Color"
msgstr "Color"
#. module: pingen
#: model:ir.model,name:pingen.model_pingen_task
msgid "pingen.task"
msgstr "pingen.task"
#. module: pingen
#: help:pingen.document,pingen_id:0
msgid "ID of the document in the Pingen Documents"
msgstr "ID du document sur Pingen"
#. module: pingen
#: code:addons/pingen/pingen_document.py:167
#: code:addons/pingen/pingen_document.py:297
#: code:addons/pingen/pingen_document.py:404
#: view:pingen.document:0
#: selection:pingen.document,state:0
#, python-format
msgid "Pingen Error"
msgstr "Erreur Pingen"
#. module: pingen
#: view:pingen.document:0
msgid "Actions"
msgstr "Actions"
#. module: pingen
#: field:pingen.document,pages:0
msgid "Pages"
msgstr "Pages"
#. module: pingen
#: field:pingen.document,currency_id:0
msgid "Currency"
msgstr "Devise"
#. module: pingen
#: field:pingen.document,cost:0
msgid "Cost"
msgstr "Coût"
#. module: pingen
#: view:pingen.document:0
msgid "Push to pingen.com"
msgstr "Ajouter sur pingen.com"
#. module: pingen
#: view:pingen.document:0
msgid "Ask pingen.com to send the document"
msgstr "Demander à pingen.com d'envoyer le document"
#. module: pingen
#: help:pingen.document,post_id:0
msgid "ID of the document in the Pingen Sendcenter"
msgstr "ID du document dans le Sendcenter Pingen"
#. module: pingen
#: field:ir.attachment,pingen_speed:0
msgid "Speed"
msgstr "Vitesse"
#. module: pingen
#: field:pingen.document,post_id:0
msgid "Pingen Post ID"
msgstr "ID de lettre Pingen"
#. module: pingen
#: help:ir.attachment,pingen_speed:0
msgid "Defines the sending speed if the document is automatically sent"
msgstr "Définit la vitesse d'envoi si le document est automatiquement envoyé"

388
pingen/i18n/pingen.pot Normal file
View File

@@ -0,0 +1,388 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * pingen
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 6.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-11-26 10:55+0000\n"
"PO-Revision-Date: 2012-11-26 10:55+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: pingen
#: code:addons/pingen/pingen_document.py:177
#: code:addons/pingen/pingen_document.py:307
#: code:addons/pingen/pingen_document.py:414
#, python-format
msgid "Unexcepted Error when updating the status of Document %s"
msgstr ""
#. module: pingen
#: field:pingen.document,push_date:0
msgid "Push Date"
msgstr ""
#. module: pingen
#: view:pingen.document:0
msgid "Errors"
msgstr ""
#. module: pingen
#: field:pingen.document,pingen_id:0
msgid "Pingen ID"
msgstr ""
#. module: pingen
#: constraint:res.company:0
msgid "Error! You can not create recursive companies."
msgstr ""
#. module: pingen
#: field:ir.attachment,pingen_send:0
msgid "Send"
msgstr ""
#. module: pingen
#: code:addons/pingen/pingen_document.py:399
#, python-format
msgid "Connection Error when updating the status of Document %s from Pingen"
msgstr ""
#. module: pingen
#: field:pingen.document,state:0
msgid "State"
msgstr ""
#. module: pingen
#: field:ir.attachment,pingen_color:0
msgid "Type of print"
msgstr ""
#. module: pingen
#: view:pingen.document:0
msgid "Attachment"
msgstr ""
#. module: pingen
#: code:addons/pingen/pingen_document.py:405
#, python-format
msgid "Error when updating the status of Document %s from Pingen: \n"
"%s"
msgstr ""
#. module: pingen
#: model:ir.actions.act_window,name:pingen.act_attachment_to_pingen_document
#: field:ir.attachment,pingen_document_ids:0
#: view:pingen.document:0
msgid "Pingen Document"
msgstr ""
#. module: pingen
#: field:pingen.document,attachment_id:0
msgid "Document"
msgstr ""
#. module: pingen
#: help:ir.attachment,pingen_send:0
msgid "Defines if a document is merely uploaded or also sent"
msgstr ""
#. module: pingen
#: view:pingen.document:0
#: selection:pingen.document,state:0
msgid "Pending"
msgstr ""
#. module: pingen
#: selection:ir.attachment,pingen_speed:0
msgid "Economy"
msgstr ""
#. module: pingen
#: view:pingen.document:0
msgid "Errors resolved"
msgstr ""
#. module: pingen
#: sql_constraint:pingen.document:0
msgid "Only one Pingen document is allowed per attachment."
msgstr ""
#. module: pingen
#: code:addons/pingen/pingen_document.py:168
#: code:addons/pingen/pingen_document.py:298
#, python-format
msgid "Error when asking Pingen to send the document %s: \n"
"%s"
msgstr ""
#. module: pingen
#: view:pingen.document:0
msgid "Update the letter's informations"
msgstr ""
#. module: pingen
#: view:pingen.document:0
#: selection:pingen.document,state:0
msgid "Canceled"
msgstr ""
#. module: pingen
#: selection:pingen.document,state:0
msgid "Connection Error"
msgstr ""
#. module: pingen
#: model:ir.actions.act_window,name:pingen.action_pingen_document
#: model:ir.ui.menu,name:pingen.menu_pingen_document
msgid "Pingen Documents"
msgstr ""
#. module: pingen
#: field:pingen.document,last_error_message:0
msgid "Error Message"
msgstr ""
#. module: pingen
#: selection:ir.attachment,pingen_color:0
msgid "B/W"
msgstr ""
#. module: pingen
#: field:res.company,pingen_staging:0
msgid "Pingen Staging"
msgstr ""
#. module: pingen
#: model:ir.model,name:pingen.model_ir_attachment
msgid "ir.attachment"
msgstr ""
#. module: pingen
#: view:pingen.document:0
#: selection:pingen.document,state:0
msgid "In Sendcenter"
msgstr ""
#. module: pingen
#: view:pingen.document:0
#: selection:pingen.document,state:0
msgid "Sent"
msgstr ""
#. module: pingen
#: field:pingen.document,post_status:0
msgid "Post Status"
msgstr ""
#. module: pingen
#: code:addons/pingen/pingen_document.py:163
#: code:addons/pingen/pingen_document.py:292
#, python-format
msgid "Connection Error when asking for sending the document %s to Pingen"
msgstr ""
#. module: pingen
#: view:res.company:0
msgid "Configuration"
msgstr ""
#. module: pingen
#: view:pingen.document:0
msgid "Data"
msgstr ""
#. module: pingen
#: view:pingen.document:0
msgid "Options"
msgstr ""
#. module: pingen
#: field:res.company,pingen_token:0
msgid "Pingen Token"
msgstr ""
#. module: pingen
#: field:ir.attachment,send_to_pingen:0
msgid "Send to Pingen.com"
msgstr ""
#. module: pingen
#: model:ir.model,name:pingen.model_pingen_document
msgid "pingen.document"
msgstr ""
#. module: pingen
#: view:pingen.document:0
msgid "Dates"
msgstr ""
#. module: pingen
#: view:pingen.document:0
msgid "Sendcenter"
msgstr ""
#. module: pingen
#: sql_constraint:res.company:0
msgid "The company name must be unique !"
msgstr ""
#. module: pingen
#: field:pingen.document,parsed_address:0
msgid "Parsed Address"
msgstr ""
#. module: pingen
#: view:ir.attachment:0
#: view:pingen.document:0
#: view:res.company:0
msgid "Pingen.com"
msgstr ""
#. module: pingen
#: field:pingen.document,country_id:0
msgid "Country"
msgstr ""
#. module: pingen
#: view:ir.attachment:0
msgid "Notes"
msgstr ""
#. module: pingen
#: view:pingen.document:0
#: selection:pingen.document,state:0
msgid "Pushed"
msgstr ""
#. module: pingen
#: model:ir.model,name:pingen.model_res_company
msgid "Companies"
msgstr ""
#. module: pingen
#: code:addons/pingen/ir_attachment.py:90
#, python-format
msgid "The attachment %s is already pushed to pingen.com."
msgstr ""
#. module: pingen
#: view:pingen.document:0
msgid "Attached To"
msgstr ""
#. module: pingen
#: code:addons/pingen/pingen_document.py:136
#, python-format
msgid "The document does not meet the Pingen requirements."
msgstr ""
#. module: pingen
#: code:addons/pingen/ir_attachment.py:89
#: code:addons/pingen/pingen_document.py:176
#: code:addons/pingen/pingen_document.py:306
#: code:addons/pingen/pingen_document.py:413
#: view:pingen.document:0
#, python-format
msgid "Error"
msgstr ""
#. module: pingen
#: code:addons/pingen/pingen_document.py:162
#: code:addons/pingen/pingen_document.py:291
#: code:addons/pingen/pingen_document.py:398
#, python-format
msgid "Pingen Connection Error"
msgstr ""
#. module: pingen
#: field:pingen.document,send_date:0
msgid "Date of sending"
msgstr ""
#. module: pingen
#: selection:ir.attachment,pingen_speed:0
msgid "Priority"
msgstr ""
#. module: pingen
#: selection:ir.attachment,pingen_color:0
msgid "Color"
msgstr ""
#. module: pingen
#: model:ir.model,name:pingen.model_pingen_task
msgid "pingen.task"
msgstr ""
#. module: pingen
#: help:pingen.document,pingen_id:0
msgid "ID of the document in the Pingen Documents"
msgstr ""
#. module: pingen
#: code:addons/pingen/pingen_document.py:167
#: code:addons/pingen/pingen_document.py:297
#: code:addons/pingen/pingen_document.py:404
#: view:pingen.document:0
#: selection:pingen.document,state:0
#, python-format
msgid "Pingen Error"
msgstr ""
#. module: pingen
#: view:pingen.document:0
msgid "Actions"
msgstr ""
#. module: pingen
#: field:pingen.document,pages:0
msgid "Pages"
msgstr ""
#. module: pingen
#: field:pingen.document,currency_id:0
msgid "Currency"
msgstr ""
#. module: pingen
#: field:pingen.document,cost:0
msgid "Cost"
msgstr ""
#. module: pingen
#: view:pingen.document:0
msgid "Push to pingen.com"
msgstr ""
#. module: pingen
#: view:pingen.document:0
msgid "Ask pingen.com to send the document"
msgstr ""
#. module: pingen
#: help:pingen.document,post_id:0
msgid "ID of the document in the Pingen Sendcenter"
msgstr ""
#. module: pingen
#: field:ir.attachment,pingen_speed:0
msgid "Speed"
msgstr ""
#. module: pingen
#: field:pingen.document,post_id:0
msgid "Pingen Post ID"
msgstr ""
#. module: pingen
#: help:ir.attachment,pingen_speed:0
msgid "Defines the sending speed if the document is automatically sent"
msgstr ""

124
pingen/ir_attachment.py Normal file
View File

@@ -0,0 +1,124 @@
# -*- 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
import requests
import base64
from openerp.osv import osv, orm, fields
from openerp.tools.translate import _
class ir_attachment(orm.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'),
}
_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,
'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``
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
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:
if document:
document.write({'state': 'pending'}, context=context)
else:
pingen_document_obj.create(
cr, uid,
self._prepare_pingen_document_vals(
cr, uid, attachment, context=context),
context=context)
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)
return
def create(self, cr, uid, vals, context=None):
attachment_id = super(ir_attachment, self).create(cr, uid, vals, context=context)
if 'send_to_pingen' in vals:
self._handle_pingen_document(cr, uid, attachment_id, context=context)
return attachment_id
def write(self, cr, uid, ids, vals, context=None):
res = super(ir_attachment, self).write(cr, uid, ids, vals, context=context)
if 'send_to_pingen' in vals:
for attachment_id in ids:
self._handle_pingen_document(cr, uid, attachment_id, context=context)
return res
def _decoded_content(self, cr, uid, attachment, context=None):
""" 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 response.ok:
decoded_document = requests.content
else:
raise Exception(
'The type of attachment %s is not handled' % attachment.type)
return decoded_document

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
<record id="view_attachment_form" model="ir.ui.view">
<field name="name">ir.attachment.pingen.view</field>
<field name="model">ir.attachment</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_attachment_form"/>
<field name="arch" type="xml">
<page string="Notes" position="before">
<page string="Pingen.com">
<field name="send_to_pingen"/>
<field name="pingen_send" attrs="{'required': [('send_to_pingen', '=', True)]}"/>
<field name="pingen_speed" attrs="{'required': [('pingen_send', '=', True)]}"/>
<field name="pingen_color" />
</page>
</page>
</field>
</record>
<act_window
context="{'search_default_attachment_id': [active_id], 'default_attachment_id': active_id}"
id="act_attachment_to_pingen_document"
name="Pingen Document"
groups=""
res_model="pingen.document"
src_model="ir.attachment"/>
</data>
</openerp>

209
pingen/pingen.py Normal file
View File

@@ -0,0 +1,209 @@
# -*- 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
import requests
import logging
import urlparse
import json
from requests.packages.urllib3.filepost import encode_multipart_formdata
_logger = logging.getLogger(__name__)
POST_SENDING_STATUS = {
100: 'Ready/Pending',
101: 'Processing',
102: 'Waiting for confirmation',
200: 'Sent',
300: 'Some error occured and object wasn\'t sent',
400: 'Sending cancelled',
}
class PingenException(RuntimeError):
"""There was an ambiguous exception that occurred while handling your
request."""
class ConnectionError(PingenException):
"""An Error occured with the pingen API"""
class APIError(PingenException):
"""An Error occured with the pingen API"""
class Pingen(object):
""" Interface to the pingen.com API """
def __init__(self, token, staging=True):
self._token = token
self.staging = staging
self._session = None
super(Pingen, self).__init__()
@property
def url(self):
if self.staging:
return 'https://stage-api.pingen.com'
return 'https://api.pingen.com'
@property
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)
return self._session
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
def close(self):
"""Dispose of any internal state. """
if self._session:
self._session.close()
def _send(self, method, endpoint, **kwargs):
""" Send a request to the pingen API using requests
Add necessary boilerplate to call pingen.com API
(authentication, configuration, ...)
:param boundmethod method: requests method to call
:param str endpoint: endpoint to call
:param kwargs: additional arguments forwarded to the requests method
"""
complete_url = urlparse.urljoin(self.url, endpoint)
response = method(complete_url, **kwargs)
if not response.ok:
raise ConnectionError(
"%s: %s" % (response.json['errorcode'],
response.json['errormessage']))
if response.json['error']:
raise APIError(
"%s: %s" % (response.json['errorcode'], response.json['errormessage']))
return response
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
:param StringIO filestream: file to push
:param boolean send: if True, the document will be sent by pingen.com
:param int/str speed: sending speed of the document if it is send
1 = Priority, 2 = Economy
:param int/str color: type of print, 0 = B/W, 1 = Color
:return: tuple with 3 items:
1. document_id on pingen.com
2. post_id on pingen.com if it has been sent or None
3. dict of the created item on pingen (details)
"""
data = {
'send': send,
'speed': speed,
'color': color,
}
# we cannot use the `files` param alongside
# with the `datas`param when data is a
# JSON-encoded data. We have to construct
# the entire body and send it to `data`
# https://github.com/kennethreitz/requests/issues/950
formdata = {
'file': (filename, filestream.read()),
'data': json.dumps(data),
}
multipart, content_type = encode_multipart_formdata(formdata)
response = self._send(
self.session.post,
'document/upload',
headers={'Content-Type': content_type},
data=multipart)
rjson = response.json
document_id = rjson['id']
if rjson.get('send'):
# confusing name but send_id is the posted id
posted_id = rjson['send'][0]['send_id']
item = rjson['item']
return document_id, posted_id, item
def send_document(self, document_id, speed=None, color=None):
""" Send a uploaded document to pingen.com
:param int document_id: id of the document to send
:param int/str speed: sending speed of the document if it is send
1 = Priority, 2 = Economy
:param int/str color: type of print, 0 = B/W, 1 = Color
:return: id of the post on pingen.com
"""
data = {
'speed': speed,
'color': color,
}
response = self._send(
self.session.post,
'document/send',
params={'id': document_id},
data={'data': json.dumps(data)})
return response.json['id']
def post_infos(self, post_id):
""" Return the information of a post
:param int post_id: id of the document to send
:return: dict of infos of the post
"""
response = self._send(
self.session.get,
'post/get',
params={'id': post_id})
return response.json['item']
@staticmethod
def is_posted(post_infos):
""" return True if the post has been sent
:param dict post_infos: post infos returned by `post_infos`
"""
return post_infos['status'] == 200

32
pingen/pingen_data.xml Normal file
View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record forcecreate="True" id="ir_cron_push_pingen" model="ir.cron">
<field name="name">Run Pingen Document Push</field>
<field eval="True" name="active"/>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">hours</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
<field name="model">pingen.document</field>
<field name="function">_push_and_send_to_pingen_cron</field>
<field name="args">(None,)</field>
</record>
<record forcecreate="True" id="ir_cron_update_pingen" model="ir.cron">
<field name="name">Run Pingen Document Update</field>
<field eval="True" name="active"/>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
<field name="model">pingen.document</field>
<field name="function">_update_post_infos_cron</field>
<field name="args">(None,)</field>
</record>
</data>
</openerp>

420
pingen/pingen_document.py Normal file
View File

@@ -0,0 +1,420 @@
# -*- 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
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
from .pingen import APIError, ConnectionError, POST_SENDING_STATUS
_logger = logging.getLogger(__name__)
class pingen_document(orm.Model):
""" A pingen document is the state of the synchronization of
an attachment with pingen.com
It stores the configuration and the current state of the synchronization.
It also serves as a queue of documents to push to pingen.com
"""
_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',
}
_sql_constraints = [
('pingen_document_attachment_uniq',
'unique (attachment_id)',
'Only one Pingen document is allowed per attachment.'),
]
def _get_pingen_session(self, cr, uid, context=None):
""" 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)
def _push_to_pingen(self, cr, uid, document, pingen=None, context=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)
if pingen is None:
pingen = self._get_pingen_session(cr, uid, context=context)
try:
doc_id, post_id, infos = pingen.push_document(
document.datas_fname,
StringIO(decoded_document),
document.pingen_send,
document.pingen_speed,
document.pingen_color)
except ConnectionError as e:
_logger.exception(
'Connection Error when pushing Pingen Document %s to %s.' %
(document.id, pingen.url))
raise
except APIError as e:
_logger.error(
'API Error when pushing Pingen Document %s to %s.' %
(document.id, pingen.url))
raise
error = False
state = 'pushed'
if post_id:
state = 'sendcenter'
elif infos['requirement_failure']:
state = 'pingen_error'
error = _('The document does not meet the Pingen requirements.')
document.write(
{'last_error_message': error,
'state': state,
'push_date': infos['date'],
'pingen_id': doc_id,
'post_id': post_id},
context=context)
_logger.info('Pingen Document %s: pushed to %s' % (document.id, pingen.url))
def push_to_pingen(self, cr, uid, ids, context=None):
""" 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)
return True
def _push_and_send_to_pingen_cron(self, cr, uid, ids, context=None):
""" 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()
return True
def _resolve_error(self, cr, uid, document, context=None):
""" A document as resolved, put in the correct state """
if document.post_id:
state = 'sendcenter'
elif document.pingen_id:
state = 'pushed'
else:
state = 'pending'
document.write({'state': state}, context=context)
def resolve_error(self, cr, uid, ids, context=None):
""" 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)
return True
def _ask_pingen_send(self, cr, uid, document, pingen, context=None):
""" 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)
try:
post_id = pingen.send_document(
document.pingen_id,
document.pingen_speed,
document.pingen_color)
except ConnectionError as e:
_logger.exception('Connection Error when asking for sending Pingen Document %s to %s.' %
(document.id, pingen.url))
raise
except APIError as e:
_logger.exception('API Error when asking for sending Pingen Document %s to %s.' %
(document.id, pingen.url))
raise
document.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))
return True
def ask_pingen_send(self, cr, uid, ids, context=None):
""" 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)
return True
def _update_post_infos(self, cr, uid, document, pingen, context=None):
""" Update the informations from pingen of a document in the Sendcenter
:param Pingen pingen: pingen object to reuse
"""
if not document.post_id:
return
try:
post_infos = pingen.post_infos(document.post_id)
except ConnectionError as e:
_logger.exception(
'Connection Error when asking for '
'sending Pingen Document %s to %s.' %
(document.id, pingen.url))
raise
except APIError as e:
_logger.exception(
'API Error when asking for sending Pingen Document %s to %s.' %
(document.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)
vals = {
'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': post_infos['date'],
'pages': post_infos['pages'],
'last_error_message': False,
}
if pingen.is_posted(post_infos):
vals['state'] = 'sent'
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
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()
return True
def update_post_infos(self, cr, uid, ids, context=None):
""" 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)
return True

View File

@@ -0,0 +1,161 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
<record id="view_pingen_document_tree" model="ir.ui.view">
<field name="name">pingen.document.tree</field>
<field name="model">pingen.document</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Pingen Document">
<field name="name"/>
<field name="datas_fname"/>
<field name="pingen_send"/>
<field name="pingen_speed"/>
<field name="pingen_color"/>
<field name="state"/>
</tree>
</field>
</record>
<record id="view_pingen_document_form" model="ir.ui.view">
<field name="name">pingen.document.form</field>
<field name="model">pingen.document</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Pingen Document">
<group colspan="4" col="6">
<field name="name" readonly="True"/>
<field name="type" readonly="True"/>
<field name="company_id" readonly="True" groups="base.group_multi_company" widget="selection"/>
</group>
<notebook colspan="4">
<page string="Pingen.com">
<separator string="Options" colspan="4"/>
<newline />
<group col="2" colspan="2">
<field name="pingen_send" attrs="{'readonly': [('state', 'in', ['sendcenter', 'sent'])]}"/>
<field name="pingen_speed" attrs="{'readonly': [('state', 'in', ['sendcenter', 'sent'])], 'required': [('pingen_send', '=', True)]}"/>
<field name="pingen_color" attrs="{'readonly': [('state', 'in', ['sendcenter', 'sent'])]}"/>
</group>
<separator string="Dates" colspan="4"/>
<newline />
<group col="2" colspan="2">
<field name="push_date"/>
</group>
<group colspan="4" attrs="{'invisible': [('last_error_message', '=', False)]}">
<separator string="Errors" colspan="4"/>
<newline />
<group col="2" colspan="2">
<field nolabel="1" name="last_error_message"/>
</group>
</group>
<group colspan="4" attrs="{'invisible': [('state', 'not in', ['sendcenter', 'sent'])]}">
<separator string="Sendcenter" colspan="4"/>
<newline />
<group col="4" colspan="2">
<field colspan="4" name="post_status"/>
<group col="3" colspan="2">
<field name="cost"/>
<field colspan="1" nolabel="1" name="currency_id"/>
</group>
<newline/>
<field name="parsed_address"/>
<field name="country_id"/>
<field name="send_date"/>
<field name="pages"/>
</group>
</group>
<separator string="Actions" colspan="4"/>
<newline />
<group col="2" colspan="2">
<button name="push_to_pingen" type="object"
states="pending"
string="Push to pingen.com" icon="terp-stage"/>
<button name="ask_pingen_send" type="object"
states="pushed"
string="Ask pingen.com to send the document" icon="gtk-print"/>
<button name="resolve_error" type="object"
states="error,pingen_error"
string="Errors resolved" icon="gtk-redo"/>
<button name="update_post_infos" type="object"
states="sendcenter"
string="Update the letter's informations" icon="gtk-refresh"/>
</group>
</page>
<page string="Attachment">
<group col="4" colspan="4">
<separator string="Data" colspan="4"/>
<newline />
<group col="2" colspan="4" attrs="{'invisible':[('type','=','url')]}">
<field name="datas" filename="datas_fname" readonly="True"/>
<field name="datas_fname" select="1" readonly="True"/>
</group>
<group col="2" colspan="4" attrs="{'invisible':[('type','=','binary')]}">
<field name="url" widget="url" readonly="True"/>
</group>
</group>
<group col="2" colspan="4">
<separator string="Attached To" colspan="2"/>
<field name="attachment_id"/>
</group>
</page>
</notebook>
<field name="state" widget="statusbar"
statusbar_visible="pending,pushed,sent"
statusbar_colors='{"error":"red","pingen_error":"red","canceled":"grey","pushed":"blue","sent":"green"}'/>
</form>
</field>
</record>
<record id="view_pingen_document_search" model="ir.ui.view">
<field name="name">pingen.document.search</field>
<field name="model">pingen.document</field>
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Pingen Document">
<filter icon="terp-project"
string="Pending"
domain="[('state','=','pending')]"/>
<filter icon="terp-stage"
string="Pushed"
domain="[('state','=','pushed')]"/>
<filter icon="gtk-print"
string="In Sendcenter"
domain="[('state','=','sendcenter')]"/>
<filter icon="kanban-apply"
string="Sent"
domain="[('state','=','sent')]"/>
<filter icon="kanban-stop"
string="Error"
domain="[('state','=','error')]"/>
<filter icon="STOCK_NO"
string="Pingen Error"
domain="[('state','=','pingen_error')]"/>
<filter icon="terp-dialog-close"
string="Canceled"
domain="[('state','=','canceled')]"/>
<separator orientation="vertical"/>
<field name="attachment_id" />
</search>
</field>
</record>
<record id="action_pingen_document" model="ir.actions.act_window">
<field name="name">Pingen Documents</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">pingen.document</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="search_view_id" ref="view_pingen_document_search"/>
</record>
<menuitem action="action_pingen_document" id="menu_pingen_document" parent="base.next_id_4"/>
</data>
</openerp>

42
pingen/res_company.py Normal file
View File

@@ -0,0 +1,42 @@
# -*- 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm, fields
from openerp.osv.orm import browse_record
from .pingen import Pingen
class res_company(orm.Model):
_inherit = 'res.company'
_columns = {
'pingen_token': fields.char('Pingen Token', size=32),
'pingen_staging': fields.boolean('Pingen Staging')
}
def _pingen(self, cr, uid, company, context=None):
""" 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_id, context=context)
return Pingen(company.pingen_token, staging=company.pingen_staging)

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
<record model="ir.ui.view" id="view_company_inherit_form">
<field name="name">res.company.form.inherit</field>
<field name="model">res.company</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_company_form"/>
<field name="arch" type="xml">
<page string="Configuration" position="inside">
<separator string="Pingen.com" colspan="4"/>
<field name="pingen_token" groups="base.group_system"/>
<field name="pingen_staging" groups="base.group_system"/>
</page>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,3 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_pingen_document_all","pingen_document all","model_pingen_document",,1,0,0,0
"access_pingen_document_group_user","pingen_document group_user","model_pingen_document","base.group_user",1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_pingen_document_all pingen_document all model_pingen_document 1 0 0 0
3 access_pingen_document_group_user pingen_document group_user model_pingen_document base.group_user 1 1 1 1

View File

@@ -0,0 +1,21 @@
# -*- 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 <http://www.gnu.org/licenses/>.
#
##############################################################################

View File

@@ -0,0 +1,41 @@
# -*- 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name' : 'pingen.com integration (document)',
'version' : '1.0',
'author' : 'Camptocamp',
'maintainer': 'Camptocamp',
'license': 'AGPL-3',
'category': 'Reporting',
'complexity': 'easy',
'depends' : ['document', 'pingen'],
'description': """
Glue module between the `pingen` and the `document` modules.
""",
'website': 'http://www.camptocamp.com',
'data': [
'pingen_document_view.xml',
],
'tests': [],
'installable': True,
'auto_install': True,
}

View File

@@ -0,0 +1,32 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * pingen_document
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 6.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-11-26 10:54+0000\n"
"PO-Revision-Date: 2012-11-26 10:54+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: pingen_document
#: view:ir.attachment:0
msgid "Notes"
msgstr "Notes"
#. module: pingen_document
#: model:ir.ui.menu,name:pingen_document.menu_pingen_document_document
msgid "Pingen Documents"
msgstr "Pingen Documents"
#. module: pingen_document
#: view:ir.attachment:0
msgid "Pingen.com"
msgstr "Pingen.com"

View File

@@ -0,0 +1,32 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * pingen_document
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 6.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-11-26 10:54+0000\n"
"PO-Revision-Date: 2012-11-26 10:54+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: pingen_document
#: view:ir.attachment:0
msgid "Notes"
msgstr "Notes"
#. module: pingen_document
#: model:ir.ui.menu,name:pingen_document.menu_pingen_document_document
msgid "Pingen Documents"
msgstr "Pingen Documents"
#. module: pingen_document
#: view:ir.attachment:0
msgid "Pingen.com"
msgstr "Pingen.com"

View File

@@ -0,0 +1,32 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * pingen_document
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 6.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-11-26 10:54+0000\n"
"PO-Revision-Date: 2012-11-26 10:54+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: pingen_document
#: view:ir.attachment:0
msgid "Notes"
msgstr "Notes"
#. module: pingen_document
#: model:ir.ui.menu,name:pingen_document.menu_pingen_document_document
msgid "Pingen Documents"
msgstr "Pingen Documents"
#. module: pingen_document
#: view:ir.attachment:0
msgid "Pingen.com"
msgstr "Pingen.com"

View File

@@ -0,0 +1,32 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * pingen_document
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 6.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-11-26 10:56+0000\n"
"PO-Revision-Date: 2012-11-26 10:56+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: pingen_document
#: view:ir.attachment:0
msgid "Notes"
msgstr "Notes"
#. module: pingen_document
#: model:ir.ui.menu,name:pingen_document.menu_pingen_document_document
msgid "Pingen Documents"
msgstr "Documents Pingen"
#. module: pingen_document
#: view:ir.attachment:0
msgid "Pingen.com"
msgstr "Pingen.com"

View File

@@ -0,0 +1,32 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * pingen_document
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 6.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-11-26 10:56+0000\n"
"PO-Revision-Date: 2012-11-26 10:56+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: pingen_document
#: view:ir.attachment:0
msgid "Notes"
msgstr ""
#. module: pingen_document
#: model:ir.ui.menu,name:pingen_document.menu_pingen_document_document
msgid "Pingen Documents"
msgstr ""
#. module: pingen_document
#: view:ir.attachment:0
msgid "Pingen.com"
msgstr ""

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
<record id="view_document_file_form" model="ir.ui.view">
<field name="name">ir.attachment.document.pingen.view</field>
<field name="model">ir.attachment</field>
<field name="type">form</field>
<field name="inherit_id" ref="document.view_document_file_form"/>
<field name="arch" type="xml">
<page string="Notes" position="before">
<page string="Pingen.com">
<field name="send_to_pingen"/>
<field name="pingen_send" attrs="{'required': [('send_to_pingen', '=', True)]}"/>
<field name="pingen_speed" attrs="{'required': [('pingen_send', '=', True)]}"/>
<field name="pingen_color" />
</page>
</page>
</field>
</record>
<menuitem action="pingen.action_pingen_document"
id="menu_pingen_document_document"
parent="document.menu_document_doc"/>
</data>
</openerp>