Merge remote-tracking branch 'odoo/master' into master-wmsstaging3-jco

This commit is contained in:
Josse Colpaert 2014-06-18 15:23:39 +02:00
commit ef228f273f
74 changed files with 104 additions and 54 deletions

View File

@ -27,11 +27,11 @@ class project_configuration(osv.osv_memory):
_inherit = 'res.config.settings'
_columns = {
'module_project_mrp': fields.boolean('Generate tasks from sale orders',
'module_sale_service': fields.boolean('Generate tasks from sale orders',
help='This feature automatically creates project tasks from service products in sale orders. '
'More precisely, tasks are created for procurement lines with product of type \'Service\', '
'procurement method \'Make to Order\', and supply method \'Manufacture\'.\n'
'-This installs the module project_mrp.'),
'-This installs the module sale_service.'),
'module_pad': fields.boolean("Use integrated collaborative note pads on task",
help='Lets the company customize which Pad installation should be used to link to new pads '
'(for example: http://ietherpad.com/).\n'

View File

@ -33,8 +33,8 @@
<label for="module_project_timesheet"/>
</div>
<div>
<field name="module_project_mrp" class="oe_inline"/>
<label for="module_project_mrp"/>
<field name="module_sale_service" class="oe_inline"/>
<label for="module_sale_service"/>
</div>
<div>
<field name="module_pad" class="oe_inline"/>

View File

@ -134,14 +134,15 @@ class Report(osv.Model):
website = None
if request and hasattr(request, 'website'):
website = request.website
values.update({
'time': time,
'translate_doc': translate_doc,
'editable': True, # Will active inherit_branding
'user': user,
'res_company': user.company_id,
'website': website,
})
values.update(
time=time,
translate_doc=translate_doc,
editable=True, # Will active inherit_branding
user=user,
res_company=user.company_id,
website=website,
editable_no_editor=True,
)
return view_obj.render(cr, uid, template, values, context=context)
#--------------------------------------------------------------------------

View File

@ -122,7 +122,7 @@ Example: 10% for retailers, promotion of 5 EUR on this product, etc."""),
def onchange_task_work(self, cr, uid, ids, task_work, context=None):
return {'value': {
'module_project_timesheet': task_work,
'module_project_mrp': task_work,
'module_sale_service': task_work,
}}
def onchange_timesheet(self, cr, uid, ids, timesheet, context=None):

View File

@ -686,7 +686,7 @@ class sale_order(osv.osv):
def procurement_needed(self, cr, uid, ids, context=None):
#when sale is installed only, there is no need to create procurements, that's only
#further installed modules (project_mrp, sale_stock) that will change this.
#further installed modules (sale_service, sale_stock) that will change this.
sale_line_obj = self.pool.get('sale.order.line')
res = []
for order in self.browse(cr, uid, ids, context=context):
@ -839,7 +839,7 @@ class sale_order_line(osv.osv):
def need_procurement(self, cr, uid, ids, context=None):
#when sale is installed only, there is no need to create procurements, that's only
#further installed modules (project_mrp, sale_stock) that will change this.
#further installed modules (sale_service, sale_stock) that will change this.
return False
def _amount_line(self, cr, uid, ids, field_name, arg, context=None):

View File

@ -19,6 +19,6 @@
#
##############################################################################
import project_mrp
import models
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -50,8 +50,8 @@ completed.
'website': 'http://www.openerp.com',
'images': ['images/product.jpeg', 'images/task_from_SO.jpeg'],
'depends': ['project', 'procurement', 'sale', 'procurement_jit'],
'data': ['project_mrp_view.xml'], #'process/project_mrp_process.xml'
'demo': ['project_mrp_demo.xml'],
'data': ['views/sale_service_view.xml'],
'demo': ['demo/sale_service_demo.xml'],
'test': ['test/project_task_procurement.yml'],
'installable': True,
'auto_install': False,

View File

@ -0,0 +1 @@
import sale_service

View File

@ -151,7 +151,7 @@ class sale_order_line(osv.osv):
_inherit = 'sale.order.line'
def need_procurement(self, cr, uid, ids, context=None):
#when sale is installed alone, there is no need to create procurements, but with project_mrp
#when sale is installed alone, there is no need to create procurements, but with sale_service
#we must create a procurement for each service that has the auto_create_task boolean set to True.
for line in self.browse(cr, uid, ids, context=context):
if line.product_id and line.product_id.type == 'service' and line.product_id.auto_create_task:

View File

@ -14,7 +14,7 @@
<record id="view_product_task_form" model="ir.ui.view">
<field name="name">product.form.view.inherit</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="stock.view_template_property_form"/>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<group name="procurement_uom" position="after">
<group string="Project Management Information" attrs="{'invisible': [('type', '!=', 'service')]}">
@ -34,8 +34,8 @@
</field>
</field>
</record>
<record id="view_project_mrp_inherit_form2" model="ir.ui.view">
<field name="name">project.mrp.form.view.inherit</field>
<record id="view_sale_service_inherit_form2" model="ir.ui.view">
<field name="name">sale.service.form.view.inherit</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_task_form2"/>
<field name="arch" type="xml">

View File

@ -35,7 +35,7 @@ class sale_configuration(osv.osv_memory):
help='Lets you transfer the entries under tasks defined for Project Management to '
'the Timesheet line entries for particular date and particular user with the effect of creating, editing and deleting either ways '
'and to automatically creates project tasks from procurement lines.\n'
'-This installs the modules project_timesheet and project_mrp.'),
'-This installs the modules project_timesheet and sale_service.'),
'default_order_policy': fields.selection(
[('manual', 'Invoice based on sales orders'), ('picking', 'Invoice based on deliveries')],
'The default invoicing method is', default_model='sale.order',
@ -50,7 +50,7 @@ class sale_configuration(osv.osv_memory):
implied_group='sale.group_mrp_properties',
help="Allows you to tag sales order lines with properties."),
'module_project_timesheet': fields.boolean("Project Timesheet"),
'module_project_mrp': fields.boolean("Project MRP"),
'module_sale_service': fields.boolean("Sale Service"),
'group_route_so_lines': fields.boolean('Choose MTO, drop shipping,... on sales order lines',
implied_group='sale_stock.group_route_so_lines',
help="Allows you to choose a delivery route on sales order lines"),
@ -63,7 +63,7 @@ class sale_configuration(osv.osv_memory):
def default_get(self, cr, uid, fields, context=None):
res = super(sale_configuration, self).default_get(cr, uid, fields, context)
# task_work, time_unit depend on other fields
res['task_work'] = res.get('module_project_mrp') and res.get('module_project_timesheet')
res['task_work'] = res.get('module_sale_service') and res.get('module_project_timesheet')
return res
def get_default_sale_config(self, cr, uid, ids, context=None):

View File

@ -38,7 +38,7 @@
</xpath>
<group name='default_options' position="after">
<field name="module_project_timesheet" invisible="1"/>
<field name="module_project_mrp" invisible="1"/>
<field name="module_sale_service" invisible="1"/>
</group>
<div name='warehouse_features' position='inside'>
<div name='default_picking_policy' attrs="{'invisible':[('group_invoice_deli_orders','=',False)]}">

View File

@ -78,7 +78,7 @@ class procurement_rule(osv.osv):
class procurement_order(osv.osv):
_inherit = "procurement.order"
_columns = {
'location_id': fields.many2one('stock.location', 'Procurement Location'), # not required because task may create procurements that aren't linked to a location with project_mrp
'location_id': fields.many2one('stock.location', 'Procurement Location'), # not required because task may create procurements that aren't linked to a location with sale_service
'partner_dest_id': fields.many2one('res.partner', 'Customer Address', help="In case of dropshipping, we need to know the destination address more precisely"),
'move_ids': fields.one2many('stock.move', 'procurement_id', 'Moves', help="Moves created by the procurement"),
'move_dest_id': fields.many2one('stock.move', 'Destination Move', help="Move which caused (created) the procurement"),

View File

@ -472,13 +472,14 @@ ul.oe_menu_editor .disclose {
right: 0;
z-index: 1000;
height: 100%;
background: #2f3129;
color: white;
}
.oe_ace_view_editor .oe_ace_view_editor_title {
width: 100%;
padding-top: 0;
padding-left: 0;
height: 30px;
background: #2f3129;
}
.oe_ace_view_editor .oe_ace_view_editor_title .oe_view_list {
width: 50%;
@ -496,13 +497,16 @@ ul.oe_menu_editor .disclose {
}
.oe_ace_view_editor .ace_editor {
position: absolute;
top: 30px;
top: 50px;
right: 0;
left: 0;
}
.oe_ace_view_editor .ace_editor .ace_gutter {
cursor: ew-resize;
}
.oe_ace_view_editor #ace-view-id {
padding: 0 1em;
}
.oe_ace_view_editor.oe_ace_open {
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=97);
opacity: 0.97;

View File

@ -408,6 +408,7 @@ $highlighted_text_color: #ffffff
/* ---- ACE EDITOR ---- {{{ */
$editorbar_height: 30px
$infobar_height: 20px
// TODO Fix => might break with themes
.oe_ace_view_editor
@ -416,12 +417,13 @@ $editorbar_height: 30px
right: 0
z-index: 1000
height: 100%
background: #2F3129
color: white
.oe_ace_view_editor_title
width: 100%
padding-top: 0
padding-left: 0
height: $editorbar_height
background: #2F3129
.oe_view_list
width: 50%
height: $editorbar_height
@ -432,12 +434,14 @@ $editorbar_height: 30px
@include editor-font
.ace_editor
position: absolute
top: $editorbar_height
top: $editorbar_height + $infobar_height
right: 0
// bottom property is set programmatically
left: 0
.ace_gutter
cursor: ew-resize
#ace-view-id
padding: 0 1em
&.oe_ace_open
+opacity(0.97)
&.oe_ace_closed

View File

@ -1,6 +1,7 @@
(function () {
'use strict';
var _t = openerp._t;
var hash = "#advanced-view-editor";
var website = openerp.website;
@ -65,12 +66,11 @@
website.ace.ViewOption = openerp.Widget.extend({
template: 'website.ace_view_option',
init: function (parent, options) {
var indent = "- ";
this.view_id = options.id;
this.view_name = options.name;
for (var i = 0; i<options.level; i++) {
this.view_name = indent + this.view_name;
}
var indent = _.str.repeat("- ", options.level);
this.view_name = _.str.sprintf("%s%s", indent, options.name);
this._super(parent);
},
});
@ -88,6 +88,7 @@
},
init: function (parent) {
this.buffers = {};
this.views = {};
this._super(parent);
},
start: function () {
@ -162,15 +163,14 @@
resizeEditorHeight(this.getParent().get('height'));
},
loadViews: function (views) {
var self = this;
var $viewList = self.$('#ace-view-list');
var views = this.buildViewGraph(views);
_.each(views, function (view) {
if (view.id) {
new website.ace.ViewOption(self, view).appendTo($viewList);
self.loadView(view.id);
}
});
var $viewList = this.$('#ace-view-list');
_(this.buildViewGraph(views)).each(function (view) {
if (!view.id) { return; }
this.views[view.id] = view;
new website.ace.ViewOption(this, view).appendTo($viewList);
this.loadView(view.id);
}.bind(this));
},
buildViewGraph: function (views) {
var activeViews = _.uniq(_.filter(views, function (view) {
@ -242,6 +242,9 @@
var editingSession = this.buffers[viewId];
if (editingSession) {
this.aceEditor.setSession(editingSession);
this.$('#ace-view-id').text(_.str.sprintf(
_t("Template ID: %s"),
this.views[viewId].xml_id));
}
},
displaySelectedView: function () {

View File

@ -3,6 +3,7 @@
var website = openerp.website;
var _t = openerp._t;
website.no_editor = !!$(document.documentElement).data('editable-no-editor');
website.add_template_file('/website/static/src/xml/website.editor.xml');
website.dom_ready.done(function () {
@ -496,6 +497,17 @@
this.$('#website-top-edit').hide();
this.$('#website-top-view').show();
var $edit_button = this.$('button[data-action=edit]')
.prop('disabled', website.no_editor);
if (website.no_editor) {
var help_text = $(document.documentElement).data('editable-no-editor');
$edit_button.parent()
// help must be set on form above button because it does
// not appear on disabled button
.attr('title', help_text);
}
$('.dropdown-toggle').dropdown();
this.customize_setup();

View File

@ -10,6 +10,7 @@
<button data-action="format" type="button" class="btn btn-warning">Format</button>
<button data-action="close" type="button" class="btn btn-info">Close</button>
</div>
<div id="ace-view-id"/>
<div id="ace-view-editor"/>
</div>
</t>

View File

@ -14,7 +14,8 @@
<span class="icon-bar"></span>
</button>
<form class="navbar-form navbar-left">
<button type="button" data-action="edit" class="btn btn-primary hidden">Edit</button>
<button type="button" data-action="edit"
class="btn btn-primary hidden">Edit</button>
</form>
</div>
<div class="collapse navbar-collapse navbar-edit-collapse">

View File

@ -59,6 +59,7 @@
<html t-att-lang="lang and lang.replace('_', '-')"
t-att-data-website-id="website.id if editable else None"
t-att-data-editable="'1' if editable else None"
t-att-data-editable-no-editor="editable_no_editor or None"
t-att-data-translatable="'1' if translatable else None"
t-att-data-view-xmlid="xmlid if editable else None"
t-att-data-main-object="repr(main_object) if editable else None"
@ -179,8 +180,10 @@
<xpath expr="//body" position="inside">
<div id="website-top-navbar-placeholder" class="navbar navbar-inverse navbar-fixed-top hidden-xs">
<div class="navbar-header">
<form class="navbar-form navbar-left">
<button type="button" class="btn btn-primary">Edit</button>
<form class="navbar-form navbar-left" title="editable_no_editor or None">
<button type="button" class="btn btn-primary"
disabled="'disabled' if editable_no_editor else None"
>Edit</button>
</form>
</div>
<div class="collapse navbar-collapse navbar-edit-collapse">

View File

@ -257,8 +257,13 @@ class QWeb(orm.AbstractModel):
uid = qwebcontext.get('request') and qwebcontext['request'].uid or None
can_see = self.user_has_groups(cr, uid, groups=attribute_value) if cr and uid else False
if not can_see:
if qwebcontext.get('editable') and not qwebcontext.get('editable_no_editor'):
errmsg = _("Editor disabled because some content can not be seen by a user who does not belong to the groups %s")
raise openerp.http.Retry(
_("User does not belong to groups %s") % attribute_value, {
'editable_no_editor': errmsg % attribute_value
})
return ''
continue
if isinstance(attribute_value, unicode):
attribute_value = attribute_value.encode("utf8")
@ -302,7 +307,7 @@ class QWeb(orm.AbstractModel):
for current_node in element.childNodes:
try:
g_inner.append(self.render_node(current_node, qwebcontext))
except QWebException:
except (QWebException, openerp.http.Retry):
raise
except Exception:
template = qwebcontext.get('__template__')

View File

@ -221,10 +221,10 @@ class res_config_installer(osv.osv_memory, res_config_module_installation_mixin)
_install_if = {
('sale','crm'): ['sale_crm'],
('sale','project'): ['project_mrp'],
('sale','project'): ['sale_service'],
}
will install both ``sale_crm`` and ``project_mrp`` if all of
will install both ``sale_crm`` and ``sale_service`` if all of
``sale``, ``crm`` and ``project`` are selected for installation.
Hook methods

View File

@ -1037,6 +1037,15 @@ mimetypes.add_type('application/font-woff', '.woff')
mimetypes.add_type('application/vnd.ms-fontobject', '.eot')
mimetypes.add_type('application/x-font-ttf', '.ttf')
class Retry(RuntimeError):
""" Exception raised during QWeb rendering to signal that the rendering
should be retried with the provided ``render_updates`` dict merged into
the previous rendering context
"""
def __init__(self, name, render_updates=None):
super(Retry, self).__init__(name)
self.updates = render_updates or {}
class Response(werkzeug.wrappers.Response):
""" Response object passed through controller route chain.
@ -1077,7 +1086,13 @@ class Response(werkzeug.wrappers.Response):
def render(self):
view_obj = request.registry["ir.ui.view"]
uid = self.uid or request.uid or openerp.SUPERUSER_ID
return view_obj.render(request.cr, uid, self.template, self.qcontext, context=request.context)
while True:
try:
return view_obj.render(
request.cr, uid, self.template, self.qcontext,
context=request.context)
except Retry, e:
self.qcontext.update(e.updates)
def flatten(self):
self.response.append(self.render())

View File

@ -1704,8 +1704,8 @@ class BaseModel(object):
:return: True if the current user is a member of one of the
given groups
"""
return any([self.pool.get('res.users').has_group(cr, uid, group_ext_id)
for group_ext_id in groups.split(',')])
return any(self.pool['res.users'].has_group(cr, uid, group_ext_id)
for group_ext_id in groups.split(','))
def _get_default_form_view(self, cr, user, context=None):
""" Generates a default single-line form view using all fields