summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorJoren Van Onder <jov@odoo.com>2015-02-11 10:18:10 +0100
committerJoren Van Onder <jov@odoo.com>2015-09-25 12:47:29 +0200
commit97b0723722d8ebd3cbf5828c871da19f3d471187 (patch)
tree1622fdda7f53e0e4a22d771a478a96d176dc289d /addons
parent9a42baa7e21f52a7d3879d6427b68a82e37f96bb (diff)
[ADD] pos_cache: add pos_cache ported to v9 from odoo-extra
Originally written by Nicolas Seinlet <nse@odoo.com>. Provides pos.config with a product.product cache. This adds a ported and refactored pos_cache from odoo-extra: - Adapt to changes in web and point_of_sale since 8.0 - Refactor the load_server_data function, so it isn't copy pasted but cleanly patched into the load_server_data super. - Make the cron inactive by default. - Also remove retrying the write 5 times, when it fails we'll just have to wait until the cron triggers again (an hour from then). - Store the product.product fields and domain on pos_cache. When cache is requested from the pos with a different field or domain it'll automatically recompute. Necessary for when someone would install a module that modifies these things in the pos after a cache has been generated. - Have multiple caches per pos_config per user - Make the module adhere to the Odoo Coding Guidelines.
Diffstat (limited to 'addons')
-rw-r--r--addons/pos_cache/__init__.py3
-rw-r--r--addons/pos_cache/__openerp__.py25
-rw-r--r--addons/pos_cache/data/pos_cache_data.xml16
-rw-r--r--addons/pos_cache/doc/cache.rst13
-rw-r--r--addons/pos_cache/models/__init__.py3
-rw-r--r--addons/pos_cache/models/pos_cache.py97
-rw-r--r--addons/pos_cache/security/ir.model.access.csv2
-rw-r--r--addons/pos_cache/static/src/js/pos_cache.js43
-rw-r--r--addons/pos_cache/views/pos_cache_templates.xml10
-rw-r--r--addons/pos_cache/views/pos_cache_views.xml18
10 files changed, 230 insertions, 0 deletions
diff --git a/addons/pos_cache/__init__.py b/addons/pos_cache/__init__.py
new file mode 100644
index 00000000000..48f417bbd93
--- /dev/null
+++ b/addons/pos_cache/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import models
diff --git a/addons/pos_cache/__openerp__.py b/addons/pos_cache/__openerp__.py
new file mode 100644
index 00000000000..708ae46108b
--- /dev/null
+++ b/addons/pos_cache/__openerp__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+{
+ 'name': "pos_cache",
+
+ 'summary': """
+ Enable a cache on products for a lower POS loading time.""",
+
+ 'description': """
+This creates a product cache per POS config. It drastically lowers the
+time it takes to load a POS session with a lot of products.
+ """,
+
+ 'author': "Odoo",
+ 'website': "https://www.odoo.com/page/point-of-sale",
+ 'category': 'Point Of Sale',
+ 'version': '1.0',
+ 'depends': ['point_of_sale'],
+ 'data': [
+ 'data/pos_cache_data.xml',
+ 'security/ir.model.access.csv',
+ 'views/pos_cache_views.xml',
+ 'views/pos_cache_templates.xml',
+ ]
+}
diff --git a/addons/pos_cache/data/pos_cache_data.xml b/addons/pos_cache/data/pos_cache_data.xml
new file mode 100644
index 00000000000..709e6edea35
--- /dev/null
+++ b/addons/pos_cache/data/pos_cache_data.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <data>
+ <record model="ir.cron" id="refresh_pos_cache_cron">
+ <field name="name">Refresh POS Cache</field>
+ <field name="active" eval="False"/>
+ <field name="interval_number">1</field>
+ <field name="interval_type">hours</field>
+ <field name="numbercall">-1</field>
+ <field name="doall" eval="False"/>
+ <field name="model" eval="'pos.cache'"/>
+ <field name="function" eval="'refresh_all_caches'"/>
+ <field name="args" eval="'()'"/>
+ </record>
+ </data>
+</odoo>
diff --git a/addons/pos_cache/doc/cache.rst b/addons/pos_cache/doc/cache.rst
new file mode 100644
index 00000000000..b01c635ef38
--- /dev/null
+++ b/addons/pos_cache/doc/cache.rst
@@ -0,0 +1,13 @@
+POS Cache
++++++++++
+
+This module enables a cache for the products in the pos configs. Each POS Config has his own Cache.
+
+The Cache is updated every hour by a cron.
+
+============
+Compute user
+============
+
+As it's a bad practice to use the admin in a multi-company configuration, a field permit to force a user to compute
+the cache. A badly chosen user can result in wrong taxes in POS in a multi-company environment.
diff --git a/addons/pos_cache/models/__init__.py b/addons/pos_cache/models/__init__.py
new file mode 100644
index 00000000000..8c6b3129856
--- /dev/null
+++ b/addons/pos_cache/models/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import pos_cache
diff --git a/addons/pos_cache/models/pos_cache.py b/addons/pos_cache/models/pos_cache.py
new file mode 100644
index 00000000000..0ae91b8bfeb
--- /dev/null
+++ b/addons/pos_cache/models/pos_cache.py
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from ast import literal_eval
+import cPickle
+
+from openerp import models, fields, api
+
+
+class pos_cache(models.Model):
+ _name = 'pos.cache'
+
+ cache = fields.Binary()
+ product_domain = fields.Text(required=True)
+ product_fields = fields.Text(required=True)
+
+ config_id = fields.Many2one('pos.config', ondelete='cascade', required=True)
+ compute_user_id = fields.Many2one('res.users', 'Cache compute user', required=True)
+
+ @api.model
+ def refresh_all_caches(self):
+ self.env['pos.cache'].search([]).refresh_cache()
+
+ @api.one
+ def refresh_cache(self):
+ products = self.env['product.product'].search(self.get_product_domain())
+ prod_ctx = products.with_context(pricelist=self.config_id.pricelist_id.id, display_default_code=False)
+ prod_ctx = prod_ctx.sudo(self.compute_user_id.id)
+ res = prod_ctx.read(self.get_product_fields())
+ datas = {
+ 'cache': cPickle.dumps(res, protocol=cPickle.HIGHEST_PROTOCOL),
+ }
+
+ self.write(datas)
+
+ @api.model
+ def get_product_domain(self):
+ return literal_eval(self.product_domain)
+
+ @api.model
+ def get_product_fields(self):
+ return literal_eval(self.product_fields)
+
+ @api.model
+ def get_cache(self, domain, fields):
+ if not self.cache or domain != self.get_product_domain() or fields != self.get_product_fields():
+ self.product_domain = str(domain)
+ self.product_fields = str(fields)
+ self.refresh_cache()
+
+ return cPickle.loads(self.cache)
+
+
+class pos_config(models.Model):
+ _inherit = 'pos.config'
+
+ @api.one
+ @api.depends('cache_ids')
+ def _get_oldest_cache_time(self):
+ pos_cache = self.env['pos.cache']
+ oldest_cache = pos_cache.search([('config_id', '=', self.id)], order='write_date', limit=1)
+ if oldest_cache:
+ self.oldest_cache_time = oldest_cache.write_date
+
+ # Use a related model to avoid the load of the cache when the pos load his config
+ cache_ids = fields.One2many('pos.cache', 'config_id')
+ oldest_cache_time = fields.Datetime(compute='_get_oldest_cache_time', string='Oldest cache time', readonly=True)
+
+ def _get_cache_for_user(self):
+ pos_cache = self.env['pos.cache']
+ cache_for_user = pos_cache.search([('id', 'in', self.cache_ids.ids), ('compute_user_id', '=', self.env.uid)])
+
+ if cache_for_user:
+ return cache_for_user[0]
+ else:
+ return None
+
+ @api.multi
+ def get_products_from_cache(self, fields, domain):
+ cache_for_user = self._get_cache_for_user()
+
+ if cache_for_user:
+ return cache_for_user.get_cache(domain, fields)
+ else:
+ pos_cache = self.env['pos.cache']
+ pos_cache.create({
+ 'config_id': self.id,
+ 'product_domain': str(domain),
+ 'product_fields': str(fields),
+ 'compute_user_id': self.env.uid
+ })
+ new_cache = self._get_cache_for_user()
+ return new_cache.get_cache(domain, fields)
+
+ @api.one
+ def delete_cache(self):
+ # throw away the old caches
+ self.cache_ids.unlink()
diff --git a/addons/pos_cache/security/ir.model.access.csv b/addons/pos_cache/security/ir.model.access.csv
new file mode 100644
index 00000000000..1a2651865f9
--- /dev/null
+++ b/addons/pos_cache/security/ir.model.access.csv
@@ -0,0 +1,2 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_pos_cache,pos.cache,model_pos_cache,point_of_sale.group_pos_user,1,1,1,1
diff --git a/addons/pos_cache/static/src/js/pos_cache.js b/addons/pos_cache/static/src/js/pos_cache.js
new file mode 100644
index 00000000000..29d5e3bfbc4
--- /dev/null
+++ b/addons/pos_cache/static/src/js/pos_cache.js
@@ -0,0 +1,43 @@
+odoo.define('pos_cache.pos_cache', function (require) {
+ "use strict";
+ var core = require('web.core');
+ var models = require('point_of_sale.models');
+ var Model = require('web.DataModel');
+ var _t = core._t;
+
+ var posmodel_super = models.PosModel.prototype;
+ models.PosModel = models.PosModel.extend({
+ load_server_data: function () {
+ var self = this;
+
+ var product_index = _.findIndex(this.models, function (model) {
+ return model.model === "product.product";
+ });
+
+ // Give both the fields and domain to pos_cache in the
+ // backend. This way we don't have to hardcode these
+ // values in the backend and they automatically stay in
+ // sync with whatever is defined (and maybe extended by
+ // other modules) in js.
+ var product_model = this.models[product_index];
+ var product_fields = product_model.fields;
+ var product_domain = product_model.domain;
+
+ // We don't want to load product.product the normal
+ // uncached way, so get rid of it.
+ if (product_index !== -1) {
+ this.models.splice(product_index, 1);
+ }
+
+ return posmodel_super.load_server_data.apply(this, arguments).then(function () {
+ var records = new Model('pos.config').call('get_products_from_cache',
+ [self.pos_session.config_id[0], product_fields, product_domain]);
+
+ self.chrome.loading_message(_t('Loading') + ' product.product', 1);
+ return records.then(function (product) {
+ self.db.add_products(product);
+ });
+ });
+ },
+ });
+});
diff --git a/addons/pos_cache/views/pos_cache_templates.xml b/addons/pos_cache/views/pos_cache_templates.xml
new file mode 100644
index 00000000000..6866bd2a24f
--- /dev/null
+++ b/addons/pos_cache/views/pos_cache_templates.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <data>
+ <template id="assets" inherit_id="point_of_sale.assets">
+ <xpath expr="." position="inside">
+ <script type="text/javascript" src="/pos_cache/static/src/js/pos_cache.js"></script>
+ </xpath>
+ </template>
+ </data>
+</odoo>
diff --git a/addons/pos_cache/views/pos_cache_views.xml b/addons/pos_cache/views/pos_cache_views.xml
new file mode 100644
index 00000000000..522d9437a0b
--- /dev/null
+++ b/addons/pos_cache/views/pos_cache_views.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<odoo>
+ <data>
+ <record model="ir.ui.view" id="view_pos_config_form">
+ <field name="name">pos.config.form.view</field>
+ <field name="model">pos.config</field>
+ <field name="inherit_id" ref="point_of_sale.view_pos_config_form" />
+ <field name="arch" type="xml">
+ <xpath expr="//button[@name='set_deprecate']" position="after">
+ <button name='delete_cache' type="object" string="Recompute cache"/>
+ </xpath>
+ <xpath expr="//field[@name='sequence_id']" position="after">
+ <field name='oldest_cache_time'/>
+ </xpath>
+ </field>
+ </record>
+ </data>
+</odoo>