[MERGE] forward port branch saas-11 up to 6854887

This commit is contained in:
Christophe Simonis
2016-09-22 14:23:07 +02:00
24 changed files with 1000 additions and 953 deletions
+11 -10
View File
@@ -264,16 +264,17 @@ class ir_model_fields_anonymize_wizard(osv.osv_memory):
for anon_field in anon_fields:
field = fields_by_id.get(anon_field.field_id.id)
values = {
'model_name': field.model_id.name,
'model_code': field.model_id.model,
'field_code': field.name,
'field_name': field.field_description,
'state': anon_field.state,
}
summary += u" * %(model_name)s (%(model_code)s) -> %(field_name)s (%(field_code)s): state: (%(state)s)\n" % values
if field:
values = {
'model_name': field.model_id.name,
'model_code': field.model_id.model,
'field_code': field.name,
'field_name': field.field_description,
'state': anon_field.state,
}
summary += u" * %(model_name)s (%(model_code)s) -> %(field_name)s (%(field_code)s): state: (%(state)s)\n" % values
else:
summary += u"* Missing local model (%s) and field (%s): state: (%s) \n" % (anon_field.model_name, anon_field.field_name, anon_field.state)
return summary
def default_get(self, cr, uid, fields_list, context=None):
+1 -1
View File
@@ -568,7 +568,7 @@
<field name="name">Departments</field>
<field name="res_model">hr.department</field>
<field name="view_type">form</field>
<field name="view_mode">kanban,form</field>
<field name="view_mode">kanban,tree,form</field>
<field name="search_view_id" ref="view_department_filter"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
+37 -37
View File
@@ -7,13 +7,13 @@ msgstr ""
"Project-Id-Version: Odoo Server 9.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-09-18 16:56+0200\n"
"PO-Revision-Date: 2015-09-18 17:09+0200\n"
"PO-Revision-Date: 2016-09-19 16:51+0200\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 1.8.4\n"
"X-Generator: Poedit 1.8.9\n"
"Last-Translator: \n"
"Language: nl_BE\n"
@@ -495,17 +495,17 @@ msgstr "Resultaten uit de omrekening van vreemde valuta"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a175111
msgid "Effets à payer \\ Fournisseurs C.E.E."
msgstr ""
msgstr "Te betalen wissels: Leveranciers van de EEG"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a175110
msgid "Effets à payer \\ Fournisseurs belges"
msgstr ""
msgstr "Te betalen wissels: Belgische leveranciers"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a175112
msgid "Effets à payer \\ Fournisseurs importation"
msgstr ""
msgstr "Te betalen wissels: Import leveranciers"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a4166
@@ -521,7 +521,7 @@ msgstr "Bedienden"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a3200
msgid "En cours de fabrication"
msgstr ""
msgstr "Goederen in bewerking"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a440
@@ -571,7 +571,7 @@ msgstr "Leveranciers"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a612
msgid "Fournitures"
msgstr ""
msgstr "Diverse leveringen"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a260
@@ -596,22 +596,22 @@ msgstr "Kosten van onderzoek en ontwikkeling"
#. module: l10n_be
#: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-CAR-EXC-C2
msgid "Frais de voiture - TVA 50% Deductible (Prix Excl.)"
msgstr ""
msgstr "Auto kosten - BTW 50% aftrekbaar (exl. prijs)"
#. module: l10n_be
#: model:account.tax.template,name:l10n_be.attn_VAT-IN-V82-CAR-EXC-C1
msgid "Frais de voiture - TVA 50% Non Deductible"
msgstr ""
msgstr "Auto kosten - BTW 50% niet aftrekbaar"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a3500
msgid "Immeubles destinés à la vente"
msgstr ""
msgstr "Onroerende goederen bestemd voor verkoop"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a2800
msgid "Immobilisations Financières"
msgstr ""
msgstr "Financiële vaste activa"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a4120
@@ -641,17 +641,17 @@ msgstr "Goederen"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a300
msgid "Matières premières"
msgstr ""
msgstr "Grondstoffen"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a24011
msgid "Matériel de bureau de bâtiments administratifs et commerciaux"
msgstr ""
msgstr "Kantooruitrusting voor gebouwen met adminestratieve en commerciële functie"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a24010
msgid "Matériel de bureau de bâtiments industriels"
msgstr ""
msgstr "Kantooruitrusting voor gebouwen met industriële functie"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a2521
@@ -681,22 +681,22 @@ msgstr "Bedragen komende van het resultaat"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a4541
msgid "ONSS. 1er trimestre"
msgstr ""
msgstr "R.S.Z. 1ste kwartaal"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a4542
msgid "ONSS. 2ème trimestre"
msgstr ""
msgstr "R.S.Z. 2de kwartaal"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a4543
msgid "ONSS. 3ème trimestre"
msgstr ""
msgstr "R.S.Z. 3de kwartaal"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a4544
msgid "ONSS. 4ème trimestre"
msgstr ""
msgstr "R.S.Z. 4de kwartaal"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a4540
@@ -762,7 +762,7 @@ msgstr "Dienstverlening buiten de E.E.G. (export)"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a11
msgid "Primes d'émission"
msgstr ""
msgstr "Uitgifte premies"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a4930
@@ -782,12 +782,12 @@ msgstr "Diverse financiële opbrengsten"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a7600
msgid "Produits exceptionnels"
msgstr ""
msgstr "Uitzonderlijke opbrengsten"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a3300
msgid "Produits finis"
msgstr ""
msgstr "Gereed product"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a404
@@ -803,7 +803,7 @@ msgstr "Voorziening voor eindejaarspremie"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a1600
msgid "Provisions pour risques et charges"
msgstr ""
msgstr "Voorzieningen voor risico's en kosten"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a4533
@@ -823,12 +823,12 @@ msgstr "Onttrekking aan het kapitaal en aan de uitgiftepremies"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a780
msgid "Prélèvements sur les impôts différés"
msgstr ""
msgstr "Onttrekking aan de uitgestelde belastingen"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a789
msgid "Prélèvements sur les réserves immunisées"
msgstr ""
msgstr "Onttrekking aan de reserves"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a706
@@ -909,27 +909,27 @@ msgstr "Regularisering van geraamde belastingen"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a6200
msgid "Rémunérations: Administrateurs ou gérants"
msgstr ""
msgstr "Bezoldigingen: Bestuurders of zaakvoerders"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a6204
msgid "Rémunérations: Autres membres du personnel"
msgstr ""
msgstr "Bezoldigingen: Andere personeelsleden, verbonden met een arbeidsovereenkomst"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a6202
msgid "Rémunérations: Employés"
msgstr ""
msgstr "Bezoldigingen: Arbeiders"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a6203
msgid "Rémunérations: Ouvriers"
msgstr ""
msgstr "Bezoldigingen: Bedienden"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a6201
msgid "Rémunérations: Personnel de direction"
msgstr ""
msgstr "Bezoldigingen: Directiepersoneel"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a130
@@ -959,7 +959,7 @@ msgstr "Kapitaal- en interestsubsidies"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a150
msgid "Subsides obtenus"
msgstr ""
msgstr "Verkregen subsidies"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a41670
@@ -1204,7 +1204,7 @@ msgstr "Terreinen"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.trans
msgid "Transferts"
msgstr ""
msgstr "Transferten"
#. module: l10n_be
#: model:account.tax.template,name:l10n_be.attn_VAT-IN-V81-06-CC-C1
@@ -1509,32 +1509,32 @@ msgstr "VAT-IN-V83-21-ROW-CC-C2"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a7001
msgid "Ventes dans les pays membres de la C.E.E. (marchandises)"
msgstr ""
msgstr "Verkopen: leden van de Europese gemeenschap (handelsgoederen)"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a7011
msgid "Ventes dans les pays membres de la C.E.E. (produits finis)"
msgstr ""
msgstr "Verkopen: leden van de Europese gemeenschap (gereed product)"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a7000
msgid "Ventes en Belgique (marchandises)"
msgstr ""
msgstr "Verkopen: België (handelsgoederen)"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a7010
msgid "Ventes en Belgique (produits finis)"
msgstr ""
msgstr "Verkopen: België (gereed product)"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a7002
msgid "Ventes à l'exportation (marchandises)"
msgstr ""
msgstr "Verkopen: export (handelsgoederen)"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a7012
msgid "Ventes à l'exportation (produits finis)"
msgstr ""
msgstr "Verkopen: export (gereed product)"
#. module: l10n_be
#: model:account.account.template,name:l10n_be.a24100
File diff suppressed because it is too large Load Diff
@@ -293,7 +293,7 @@
<tr>
<td class="row" style="padding:10px 0;text-align:center">
&amp;nbsp;
<a href="#" style="-webkit-user-select: none; padding: 6px 12px; font-size: 14px; line-height: 20px; color: rgb(255,255,255); border: none; text-decoration: none; display: inline-block; margin-bottom: 0px; font-weight: 400; text-align: center; vertical-align: middle; cursor: pointer; white-space: nowrap; background-image: none; background-color: rgb(101,101,101);border-radius:4px">My Account</a>
<a class="btn" href="#" style="color:#fff; background-color: rgb(101,101,101);">My Account</a>
&amp;nbsp;
</td>
</tr>
@@ -34,13 +34,13 @@
<strong>Printing date:</strong><br/>
<span t-esc="time.strftime('%Y-%m-%d')"/>
</div>
<div class="col-xs-3" t-if="'sale_ref' in o._columns.keys()">
<div class="col-xs-3" t-if="'sale_ref' in o._fields.keys()">
<strong>Partner Ref:</strong><br/>
<span t-esc="'sale_ref' in o._columns.keys() and o.sale_ref"/>
<span t-esc="'sale_ref' in o._fields.keys() and o.sale_ref"/>
</div>
<div class="col-xs-3" t-if="'sale_name' in o._columns.keys()">
<div class="col-xs-3" t-if="'sale_name' in o._fields.keys()">
<strong>SO Number:</strong><br/>
<span t-esc="'sale_name' in o._columns.keys() and o.sale_name"/>
<span t-esc="'sale_name' in o._fields.keys() and o.sale_name"/>
</div>
</div>
+14 -1
View File
@@ -98,7 +98,20 @@ class PadCommon(models.AbstractModel):
@api.model
def create(self, vals):
self._set_pad_value(vals)
return super(PadCommon, self).create(vals)
pad = super(PadCommon, self).create(vals)
# In case the pad is created programmatically, the content is not filled in yet since it is
# normally initialized by the JS layer
for k, field in self._fields.iteritems():
if hasattr(field, 'pad_content_field') and k not in vals:
ctx = {
'model': self._name,
'field_name': k,
'object_id': pad.id,
}
pad_info = self.with_context(**ctx).pad_generate_url()
pad[k] = pad_info.get('url')
return pad
# Set the pad content in vals
def _set_pad_value(self, vals):
+1 -1
View File
@@ -577,7 +577,7 @@ class PosOrder(models.Model):
Move += Move.create({
'name': line.name,
'product_uom': line.product_id.uom_id.id,
'picking_id': picking_id.id,
'picking_id': picking_id and picking_id.id or False,
'picking_type_id': picking_type.id,
'product_id': line.product_id.id,
'product_uom_qty': abs(line.qty),
+2 -1
View File
@@ -1,5 +1,6 @@
from openerp import http
import json
from openerp.tools import ustr
from openerp.http import request, serialize_exception as _serialize_exception
from openerp.tools.misc import xlwt
from cStringIO import StringIO
@@ -67,7 +68,7 @@ class TableExporter(http.Controller):
# Step 3: writing data
x = 0
for row in jdata['rows']:
worksheet.write(y, x, row['indent'] * ' ' + row['title'], header_plain)
worksheet.write(y, x, row['indent'] * ' ' + ustr(row['title']), header_plain)
for cell in row['values']:
x = x + 1
if cell.get('is_bold', False):
@@ -616,7 +616,6 @@ define([
var linkUrl = linkInfo.url;
var linkText = linkInfo.text;
var isNewWindow = linkInfo.isNewWindow;
var className = linkInfo.className || null; // ODOO: addition
var rng = linkInfo.range || this.createRange($editable);
var isTextChanged = rng.toString() !== linkText;
@@ -647,7 +646,8 @@ define([
$.each(anchors, function (idx, anchor) {
$(anchor).attr('href', linkUrl);
$(anchor).attr('class', className); // ODOO: addition
$(anchor).attr('class', linkInfo.className || null); // ODOO: addition
$(anchor).css(linkInfo.style || {}); // ODOO: addition
if (isNewWindow) {
$(anchor).attr('target', '_blank');
} else {
+1 -1
View File
@@ -516,7 +516,7 @@ var RTE = Widget.extend({
// add contenteditable on link to improve its editing behaviour
$target.attr('contenteditable', true);
setTimeout(function () {
$editable.attr('contenteditable', false);
$editable.not($target).attr('contenteditable', false);
});
// once clicked outside, remove contenteditable on link
var reactive_editable = function(e){
+13 -3
View File
@@ -768,7 +768,9 @@ var fontIconsDialog = Widget.extend({
});
function createVideoNode(url) {
function createVideoNode(url, options) {
options = options || {};
// video url patterns(youtube, instagram, vimeo, dailymotion, youku)
var ytRegExp = /^(?:(?:https?:)?\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;
var ytMatch = url.match(ytRegExp);
@@ -838,6 +840,10 @@ function createVideoNode(url) {
});
}
if (options.autoplay) {
$video.attr("src", $video.attr("src") + "?autoplay=1");
}
$video.attr('frameborder', 0);
return $video;
@@ -852,6 +858,7 @@ var VideoDialog = Widget.extend({
events : _.extend({}, Dialog.prototype.events, {
'click input#urlvideo ~ button': 'get_video',
'click input#embedvideo ~ button': 'get_embed_video',
'change input#autoplay': 'get_video',
'change input#urlvideo': 'change_input',
'keyup input#urlvideo': 'change_input',
'change input#embedvideo': 'change_input',
@@ -870,6 +877,7 @@ var VideoDialog = Widget.extend({
if ($media.hasClass("media_iframe_video")) {
var src = $media.data('src');
this.$("input#urlvideo").val(src);
this.$("input#autoplay").prop("checked", (src || "").indexOf("autoplay") >= 0);
this.get_video();
}
return this._super.apply(this, arguments);
@@ -894,7 +902,7 @@ var VideoDialog = Widget.extend({
},
get_video: function (event) {
if (event) event.preventDefault();
var $video = createVideoNode(this.$("input#urlvideo").val());
var $video = createVideoNode(this.$("input#urlvideo").val(), {autoplay: this.$("input#autoplay").is(":checked")});
this.$iframe.replaceWith($video);
this.$iframe = $video;
return false;
@@ -1085,7 +1093,9 @@ var LinkDialog = Dialog.extend({
self.data.isNewWindow = new_window;
self.data.text = label;
self.data.className = classes.replace(/\s+/gi, ' ').replace(/^\s+|\s+$/gi, '');
if (classes.replace(/(^|[ ])(btn-default|btn-success|btn-primary|btn-info|btn-warning|btn-danger)([ ]|$)/gi, ' ')) {
self.data.style = {'background-color': '', 'color': ''};
}
self.trigger("save", self.data);
}).then(_super);
},
@@ -992,7 +992,7 @@ body.editor_enable {
p {
margin: initial;
}
a, a:hover {
a:not(.btn), a:not(.btn):hover {
color: inherit;
}
}
+1 -1
View File
@@ -210,7 +210,7 @@
</div>
<div class="text-center mt32">
<div class="checkbox">
<label>
<label title="The feature might not be supported for every video type">
<input id="autoplay" type="checkbox"/>Autoplay</label>
</div>
</div>
+1 -1
View File
@@ -124,7 +124,7 @@ class WebsiteBlog(http.Controller):
blog_post_ids = blog_post_obj.search(cr, uid, domain, order="published_date desc, create_date desc", context=context)
blog_posts = blog_post_obj.browse(cr, uid, blog_post_ids, context=context)
pager = request.website.pager(
url=request.httprequest.path,
url=request.httprequest.path.partition('/page/')[0],
total=len(blog_posts),
page=page,
step=self._blog_post_per_page,
-1
View File
@@ -5,7 +5,6 @@
<xpath expr="//div[@name='mail_button']" position="replace">
<div>
<form action="/website_form/" method="post" data-model_name="crm.lead" data-success_page="/page/website_crm.contactus_thanks" class="s_website_form form-horizontal container-fluid mt32" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<div class="form-group form-field o_website_form_required_custom">
<label class="col-md-3 col-sm-4 control-label" for="contact_name">Your Name</label>
<div class="col-md-7 col-sm-8">
@@ -151,7 +151,6 @@
<div class="row">
<section id="forms">
<form action="/website_form/" method="post" class="s_website_form form-horizontal container-fluid mt32" enctype="multipart/form-data" data-model_name="hr.applicant" data-success_page="/page/website_hr_recruitment.thankyou" t-att-data-form_field_department_id="job and job.department_id.id or False" t-att-data-form_field_job_id="job and job.id or False">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<div class="form-group form-field o_website_form_required_custom">
<div class="col-md-3 col-sm-4 text-right">
<label class="control-label" for="partner_name">Your Name</label>
+1 -1
View File
@@ -2033,7 +2033,7 @@ msgstr ""
#. module: website_sale
#: model:ir.ui.view,arch_db:website_sale.products
msgid "Use the <i>'Content'</i> top menu to create a new product."
msgid "Use the <i>'New page'</i> top menu to create a new product."
msgstr ""
#. module: website_sale
+1 -1
View File
@@ -195,7 +195,7 @@
<div class="text-center text-muted">
<h3 class="css_editable_display">No product defined.</h3>
<t groups="base.group_website_publisher">
<p groups="sales_team.group_sale_manager">Use the <i>'Content'</i> top menu to create a new product.</p>
<p groups="sales_team.group_sale_manager">Use the <i>'New page'</i> top menu to create a new product.</p>
</t>
</div>
</t>
+4
View File
@@ -130,6 +130,10 @@ preserve order.
* ``record in set`` returns whether ``record`` (which must be a 1-element
recordset) is present in ``set``. ``record not in set`` is the inverse
operation
* ``set1 <= set2`` and ``set1 < set2`` return whether ``set1`` is a subset
of ``set2`` (resp. strict)
* ``set1 >= set2`` and ``set1 > set2`` return whether ``set1`` is a superset
of ``set2`` (resp. strict)
* ``set1 | set2`` returns the union of the two recordsets, a new recordset
containing all records present in either source
* ``set1 & set2`` returns the intersection of two recordsets, a new recordset
+1 -1
View File
@@ -341,7 +341,7 @@ The ``request_sftp_access`` method returns a JSON dictionary containing the foll
SSH_KEYS=/path/to/your/authorized_keys
KEY="Aw7pItGVKFuZ_FOR3U8VFQ=="
REQUEST_ID="10534"
URL_PARAMS="key=${KEY}&request=${REQUEST}"
URL_PARAMS="key=${KEY}&request=${REQUEST_ID}"
curl -sS "${REQUEST_SFTP_ACCESS_URL}?${URL_PARAMS}" -F ssh_keys=@${SSH_KEYS} > request_sftp_result.json
+8 -5
View File
@@ -402,6 +402,7 @@ class IrModelFields(models.Model):
any(field.state != 'manual' for field in self):
raise UserError(_("This column contains module data and cannot be removed!"))
model_names = self.mapped('model')
self._drop_column()
res = super(IrModelFields, self).unlink()
@@ -410,7 +411,9 @@ class IrModelFields(models.Model):
if not self._context.get(MODULE_UNINSTALL_FLAG):
self._cr.commit()
api.Environment.reset()
RegistryManager.new(self._cr.dbname)
registry = RegistryManager.new(self._cr.dbname)
models = registry.descendants(model_names, '_inherits')
init_models(models, self._cr, dict(self._context, update_custom_fields=True))
RegistryManager.signal_registry_change(self._cr.dbname)
return res
@@ -443,9 +446,9 @@ class IrModelFields(models.Model):
if vals['model'] in self.pool:
# setup models; this re-initializes model in registry
self.pool.setup_models(self._cr, partial=(not self.pool.ready))
# update database schema
model = self.pool[vals['model']]
init_models([model], self._cr, dict(self._context, update_custom_fields=True))
# update database schema of model and its descendant models
models = self.pool.descendants([vals['model']], '_inherits')
init_models(models, self._cr, dict(self._context, update_custom_fields=True))
RegistryManager.signal_registry_change(self._cr.dbname)
return res
@@ -527,7 +530,7 @@ class IrModelFields(models.Model):
if patched_models:
# update the database schema of the models to patch
models = [self.pool[name] for name in patched_models]
models = self.pool.descendants(patched_models, '_inherits')
init_models(models, self._cr, dict(self._context, update_custom_fields=True))
if column_rename or patched_models:
+9 -3
View File
@@ -453,6 +453,7 @@ class BaseModel(object):
cr.execute(query, vals)
self.invalidate_cache()
@api.model
def _add_field(self, name, field):
""" Add the given ``field`` under the given ``name`` in the class """
@@ -615,6 +616,7 @@ class BaseModel(object):
'_register': False,
'_original_module': cls._module,
'_inherit_children': OrderedSet(), # names of children models
'_inherits_children': set(), # names of children models
'_fields': {}, # populated in _setup_base()
'_defaults': {}, # populated in _setup_base()
})
@@ -704,7 +706,11 @@ class BaseModel(object):
cls._sequence = cls._sequence or (cls._table + '_id_seq')
cls._constraints = cls._constraints.values()
# recompute attributes of children models
# update _inherits_children of parent models
for parent_name in cls._inherits:
pool[parent_name]._inherits_children.add(cls._name)
# recompute attributes of _inherit_children models
for child_name in cls._inherit_children:
child_class = type(pool[child_name])
child_class._build_model_attributes(pool)
@@ -2086,7 +2092,7 @@ class BaseModel(object):
"""
result = self._read_group_raw(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
groupby = [groupby] if isinstance(groupby, basestring) else groupby
groupby = [groupby] if isinstance(groupby, basestring) else list(OrderedSet(groupby))
dt = [
f for f in groupby
if self._fields[f.split(':')[0]].type in ('date', 'datetime')
@@ -2110,7 +2116,7 @@ class BaseModel(object):
query = self._where_calc(domain)
fields = fields or self._columns.keys()
groupby = [groupby] if isinstance(groupby, basestring) else groupby
groupby = [groupby] if isinstance(groupby, basestring) else list(OrderedSet(groupby))
groupby_list = groupby[:1] if lazy else groupby
annotated_groupbys = [self._read_group_process_groupby(gb, query) for gb in groupby_list]
groupby_fields = [g['field'] for g in annotated_groupbys]
+21 -10
View File
@@ -4,7 +4,8 @@
""" Models registries.
"""
from collections import Mapping, defaultdict
from collections import Mapping, defaultdict, deque
from operator import attrgetter
import logging
import os
import threading
@@ -149,6 +150,22 @@ class Registry(Mapping):
""" Add or replace a model in the registry."""
self.models[model_name] = model
def descendants(self, model_names, *kinds):
""" Return the models corresponding to ``model_names`` and all those
that inherit/inherits from them.
"""
assert all(kind in ('_inherit', '_inherits') for kind in kinds)
funcs = [attrgetter(kind + '_children') for kind in kinds]
models = OrderedSet()
queue = deque(model_names)
while queue:
model = self[queue.popleft()]
models.add(model)
for func in funcs:
queue.extend(func(model))
return models
def load(self, cr, module):
""" Load a given module in the registry.
@@ -160,23 +177,17 @@ class Registry(Mapping):
"""
from .. import models
loaded_models = OrderedSet()
def mark_loaded(model):
# recursively mark model and its children
loaded_models.add(model._name)
for child_name in model._inherit_children:
mark_loaded(self[child_name])
lazy_property.reset_all(self)
# Instantiate registered classes (via the MetaModel automatic discovery
# or via explicit constructor call), and add them to the pool.
model_names = []
for cls in models.MetaModel.module_to_models.get(module.name, []):
# models register themselves in self.models
model = cls._build_model(self, cr)
mark_loaded(model)
model_names.append(model._name)
return map(self, loaded_models)
return self.descendants(model_names, '_inherit')
def setup_models(self, cr, partial=False):
""" Complete the setup of models.