[MERGE] Forward port of addons 7.0 until 8584.

bzr revid: tde@openerp.com-20130115100302-rhhla88naie18hug
bzr revid: tde@openerp.com-20130115141932-60y8i1mbi2x88ess
This commit is contained in:
Thibault Delavallée 2013-01-15 15:19:32 +01:00
commit 1f050cd40e
24 changed files with 218 additions and 140 deletions

View File

@ -1137,9 +1137,11 @@ class account_move_line(osv.osv):
if context is None:
context = {}
if vals.get('move_id', False):
company_id = self.pool.get('account.move').read(cr, uid, vals['move_id'], ['company_id']).get('company_id', False)
if company_id:
vals['company_id'] = company_id[0]
move = self.pool.get('account.move').browse(cr, uid, vals['move_id'], context=context)
if move.company_id:
vals['company_id'] = move.company_id.id
if move.date and not vals.get('date'):
vals['date'] = move.date
if ('account_id' in vals) and not account_obj.read(cr, uid, vals['account_id'], ['active'])['active']:
raise osv.except_osv(_('Bad Account!'), _('You cannot use an inactive account.'))
if 'journal_id' in vals and vals['journal_id']:

View File

@ -22,8 +22,8 @@
<label for="quantity_max"/>
<div>
<field name="quantity_max"/>
<div attrs="{'invisible': [('quantity','=',0)]}" class="oe_grey">
<field name="quantity" class="oe_inline"/> Units Done
<div attrs="{'invisible': [('hours_quantity','=',0)]}" class="oe_grey">
<field name="hours_quantity" class="oe_inline"/> Units Consumed
</div>
<div attrs="{'invisible': [('quantity_max','=',0)]}" class="oe_grey">
<field name="remaining_hours" class="oe_inline"/> Units Remaining

View File

