[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.
This commit is contained in:
Joren Van Onder
2015-09-25 12:47:29 +02:00
parent 9a42baa7e2
commit 97b0723722
10 changed files with 230 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import models
+25
View File
@@ -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',
]
}
+16
View File
@@ -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>
+13
View File
@@ -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.
+3
View File
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import pos_cache
+97
View File
@@ -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()
@@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_pos_cache pos.cache model_pos_cache point_of_sale.group_pos_user 1 1 1 1
@@ -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);
});
});
},
});
});
@@ -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>
@@ -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>