[REF] Rename to attachement_queue
parent
a35eb5ef07
commit
8d469681fd
|
@ -0,0 +1 @@
|
||||||
|
from . import models
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Copyright 2015 Florian DA COSTA @ Akretion
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'Attachment Queue',
|
||||||
|
'version': '12.0.1.0.0',
|
||||||
|
'author': 'Akretion,Odoo Community Association (OCA)',
|
||||||
|
'summary': "Base module that add the concept of queue for processing file",
|
||||||
|
'website': 'https://github.com/OCA/server-tools',
|
||||||
|
'maintainers': ['florian-dacosta', 'sebastienbeau'],
|
||||||
|
'license': 'AGPL-3',
|
||||||
|
'category': 'Generic Modules',
|
||||||
|
'depends': [
|
||||||
|
'base',
|
||||||
|
'mail',
|
||||||
|
],
|
||||||
|
'data': [
|
||||||
|
'views/attachment_queue_view.xml',
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
'data/cron.xml',
|
||||||
|
'data/ir_config_parameter.xml',
|
||||||
|
'data/mail_template.xml',
|
||||||
|
],
|
||||||
|
'demo': [
|
||||||
|
'demo/attachment_queue_demo.xml'
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<odoo noupdate="1">
|
||||||
|
|
||||||
|
<record model="ir.cron" id="cronjob_run_attachment_queue">
|
||||||
|
<field name='name'>Run Attachments Queue</field>
|
||||||
|
<field name='interval_number'>30</field>
|
||||||
|
<field name='interval_type'>minutes</field>
|
||||||
|
<field name="numbercall">-1</field>
|
||||||
|
<field name="active">False</field>
|
||||||
|
<field name="doall" eval="False" />
|
||||||
|
<field name="model_id" ref="model_attachment_queue"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">model.run_attachment_queue_scheduler()</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo noupdate="1">
|
||||||
|
<record id="attachment_queue_cron_batch_limit" model="ir.config_parameter">
|
||||||
|
<field name="key">attachment_queue_cron_batch_limit</field>
|
||||||
|
<field name="value">200</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo noupdate="1">
|
||||||
|
|
||||||
|
<record id="attachment_failure_notification" model="mail.template">
|
||||||
|
<field name="email_to">${object.failure_emails}</field>
|
||||||
|
<field name="name">Attachment Failure notification</field>
|
||||||
|
<field name="subject">The attachment ${object.name} has failed</field>
|
||||||
|
<field name="model_id" ref="attachment_queue.model_attachment_queue"/>
|
||||||
|
<field name="body_html"><![CDATA[
|
||||||
|
<p style="margin:0px 0px 10px 0px;font-size:13px;font-family:"Lucida Grande", Helvetica, Verdana, Arial, sans-serif;">Hello,<br><br></p>
|
||||||
|
<p style="margin:0px 0px 10px 0px;font-size:13px;font-family:"Lucida Grande", Helvetica, Verdana, Arial, sans-serif;">The attachment ${object.name} has failed with the following error message : <br>${object.state_message}<br></p><p style="margin:0px 0px 10px 0px;font-size:13px;font-family:"Lucida Grande", Helvetica, Verdana, Arial, sans-serif;"></p>
|
||||||
|
<p style="margin:0px 0px 10px 0px;font-size:13px;font-family:"Lucida Grande", Helvetica, Verdana, Arial, sans-serif;">Regards,<br></p>
|
||||||
|
]]></field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<odoo noupdate="1">
|
||||||
|
|
||||||
|
<record id="attachment_queue_demo" model="attachment.queue">
|
||||||
|
<field name="datas">bWlncmF0aW9uIHRlc3Q=</field>
|
||||||
|
<field name="datas_fname">attachment_queue_demo.doc</field>
|
||||||
|
<field name="name">attachment_queue_demo.doc</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
|
@ -0,0 +1 @@
|
||||||
|
from . import attachment_queue
|
|
@ -0,0 +1,106 @@
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from odoo import api, fields, models, registry
|
||||||
|
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AttachmentQueue(models.Model):
|
||||||
|
_name = 'attachment.queue'
|
||||||
|
_inherits = {'ir.attachment': 'attachment_id'}
|
||||||
|
_inherit = ['mail.thread']
|
||||||
|
|
||||||
|
attachment_id = fields.Many2one(
|
||||||
|
'ir.attachment', required=True, ondelete='cascade',
|
||||||
|
help="Link to ir.attachment model ")
|
||||||
|
file_type = fields.Selection(
|
||||||
|
selection=[],
|
||||||
|
help="The file type determines an import method to be used "
|
||||||
|
"to parse and transform data before their import in ERP or an export")
|
||||||
|
date_done = fields.Datetime()
|
||||||
|
state = fields.Selection([
|
||||||
|
('pending', 'Pending'),
|
||||||
|
('failed', 'Failed'),
|
||||||
|
('done', 'Done'),
|
||||||
|
], readonly=False, required=True, default='pending')
|
||||||
|
state_message = fields.Text()
|
||||||
|
failure_emails = fields.Char(
|
||||||
|
compute='_compute_failure_emails',
|
||||||
|
string="Failure Emails",
|
||||||
|
help="list of email (separated by comma) which should be notified in "
|
||||||
|
"case of failure")
|
||||||
|
|
||||||
|
def _compute_failure_emails(self):
|
||||||
|
for attach in self:
|
||||||
|
attach.failure_emails = attach._get_failure_emails()
|
||||||
|
|
||||||
|
def _get_failure_emails(self):
|
||||||
|
# to be overriden in submodules implementing the file_type
|
||||||
|
self.ensure_one()
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def run_attachment_queue_scheduler(self, domain=None):
|
||||||
|
if domain is None:
|
||||||
|
domain = [('state', '=', 'pending')]
|
||||||
|
batch_limit = self.env.ref(
|
||||||
|
'attachment_queue.attachment_queue_cron_batch_limit') \
|
||||||
|
.value
|
||||||
|
if batch_limit and batch_limit.isdigit():
|
||||||
|
limit = int(batch_limit)
|
||||||
|
else:
|
||||||
|
limit = 200
|
||||||
|
attachments = self.search(domain, limit=limit)
|
||||||
|
if attachments:
|
||||||
|
return attachments.run()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""
|
||||||
|
Run the process for each attachment queue
|
||||||
|
"""
|
||||||
|
failure_tmpl = self.env.ref(
|
||||||
|
'attachment_queue.attachment_failure_notification')
|
||||||
|
for attachment in self:
|
||||||
|
with api.Environment.manage():
|
||||||
|
with registry(self.env.cr.dbname).cursor() as new_cr:
|
||||||
|
new_env = api.Environment(
|
||||||
|
new_cr, self.env.uid, self.env.context)
|
||||||
|
attach = attachment.with_env(new_env)
|
||||||
|
try:
|
||||||
|
attach._run()
|
||||||
|
# pylint: disable=broad-except
|
||||||
|
except Exception as e:
|
||||||
|
attach.env.cr.rollback()
|
||||||
|
_logger.exception(str(e))
|
||||||
|
attach.write({
|
||||||
|
'state': 'failed',
|
||||||
|
'state_message': str(e),
|
||||||
|
})
|
||||||
|
emails = attach.failure_emails
|
||||||
|
if emails:
|
||||||
|
failure_tmpl.send_mail(attach.id)
|
||||||
|
attach.env.cr.commit()
|
||||||
|
else:
|
||||||
|
vals = {
|
||||||
|
'state': 'done',
|
||||||
|
'date_done': fields.Datetime.now(),
|
||||||
|
}
|
||||||
|
attach.write(vals)
|
||||||
|
attach.env.cr.commit()
|
||||||
|
return True
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def _run(self):
|
||||||
|
self.ensure_one()
|
||||||
|
_logger.info('Start to process attachment queue id %d', self.id)
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def set_done(self):
|
||||||
|
"""
|
||||||
|
Manually set to done
|
||||||
|
"""
|
||||||
|
message = "Manually set to done by %s" % self.env.user.name
|
||||||
|
self.write({'state_message': message, 'state': 'done'})
|
|
@ -0,0 +1,4 @@
|
||||||
|
* Valentin CHEMIERE <valentin.chemiere@akretion.com>
|
||||||
|
* Florian da Costa <florian.dacosta@akretion.com>
|
||||||
|
* Angel Moya <http://angelmoya.es>
|
||||||
|
* Dan Kiplangat <dan@sunflowerweb.nl>
|
|
@ -0,0 +1,5 @@
|
||||||
|
This module implement a queue for processing file.
|
||||||
|
File are stored in Odoo standard ir.attachment.
|
||||||
|
The attachments will be processed depending on their type.
|
||||||
|
|
||||||
|
An example of the use of this module, can be found in the module `attachment_synchronize`.
|
|
@ -0,0 +1,8 @@
|
||||||
|
Go the menu Settings > Technical > Database Structure > Attachments Queue
|
||||||
|
|
||||||
|
You can create / see standard attachments with additional fields
|
||||||
|
|
||||||
|
Configure the batch limit for attachments that can be sync by the cron task at a go:
|
||||||
|
|
||||||
|
Settings > Technical > System parameters > attachment_queue_cron_batch_limit
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_attachment_queue_user,attachment.queue.user,model_attachment_queue,,1,0,0,0
|
||||||
|
access_attachment_queue_manager,attachment.queue.manager,model_attachment_queue,base.group_no_one,1,1,1,1
|
|
Binary file not shown.
After Width: | Height: | Size: 9.2 KiB |
|
@ -0,0 +1,3 @@
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from . import test_attachment_queue
|
|
@ -0,0 +1,50 @@
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo.tests.common import TransactionCase
|
||||||
|
import odoo
|
||||||
|
from odoo import api
|
||||||
|
|
||||||
|
|
||||||
|
class TestAttachmentBaseQueue(TransactionCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.registry.enter_test_mode(self.env.cr)
|
||||||
|
self.env = api.Environment(self.registry.test_cr, self.env.uid,
|
||||||
|
self.env.context)
|
||||||
|
self.attachment = self.env.ref('attachment_queue.attachment_queue_demo')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.registry.leave_test_mode()
|
||||||
|
super().tearDown()
|
||||||
|
|
||||||
|
def test_attachment_queue(self):
|
||||||
|
"""Test run_attachment_queue_scheduler to ensure set state to done
|
||||||
|
"""
|
||||||
|
self.assertEqual(
|
||||||
|
self.attachment.state,
|
||||||
|
'pending'
|
||||||
|
)
|
||||||
|
self.env['attachment.queue'].run_attachment_queue_scheduler()
|
||||||
|
self.env.cache.invalidate()
|
||||||
|
with odoo.registry(self.env.cr.dbname).cursor() as new_cr:
|
||||||
|
new_env = api.Environment(
|
||||||
|
new_cr, self.env.uid, self.env.context)
|
||||||
|
attach = self.attachment.with_env(new_env)
|
||||||
|
self.assertEqual(
|
||||||
|
attach.state,
|
||||||
|
'done'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_set_done(self):
|
||||||
|
"""Test set_done manually
|
||||||
|
"""
|
||||||
|
self.assertEqual(
|
||||||
|
self.attachment.state,
|
||||||
|
'pending'
|
||||||
|
)
|
||||||
|
self.attachment.set_done()
|
||||||
|
self.assertEqual(
|
||||||
|
self.attachment.state,
|
||||||
|
'done'
|
||||||
|
)
|
|
@ -0,0 +1,106 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="view_attachment_queue_form" model="ir.ui.view">
|
||||||
|
<field name="model">attachment.queue</field>
|
||||||
|
<field name="inherit_id" ref="base.view_attachment_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="/form/*" position="before">
|
||||||
|
<header>
|
||||||
|
<button name="run" states="pending,failed"
|
||||||
|
string="Run" type="object" class="oe_highlight"/>
|
||||||
|
<button name="set_done" states="pending,failed"
|
||||||
|
string="Set to Done" type="object"/>
|
||||||
|
</header>
|
||||||
|
</xpath>
|
||||||
|
<field name="url" position="after">
|
||||||
|
<field name="date_done"/>
|
||||||
|
<field name="state"/>
|
||||||
|
<field name="file_type"/>
|
||||||
|
</field>
|
||||||
|
<group name="description_group">
|
||||||
|
<group name="state_message" string="Error" colspan="4">
|
||||||
|
<field name="state_message" nolabel="1"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_attachment_queue_tree" model="ir.ui.view">
|
||||||
|
<field name="model">attachment.queue</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree default_order='create_date desc'>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="datas_fname"/>
|
||||||
|
<field name="file_type"/>
|
||||||
|
<field name="type"/>
|
||||||
|
<field name="create_date"/>
|
||||||
|
<field name="state"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_attachment_queue_search" model="ir.ui.view">
|
||||||
|
<field name="model">attachment.queue</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="Attachments">
|
||||||
|
<field name="name" filter_domain="['|', ('name','ilike',self), ('datas_fname','ilike',self)]" string="Attachment"/>
|
||||||
|
<field name="create_date"/>
|
||||||
|
<filter name="url"
|
||||||
|
string="URL"
|
||||||
|
domain="[('type','=','url')]"/>
|
||||||
|
<filter name="binary"
|
||||||
|
string="Binary"
|
||||||
|
domain="[('type','=','binary')]"/>
|
||||||
|
<separator/>
|
||||||
|
<filter name="my_documents_filter"
|
||||||
|
string="My Document(s)"
|
||||||
|
domain="[('create_uid','=',uid)]"
|
||||||
|
help="Filter on my documents"/>
|
||||||
|
<field name="create_uid"/>
|
||||||
|
<field name="type"/>
|
||||||
|
<filter string="Pending" name="pending" domain="[('state', '=', 'pending')]"/>
|
||||||
|
<filter string="Failed" name="failed" domain="[('state', '=', 'failed')]"/>
|
||||||
|
<filter string="Done" name="done" domain="[('state', '=', 'done')]"/>
|
||||||
|
<group expand="0" string="Group By">
|
||||||
|
<filter string="Owner" name="owner" icon="terp-personal" domain="[]" context="{'group_by':'create_uid'}"/>
|
||||||
|
<filter string="Type" name="type" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by':'type'}" groups="base.group_no_one"/>
|
||||||
|
<filter string="Company" name="company" icon="terp-gtk-home" domain="[]" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
|
||||||
|
<filter string="Creation Month" name="creation_month" icon="terp-go-month" domain="[]" context="{'group_by':'create_date'}"/>
|
||||||
|
<filter string="State" name="state" domain="[]" context="{'group_by': 'state'}"/>
|
||||||
|
<filter string="File type" name="file_type" domain="[]" context="{'group_by': 'file_type'}"/>
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_attachment_queue" model="ir.actions.act_window">
|
||||||
|
<field name="name">Attachments Queue</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">attachment.queue</field>
|
||||||
|
<field name="view_type">form</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="view_id" eval="False"/>
|
||||||
|
<field name="search_view_id" ref="view_attachment_queue_search"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="act_open_attachment_que_view_tree" model="ir.actions.act_window.view">
|
||||||
|
<field eval="10" name="sequence"/>
|
||||||
|
<field name="view_mode">tree</field>
|
||||||
|
<field name="view_id" ref="view_attachment_queue_tree"/>
|
||||||
|
<field name="act_window_id" ref="action_attachment_queue"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="act_open_attachment_que_view_form" model="ir.actions.act_window.view">
|
||||||
|
<field eval="10" name="sequence"/>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="view_id" ref="view_attachment_queue_form"/>
|
||||||
|
<field name="act_window_id" ref="action_attachment_queue"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="menu_attachment_queue"
|
||||||
|
parent="base.next_id_9"
|
||||||
|
sequence="20"
|
||||||
|
action="action_attachment_queue"/>
|
||||||
|
|
||||||
|
</odoo>
|
Loading…
Reference in New Issue