From: Christophe Simonis Date: Tue, 21 Oct 2014 12:33:36 +0000 (+0200) Subject: [MERGE] forward port of branch 8.0 up to d80376a X-Git-Url: http://git.inspyration.org/?a=commitdiff_plain;h=262eb662de489d98b76dece7305b594399161014;p=odoo%2Fodoo.git [MERGE] forward port of branch 8.0 up to d80376a --- 262eb662de489d98b76dece7305b594399161014 diff --cc addons/mail/mail_thread.py index 495b841,7733239..4356ee6 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@@ -1611,8 -1537,10 +1611,10 @@@ class mail_thread(osv.AbstractModel) self.message_subscribe(cr, uid, [thread_id], list(partner_to_subscribe), context=context) # _mail_flat_thread: automatically set free messages to the first posted message - if self._mail_flat_thread and not parent_id and thread_id: + if self._mail_flat_thread and model and not parent_id and thread_id: - message_ids = mail_message.search(cr, uid, ['&', ('res_id', '=', thread_id), ('model', '=', model)], context=context, order="id ASC", limit=1) + message_ids = mail_message.search(cr, uid, ['&', ('res_id', '=', thread_id), ('model', '=', model), ('type', '=', 'email')], context=context, order="id ASC", limit=1) + if not message_ids: + message_ids = message_ids = mail_message.search(cr, uid, ['&', ('res_id', '=', thread_id), ('model', '=', model)], context=context, order="id ASC", limit=1) parent_id = message_ids and message_ids[0] or False # we want to set a parent: force to set the parent_id to the oldest ancestor, to avoid having more than 1 level of thread elif parent_id: diff --cc addons/point_of_sale/static/src/js/models.js index 31ede08,17eb0f6..4719b82 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@@ -120,233 -119,6 +120,234 @@@ function openerp_pos_models(instance, m this.pos_widget.loading_message(_t('Loading')+' '+model,this._load_progress); return new instance.web.Model(model).query(fields).filter(domain).context(ctx).all() }, + + // Server side model loaders. This is the list of the models that need to be loaded from + // the server. The models are loaded one by one by this list's order. The 'loaded' callback + // is used to store the data in the appropriate place once it has been loaded. This callback + // can return a deferred that will pause the loading of the next module. + // a shared temporary dictionary is available for loaders to communicate private variables + // used during loading such as object ids, etc. + models: [ + { + model: 'res.users', + fields: ['name','company_id'], + domain: function(self){ return [['id','=',self.session.uid]]; }, + loaded: function(self,users){ self.user = users[0]; }, + },{ + model: 'res.company', + fields: [ 'currency_id', 'email', 'website', 'company_registry', 'vat', 'name', 'phone', 'partner_id' , 'country_id'], + domain: function(self){ return [['id','=',self.user.company_id[0]]]; }, + loaded: function(self,companies){ self.company = companies[0]; }, + },{ + model: 'product.uom', + fields: [], + domain: null, + loaded: function(self,units){ + self.units = units; + var units_by_id = {}; + for(var i = 0, len = units.length; i < len; i++){ + units_by_id[units[i].id] = units[i]; + units[i].groupable = ( units[i].category_id[0] === 1 ); + units[i].is_unit = ( units[i].id === 1 ); + } + self.units_by_id = units_by_id; + } + },{ + model: 'res.users', + fields: ['name','ean13'], + domain: null, + loaded: function(self,users){ self.users = users; }, + },{ + model: 'res.partner', + fields: ['name','street','city','state_id','country_id','vat','phone','zip','mobile','email','ean13','write_date'], + domain: null, + loaded: function(self,partners){ + self.partners = partners; + self.db.add_partners(partners); + }, + },{ + model: 'res.country', + fields: ['name'], + loaded: function(self,countries){ + self.countries = countries; + self.company.country = null; + for (var i = 0; i < countries.length; i++) { + if (countries[i].id === self.company.country_id[0]){ + self.company.country = countries[i]; + } + } + }, + },{ + model: 'account.tax', + fields: ['name','amount', 'price_include', 'type'], + domain: null, + loaded: function(self,taxes){ self.taxes = taxes; }, + },{ + model: 'pos.session', + fields: ['id', 'journal_ids','name','user_id','config_id','start_at','stop_at','sequence_number','login_number'], + domain: function(self){ return [['state','=','opened'],['user_id','=',self.session.uid]]; }, + loaded: function(self,pos_sessions){ + self.pos_session = pos_sessions[0]; + + var orders = self.db.get_orders(); + for (var i = 0; i < orders.length; i++) { + self.pos_session.sequence_number = Math.max(self.pos_session.sequence_number, orders[i].data.sequence_number+1); + } + }, + },{ + model: 'pos.config', + fields: [], + domain: function(self){ return [['id','=', self.pos_session.config_id[0]]]; }, + loaded: function(self,configs){ + self.config = configs[0]; + self.config.use_proxy = self.config.iface_payment_terminal || + self.config.iface_electronic_scale || + self.config.iface_print_via_proxy || + self.config.iface_scan_via_proxy || + self.config.iface_cashdrawer; + + self.barcode_reader.add_barcode_patterns({ + 'product': self.config.barcode_product, + 'cashier': self.config.barcode_cashier, + 'client': self.config.barcode_customer, + 'weight': self.config.barcode_weight, + 'discount': self.config.barcode_discount, + 'price': self.config.barcode_price, + }); + + if (self.config.company_id[0] !== self.user.company_id[0]) { + throw new Error(_t("Error: The Point of Sale User must belong to the same company as the Point of Sale. You are probably trying to load the point of sale as an administrator in a multi-company setup, with the administrator account set to the wrong company.")); + } + }, + },{ + model: 'stock.location', + fields: [], + domain: function(self){ return [['id','=', self.config.stock_location_id[0]]]; }, + loaded: function(self, locations){ self.shop = locations[0]; }, + },{ + model: 'product.pricelist', + fields: ['currency_id'], + domain: function(self){ return [['id','=',self.config.pricelist_id[0]]]; }, + loaded: function(self, pricelists){ self.pricelist = pricelists[0]; }, + },{ + model: 'res.currency', + fields: ['symbol','position','rounding','accuracy'], + domain: function(self){ return [['id','=',self.pricelist.currency_id[0]]]; }, + loaded: function(self, currencies){ + self.currency = currencies[0]; + }, + },{ + model: 'product.packaging', + fields: ['ean','product_tmpl_id'], + domain: null, + loaded: function(self, packagings){ + self.db.add_packagings(packagings); + }, + },{ + model: 'pos.category', + fields: ['id','name','parent_id','child_id','image'], + domain: null, + loaded: function(self, categories){ + self.db.add_categories(categories); + }, + },{ + model: 'product.product', + fields: ['display_name', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', 'default_code', + 'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description', + 'product_tmpl_id'], + domain: function(self){ return [['sale_ok','=',true],['available_in_pos','=',true]]; }, + context: function(self){ return { pricelist: self.pricelist.id, display_default_code: false }; }, + loaded: function(self, products){ + self.db.add_products(products); + }, + },{ + model: 'account.bank.statement', + fields: ['account_id','currency','journal_id','state','name','user_id','pos_session_id'], + domain: function(self){ return [['state', '=', 'open'],['pos_session_id', '=', self.pos_session.id]]; }, + loaded: function(self, bankstatements, tmp){ + self.bankstatements = bankstatements; + + tmp.journals = []; + _.each(bankstatements,function(statement){ + tmp.journals.push(statement.journal_id[0]); + }); + }, + },{ + model: 'account.journal', + fields: [], + domain: function(self,tmp){ return [['id','in',tmp.journals]]; }, + loaded: function(self, journals){ + self.journals = journals; + + // associate the bank statements with their journals. + var bankstatements = self.bankstatements; + for(var i = 0, ilen = bankstatements.length; i < ilen; i++){ + for(var j = 0, jlen = journals.length; j < jlen; j++){ + if(bankstatements[i].journal_id[0] === journals[j].id){ + bankstatements[i].journal = journals[j]; + } + } + } + self.cashregisters = bankstatements; + }, + },{ + label: 'fonts', + loaded: function(self){ + var fonts_loaded = new $.Deferred(); + + // Waiting for fonts to be loaded to prevent receipt printing + // from printing empty receipt while loading Inconsolata + // ( The font used for the receipt ) + waitForWebfonts(['Lato','Inconsolata'], function(){ + fonts_loaded.resolve(); + }); + + // The JS used to detect font loading is not 100% robust, so + // do not wait more than 5sec + setTimeout(function(){ + fonts_loaded.resolve(); + },5000); + + return fonts_loaded; + }, + },{ + label: 'pictures', + loaded: function(self){ + self.company_logo = new Image(); + var logo_loaded = new $.Deferred(); + self.company_logo.onload = function(){ + var img = self.company_logo; + var ratio = 1; + var targetwidth = 300; + var maxheight = 150; + if( img.width !== targetwidth ){ + ratio = targetwidth / img.width; + } + if( img.height * ratio > maxheight ){ + ratio = maxheight / img.height; + } + var width = Math.floor(img.width * ratio); + var height = Math.floor(img.height * ratio); + var c = document.createElement('canvas'); + c.width = width; + c.height = height + var ctx = c.getContext('2d'); + ctx.drawImage(self.company_logo,0,0, width, height); - ++ + self.company_logo_base64 = c.toDataURL(); + logo_loaded.resolve(); + }; + self.company_logo.onerror = function(){ + logo_loaded.reject(); + }; ++ self.company_logo.crossOrigin = "anonymous"; + self.company_logo.src = '/web/binary/company_logo' +'?_'+Math.random(); + + return logo_loaded; + }, + }, + ], + // loads all the needed data on the sever. returns a deferred indicating when all the data has loaded. load_server_data: function(){ var self = this; diff --cc addons/web/controllers/main.py index 908f56c,295783d..c057f7c --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@@ -1179,10 -1471,9 +1179,10 @@@ class Binary(http.Controller) '/web/binary/company_logo', '/logo', '/logo.png', - ], type='http', auth="none") + ], type='http', auth="none", cors="*") def company_logo(self, dbname=None, **kw): - # TODO add etag, refactor to use /image code for etag + imgname = 'logo.png' + placeholder = functools.partial(get_module_resource, 'web', 'static', 'src', 'img') uid = None if request.session.db: dbname = request.session.db diff --cc openerp/addons/base/res/res_partner.py index 70f08ac,27a497c..4f21ea0 --- a/openerp/addons/base/res/res_partner.py +++ b/openerp/addons/base/res/res_partner.py @@@ -233,8 -233,9 +233,9 @@@ class res_partner(osv.Model, format_add 'date': fields.date('Date', select=1), 'title': fields.many2one('res.partner.title', 'Title'), 'parent_id': fields.many2one('res.partner', 'Related Company', select=True), + 'parent_name': fields.related('parent_id', 'name', type='char', readonly=True, string='Parent name'), 'child_ids': fields.one2many('res.partner', 'parent_id', 'Contacts', domain=[('active','=',True)]), # force "active_test" domain to bypass _search() override - 'ref': fields.char('Reference', size=64, select=1), + 'ref': fields.char('Contact Reference', select=1), 'lang': fields.selection(_lang_get, 'Language', help="If the selected language is loaded in the system, all documents related to this contact will be printed in this language. If not, it will be English."), 'tz': fields.selection(_tz_get, 'Timezone', size=64, @@@ -784,14 -774,14 +785,14 @@@ # get the information that will be injected into the display format # get the address format - address_format = address.country_id and address.country_id.address_format or \ + address_format = address.country_id.address_format or \ "%(street)s\n%(street2)s\n%(city)s %(state_code)s %(zip)s\n%(country_name)s" args = { - 'state_code': address.state_id and address.state_id.code or '', - 'state_name': address.state_id and address.state_id.name or '', - 'country_code': address.country_id and address.country_id.code or '', - 'country_name': address.country_id and address.country_id.name or '', - 'company_name': address.parent_id and address.parent_name or '', + 'state_code': address.state_id.code or '', + 'state_name': address.state_id.name or '', + 'country_code': address.country_id.code or '', + 'country_name': address.country_id.name or '', - 'company_name': address.parent_id.name or '', ++ 'company_name': address.parent_name or '', } for field in self._address_fields(cr, uid, context=context): args[field] = getattr(address, field) or ''