- Balance in Odoo
+ Balance in GL
diff --git a/addons/base_import/controllers.py b/addons/base_import/controllers.py
index 9a49384900f..84b645f149f 100644
--- a/addons/base_import/controllers.py
+++ b/addons/base_import/controllers.py
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
+import cgi
import json
from openerp.http import Controller, route
@@ -15,4 +16,4 @@ class ImportController(Controller):
}, req.context)
return 'window.top.%s(%s)' % (
- jsonp, json.dumps({'result': written}))
+ cgi.escape(jsonp), json.dumps({'result': written}))
diff --git a/addons/base_import/static/csv/database_import_test.sql b/addons/base_import/static/csv/database_import_test.sql
index 1f79179145e..13a735d106d 100644
--- a/addons/base_import/static/csv/database_import_test.sql
+++ b/addons/base_import/static/csv/database_import_test.sql
@@ -2,12 +2,30 @@
-- PostgreSQL database dump
--
+-- Dumped from database version 9.5.2
+-- Dumped by pg_dump version 9.5.2
+
SET statement_timeout = 0;
+SET lock_timeout = 0;
SET client_encoding = 'UTF8';
-SET standard_conforming_strings = off;
+SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;
-SET escape_string_warning = off;
+SET row_security = off;
+
+--
+-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: -
+--
+
+CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
+
+
+--
+-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: -
+--
+
+COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
+
SET search_path = public, pg_catalog;
@@ -16,7 +34,7 @@ SET default_tablespace = '';
SET default_with_oids = false;
--
--- Name: companies; Type: TABLE; Schema: public; Owner: fp; Tablespace:
+-- Name: companies; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE companies (
@@ -25,38 +43,27 @@ CREATE TABLE companies (
);
-ALTER TABLE public.companies OWNER TO fp;
-
--
--- Name: companies_id_seq; Type: SEQUENCE; Schema: public; Owner: fp
+-- Name: companies_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE companies_id_seq
START WITH 1
INCREMENT BY 1
- NO MAXVALUE
NO MINVALUE
+ NO MAXVALUE
CACHE 1;
-ALTER TABLE public.companies_id_seq OWNER TO fp;
-
--
--- Name: companies_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: fp
+-- Name: companies_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE companies_id_seq OWNED BY companies.id;
--
--- Name: companies_id_seq; Type: SEQUENCE SET; Schema: public; Owner: fp
---
-
-SELECT pg_catalog.setval('companies_id_seq', 3, true);
-
-
---
--- Name: persons; Type: TABLE; Schema: public; Owner: fp; Tablespace:
+-- Name: persons; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE persons (
@@ -66,52 +73,41 @@ CREATE TABLE persons (
);
-ALTER TABLE public.persons OWNER TO fp;
-
--
--- Name: persons_id_seq; Type: SEQUENCE; Schema: public; Owner: fp
+-- Name: persons_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE persons_id_seq
START WITH 1
INCREMENT BY 1
- NO MAXVALUE
NO MINVALUE
+ NO MAXVALUE
CACHE 1;
-ALTER TABLE public.persons_id_seq OWNER TO fp;
-
--
--- Name: persons_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: fp
+-- Name: persons_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE persons_id_seq OWNED BY persons.id;
--
--- Name: persons_id_seq; Type: SEQUENCE SET; Schema: public; Owner: fp
---
-
-SELECT pg_catalog.setval('persons_id_seq', 4, true);
-
-
---
--- Name: id; Type: DEFAULT; Schema: public; Owner: fp
+-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY companies ALTER COLUMN id SET DEFAULT nextval('companies_id_seq'::regclass);
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: fp
+-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY persons ALTER COLUMN id SET DEFAULT nextval('persons_id_seq'::regclass);
--
--- Data for Name: companies; Type: TABLE DATA; Schema: public; Owner: fp
+-- Data for Name: companies; Type: TABLE DATA; Schema: public; Owner: -
--
COPY companies (id, company_name) FROM stdin;
@@ -122,19 +118,33 @@ COPY companies (id, company_name) FROM stdin;
--
--- Data for Name: persons; Type: TABLE DATA; Schema: public; Owner: fp
+-- Name: companies_id_seq; Type: SEQUENCE SET; Schema: public; Owner: -
+--
+
+SELECT pg_catalog.setval('companies_id_seq', 3, true);
+
+
+--
+-- Data for Name: persons; Type: TABLE DATA; Schema: public; Owner: -
--
COPY persons (id, company_id, person_name) FROM stdin;
1 1 Fabien
2 1 Laurence
3 2 Eric
-4 3 Ramsy
+4 3 Ramzy
\.
--
--- Name: companies_pkey; Type: CONSTRAINT; Schema: public; Owner: fp; Tablespace:
+-- Name: persons_id_seq; Type: SEQUENCE SET; Schema: public; Owner: -
+--
+
+SELECT pg_catalog.setval('persons_id_seq', 4, true);
+
+
+--
+-- Name: companies_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY companies
@@ -142,13 +152,23 @@ ALTER TABLE ONLY companies
--
--- Name: persons_company_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: fp
+-- Name: persons_company_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY persons
ADD CONSTRAINT persons_company_id_fkey FOREIGN KEY (company_id) REFERENCES companies(id);
+--
+-- Name: public; Type: ACL; Schema: -; Owner: -
+--
+
+REVOKE ALL ON SCHEMA public FROM PUBLIC;
+REVOKE ALL ON SCHEMA public FROM postgres;
+GRANT ALL ON SCHEMA public TO postgres;
+GRANT ALL ON SCHEMA public TO PUBLIC;
+
+
--
-- PostgreSQL database dump complete
--
diff --git a/addons/base_import/static/csv/o2m_purchase_order_lines.csv b/addons/base_import/static/csv/o2m_purchase_order_lines.csv
index 4778b99cb81..744f13f8fab 100644
--- a/addons/base_import/static/csv/o2m_purchase_order_lines.csv
+++ b/addons/base_import/static/csv/o2m_purchase_order_lines.csv
@@ -1,10 +1,6 @@
-Order Date,Order Reference,Supplier,Destination,Pricelist,Order Lines / Product,Order Lines / Quantity
-2012-12-15,PO00008,ASUSTeK,Stock,Default Purchase Pricelist,ADPT,20
-,,,,,CARD,30
-,,,,,C-Case,40
-2012-12-15,PO00009,Axelor,Stock,Default Purchase Pricelist,CD,5
-,,,,,CPUa8,15
-2012-12-15,PO000010,China Export,Stock,Default Purchase Pricelist,HDD SH-1,10
-,,,,,HDD SH-2,20
-,,,,,LAP-CUS,35
-,,,,,LAP-E5,40
+"Order Date","Order Reference",Vendor,"Order Lines / Product","Order Lines / Description","Order Lines / Quantity","Order Lines / Product Unit of Measure","Order Lines / Unit Price","Order Lines / Scheduled Date"
+2016-12-15,PO00008,ASUSTeK,ADPT,ADPT,20.0,"Unit(s)",13.0,2016-12-15
+,,,CARD,CARD,30,"Unit(s)",876.0,2016-12-15
+,,,C-Case,C-Case,40,"Unit(s)",20.0,2016-12-15
+2016-12-15,PO00009,Camptocamp,CD,CD,5,"Unit(s)",18.4,2016-12-15
+,,,CPUa8,CPUa8,15,"Unit(s)",1910.0,2016-12-15
diff --git a/addons/base_import/static/csv/purchase.order_functional_error_line_cant_adpat.csv b/addons/base_import/static/csv/purchase.order_functional_error_line_cant_adpat.csv
index 3b428763431..35c07c08257 100644
--- a/addons/base_import/static/csv/purchase.order_functional_error_line_cant_adpat.csv
+++ b/addons/base_import/static/csv/purchase.order_functional_error_line_cant_adpat.csv
@@ -1,6 +1,3 @@
-"Order Reference","Supplier","Destination","Pricelist","Order Lines / Product","Order Lines / Quantity"
-"PO000020","ASUSTeK","Stock","Default Purchase Pricelist","ADPT",20
-,,,,"CARD",30
-,,,,"C-Case",40
-"PO000021","Axelor","Stock","Default Purchase Pricelist","CD",5
-,,,,"CPUa8",15
+"Order Reference","Vendor"
+"PO000020","ASUSTeK"
+"PO000021","Camptocamp"
diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py
index 091eddd18a6..72cdb2b0348 100644
--- a/addons/calendar/calendar.py
+++ b/addons/calendar/calendar.py
@@ -1372,6 +1372,30 @@ class calendar_event(osv.Model):
('user_id', '=', uid),
]
+ @api.multi
+ def _get_message_unread(self):
+ id_map = {x: calendar_id2real_id(x) for x in self.ids}
+ real = self.browse(set(id_map.values()))
+ super(calendar_event, real)._get_message_unread()
+ for event in self:
+ if event.id == id_map[event.id]:
+ continue
+ rec = self.browse(id_map[event.id])
+ event.message_unread_counter = rec.message_unread_counter
+ event.message_unread = rec.message_unread
+
+ @api.multi
+ def _get_message_needaction(self):
+ id_map = {x: calendar_id2real_id(x) for x in self.ids}
+ real = self.browse(set(id_map.values()))
+ super(calendar_event, real)._get_message_needaction()
+ for event in self:
+ if event.id == id_map[event.id]:
+ continue
+ rec = self.browse(id_map[event.id])
+ event.message_needaction_counter = rec.message_needaction_counter
+ event.message_needaction = rec.message_needaction
+
@api.cr_uid_ids_context
def message_post(self, cr, uid, thread_id, context=None, **kwargs):
if isinstance(thread_id, basestring):
diff --git a/addons/fleet/fleet.py b/addons/fleet/fleet.py
index 4565e1eefd1..e3564a0eaed 100644
--- a/addons/fleet/fleet.py
+++ b/addons/fleet/fleet.py
@@ -142,30 +142,24 @@ class fleet_vehicle_model_brand(osv.Model):
image = openerp.fields.Binary("Logo", attachment=True,
help="This field holds the image used as logo for the brand, limited to 1024x1024px.")
- image_medium = openerp.fields.Binary("Medium-sized image",
- compute='_compute_images', inverse='_inverse_image_medium', store=True, attachment=True,
+ image_medium = openerp.fields.Binary("Medium-sized image", attachment=True,
help="Medium-sized logo of the brand. It is automatically "\
"resized as a 128x128px image, with aspect ratio preserved. "\
"Use this field in form views or some kanban views.")
- image_small = openerp.fields.Binary("Small-sized image",
- compute='_compute_images', inverse='_inverse_image_small', store=True, attachment=True,
+ image_small = openerp.fields.Binary("Small-sized image", attachment=True,
help="Small-sized logo of the brand. It is automatically "\
"resized as a 64x64px image, with aspect ratio preserved. "\
"Use this field anywhere a small image is required.")
- @openerp.api.depends('image')
- def _compute_images(self):
- for rec in self:
- rec.image_medium = tools.image_resize_image_medium(rec.image)
- rec.image_small = tools.image_resize_image_small(rec.image)
+ @openerp.api.model
+ def create(self, vals):
+ tools.image_resize_images(vals)
+ return super(fleet_vehicle_model_brand, self).create(vals)
- def _inverse_image_medium(self):
- for rec in self:
- rec.image = tools.image_resize_image_big(rec.image_medium)
-
- def _inverse_image_small(self):
- for rec in self:
- rec.image = tools.image_resize_image_big(rec.image_small)
+ @openerp.api.multi
+ def write(self, vals):
+ tools.image_resize_images(vals)
+ return super(fleet_vehicle_model_brand, self).write(vals)
class fleet_vehicle(osv.Model):
diff --git a/addons/hr/hr.py b/addons/hr/hr.py
index cdd88e67d0c..54b45dea605 100644
--- a/addons/hr/hr.py
+++ b/addons/hr/hr.py
@@ -172,31 +172,15 @@ class hr_employee(osv.osv):
# image: all image fields are base64 encoded and PIL-supported
image = openerp.fields.Binary("Photo", attachment=True,
help="This field holds the image used as photo for the employee, limited to 1024x1024px.")
- image_medium = openerp.fields.Binary("Medium-sized photo",
- compute='_compute_images', inverse='_inverse_image_medium', store=True, attachment=True,
+ image_medium = openerp.fields.Binary("Medium-sized photo", attachment=True,
help="Medium-sized photo of the employee. It is automatically "\
"resized as a 128x128px image, with aspect ratio preserved. "\
"Use this field in form views or some kanban views.")
- image_small = openerp.fields.Binary("Small-sized photo",
- compute='_compute_images', inverse='_inverse_image_small', store=True, attachment=True,
+ image_small = openerp.fields.Binary("Small-sized photo", attachment=True,
help="Small-sized photo of the employee. It is automatically "\
"resized as a 64x64px image, with aspect ratio preserved. "\
"Use this field anywhere a small image is required.")
- @api.depends('image')
- def _compute_images(self):
- for rec in self:
- rec.image_medium = tools.image_resize_image_medium(rec.image)
- rec.image_small = tools.image_resize_image_small(rec.image)
-
- def _inverse_image_medium(self):
- for rec in self:
- rec.image = tools.image_resize_image_big(rec.image_medium)
-
- def _inverse_image_small(self):
- for rec in self:
- rec.image = tools.image_resize_image_big(rec.image_small)
-
def _get_default_image(self, cr, uid, context=None):
image_path = get_module_resource('hr', 'static/src/img', 'default_image.png')
return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64'))
@@ -207,6 +191,16 @@ class hr_employee(osv.osv):
'color': 0,
}
+ @api.model
+ def create(self, vals):
+ tools.image_resize_images(vals)
+ return super(hr_employee, self).create(vals)
+
+ @api.multi
+ def write(self, vals):
+ tools.image_resize_images(vals)
+ return super(hr_employee, self).write(vals)
+
def unlink(self, cr, uid, ids, context=None):
resource_ids = []
for employee in self.browse(cr, uid, ids, context=context):
diff --git a/addons/im_livechat/models/im_livechat_channel.py b/addons/im_livechat/models/im_livechat_channel.py
index c4f66518c50..cc2bd7bc729 100644
--- a/addons/im_livechat/models/im_livechat_channel.py
+++ b/addons/im_livechat/models/im_livechat_channel.py
@@ -48,13 +48,11 @@ class ImLivechatChannel(models.Model):
# images fields
image = fields.Binary('Image', default=_default_image, attachment=True,
help="This field holds the image used as photo for the group, limited to 1024x1024px.")
- image_medium = fields.Binary('Medium',
- compute='_compute_image', store=True, attachment=True,
+ image_medium = fields.Binary('Medium', attachment=True,
help="Medium-sized photo of the group. It is automatically "\
"resized as a 128x128px image, with aspect ratio preserved. "\
"Use this field in form views or some kanban views.")
- image_small = fields.Binary('Thumbnail',
- compute='_compute_image', store=True, attachment=True,
+ image_small = fields.Binary('Thumbnail', attachment=True,
help="Small-sized photo of the group. It is automatically "\
"resized as a 64x64px image, with aspect ratio preserved. "\
"Use this field anywhere a small image is required.")
@@ -69,12 +67,6 @@ class ImLivechatChannel(models.Model):
def _are_you_inside(self):
self.are_you_inside = bool(self.env.uid in [u.id for u in self.user_ids])
- @api.one
- @api.depends('image')
- def _compute_image(self):
- self.image_medium = tools.image_resize_image_medium(self.image)
- self.image_small = tools.image_resize_image_small(self.image)
-
@api.multi
def _compute_script_external(self):
view = self.env['ir.model.data'].get_object('im_livechat', 'external_loader')
@@ -111,6 +103,16 @@ class ImLivechatChannel(models.Model):
else:
record.rating_percentage_satisfaction = -1
+ @api.model
+ def create(self, vals):
+ tools.image_resize_images(vals)
+ return super(ImLivechatChannel, self).create(vals)
+
+ @api.multi
+ def write(self, vals):
+ tools.image_resize_images(vals)
+ return super(ImLivechatChannel, self).write(vals)
+
# --------------------------
# Action Methods
# --------------------------
diff --git a/addons/mail/models/mail_channel.py b/addons/mail/models/mail_channel.py
index 98ace2efd3a..41fc8e0d551 100644
--- a/addons/mail/models/mail_channel.py
+++ b/addons/mail/models/mail_channel.py
@@ -69,13 +69,11 @@ class Channel(models.Model):
# image: all image fields are base64 encoded and PIL-supported
image = fields.Binary("Photo", default=_get_default_image, attachment=True,
help="This field holds the image used as photo for the group, limited to 1024x1024px.")
- image_medium = fields.Binary('Medium-sized photo',
- compute='_get_image', inverse='_set_image_medium', store=True, attachment=True,
+ image_medium = fields.Binary('Medium-sized photo', attachment=True,
help="Medium-sized photo of the group. It is automatically "
"resized as a 128x128px image, with aspect ratio preserved. "
"Use this field in form views or some kanban views.")
- image_small = fields.Binary('Small-sized photo',
- compute='_get_image', inverse='_set_image_small', store=True, attachment=True,
+ image_small = fields.Binary('Small-sized photo', attachment=True,
help="Small-sized photo of the group. It is automatically "
"resized as a 64x64px image, with aspect ratio preserved. "
"Use this field anywhere a small image is required.")
@@ -100,20 +98,9 @@ class Channel(models.Model):
for record in self:
record.is_member = record in membership_ids
- @api.one
- @api.depends('image')
- def _get_image(self):
- self.image_medium = tools.image_resize_image_medium(self.image)
- self.image_small = tools.image_resize_image_small(self.image)
-
- def _set_image_medium(self):
- self.image = tools.image_resize_image_big(self.image_medium)
-
- def _set_image_small(self):
- self.image = tools.image_resize_image_big(self.image_small)
-
@api.model
def create(self, vals):
+ tools.image_resize_images(vals)
# Create channel and alias
channel = super(Channel, self.with_context(
alias_model_name=self._name, alias_parent_model_name=self._name, mail_create_nolog=True, mail_create_nosubscribe=True)
@@ -147,6 +134,7 @@ class Channel(models.Model):
@api.multi
def write(self, vals):
+ tools.image_resize_images(vals)
result = super(Channel, self).write(vals)
if vals.get('group_ids'):
self._subscribe_users()
diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py
index fece7588eac..3c4188ef047 100644
--- a/addons/payment/models/payment_acquirer.py
+++ b/addons/payment/models/payment_acquirer.py
@@ -3,7 +3,7 @@ import logging
import openerp
from openerp.osv import osv, fields
-from openerp.tools import float_round, float_repr, image_get_resized_images, image_resize_image_big
+from openerp.tools import float_round, float_repr, image_resize_images
from openerp.tools.translate import _
_logger = logging.getLogger(__name__)
@@ -97,31 +97,15 @@ class PaymentAcquirer(osv.Model):
image = openerp.fields.Binary("Image", attachment=True,
help="This field holds the image used for this provider, limited to 1024x1024px")
- image_medium = openerp.fields.Binary("Medium-sized image",
- compute='_compute_images', inverse='_inverse_image_medium', store=True, attachment=True,
+ image_medium = openerp.fields.Binary("Medium-sized image", attachment=True,
help="Medium-sized image of this provider. It is automatically "\
"resized as a 128x128px image, with aspect ratio preserved. "\
"Use this field in form views or some kanban views.")
- image_small = openerp.fields.Binary("Small-sized image",
- compute='_compute_images', inverse='_inverse_image_small', store=True, attachment=True,
+ image_small = openerp.fields.Binary("Small-sized image", attachment=True,
help="Small-sized image of this provider. It is automatically "\
"resized as a 64x64px image, with aspect ratio preserved. "\
"Use this field anywhere a small image is required.")
- @openerp.api.depends('image')
- def _compute_images(self):
- for rec in self:
- rec.image_medium = openerp.tools.image_resize_image_medium(rec.image)
- rec.image_small = openerp.tools.image_resize_image_small(rec.image)
-
- def _inverse_image_medium(self):
- for rec in self:
- rec.image = openerp.tools.image_resize_image_big(rec.image_medium)
-
- def _inverse_image_small(self):
- for rec in self:
- rec.image = openerp.tools.image_resize_image_big(rec.image_small)
-
_defaults = {
'company_id': lambda self, cr, uid, obj, ctx=None: self.pool['res.users'].browse(cr, uid, uid).company_id.id,
'environment': 'test',
@@ -145,6 +129,16 @@ class PaymentAcquirer(osv.Model):
(_check_required_if_provider, 'Required fields not filled', ['required for this provider']),
]
+ @openerp.api.model
+ def create(self, vals):
+ image_resize_images(vals)
+ return super(PaymentAcquirer, self).create(vals)
+
+ @openerp.api.multi
+ def write(self, vals):
+ image_resize_images(vals)
+ return super(PaymentAcquirer, self).write(vals)
+
def get_form_action_url(self, cr, uid, id, context=None):
""" Returns the form action URL, for form-based acquirer implementations. """
acquirer = self.browse(cr, uid, id, context=context)
diff --git a/addons/point_of_sale/models/pos_category.py b/addons/point_of_sale/models/pos_category.py
index ff68328cbaa..515c188f10f 100644
--- a/addons/point_of_sale/models/pos_category.py
+++ b/addons/point_of_sale/models/pos_category.py
@@ -2,7 +2,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, tools, _
-
class PosCategory(models.Model):
_name = "pos.category"
_description = "Public Category"
@@ -23,30 +22,24 @@ class PosCategory(models.Model):
# buttons have consistent styling.
image = fields.Binary(attachment=True,
help="This field holds the image used as image for the cateogry, limited to 1024x1024px.")
- image_medium = fields.Binary(string="Medium-sized image",
- compute='_compute_images', inverse='_inverse_image_medium', store=True, attachment=True,
+ image_medium = fields.Binary(string="Medium-sized image", attachment=True,
help="Medium-sized image of the category. It is automatically "
"resized as a 128x128px image, with aspect ratio preserved. "
"Use this field in form views or some kanban views.")
- image_small = fields.Binary(string="Small-sized image",
- compute='_compute_images', inverse='_inverse_image_small', store=True, attachment=True,
+ image_small = fields.Binary(string="Small-sized image", attachment=True,
help="Small-sized image of the category. It is automatically "
"resized as a 64x64px image, with aspect ratio preserved. "
"Use this field anywhere a small image is required.")
- @api.depends('image')
- def _compute_images(self):
- for rec in self:
- rec.image_medium = tools.image_resize_image_medium(rec.image)
- rec.image_small = tools.image_resize_image_small(rec.image)
+ @api.model
+ def create(self, vals):
+ tools.image_resize_images(vals)
+ return super(PosCategory, self).create(vals)
- def _inverse_image_medium(self):
- for rec in self:
- rec.image = tools.image_resize_image_big(rec.image_medium)
-
- def _inverse_image_small(self):
- for rec in self:
- rec.image = tools.image_resize_image_big(rec.image_small)
+ @api.multi
+ def write(self, vals):
+ tools.image_resize_images(vals)
+ return super(PosCategory, self).write(vals)
@api.multi
def name_get(self):
diff --git a/addons/product/product.py b/addons/product/product.py
index ea3f69ac5d8..f537c6e3d55 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -537,31 +537,15 @@ class product_template(osv.osv):
# image: all image fields are base64 encoded and PIL-supported
image = openerp.fields.Binary("Image", attachment=True,
help="This field holds the image used as image for the product, limited to 1024x1024px.")
- image_medium = openerp.fields.Binary("Medium-sized image",
- compute='_compute_images', inverse='_inverse_image_medium', store=True, attachment=True,
+ image_medium = openerp.fields.Binary("Medium-sized image", attachment=True,
help="Medium-sized image of the product. It is automatically "\
"resized as a 128x128px image, with aspect ratio preserved, "\
"only when the image exceeds one of those sizes. Use this field in form views or some kanban views.")
- image_small = openerp.fields.Binary("Small-sized image",
- compute='_compute_images', inverse='_inverse_image_small', store=True, attachment=True,
+ image_small = openerp.fields.Binary("Small-sized image", attachment=True,
help="Small-sized image of the product. It is automatically "\
"resized as a 64x64px image, with aspect ratio preserved. "\
"Use this field anywhere a small image is required.")
- @api.depends('image')
- def _compute_images(self):
- for rec in self:
- rec.image_medium = tools.image_resize_image_medium(rec.image, avoid_if_small=True)
- rec.image_small = tools.image_resize_image_small(rec.image)
-
- def _inverse_image_medium(self):
- for rec in self:
- rec.image = tools.image_resize_image_big(rec.image_medium)
-
- def _inverse_image_small(self):
- for rec in self:
- rec.image = tools.image_resize_image_big(rec.image_small)
-
def _price_get(self, cr, uid, products, ptype='list_price', context=None):
if context is None:
context = {}
@@ -687,6 +671,7 @@ class product_template(osv.osv):
def create(self, cr, uid, vals, context=None):
''' Store the initial standard price in order to be able to retrieve the cost of a product template for a given date'''
+ tools.image_resize_images(vals)
product_template_id = super(product_template, self).create(cr, uid, vals, context=context)
if not context or "create_product_product" not in context:
self.create_variant_ids(cr, uid, [product_template_id], context=context)
@@ -710,6 +695,7 @@ class product_template(osv.osv):
return product_template_id
def write(self, cr, uid, ids, vals, context=None):
+ tools.image_resize_images(vals)
res = super(product_template, self).write(cr, uid, ids, vals, context=context)
if 'attribute_line_ids' in vals or vals.get('active'):
self.create_variant_ids(cr, uid, ids, context=context)
diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py
index 97d1866295d..3abc4ce88f9 100644
--- a/addons/purchase/purchase.py
+++ b/addons/purchase/purchase.py
@@ -348,14 +348,17 @@ class PurchaseOrder(models.Model):
# this product. We limit to 10 the number of suppliers for a product to avoid the mess that
# could be caused for some generic products ("Miscellaneous").
for line in self.order_line:
- if self.partner_id not in line.product_id.seller_ids.mapped('name') and len(line.product_id.seller_ids) <= 10:
+ # Do not add a contact as a supplier
+ partner = self.partner_id if not self.partner_id.parent_id else self.partner_id.parent_id
+ if partner not in line.product_id.seller_ids.mapped('name') and len(line.product_id.seller_ids) <= 10:
+ currency = partner.property_purchase_currency_id or self.env.user.company_id.currency_id
supplierinfo = {
- 'name': self.partner_id.id,
+ 'name': partner.id,
'sequence': max(line.product_id.seller_ids.mapped('sequence')) + 1 if line.product_id.seller_ids else 1,
'product_uom': line.product_uom.id,
'min_qty': 0.0,
- 'price': line.price_unit,
- 'currency_id': self.partner_id.currency_id.id,
+ 'price': self.currency_id.compute(line.price_unit, currency),
+ 'currency_id': currency.id,
'delay': 0,
}
vals = {
diff --git a/addons/sales_team/models/crm_team.py b/addons/sales_team/models/crm_team.py
index fb371740f7e..fb01d618331 100644
--- a/addons/sales_team/models/crm_team.py
+++ b/addons/sales_team/models/crm_team.py
@@ -15,13 +15,15 @@ class CrmTeam(models.Model):
def _get_default_team_id(self, user_id=None):
if not user_id:
user_id = self.env.uid
- team_id = self.env['crm.team'].search([
- '|',
- ('user_id', '=', user_id),
- ('member_ids', 'in', [user_id])],
- limit=1) or self.env.ref('sales_team.team_sales_department', raise_if_not_found=False)
- if not team_id and self.env.context.get('default_team_id'):
+ team_id = self.env['crm.team'].search(
+ ['|', ('user_id', '=', user_id), ('member_ids', 'in', [user_id])],
+ limit=1)
+ if not team_id and 'default_team_id' in self.env.context:
team_id = self.env['crm.team'].browse(self.env.context.get('default_team_id'))
+ if not team_id and 'default_team_id' not in self.env.context:
+ default_team_id = self.env.ref('sales_team.team_sales_department', raise_if_not_found=False)
+ if default_team_id and (self.env.context.get('default_type') != 'lead' or default_team_id.use_leads):
+ team_id = default_team_id
return team_id
name = fields.Char('Sales Team', required=True, translate=True)
diff --git a/addons/web/static/src/js/framework/mixins.js b/addons/web/static/src/js/framework/mixins.js
index 40bdaeb51b4..05d4105e466 100644
--- a/addons/web/static/src/js/framework/mixins.js
+++ b/addons/web/static/src/js/framework/mixins.js
@@ -336,9 +336,8 @@ var PropertiesMixin = _.extend({}, EventDispatcherMixin, {
// remove this, or move it elsewhere. Also, learn OO programming.
if (key === 'value' && self.field && self.field.type === 'float' && tmp && val){
var digits = self.field.digits;
- if (digits !== 0){
- digits = digits ? digits[1] : 2;
- if (utils.float_is_zero(tmp - val, digits)) {
+ if (_.isArray(digits)) {
+ if (utils.float_is_zero(tmp - val, digits[1])) {
return;
}
}
@@ -366,4 +365,3 @@ return {
};
});
-
diff --git a/addons/web/static/src/js/views/list_view.js b/addons/web/static/src/js/views/list_view.js
index 6c3de0f408a..b8d0c569609 100644
--- a/addons/web/static/src/js/views/list_view.js
+++ b/addons/web/static/src/js/views/list_view.js
@@ -1155,7 +1155,7 @@ ListView.List = Class.extend( /** @lends instance.web.ListView.List# */{
record.set(column.id, names[0]);
});
}
- } else if (column.type === 'many2many' || column.type === 'one2many') {
+ } else if (column.type === 'many2many') {
value = record.get(column.id);
// non-resolved (string) m2m values are arrays
if (value instanceof Array && !_.isEmpty(value)
diff --git a/addons/web_calendar/static/src/js/web_calendar.js b/addons/web_calendar/static/src/js/web_calendar.js
index 7b03e1b73d7..b42bdd5c2d9 100644
--- a/addons/web_calendar/static/src/js/web_calendar.js
+++ b/addons/web_calendar/static/src/js/web_calendar.js
@@ -86,6 +86,9 @@ var CalendarView = View.extend({
this.title = (this.options.action)? this.options.action.name : '';
this.shown = $.Deferred();
+ self.current_start = null;
+ self.current_end = null;
+ self.previous_ids = [];
},
set_default_options: function(options) {
@@ -729,11 +732,25 @@ var CalendarView = View.extend({
);
}
}
+
+ // read_slice is launched uncoditionally, when quickly
+ // changing the range in the calender view, all of
+ // these RPC calls will race each other. Because of
+ // this we keep track of the current range of the
+ // calendar view.
+ self.current_start = start;
+ self.current_end = end;
self.dataset.read_slice(_.keys(self.fields), {
offset: 0,
domain: event_domain,
context: context,
}).done(function(events) {
+ // undo the read_slice if it the range has changed since it launched
+ if (self.current_start.getTime() != start.getTime() || self.current_end.getTime() != end.getTime()) {
+ self.dataset.ids = self.previous_ids;
+ return;
+ }
+ self.previous_ids = self.dataset.ids.slice();
if (self.dataset.index === null) {
if (events.length) {
self.dataset.index = 0;
diff --git a/addons/website_sale/models/product.py b/addons/website_sale/models/product.py
index 9fb470c2f79..c7b2ec373f2 100644
--- a/addons/website_sale/models/product.py
+++ b/addons/website_sale/models/product.py
@@ -60,30 +60,24 @@ class product_public_category(osv.osv):
# In this case, the default image is set by the js code.
image = openerp.fields.Binary("Image", attachment=True,
help="This field holds the image used as image for the category, limited to 1024x1024px.")
- image_medium = openerp.fields.Binary("Medium-sized image",
- compute='_compute_images', inverse='_inverse_image_medium', store=True, attachment=True,
+ image_medium = openerp.fields.Binary("Medium-sized image", attachment=True,
help="Medium-sized image of the category. It is automatically "\
"resized as a 128x128px image, with aspect ratio preserved. "\
"Use this field in form views or some kanban views.")
- image_small = openerp.fields.Binary("Small-sized image",
- compute='_compute_images', inverse='_inverse_image_small', store=True, attachment=True,
+ image_small = openerp.fields.Binary("Small-sized image", attachment=True,
help="Small-sized image of the category. It is automatically "\
"resized as a 64x64px image, with aspect ratio preserved. "\
"Use this field anywhere a small image is required.")
- @openerp.api.depends('image')
- def _compute_images(self):
- for rec in self:
- rec.image_medium = tools.image_resize_image_medium(rec.image)
- rec.image_small = tools.image_resize_image_small(rec.image)
+ @openerp.api.model
+ def create(self, vals):
+ tools.image_resize_images(vals)
+ return super(product_public_category, self).create(vals)
- def _inverse_image_medium(self):
- for rec in self:
- rec.image = tools.image_resize_image_big(rec.image_medium)
-
- def _inverse_image_small(self):
- for rec in self:
- rec.image = tools.image_resize_image_big(rec.image_small)
+ @openerp.api.multi
+ def write(self, vals):
+ tools.image_resize_images(vals)
+ return super(product_public_category, self).write(vals)
class product_template(osv.Model):
_inherit = ["product.template", "website.seo.metadata", 'website.published.mixin', 'rating.mixin']
diff --git a/openerp/addons/base/ir/ir_attachment.py b/openerp/addons/base/ir/ir_attachment.py
index 2488726ff48..c94a3743830 100644
--- a/openerp/addons/base/ir/ir_attachment.py
+++ b/openerp/addons/base/ir/ir_attachment.py
@@ -12,7 +12,7 @@ from openerp import SUPERUSER_ID, tools
from openerp.exceptions import AccessError
from openerp.osv import fields,osv
from openerp.tools.translate import _
-from openerp.tools.misc import ustr
+from openerp.tools.misc import ustr, html_escape
from openerp.tools.mimetypes import guess_mimetype
_logger = logging.getLogger(__name__)
@@ -199,14 +199,26 @@ class ir_attachment(osv.osv):
:param values : dict of values to create or write an ir_attachment
:return mime : string indicating the mimetype, or application/octet-stream by default
"""
- mimetype = 'application/octet-stream'
- if values.get('datas_fname'):
+ mimetype = None
+ if values.get('mimetype'):
+ mimetype = values['mimetype']
+ if not mimetype and values.get('datas_fname'):
mimetype = mimetypes.guess_type(values['datas_fname'])[0]
- if values.get('url'):
+ if not mimetype and values.get('url'):
mimetype = mimetypes.guess_type(values['url'])[0]
if values.get('datas') and (not mimetype or mimetype == 'application/octet-stream'):
mimetype = guess_mimetype(values['datas'].decode('base64'))
- return mimetype
+ return mimetype or 'application/octet-stream'
+
+ def _check_contents(self, cr, uid, values, context=None):
+ mimetype = values['mimetype'] = self._compute_mimetype(values)
+ needs_escape = 'htm' in mimetype or '/ht' in mimetype # hta, html, xhtml, etc.
+ if needs_escape and not self.pool['res.users']._is_admin(cr, uid, [uid]):
+ if 'datas' in values:
+ values['datas'] = html_escape(values['datas'].decode('base64')).encode('base64')
+ else:
+ values['mimetype'] = 'text/plain'
+ return values
def _index(self, cr, uid, bin_data, datas_fname, file_type):
""" compute the index content of the given filename, or binary data.
@@ -382,6 +394,8 @@ class ir_attachment(osv.osv):
# remove computed field depending of datas
for field in ['file_size', 'checksum']:
vals.pop(field, False)
+ if 'mimetype' in vals or 'datas' in vals:
+ vals = self._check_contents(cr, uid, vals, context=context)
return super(ir_attachment, self).write(cr, uid, ids, vals, context)
def copy(self, cr, uid, id, default=None, context=None):
@@ -409,9 +423,7 @@ class ir_attachment(osv.osv):
# remove computed field depending of datas
for field in ['file_size', 'checksum']:
values.pop(field, False)
- # if mimetype not given, compute it !
- if 'mimetype' not in values:
- values['mimetype'] = self._compute_mimetype(values)
+ values = self._check_contents(cr, uid, values, context=context)
self.check(cr, uid, [], mode='write', context=context, values=values)
return super(ir_attachment, self).create(cr, uid, values, context)
diff --git a/openerp/addons/base/res/res_partner.py b/openerp/addons/base/res/res_partner.py
index d3eab722c19..3a85aeccdb1 100644
--- a/openerp/addons/base/res/res_partner.py
+++ b/openerp/addons/base/res/res_partner.py
@@ -198,13 +198,11 @@ class Partner(models.Model, FormatAddress):
# image: all image fields are base64 encoded and PIL-supported
image = fields.Binary("Image", attachment=True,
help="This field holds the image used as avatar for this contact, limited to 1024x1024px",)
- image_medium = fields.Binary("Medium-sized image",
- compute='_compute_images', inverse='_inverse_image_medium', store=True, attachment=True,
+ image_medium = fields.Binary("Medium-sized image", attachment=True,
help="Medium-sized image of this contact. It is automatically "\
"resized as a 128x128px image, with aspect ratio preserved. "\
"Use this field in form views or some kanban views.")
- image_small = fields.Binary("Small-sized image",
- compute='_compute_images', inverse='_inverse_image_small', store=True, attachment=True,
+ image_small = fields.Binary("Small-sized image", attachment=True,
help="Small-sized image of this contact. It is automatically "\
"resized as a 64x64px image, with aspect ratio preserved. "\
"Use this field anywhere a small image is required.")
@@ -238,20 +236,6 @@ class Partner(models.Model, FormatAddress):
else:
partner.commercial_partner_id = partner.parent_id.commercial_partner_id
- @api.depends('image')
- def _compute_images(self):
- for rec in self:
- rec.image_medium = tools.image_resize_image_medium(rec.image)
- rec.image_small = tools.image_resize_image_small(rec.image)
-
- def _inverse_image_medium(self):
- for rec in self:
- rec.image = tools.image_resize_image_big(rec.image_medium)
-
- def _inverse_image_small(self):
- for rec in self:
- rec.image = tools.image_resize_image_big(rec.image_small)
-
@api.model
def _get_default_image(self, partner_type, is_company, parent_id):
if getattr(threading.currentThread(), 'testing', False) or self._context.get('install_mode'):
@@ -484,10 +468,13 @@ class Partner(models.Model, FormatAddress):
companies = set(user.company_id for user in partner.user_ids)
if len(companies) > 1 or company not in companies:
raise UserError(_("You can not change the company as the partner/user has multiple user linked with different companies."))
+ tools.image_resize_images(vals)
result = super(Partner, self).write(vals)
for partner in self:
partner._fields_sync(vals)
+ if any(u.has_group('base.group_user') for u in partner.user_ids):
+ self.env['res.users'].check_access_rights('write')
return result
@api.model
@@ -501,6 +488,7 @@ class Partner(models.Model, FormatAddress):
partner = super(Partner, self).create(vals)
partner._fields_sync(vals)
partner._handle_first_contact_creation()
+ tools.image_resize_images(vals)
return partner
@api.multi
diff --git a/openerp/addons/base/res/res_users.py b/openerp/addons/base/res/res_users.py
index b698db693c7..876598ffb0a 100644
--- a/openerp/addons/base/res/res_users.py
+++ b/openerp/addons/base/res/res_users.py
@@ -174,7 +174,7 @@ class Users(models.Model):
__uid_cache = defaultdict(dict) # {dbname: {uid: password}}
# User can write on a few of his own fields (but not his groups for example)
- SELF_WRITEABLE_FIELDS = ['password', 'signature', 'action_id', 'company_id', 'email', 'name', 'image', 'image_medium', 'image_small', 'lang', 'tz']
+ SELF_WRITEABLE_FIELDS = ['signature', 'action_id', 'company_id', 'email', 'name', 'image', 'image_medium', 'image_small', 'lang', 'tz']
# User can read a few of his own fields
SELF_READABLE_FIELDS = ['signature', 'company_id', 'login', 'email', 'name', 'image', 'image_medium', 'image_small', 'lang', 'tz', 'tz_offset', 'groups_id', 'partner_id', '__last_update', 'action_id']
@@ -510,8 +510,8 @@ class Users(models.Model):
"""
self.check(self._cr.dbname, self._uid, old_passwd)
if new_passwd:
- # do not use self.env.user here, since it has uid=SUPERUSER_ID
- return self.browse(self._uid).write({'password': new_passwd})
+ # use self.env.user here, because it has uid=SUPERUSER_ID
+ return self.env.user.write({'password': new_passwd})
raise UserError(_("Setting empty passwords is not allowed for security reasons!"))
@api.multi
diff --git a/openerp/modules/loading.py b/openerp/modules/loading.py
index 0ccb599b226..017fd411ea1 100644
--- a/openerp/modules/loading.py
+++ b/openerp/modules/loading.py
@@ -418,7 +418,10 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
t0 = time.time()
t0_sql = openerp.sql_db.sql_counter
if openerp.tools.config['test_enable']:
- cr.execute("SELECT name FROM ir_module_module WHERE state='installed'")
+ if update_module:
+ cr.execute("SELECT name FROM ir_module_module WHERE state='installed' and name = ANY(%s)", (processed_modules,))
+ else:
+ cr.execute("SELECT name FROM ir_module_module WHERE state='installed'")
for module_name in cr.fetchall():
report.record_result(openerp.modules.module.run_unit_tests(module_name[0], cr.dbname, position=runs_post_install))
_logger.log(25, "All post-tested in %.2fs, %s queries", time.time() - t0, openerp.sql_db.sql_counter - t0_sql)
diff --git a/openerp/tools/image.py b/openerp/tools/image.py
index c6d0ad9aa43..f17623cbe6e 100644
--- a/openerp/tools/image.py
+++ b/openerp/tools/image.py
@@ -256,6 +256,24 @@ def image_get_resized_images(base64_source, return_big=False, return_medium=True
return_dict[small_name] = image_resize_image_small(base64_source, avoid_if_small=avoid_resize_small)
return return_dict
+def image_resize_images(vals, big_name='image', medium_name='image_medium', small_name='image_small'):
+ """ Update ``vals`` with image fields resized as expected. """
+ if big_name in vals:
+ vals.update(image_get_resized_images(vals[big_name],
+ return_big=True, return_medium=True, return_small=True,
+ big_name=big_name, medium_name=medium_name, small_name=small_name,
+ avoid_resize_big=True, avoid_resize_medium=False, avoid_resize_small=False))
+ elif medium_name in vals:
+ vals.update(image_get_resized_images(vals[medium_name],
+ return_big=True, return_medium=True, return_small=True,
+ big_name=big_name, medium_name=medium_name, small_name=small_name,
+ avoid_resize_big=True, avoid_resize_medium=True, avoid_resize_small=False))
+ elif small_name in vals:
+ vals.update(image_get_resized_images(vals[small_name],
+ return_big=True, return_medium=True, return_small=True,
+ big_name=big_name, medium_name=medium_name, small_name=small_name,
+ avoid_resize_big=True, avoid_resize_medium=True, avoid_resize_small=True))
+
if __name__=="__main__":
import sys