@ -166,8 +166,8 @@ class res_partner(osv.osv):
action_text = partner.latest_followup_level_id_without_lit.manual_action_note or ''
#Check date: put the minimum date if it existed already
action_date = (partner.payment_next_action_date and min(partner.payment_next_action_date, fields.date.context_today(cr, uid, context))
) or fields.date.context_today(cr, uid, context)
action_date = (partner.payment_next_action_date and min(partner.payment_next_action_date, fields.date.context_today(self, cr, uid, context=context))
) or fields.date.context_today(self, cr, uid, context=context)
# Check responsible: if partner has not got a responsible already, take from follow-up
responsible_id = False
@ -218,9 +218,9 @@ class res_partner(osv.osv):
unknown_mails = unknown_mails + 1
action_text = _("Email not sent because of email address of partner not filled in")
if partner.payment_next_action_date:
payment_action_date = min(fields.date.context_today(cr, uid, ctx), partner.payment_next_action_date)
payment_action_date = min(fields.date.context_today(self, cr, uid, context=ctx), partner.payment_next_action_date)
else:
payment_action_date = fields.date.context_today(cr, uid, ctx)
payment_action_date = fields.date.context_today(self, cr, uid, context=ctx)
if partner.payment_next_action:
payment_next_action = partner.payment_next_action + " \n " + action_text
else:
@ -242,7 +242,7 @@ class res_partner(osv.osv):
followup_table = ''
if partner.unreconciled_aml_ids:
company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id
current_date = fields.date.context_today(cr, uid, context)
current_date = fields.date.context_today(self, cr, uid, context=context)
rml_parse = account_followup_print.report_rappel(cr, uid, "followup_rml_parser")
final_res = rml_parse._lines_get_with_partner(partner, company.id)
@ -311,7 +311,7 @@ class res_partner(osv.osv):
'''
res = {}
company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id
current_date = fields.date.context_today(cr, uid, context)
current_date = fields.date.context_today(self, cr, uid, context=context)
for partner in self.browse(cr, uid, ids, context=context):
worst_due_date = False
amount_due = amount_overdue = 0.0

View File

@ -87,12 +87,13 @@ class crm_lead(base_stage, format_address, osv.osv):
def create(self, cr, uid, vals, context=None):
if context is None:
context = {}
if not vals.get('stage_id') and vals.get('section_id'):
if not vals.get('stage_id'):
ctx = context.copy()
ctx['default_section_id'] = vals['section_id']
if vals.get('section_id'):
ctx['default_section_id'] = vals['section_id']
if vals.get('type'):
ctx['default_type'] = vals['type']
vals['stage_id'] = self._get_default_stage_id(cr, uid, context=ctx)
elif not vals.get('stage_id') and context.get('default_section_id'):
vals['stage_id'] = self._get_default_stage_id(cr, uid, context=context)
return super(crm_lead, self).create(cr, uid, vals, context=context)
def _get_default_section_id(self, cr, uid, context=None):
@ -974,16 +975,17 @@ class crm_lead(base_stage, format_address, osv.osv):
if custom_values is None: custom_values = {}
desc = html2plaintext(msg.get('body')) if msg.get('body') else ''
custom_values.update({
defaults = {
'name': msg.get('subject') or _("No Subject"),
'description': desc,
'email_from': msg.get('from'),
'email_cc': msg.get('cc'),
'user_id': False,
})
}
if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES):
custom_values['priority'] = msg.get('priority')
return super(crm_lead, self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
defaults['priority'] = msg.get('priority')
defaults.update(custom_values)
return super(crm_lead, self).message_new(cr, uid, msg, custom_values=defaults, context=context)
def message_update(self, cr, uid, ids, msg, update_vals=None, context=None):
""" Overrides mail_thread message_update that is called by the mailgateway

View File

@ -190,15 +190,16 @@ class crm_claim(base_stage, osv.osv):
"""
if custom_values is None: custom_values = {}
desc = html2plaintext(msg.get('body')) if msg.get('body') else ''
custom_values.update({
defaults = {
'name': msg.get('subject') or _("No Subject"),
'description': desc,
'email_from': msg.get('from'),
'email_cc': msg.get('cc'),
})
}
if msg.get('priority'):
custom_values['priority'] = msg.get('priority')
return super(crm_claim,self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
defaults['priority'] = msg.get('priority')
defaults.update(custom_values)
return super(crm_claim,self).message_new(cr, uid, msg, custom_values=defaults, context=context)
def message_update(self, cr, uid, ids, msg, update_vals=None, context=None):
""" Overrides mail_thread message_update that is called by the mailgateway

View File

@ -100,14 +100,15 @@ class crm_helpdesk(base_state, base_stage, osv.osv):
"""
if custom_values is None: custom_values = {}
desc = html2plaintext(msg.get('body')) if msg.get('body') else ''
custom_values.update({
defaults = {
'name': msg.get('subject') or _("No Subject"),
'description': desc,
'email_from': msg.get('from'),
'email_cc': msg.get('cc'),
'user_id': False,
})
return super(crm_helpdesk,self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
}
defaults.update(custom_values)
return super(crm_helpdesk,self).message_new(cr, uid, msg, custom_values=defaults, context=context)
def message_update(self, cr, uid, ids, msg, update_vals=None, context=None):
""" Overrides mail_thread message_update that is called by the mailgateway

View File

@ -347,16 +347,17 @@ class hr_applicant(base_stage, osv.Model):
"""
if custom_values is None: custom_values = {}
desc = html2plaintext(msg.get('body')) if msg.get('body') else ''
custom_values.update({
defaults = {
'name': msg.get('subject') or _("No Subject"),
'description': desc,
'email_from': msg.get('from'),
'email_cc': msg.get('cc'),
'user_id': False,
})
}
if msg.get('priority'):
custom_values['priority'] = msg.get('priority')
return super(hr_applicant,self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
defaults['priority'] = msg.get('priority')
defaults.update(custom_values)
return super(hr_applicant,self).message_new(cr, uid, msg, custom_values=defaults, context=context)
def message_update(self, cr, uid, ids, msg, update_vals=None, context=None):
""" Override mail_thread message_update that is called by the mailgateway

View File

@ -107,6 +107,7 @@ class lunch_order(osv.Model):
elif alert.alter_type == 'week':
#the alert is activated during some days of the week
return self.check_day(alert)
return True # alter_type == 'days' (every day)
def _default_alerts_get(self, cr, uid, context=None):
"""

View File

@ -66,7 +66,7 @@ class mail_alias(osv.Model):
'alias_name': fields.char('Alias', required=True,
help="The name of the email alias, e.g. 'jobs' "
"if you want to catch emails for <jobs@example.my.openerp.com>",),
'alias_model_id': fields.many2one('ir.model', 'Aliased Model', required=True,
'alias_model_id': fields.many2one('ir.model', 'Aliased Model', required=True, ondelete="cascade",
help="The model (OpenERP Document Kind) to which this alias "
"corresponds. Any incoming email that does not reply to an "
"existing record will cause the creation of a new record "

View File

@ -78,7 +78,7 @@ class mail_message(osv.Model):
for message in self.read(cr, uid, ids, ['model', 'res_id'], context=context):
if not message.get('model') or not message.get('res_id') or not self.pool.get(message['model']):
continue
result[message['id']] = self._shorten_name(self.pool.get(message['model']).name_get(cr, SUPERUSER_ID, [message['res_id']], context=context)[0][1])
result[message['id']] = self.pool.get(message['model']).name_get(cr, SUPERUSER_ID, [message['res_id']], context=context)[0][1]
return result
def _get_to_read(self, cr, uid, ids, name, arg, context=None):

View File

@ -839,6 +839,7 @@ class mail_thread(osv.AbstractModel):
ids = partner_obj.search(cr, SUPERUSER_ID, [('email', '=', email)], context=context)
if ids:
partner_ids.append(ids[0])
partner_id = ids[0]
else:
partner_id = partner_obj.create(cr, uid, {
'name': name or email,

View File

@ -62,16 +62,6 @@ class res_users(osv.Model):
# create aliases for all users and avoid constraint errors
self.pool.get('mail.alias').migrate_to_alias(cr, self._name, self._table, super(res_users, self)._auto_init,
self._columns['alias_id'], 'login', alias_force_key='id', context=context)
# make already existing users follow themselves, using SQL to avoid using the ORM during the auto_init
cr.execute(""" SELECT p.id FROM res_partner p
LEFT JOIN mail_followers n
ON (n.partner_id = p.id AND n.res_model = 'res.partner' AND n.res_id = p.id)
WHERE n.id IS NULL
""")
params = [(res[0], res[0]) for res in cr.fetchall()]
cr.executemany(""" INSERT INTO mail_followers (partner_id, res_model, res_id)
VALUES (%s, 'res.partner', %s)
""", params)
def create(self, cr, uid, data, context=None):
# create default alias same as the login
@ -81,12 +71,11 @@ class res_users(osv.Model):
mail_alias = self.pool.get('mail.alias')
alias_id = mail_alias.create_unique_alias(cr, uid, {'alias_name': data['login']}, model_name=self._name, context=context)
data['alias_id'] = alias_id
data.pop('alias_name', None) # prevent errors during copy()
data.pop('alias_name', None) # prevent errors during copy()
# create user that follows its related partner
# create user
user_id = super(res_users, self).create(cr, uid, data, context=context)
user = self.browse(cr, uid, user_id, context=context)
self.pool.get('res.partner').message_subscribe(cr, uid, [user.partner_id.id], [user.partner_id.id], context=context)
# alias
mail_alias.write(cr, SUPERUSER_ID, [alias_id], {"alias_force_thread_id": user_id}, context)
# create a welcome message
@ -150,6 +139,9 @@ class res_users(osv.Model):
self.pool.get('res.partner').message_subscribe(cr, uid, [partner_id], partner_ids, subtype_ids=subtype_ids, context=context)
return True
def message_create_partners_from_emails(self, cr, uid, emails, context=None):
return self.pool.get('res.partner').message_create_partners_from_emails(cr, uid, emails, context=context)
class res_users_mail_group(osv.Model):
""" Update of res.users class

View File

@ -580,6 +580,9 @@ openerp.mail = function (session) {
}
});
var deferred_check = $.Deferred();
if (emails.length == 0) {
return deferred_check.resolve(partners_from);
}
self.parent_thread.ds_thread._model.call('message_create_partners_from_emails', [emails]).then(function (partners) {
partners_from = _.clone(partners.partner_ids);
var deferreds = [];

View File

@ -365,11 +365,3 @@ class test_mail_access_rights(TestMailBase):
{'subject': 'Subject', 'body': 'Body text'},
{'default_composition_mode': 'reply', 'default_parent_id': pigs_msg_id})
mail_compose.send_mail(cr, user_raoul_id, [compose_id])
# Do: Raoul writes on its own profile, ok because follower of its partner
self.res_users.message_post(cr, user_raoul_id, user_raoul_id, body='I love Raoul')
self.res_partner.message_post(cr, user_raoul_id, partner_raoul_id, body='I love Raoul')
compose_id = mail_compose.create(cr, user_raoul_id,
{'subject': 'Subject', 'body': 'Body text', 'partner_ids': []},
{'default_composition_mode': 'comment', 'default_model': 'res.users', 'default_res_id': self.user_raoul_id})
mail_compose.send_mail(cr, user_raoul_id, [compose_id])

View File

@ -1,8 +1,34 @@
function openerp_pos_db(instance, module){
/* PosLS is a LocalStorage based implementation of the point of sale database,
it performs better for few products, but does not scale beyond 500 products.
*/
/* The db module was intended to be used to store all the data needed to run the Point
* of Sale in offline mode. (Products, Categories, Orders, ...) It would also use WebSQL
* or IndexedDB to make the searching and sorting products faster. It turned out not to be
* a so good idea after all.
*
* First it is difficult to make the Point of Sale truly independant of the server. A lot
* of functionality cannot realistically run offline, like generating invoices.
*
* IndexedDB turned out to be complicated and slow as hell, and loading all the data at the
* start made the point of sale take forever to load over small connections.
*
* LocalStorage has a hard 5.0MB on chrome. For those kind of sizes, it is just better
* to put the data in memory and it's not too big to download each time you launch the PoS.
*
* So at this point we are dropping the support for offline mode, and this module doesn't really
* make sense anymore. But if at some point you want to store millions of products and if at
* that point indexedDB has improved to the point it is usable, you can just implement this API.
*
* You would also need to change the way the models are loaded at the start to not reload all your
* product data.
*/
/* PosLS is a localstorage based implementation of the point of sale database.
* FIXME: The Products definitions and categories are stored on the locastorage even tough they're
* always reloaded at launch. This could induce a slowdown because the data needs to be reparsed from
* JSON before each operation. If you have a huge amount of products (around 25000) it can also
* blow the 5.0MB localstorage limit.
*/
module.PosLS = instance.web.Class.extend({
name: 'openerp_pos_ls', //the prefix of the localstorage data
limit: 100, // the maximum number of results returned by a search
@ -110,6 +136,8 @@ function openerp_pos_db(instance, module){
},
/* saves a record store to the database */
save: function(store,data){
var str_data = JSON.stringify(data);
console.log('Storing '+ Math.round(str_data.length/1024.0)+' KB of data to store: '+store);
localStorage[this.name + '_' + store] = JSON.stringify(data);
this.cache[store] = data;
},

View File

@ -438,7 +438,7 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
// The barcode readers acts as a keyboard, we catch all keyup events and try to find a
// barcode sequence in the typed keys, then act accordingly.
$('body').delegate('','keyup', function (e){
console.log('keyup:'+String.fromCharCode(e.keyCode)+' '+e.keyCode,e);
//console.log('keyup:'+String.fromCharCode(e.keyCode)+' '+e.keyCode,e);
//We only care about numbers
if (e.keyCode >= 48 && e.keyCode < 58){

View File

@ -125,7 +125,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
return self.fetch('res.partner', ['name','ean13'], [['ean13', '!=', false]]);
}).then(function(partners){
self.set('partner_list',partners);
console.log('Loaded partners:',partners);
return self.fetch('account.tax', ['amount', 'price_include', 'type']);
}).then(function(taxes){
@ -242,7 +241,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
// saves the order locally and try to send it to the backend. 'record' is a bizzarely defined JSON version of the Order
push_order: function(record) {
console.log('PUSHING NEW ORDER:',record);
this.db.add_order(record);
this.flush();
},
@ -259,7 +257,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
// it has been confirmed that they have been sent correctly.
flush: function() {
//TODO make the mutex work
console.log('FLUSH');
//this makes sure only one _int_flush is called at the same time
/*
return this.flush_mutex.exec(_.bind(function() {
@ -275,7 +272,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
var self = this;
var orders = this.db.get_orders();
self.set('nbr_pending_operations',orders.length);
console.log('TRYING TO FLUSH ORDER:',index,'Of',orders.length);
var order = orders[index];
if(!order){
@ -291,7 +287,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
})
.done(function(){
//remove from db if success
console.log('Order successfully sent');
self.db.remove_order(order.id);
self._flush(index);
});

View File

@ -864,6 +864,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.pos.bind('change:selectedOrder', this.change_selected_order, this);
this.bindPaymentLineEvents();
this.bind_orderline_events();
this.paymentlinewidgets = [];
},
show: function(){
this._super();
@ -932,18 +933,24 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
},
addPaymentLine: function(newPaymentLine) {
var self = this;
var x = new module.PaymentlineWidget(null, {
var l = new module.PaymentlineWidget(null, {
payment_line: newPaymentLine
});
x.on('delete_payment_line', self, function(r) {
l.on('delete_payment_line', self, function(r) {
self.deleteLine(r);
});
x.appendTo(this.$('#paymentlines'));
l.appendTo(this.$('#paymentlines'));
this.paymentlinewidgets.push(l);
this.$('.paymentline-amount input:last').focus();
},
renderElement: function() {
this._super();
this.$('#paymentlines').empty();
for(var i = 0, len = this.paymentlinewidgets.length; i < len; i++){
this.paymentlinewidgets[i].destroy();
}
this.paymentlinewidgets = [];
this.currentPaymentLines.each(_.bind( function(paymentLine) {
this.addPaymentLine(paymentLine);
}, this));

View File

@ -165,6 +165,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this.set_numpad_state(options.numpadState);
this.pos.bind('change:selectedOrder', this.change_selected_order, this);
this.bind_orderline_events();
this.orderlinewidgets = [];
},
set_numpad_state: function(numpadState) {
if (this.numpadState) {
@ -210,6 +211,16 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
var self = this;
this._super();
// freeing subwidgets
if(this.scrollbar){
this.scrollbar.destroy();
}
for(var i = 0, len = this.orderlinewidgets.length; i < len; i++){
this.orderlinewidgets[i].destroy();
}
this.orderlinewidgets = [];
if(this.display_mode === 'maximized'){
$('.point-of-sale .order-container').css({'bottom':'0px'});
}else if(this.display_mode === 'actionbar'){
@ -227,6 +238,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
line.on('order_line_selected', self, self.update_numpad);
line.on('order_line_refreshed', self, self.update_summary);
line.appendTo($content);
self.orderlinewidgets.push(line);
}, this));
this.update_numpad();
this.update_summary();
@ -571,7 +583,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
var self = this;
this._super(parent,options);
this.model = options.model;
this.product_list = [];
this.productwidgets = [];
this.weight = options.weight || 0;
this.show_scale = options.show_scale || false;
this.next_screen = options.next_screen || false;
@ -584,7 +596,17 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
renderElement: function() {
var self = this;
this._super();
this.product_list = [];
// free subwidgets memory from previous renders
for(var i = 0, len = this.productwidgets.length; i < len; i++){
this.productwidgets[i].destroy();
}
this.productwidgets = [];
if(this.scrollbar){
this.scrollbar.destroy();
}
this.pos.get('products')
.chain()
.map(function(product) {
@ -593,7 +615,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
weight: self.weight,
click_product_action: self.click_product_action,
})
self.product_list.push(product);
self.productwidgets.push(product);
return product;
})
.invoke('appendTo', this.$('.product-list'));

View File

@ -1091,12 +1091,11 @@ class task(base_stage, osv.osv):
def create(self, cr, uid, vals, context=None):
if context is None:
context = {}
if not vals.get('stage_id') and vals.get('project_id'):
if not vals.get('stage_id'):
ctx = context.copy()
ctx['default_project_id'] = vals['project_id']
if vals.get('project_id'):
ctx['default_project_id'] = vals['project_id']
vals['stage_id'] = self._get_default_stage_id(cr, uid, context=ctx)
elif not vals.get('stage_id') and context.get('default_project_id'):
vals['stage_id'] = self._get_default_stage_id(cr, uid, context=context)
task_id = super(task, self).create(cr, uid, vals, context=context)
self._store_history(cr, uid, [task_id], context=context)
return task_id
@ -1174,11 +1173,12 @@ class task(base_stage, osv.osv):
def message_new(self, cr, uid, msg, custom_values=None, context=None):
""" Override to updates the document according to the email. """
if custom_values is None: custom_values = {}
custom_values.update({
defaults = {
'name': msg.get('subject'),
'planned_hours': 0.0,
})
return super(task,self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
}
defaults.update(custom_values)
return super(task,self).message_new(cr, uid, msg, custom_values=defaults, context=context)
def message_update(self, cr, uid, ids, msg, update_vals=None, context=None):
""" Override to update the task according to the email. """

View File

@ -11,12 +11,56 @@
<menuitem id="menu_project_management" name="Project" parent="base.menu_main_pm" sequence="1"/>
<menuitem id="base.menu_definitions" name="Configuration" parent="base.menu_main_pm" sequence="60"/>
<record id="view_task_search_form" model="ir.ui.view">
<field name="name">project.task.search.form</field>
<field name="model">project.task</field>
<field name="arch" type="xml">
<search string="Tasks">
<field name="name" string="Tasks"/>
<field name="categ_ids"/>
<separator/>
<filter icon="terp-mail-message-new" string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator/>
<filter name="draft" string="New" domain="[('state','=','draft')]" help="New Tasks" icon="terp-check"/>
<filter name="open" string="In Progress" domain="[('state','=','open')]" help="In Progress Tasks" icon="terp-camera_test"/>
<filter string="Pending" domain="[('state','=','pending')]" context="{'show_delegated':False}" help="Pending Tasks" icon="terp-gtk-media-pause"/>
<separator/>
<filter name="My project" string="Project" domain="[('project_id.user_id','=',uid)]" help="My Projects" icon="terp-check"/>
<separator/>
<filter string="My Tasks" domain="[('user_id','=',uid)]" help="My Tasks" icon="terp-personal"/>
<filter string="Unassigned Tasks" domain="[('user_id','=',False)]" help="Unassigned Tasks" icon="terp-personal-"/>
<separator/>
<filter string="Deadlines" context="{'deadline_visible': False}" domain="[('date_deadline','&lt;&gt;',False)]"
help="Show only tasks having a deadline" icon="terp-gnome-cpu-frequency-applet+"/>
<field name="project_id"/>
<field name="user_id"/>
<group expand="0" string="Group By...">
<filter string="Users" name="group_user_id" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Project" name="group_project_id" icon="terp-folder-violet" domain="[]" context="{'group_by':'project_id'}"/>
<filter string="Stage" name="group_stage_id" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}"/>
<filter string="Deadline" icon="terp-gnome-cpu-frequency-applet+" domain="[]" context="{'group_by':'date_deadline'}"/>
<filter string="Start Date" icon="terp-go-month" domain="[]" context="{'group_by':'date_start'}" groups="base.group_no_one"/>
<filter string="End Date" icon="terp-go-month" domain="[]" context="{'group_by':'date_end'}" groups="base.group_no_one"/>
</group>
</search>
</field>
</record>
<record id="act_project_project_2_project_task_all" model="ir.actions.act_window">
<field name="res_model">project.task</field>
<field name="view_type">form</field>
<field name="name">Tasks</field>
<field name="view_mode">kanban,tree,form,calendar,graph</field>
<field name="res_model">project.task</field>
<field name="view_mode">kanban,tree,form,calendar,gantt,graph</field>
<field name="context">{'search_default_project_id': [active_id], 'default_project_id': active_id}</field>
<field name="search_view_id" ref="view_task_search_form"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a new task.
</p><p>
OpenERP's project management allows you to manage the pipeline
of tasks in order to get things done efficiently. You can
track progress, discuss on tasks, attach documents, etc.
</p>
</field>
</record>
<!-- Project -->
@ -334,7 +378,6 @@
<group>
<field name="project_id" on_change="onchange_project(project_id)" context="{'default_use_tasks':1}"/>
<field name="user_id" attrs="{'readonly':[('state','in',['done', 'cancelled'])]}" options='{"no_open": True}'/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
<field name="planned_hours" widget="float_time"
groups="project.group_time_work_estimation_tasks"
on_change="onchange_planned(planned_hours, effective_hours)"/>
@ -394,6 +437,15 @@
<field name="sequence"/>
<field name="partner_id"/>
<field name="state" invisible="1"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
</group>
<group>
<group string="Gantt View">
<field name="date_start"/>
<field name="date_end"/>
</group>
<group>
</group>
</group>
</page>
</notebook>
@ -501,8 +553,8 @@
<field name="date_deadline" invisible="context.get('deadline_visible',True)"/>
<field name="stage_id" invisible="context.get('set_visible',False)"/>
<field name="state" invisible="1"/>
<field name="date_start" invisible="1" groups="base.group_no_one"/>
<field name="date_end" invisible="1" groups="base.group_no_one"/>
<field name="date_start" groups="base.group_no_one"/>
<field name="date_end" groups="base.group_no_one"/>
<field name="progress" widget="progressbar" invisible="context.get('set_visible',False)"/>
</tree>
</field>
@ -542,41 +594,6 @@
</field>
</record>
<record id="view_task_search_form" model="ir.ui.view">
<field name="name">project.task.search.form</field>
<field name="model">project.task</field>
<field name="arch" type="xml">
<search string="Tasks">
<field name="name" string="Tasks"/>
<field name="categ_ids"/>
<separator/>
<filter icon="terp-mail-message-new" string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator/>
<filter name="draft" string="New" domain="[('state','=','draft')]" help="New Tasks" icon="terp-check"/>
<filter name="open" string="In Progress" domain="[('state','=','open')]" help="In Progress Tasks" icon="terp-camera_test"/>
<filter string="Pending" domain="[('state','=','pending')]" context="{'show_delegated':False}" help="Pending Tasks" icon="terp-gtk-media-pause"/>
<separator/>
<filter name="My project" string="Project" domain="[('project_id.user_id','=',uid)]" help="My Projects" icon="terp-check"/>
<separator/>
<filter string="My Tasks" domain="[('user_id','=',uid)]" help="My Tasks" icon="terp-personal"/>
<filter string="Unassigned Tasks" domain="[('user_id','=',False)]" help="Unassigned Tasks" icon="terp-personal-"/>
<separator/>
<filter string="Deadlines" context="{'deadline_visible': False}" domain="[('date_deadline','&lt;&gt;',False)]"
help="Show only tasks having a deadline" icon="terp-gnome-cpu-frequency-applet+"/>
<field name="project_id"/>
<field name="user_id"/>
<group expand="0" string="Group By...">
<filter string="Users" name="group_user_id" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Project" name="group_project_id" icon="terp-folder-violet" domain="[]" context="{'group_by':'project_id'}"/>
<filter string="Stage" name="group_stage_id" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}"/>
<filter string="Deadline" icon="terp-gnome-cpu-frequency-applet+" domain="[]" context="{'group_by':'date_deadline'}"/>
<filter string="Start Date" icon="terp-go-month" domain="[]" context="{'group_by':'date_start'}" groups="base.group_no_one"/>
<filter string="End Date" icon="terp-go-month" domain="[]" context="{'group_by':'date_end'}" groups="base.group_no_one"/>
</group>
</search>
</field>
</record>
<record id="analytic_account_inherited_form" model="ir.ui.view">
<field name="name">account.analytic.account.form.inherit</field>
<field name="model">account.analytic.account</field>
@ -593,11 +610,7 @@
<record id="action_view_task" model="ir.actions.act_window">
<field name="name">Tasks</field>
<field name="res_model">project.task</field>
<field name="view_type">form</field>
<field name="view_mode">kanban,tree,form,calendar,gantt,graph</field>
<field eval="False" name="filter"/>
<field name="view_id" eval="False"/>
<field name="context">{}</field>
<field name="search_view_id" ref="view_task_search_form"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">

View File

@ -67,12 +67,11 @@ class project_issue(base_stage, osv.osv):
def create(self, cr, uid, vals, context=None):
if context is None:
context = {}
if not vals.get('stage_id') and vals.get('project_id'):
if not vals.get('stage_id'):
ctx = context.copy()
ctx['default_project_id'] = vals['project_id']
if vals.get('project_id'):
ctx['default_project_id'] = vals['project_id']
vals['stage_id'] = self._get_default_stage_id(cr, uid, context=ctx)
elif not vals.get('stage_id') and context.get('default_project_id'):
vals['stage_id'] = self._get_default_stage_id(cr, uid, context=context)
return super(project_issue, self).create(cr, uid, vals, context=context)
def _get_default_project_id(self, cr, uid, context=None):
@ -497,17 +496,18 @@ class project_issue(base_stage, osv.osv):
desc = html2plaintext(msg.get('body')) if msg.get('body') else ''
custom_values.update({
defaults = {
'name': msg.get('subject') or _("No Subject"),
'description': desc,
'email_from': msg.get('from'),
'email_cc': msg.get('cc'),
'user_id': False,
})
}
if msg.get('priority'):
custom_values['priority'] = msg.get('priority')
defaults['priority'] = msg.get('priority')
res_id = super(project_issue, self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
defaults.update(custom_values)
res_id = super(project_issue, self).message_new(cr, uid, msg, custom_values=defaults, context=context)
return res_id
def message_update(self, cr, uid, ids, msg, update_vals=None, context=None):

View File

@ -98,13 +98,20 @@ Example: Product: this product is deprecated, do not purchase more than 5.
wizard = self.browse(cr, uid, ids)[0]
if wizard.time_unit:
product = ir_model_data.get_object(cr, uid, 'product', 'product_product_consultant')
product.write({'uom_id': wizard.time_unit.id, 'uom_po_id': wizard.time_unit.id})
product = False
try:
product = ir_model_data.get_object(cr, uid, 'product', 'product_product_consultant')
except:
#product with xml_id product_product_consultant has not been found. Don't do anything except logging the exception
import logging
_logger = logging.getLogger(__name__)
_logger.warning("Warning, product with xml_id 'product_product_consultant' hasn't been found")
if product:
product.write({'uom_id': wizard.time_unit.id, 'uom_po_id': wizard.time_unit.id})
if wizard.module_project and wizard.time_unit:
user = self.pool.get('res.users').browse(cr, uid, uid, context)
user.company_id.write({'project_time_mode_id': wizard.time_unit.id})
return {}
def onchange_task_work(self, cr, uid, ids, task_work, context=None):

View File

@ -38,7 +38,8 @@ openerp.web_analytics = function(instance) {
},
/*
* This method MUST be overriden by saas_demo and saas_trial in order to
* set the correct user type. By default, the user connected is local to the DB.
* set the correct user type. By default, the user connected is local to
* the DB (like in accounts).
*/
_get_user_type: function() {
return 'Local User';
@ -67,22 +68,28 @@ openerp.web_analytics = function(instance) {
* stored in GA. Also other modules can override it to add new custom variables
*/
initialize_custom: function() {
// Track User Access Level, Custom Variable 4 in GA with visitor level scope
// Values: 'Admin User', 'Normal User', 'Portal User', 'Anonymous User'
_gaq.push(['_setCustomVar', 4, 'User Access Level', this.user_access_level, 1]);
// Track User Type Conversion, Custom Variable 3 in GA with session level scope
// Values: 'Visitor', 'Demo', 'Online Trial', 'Online Paying', 'Local User'
_gaq.push(['_setCustomVar', 1, 'User Type Conversion', this._get_user_type(), 2]);
var self = this;
return instance.session.rpc("/web/webclient/version_info", {})
.done(function(res) {
_gaq.push(['_setCustomVar', 5, 'Version', res.server_version, 3]);
// Track User Access Level, Custom Variable 4 in GA with visitor level scope
// Values: 'Admin User', 'Normal User', 'Portal User', 'Anonymous User'
_gaq.push(['_setCustomVar', 4, 'User Access Level', self.user_access_level, 1]);
// Track User Type Conversion, Custom Variable 3 in GA with session level scope
// Values: 'Visitor', 'Demo', 'Online Trial', 'Online Paying', 'Local User'
_gaq.push(['_setCustomVar', 1, 'User Type Conversion', self._get_user_type(), 2]);
_gaq.push(['_trackPageview']);
return;
});
},
/*
* Method called in order to send _trackPageview to GA
*/
_push_pageview: function(url) {
_gaq.push(['_trackPageview', url]);
},
/*
* Method called in order to send _trackEvent to GA
*/
_push_event: function(options) {
@ -155,6 +162,7 @@ openerp.web_analytics = function(instance) {
'category': r.model,
'action': 'form',
'label': url,
'noninteraction': true,
});
});
this.on('record_saved', self, function(r) {
@ -163,6 +171,7 @@ openerp.web_analytics = function(instance) {
'category': r.model,
'action': 'form',
'label': url,
'noninteraction': true,
});
});
}
@ -197,6 +206,7 @@ openerp.web_analytics = function(instance) {
'category': category,
'action': action,
'label': url,
'noninteraction': true,
});
return this._super.apply(this, arguments);
},