diff --git a/pingen/__init__.py b/pingen/__init__.py
new file mode 100644
index 0000000..e5f5183
--- /dev/null
+++ b/pingen/__init__.py
@@ -0,0 +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 .
+#
+##############################################################################
+
+import ir_attachment
+import pingen_task
diff --git a/pingen/__openerp__.py b/pingen/__openerp__.py
new file mode 100644
index 0000000..3b48d34
--- /dev/null
+++ b/pingen/__openerp__.py
@@ -0,0 +1,57 @@
+# -*- 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 .
+#
+##############################################################################
+
+{'name' : 'pingen.com integration',
+ 'version' : '1.0',
+ 'author' : 'Camptocamp',
+ 'maintainer': 'Camptocamp',
+ 'license': 'AGPL-3',
+ 'category': 'category',
+ 'complexity': 'easy',
+ 'depends' : [],
+ 'description': """
+Integration with pingen.com
+===========================
+
+What is pingen.com
+------------------
+
+Pingen.com is a paid online service.
+It send uploaded documents by letter post.
+
+Integration
+-----------
+
+One can decide, per document / attachment, if it should be pushed
+to pingen.com. The documents are pushed asynchronously.
+
+ """,
+ 'website': 'http://www.camptocamp.com',
+ 'init_xml': [],
+ 'update_xml': [
+ 'ir_attachment_view.xml',
+ 'pingen_task_view.xml',
+ ],
+ 'demo_xml': [],
+ 'tests': [],
+ 'installable': True,
+ 'auto_install': False,
+}
diff --git a/pingen/ir_attachment.py b/pingen/ir_attachment.py
new file mode 100644
index 0000000..82558d2
--- /dev/null
+++ b/pingen/ir_attachment.py
@@ -0,0 +1,90 @@
+# -*- 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 .
+#
+##############################################################################
+
+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_task_ids': fields.one2many(
+ 'pingen.task', 'attachment_id',
+ string="Pingen Task", readonly=True),
+ }
+
+ def _prepare_pingen_task_vals(self, cr, uid, attachment, context=None):
+ return {'attachment_id': attachment.id,
+ 'config': 'created from attachment'}
+
+ def _handle_pingen_task(self, cr, uid, attachment_id, context=None):
+ """ Reponsible of the related ``pingen.task`` when the ``send_to_pingen``
+ field is modified.
+
+ Only one pingen task can be created per attachment.
+
+ When ``send_to_pingen`` is activated:
+ * Create a ``pingen.task`` if it does not already exist
+ * Put the related ``pingen.task`` to ``pending`` if it already exist
+ When it is deactivated:
+ * Do nothing if no related ``pingen.task`` exists
+ * Or cancel it
+ * If it has already been pushed to pingen.com, raises
+ an `osv.except_osv` exception
+ """
+ pingen_task_obj = self.pool.get('pingen.task')
+ attachment = self.browse(cr, uid, attachment_id, context=context)
+ task = attachment.pingen_task_ids[0] if attachment.pingen_task_ids else None
+ if attachment.send_to_pingen:
+ if task:
+ task.write({'state': 'pending'}, context=context)
+ else:
+ pingen_task_obj.create(
+ cr, uid,
+ self._prepare_pingen_task_vals(
+ cr, uid, attachment, context=context),
+ context=context)
+ else:
+ if task:
+ if task.state == 'pushed':
+ raise osv.except_osv(
+ _('Error'),
+ _('The attachment %s is already pushed to pingen.com.') % \
+ attachment.name)
+ task.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_task(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_task(cr, uid, attachment_id, context=context)
+ return res
+
diff --git a/pingen/ir_attachment_view.xml b/pingen/ir_attachment_view.xml
new file mode 100644
index 0000000..e0a27e0
--- /dev/null
+++ b/pingen/ir_attachment_view.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+ ir.attachment.pingen.view
+ ir.attachment
+ form
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pingen/pingen_task.py b/pingen/pingen_task.py
new file mode 100644
index 0000000..af6bd74
--- /dev/null
+++ b/pingen/pingen_task.py
@@ -0,0 +1,57 @@
+# -*- 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 .
+#
+##############################################################################
+
+from openerp.osv import orm, fields
+
+
+class pingen_task(orm.Model):
+ """ A pingen task 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.task'
+ _inherits = {'ir.attachment': 'attachment_id'}
+
+ _columns = {
+ 'attachment_id': fields.many2one(
+ 'ir.attachment', 'Document', required=True, ondelete='cascade'),
+ 'state': fields.selection(
+ [('pending', 'Pending'),
+ ('pushed', 'Pushed'),
+ ('error', 'Error'),
+ ('canceled', 'Canceled')],
+ string='State', readonly=True, required=True),
+ 'config': fields.text('Configuration (tmp)'),
+ }
+
+ _defaults = {
+ 'state': 'pending',
+ }
+
+ _sql_constraints = [
+ ('pingen_task_attachment_uniq',
+ 'unique (attachment_id)',
+ 'Only one Pingen task is allowed per attachment.'),
+ ]
+
diff --git a/pingen/pingen_task_view.xml b/pingen/pingen_task_view.xml
new file mode 100644
index 0000000..83540ca
--- /dev/null
+++ b/pingen/pingen_task_view.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+ pingen.task.tree
+ pingen.task
+ tree
+
+
+
+
+
+
+
+
+
+ pingen.task.form
+ pingen.task
+ form
+
+
+
+
+
+
+ pingen.task.search
+ pingen.task
+ search
+
+
+
+
+
+
+
+
+
+
+
+
+ Pingen Tasks
+ ir.actions.act_window
+ pingen.task
+ form
+ tree,form
+
+
+
+
+
+
+