[MERGE] forward port of branch 8.0 up to d80376a
authorChristophe Simonis <chs@odoo.com>
Tue, 21 Oct 2014 12:33:36 +0000 (14:33 +0200)
committerChristophe Simonis <chs@odoo.com>
Tue, 21 Oct 2014 12:33:36 +0000 (14:33 +0200)
1  2 
addons/mail/mail_thread.py
addons/point_of_sale/report/pos_order_report.py
addons/point_of_sale/static/src/js/models.js
addons/web/controllers/main.py
openerp/addons/base/res/res_partner.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:
@@@ -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;
@@@ -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
@@@ -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,
  
          # 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 ''