[MERGE] forward port branch saas-11.3 up to 6cc7d3f945

This commit is contained in:
Christophe Simonis
2018-08-21 18:48:13 +02:00
43 changed files with 372 additions and 79 deletions
+6 -6
View File
@@ -112,6 +112,12 @@
<t t-foreach="o._get_tax_amount_by_group()" t-as="amount_by_group"> <t t-foreach="o._get_tax_amount_by_group()" t-as="amount_by_group">
<tr style="border-bottom:1px solid #dddddd;"> <tr style="border-bottom:1px solid #dddddd;">
<t t-if="len(o.tax_line_ids) == 1 and o.amount_untaxed == amount_by_group[2]"> <t t-if="len(o.tax_line_ids) == 1 and o.amount_untaxed == amount_by_group[2]">
<td><span t-esc="amount_by_group[0]"/></td>
<td class="text-right">
<span t-esc="amount_by_group[3]" />
</td>
</t>
<t t-else="">
<td> <td>
<span t-esc="amount_by_group[0]"/> <span t-esc="amount_by_group[0]"/>
<span>&amp;nbsp;<span>on</span> <span>&amp;nbsp;<span>on</span>
@@ -122,12 +128,6 @@
<span t-esc="amount_by_group[3]"/> <span t-esc="amount_by_group[3]"/>
</td> </td>
</t> </t>
<t t-else="">
<td><span t-esc="amount_by_group[0]"/></td>
<td class="text-right">
<span t-esc="amount_by_group[3]" />
</td>
</t>
</tr> </tr>
</t> </t>
<tr class="border-black"> <tr class="border-black">
+1 -1
View File
@@ -9,7 +9,7 @@ class IrModelFields(models.Model):
ttype = fields.Selection(selection_add=[('serialized', 'serialized')]) ttype = fields.Selection(selection_add=[('serialized', 'serialized')])
serialization_field_id = fields.Many2one('ir.model.fields', string='Serialization Field', serialization_field_id = fields.Many2one('ir.model.fields', string='Serialization Field',
ondelete='cascade', domain="[('ttype','=','serialized')]", ondelete='cascade', domain="[('ttype','=','serialized'), ('model_id', '=', model_id)]",
help="If set, this field will be stored in the sparse structure of the " help="If set, this field will be stored in the sparse structure of the "
"serialization field, instead of having its own database column. " "serialization field, instead of having its own database column. "
"This cannot be changed after creation.", "This cannot be changed after creation.",
+1 -1
View File
@@ -22,7 +22,7 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="related" position="before"> <field name="related" position="before">
<field name="serialization_field_id" <field name="serialization_field_id"
domain="[('ttype','=','serialized'), ('model_id','=',model_id)]" context="{'default_model_id': model_id, 'default_ttype': 'serialized'}"
attrs="{'readonly': [('state','=','base')]}"/> attrs="{'readonly': [('state','=','base')]}"/>
</field> </field>
</field> </field>
+4 -2
View File
@@ -950,11 +950,13 @@ class Meeting(models.Model):
@api.onchange('start_date') @api.onchange('start_date')
def _onchange_start_date(self): def _onchange_start_date(self):
self.start = self.start_date if self.start_date:
self.start = self.start_date
@api.onchange('stop_date') @api.onchange('stop_date')
def _onchange_stop_date(self): def _onchange_stop_date(self):
self.stop = self.stop_date if self.stop_date:
self.stop = self.stop_date
#################################################### ####################################################
# Calendar Business, Reccurency, ... # Calendar Business, Reccurency, ...
+1 -1
View File
@@ -708,7 +708,7 @@ class Lead(models.Model):
def _get_duplicated_leads_by_emails(self, partner_id, email, include_lost=False): def _get_duplicated_leads_by_emails(self, partner_id, email, include_lost=False):
""" Search for opportunities that have the same partner and that arent done or cancelled """ """ Search for opportunities that have the same partner and that arent done or cancelled """
if not email: if not email:
return [] return self.env['crm.lead']
partner_match_domain = [] partner_match_domain = []
for email in set(email_split(email) + [email]): for email in set(email_split(email) + [email]):
partner_match_domain.append(('email_from', '=ilike', email)) partner_match_domain.append(('email_from', '=ilike', email))
+22 -11
View File
@@ -15,12 +15,13 @@ Sidebar.include({
*/ */
init : function (parent, options) { init : function (parent, options) {
this._super.apply(this, arguments); this._super.apply(this, arguments);
if (options.viewType === "form") { this.hasAttachments = options.viewType === "form";
if (this.hasAttachments) {
this.sections.splice(1, 0, { 'name' : 'files', 'label' : _t('Attachment(s)'), }); this.sections.splice(1, 0, { 'name' : 'files', 'label' : _t('Attachment(s)'), });
this.items.files = []; this.items.files = [];
this.fileuploadId = _.uniqueId('oe_fileupload');
$(window).on(this.fileuploadId, this._onFileUploaded.bind(this));
} }
this.fileuploadId = _.uniqueId('oe_fileupload');
$(window).on(this.fileuploadId, this._onFileUploaded.bind(this));
}, },
/** /**
* Get the attachment linked to the record when the toolbar started * Get the attachment linked to the record when the toolbar started
@@ -29,13 +30,16 @@ Sidebar.include({
*/ */
start: function () { start: function () {
var _super = this._super.bind(this); var _super = this._super.bind(this);
this._updateAttachments().then(_super); var def = this.hasAttachments ? this._updateAttachments() : $.when();
return def.then(_super);
}, },
/** /**
* @override * @override
*/ */
destroy: function () { destroy: function () {
$(window).off(this.fileuploadId); if (this.hasAttachments) {
$(window).off(this.fileuploadId);
}
this._super.apply(this, arguments); this._super.apply(this, arguments);
}, },
@@ -46,8 +50,10 @@ Sidebar.include({
* @override * @override
*/ */
updateEnv: function (env) { updateEnv: function (env) {
this.env = env; if (this.hasAttachments) {
this._updateAttachments().then(this._redraw.bind(this)); this.env = env;
this._updateAttachments().then(this._redraw.bind(this));
}
}, },
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
@@ -90,10 +96,12 @@ Sidebar.include({
*/ */
_redraw: function () { _redraw: function () {
this._super.apply(this, arguments); this._super.apply(this, arguments);
this.$('.o_sidebar_add_attachment .o_form_binary_form') if (this.hasAttachments) {
.change(this._onAddAttachment.bind(this)); this.$('.o_sidebar_add_attachment .o_form_binary_form')
this.$('.o_sidebar_delete_attachment') .change(this._onAddAttachment.bind(this));
.click(this._onDeleteAttachment.bind(this)); this.$('.o_sidebar_delete_attachment')
.click(this._onDeleteAttachment.bind(this));
}
}, },
/** /**
* Update the attachments to be displayed in the attachment section * Update the attachments to be displayed in the attachment section
@@ -102,6 +110,9 @@ Sidebar.include({
* @private * @private
*/ */
_updateAttachments: function () { _updateAttachments: function () {
if (this.items.files === undefined) {
return $.when();
}
var activeId = this.env.activeIds[0]; var activeId = this.env.activeIds[0];
if (!activeId) { if (!activeId) {
this.items.files = []; this.items.files = [];
@@ -3,6 +3,7 @@ odoo.define('document.tests', function (require) {
var testUtils = require('web.test_utils'); var testUtils = require('web.test_utils');
var FormView = require('web.FormView'); var FormView = require('web.FormView');
var ListView = require('web.ListView');
var createView = testUtils.createView; var createView = testUtils.createView;
@@ -16,6 +17,9 @@ odoo.define('document.tests', function (require) {
records: [{ records: [{
id: 1, id: 1,
display_name: "first record", display_name: "first record",
}, {
id: 2,
display_name: "second record",
}] }]
}, },
'ir.attachment': { 'ir.attachment': {
@@ -74,5 +78,34 @@ odoo.define('document.tests', function (require) {
assert.strictEqual(form.sidebar.$('.o_sidebar_delete_attachment').length, 1, "there should be only one attachment"); assert.strictEqual(form.sidebar.$('.o_sidebar_delete_attachment').length, 1, "there should be only one attachment");
form.destroy(); form.destroy();
}); });
QUnit.test('no attachment on list view', function (assert) {
assert.expect(4);
var list = createView({
View: ListView,
model: 'partner',
data: this.data,
groupBy: ['display_name'],
viewOptions: {sidebar: true},
arch: '<tree string="Partners">' +
'<field name="display_name"/>' +
'</tree>',
mockRPC: function (route, args) {
assert.step(args.model);
return this._super.apply(this, arguments);
}
});
// select record then trigger render
list.$('.o_group_header:last').click();
list.$('.o_data_row input').click();
list.$('.o_group_header:first').click();
assert.verifySteps(['partner', 'partner', 'partner'],
"ir.attachment not called when selecting record in list view");
list.destroy();
});
}); });
}); });
+4 -4
View File
@@ -161,13 +161,13 @@ class GamificationBadge(models.Model):
for badge in self: for badge in self:
owners = badge.owner_ids owners = badge.owner_ids
badge.stats_my = sum(o.user_id == self.env.user for o in owners) badge.stat_my = sum(o.user_id == self.env.user for o in owners)
badge.stats_this_month = sum(o.create_date >= first_month_day for o in owners) badge.stat_this_month = sum(o.create_date >= first_month_day for o in owners)
badge.stats_my_this_month = sum( badge.stat_my_this_month = sum(
o.user_id == self.env.user and o.create_date >= first_month_day o.user_id == self.env.user and o.create_date >= first_month_day
for o in owners for o in owners
) )
badge.stats_my_monthly_sending = sum( badge.stat_my_monthly_sending = sum(
o.create_uid == self.env.user and o.create_date >= first_month_day o.create_uid == self.env.user and o.create_date >= first_month_day
for o in owners for o in owners
) )
+2 -2
View File
@@ -417,7 +417,7 @@
<kanban class="o_kanban_mobile"> <kanban class="o_kanban_mobile">
<templates> <templates>
<t t-name="kanban-box"> <t t-name="kanban-box">
<div t-attf-class="oe_kanban_global_click"> <div class="oe_kanban_global_click">
<div> <div>
<strong><field name="name"/></strong> <strong><field name="name"/></strong>
</div> </div>
@@ -426,7 +426,7 @@
</div> </div>
<div> <div>
<span>Vacancies : <field name="expected_employees"/></span> <span>Vacancies : <field name="expected_employees"/></span>
<span t-att-class="'pull-right label label-success' if record.state.raw_value == 'recruit' else 'pull-right label label-danger'"> <span t-att-class="record.state.raw_value == 'recruit' and 'pull-right label label-success' or 'pull-right label label-danger'">
<field name="state"/> <field name="state"/>
</span> </span>
</div> </div>
+2 -2
View File
@@ -622,12 +622,12 @@ class HolidaysRequest(models.Model):
elif holiday.state == 'confirm': elif holiday.state == 'confirm':
holiday.activity_schedule( holiday.activity_schedule(
'hr_holidays.mail_act_leave_approval', fields.Date.today(), 'hr_holidays.mail_act_leave_approval', fields.Date.today(),
user_id=holiday._get_responsible_for_approval().id) user_id=holiday.sudo()._get_responsible_for_approval().id)
elif holiday.state == 'validate1': elif holiday.state == 'validate1':
holiday.activity_feedback(['hr_holidays.mail_act_leave_approval']) holiday.activity_feedback(['hr_holidays.mail_act_leave_approval'])
holiday.activity_schedule( holiday.activity_schedule(
'hr_holidays.mail_act_leave_second_approval', fields.Date.today(), 'hr_holidays.mail_act_leave_second_approval', fields.Date.today(),
user_id=holiday._get_responsible_for_approval().id) user_id=holiday.sudo()._get_responsible_for_approval().id)
elif holiday.state == 'validate': elif holiday.state == 'validate':
to_do |= holiday to_do |= holiday
elif holiday.state == 'refuse': elif holiday.state == 'refuse':
+6 -5
View File
@@ -9,7 +9,7 @@ class MaintenanceEquipment(models.Model):
employee_id = fields.Many2one('hr.employee', string='Assigned to Employee', track_visibility='onchange') employee_id = fields.Many2one('hr.employee', string='Assigned to Employee', track_visibility='onchange')
department_id = fields.Many2one('hr.department', string='Assigned to Department', track_visibility='onchange') department_id = fields.Many2one('hr.department', string='Assigned to Department', track_visibility='onchange')
equipment_assign_to = fields.Selection( equipment_assign_to = fields.Selection(
[('department', 'Department'), ('employee', 'Employee') ,('other', 'Other')], [('department', 'Department'), ('employee', 'Employee'), ('other', 'Other')],
string='Used By', string='Used By',
required=True, required=True,
default='employee') default='employee')
@@ -82,10 +82,11 @@ class MaintenanceRequest(models.Model):
@api.depends('employee_id', 'department_id') @api.depends('employee_id', 'department_id')
def _compute_owner(self): def _compute_owner(self):
if self.equipment_id.equipment_assign_to == 'employee': for r in self:
self.owner_user_id = self.employee_id.user_id.id if r.equipment_id.equipment_assign_to == 'employee':
elif self.equipment_id.equipment_assign_to == 'department': r.owner_user_id = r.employee_id.user_id.id
self.owner_user_id = self.department_id.manager_id.user_id.id elif r.equipment_id.equipment_assign_to == 'department':
r.owner_user_id = r.department_id.manager_id.user_id.id
@api.onchange('employee_id', 'department_id') @api.onchange('employee_id', 'department_id')
def onchange_department_or_employee_id(self): def onchange_department_or_employee_id(self):
+1 -1
View File
@@ -111,7 +111,7 @@
<field eval="2" name="priority"/> <field eval="2" name="priority"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="user_id" position="after"> <field name="user_id" position="after">
<field name="planned_hours"/> <field name="planned_hours" widget="float_time" sum="Initially Planned Hours" />
<field name="remaining_hours" widget="float_time" sum="Remaining Hours" readonly="1"/> <field name="remaining_hours" widget="float_time" sum="Remaining Hours" readonly="1"/>
<field name="effective_hours" widget="float_time" sum="Spent Hours" invisible="1"/> <field name="effective_hours" widget="float_time" sum="Spent Hours" invisible="1"/>
</field> </field>
@@ -12,4 +12,15 @@
</field> </field>
</field> </field>
</record> </record>
<record id="supplier_invoice_bbacomm_form" model="ir.ui.view">
<field name="name">account.invoice.supplier.form</field>
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.invoice_supplier_form"/>
<field name="arch" type="xml">
<field name="reference" position="before">
<field name="reference_type" attrs="{'readonly':[('state','!=','draft')]}"/>
</field>
</field>
</record>
</odoo> </odoo>
+32 -1
View File
@@ -44,6 +44,10 @@
<field name="name">Switzerland VAT Form: grid 381 base</field> <field name="name">Switzerland VAT Form: grid 381 base</field>
<field name="applicability">taxes</field> <field name="applicability">taxes</field>
</record> </record>
<record id="vat_tag_382_a" model="account.account.tag">
<field name="name">Switzerland VAT Form: grid 382 base</field>
<field name="applicability">taxes</field>
</record>
<record id="vat_tag_302_b" model="account.account.tag"> <record id="vat_tag_302_b" model="account.account.tag">
<field name="name">Switzerland VAT Form: grid 302 tax</field> <field name="name">Switzerland VAT Form: grid 302 tax</field>
<field name="applicability">taxes</field> <field name="applicability">taxes</field>
@@ -60,6 +64,10 @@
<field name="name">Switzerland VAT Form: grid 381 tax</field> <field name="name">Switzerland VAT Form: grid 381 tax</field>
<field name="applicability">taxes</field> <field name="applicability">taxes</field>
</record> </record>
<record id="vat_tag_382_b" model="account.account.tag">
<field name="name">Switzerland VAT Form: grid 382 tax</field>
<field name="applicability">taxes</field>
</record>
<record id="vat_tag_400" model="account.account.tag"> <record id="vat_tag_400" model="account.account.tag">
<field name="name">Switzerland VAT Form: grid 400</field> <field name="name">Switzerland VAT Form: grid 400</field>
<field name="applicability">taxes</field> <field name="applicability">taxes</field>
@@ -82,7 +90,7 @@
</record> </record>
<record id="vat_tag_dedouanement" model="account.account.tag"> <record id="vat_tag_dedouanement" model="account.account.tag">
<field name="name">Switzerland VAT Form: 100% dédouanement (base in grid 400)</field> <field name="name">Switzerland VAT Form: 100% dédouanement (base in grid 400)</field>
<field name="applicability">taxes</field> <field name="applicability">taxes</field>
</record> </record>
@@ -320,6 +328,18 @@
<field name="tag_ids" eval="[(6,0,[ref('vat_tag_405')])]"/> <field name="tag_ids" eval="[(6,0,[ref('vat_tag_405')])]"/>
<field name="tax_group_id" ref="tax_group_tva_77"/> <field name="tax_group_id" ref="tax_group_tva_77"/>
</record> </record>
<record model="account.tax.template" id="vat_77_purchase_return">
<field name="name">TVA due a 7.7% (TN) (return)</field>
<field name="description">7.7% achat (return)</field>
<field name="amount" eval="-7.7"/>
<field name="amount_type">percent</field>
<field name="sequence" eval="0"/>
<field name="chart_template_id" ref="l10nch_chart_template"/>
<field name="type_tax_use">purchase</field>
<field name="refund_account_id" ref="ch_coa_1170"/>
<field name="account_id" ref="ch_coa_1170"/>
<field name="tag_ids" eval="[(6,0,[ref('vat_tag_382_a'),ref('vat_tag_382_b')])]"/>
</record>
<record model="account.tax.template" id="vat_XO"> <record model="account.tax.template" id="vat_XO">
<field name="name">TVA due a 0% (Exportations)</field> <field name="name">TVA due a 0% (Exportations)</field>
<field name="amount" eval="0.00"/> <field name="amount" eval="0.00"/>
@@ -368,4 +388,15 @@
<field name="tag_ids" eval="[(6,0,[ref('vat_tag_dedouanement')])]"/> <field name="tag_ids" eval="[(6,0,[ref('vat_tag_dedouanement')])]"/>
<field name="tax_group_id" ref="tax_group_tva_0"/> <field name="tax_group_id" ref="tax_group_tva_0"/>
</record> </record>
<!--# for reverse charge or VAT on Acquisition (group of taxes)-->
<record model="account.tax.template" id="vat_77_purchase_reverse">
<field name="description">7.7% achat</field>
<field name="name">TVA 7.7% sur achat service a l'etranger (reverse charge)</field>
<field name="amount" eval="7.7"/>
<field name="amount_type">group</field>
<field name="type_tax_use">purchase</field>
<field name="children_tax_ids" eval="[(6, 0, [ref('vat_77_purchase_return'), ref('vat_77_purchase')])]"/>
<field name="chart_template_id" ref="l10nch_chart_template"/>
<field name="tag_ids" eval="[(6,0,[ref('vat_tag_382_a'), ref('vat_tag_382_b')])]"/>
</record>
</odoo> </odoo>
@@ -173,7 +173,6 @@ co_puc_413568,413568,Venta de equipo profesional y científico,FALSE,account.dat
co_puc_413570,413570,"Venta de loterías, rifas, chance, apuestas y similares",FALSE,account.data_account_type_revenue,l10n_co_chart_template_generic co_puc_413570,413570,"Venta de loterías, rifas, chance, apuestas y similares",FALSE,account.data_account_type_revenue,l10n_co_chart_template_generic
co_puc_413572,413572,Reparación de efectos personales y electrodomésticos,FALSE,account.data_account_type_revenue,l10n_co_chart_template_generic co_puc_413572,413572,Reparación de efectos personales y electrodomésticos,FALSE,account.data_account_type_revenue,l10n_co_chart_template_generic
co_puc_413595,413595,Venta de Otros Productos,FALSE,account.data_account_type_revenue,l10n_co_chart_template_generic co_puc_413595,413595,Venta de Otros Productos,FALSE,account.data_account_type_revenue,l10n_co_chart_template_generic
co_puc_413595,413595,Venta de otros productos,FALSE,account.data_account_type_revenue,l10n_co_chart_template_generic
co_puc_415505,415505,Arrendamientos de Bienes Inmuebles,FALSE,account.data_account_type_revenue,l10n_co_chart_template_generic co_puc_415505,415505,Arrendamientos de Bienes Inmuebles,FALSE,account.data_account_type_revenue,l10n_co_chart_template_generic
co_puc_421005,421005,Intereses,FALSE,account.data_account_type_revenue,l10n_co_chart_template_generic co_puc_421005,421005,Intereses,FALSE,account.data_account_type_revenue,l10n_co_chart_template_generic
co_puc_421020,421020,Diferencia en Cambio,FALSE,account.data_account_type_revenue,l10n_co_chart_template_generic co_puc_421020,421020,Diferencia en Cambio,FALSE,account.data_account_type_revenue,l10n_co_chart_template_generic
1 id code name reconcile user_type_id:id chart_template_id:id
173 co_puc_413570 413570 Venta de loterías, rifas, chance, apuestas y similares FALSE account.data_account_type_revenue l10n_co_chart_template_generic
174 co_puc_413572 413572 Reparación de efectos personales y electrodomésticos FALSE account.data_account_type_revenue l10n_co_chart_template_generic
175 co_puc_413595 413595 Venta de Otros Productos FALSE account.data_account_type_revenue l10n_co_chart_template_generic
co_puc_413595 413595 Venta de otros productos FALSE account.data_account_type_revenue l10n_co_chart_template_generic
176 co_puc_415505 415505 Arrendamientos de Bienes Inmuebles FALSE account.data_account_type_revenue l10n_co_chart_template_generic
177 co_puc_421005 421005 Intereses FALSE account.data_account_type_revenue l10n_co_chart_template_generic
178 co_puc_421020 421020 Diferencia en Cambio FALSE account.data_account_type_revenue l10n_co_chart_template_generic
@@ -12,7 +12,7 @@
<h4>Paie du <span t-field="o.date_from"/> au <span t-field="o.date_to"/></h4> <h4>Paie du <span t-field="o.date_from"/> au <span t-field="o.date_to"/></h4>
</div> </div>
<div class="col-xs-4 pull-right"> <div class="col-xs-4 pull-right">
<table class="table table-bordered" stype="border: 2px solid black;"> <table class="table table-bordered" style="border: 2px solid black;">
<tr> <tr>
<td> <td>
<div t-field="o.employee_id.company_id.partner_id" <div t-field="o.employee_id.company_id.partner_id"
+1 -1
View File
@@ -723,7 +723,7 @@ class MailThread(models.AbstractModel):
view_title = _('View') view_title = _('View')
default_groups = [ default_groups = [
('user', lambda partner: bool(partner.user_ids) and not any(user.share for user in partner.user_ids), {}), ('user', lambda partner: bool(partner.user_ids) and any(not user.share for user in partner.user_ids), {}),
('portal', lambda partner: bool(partner.user_ids) and all(user.share for user in partner.user_ids), { ('portal', lambda partner: bool(partner.user_ids) and all(user.share for user in partner.user_ids), {
'has_button_access': False, 'has_button_access': False,
}), }),
@@ -22,6 +22,15 @@ class Invoice(models.Model):
]).write({'date_cancel': fields.Date.today()}) ]).write({'date_cancel': fields.Date.today()})
return super(Invoice, self).action_cancel() return super(Invoice, self).action_cancel()
@api.multi
def write(self, vals):
'''Change the partner on related membership_line'''
if 'partner_id' in vals:
self.env['membership.membership_line'].search([
('account_invoice_line', 'in', self.mapped('invoice_line_ids').ids)
]).write({'partner': vals['partner_id']})
return super(Invoice, self).write(vals)
class AccountInvoiceLine(models.Model): class AccountInvoiceLine(models.Model):
_inherit = 'account.invoice.line' _inherit = 'account.invoice.line'
+9 -6
View File
@@ -39,6 +39,8 @@ class Partner(models.Model):
@api.depends('member_lines.account_invoice_line.invoice_id.state', @api.depends('member_lines.account_invoice_line.invoice_id.state',
'member_lines.account_invoice_line.invoice_id.invoice_line_ids', 'member_lines.account_invoice_line.invoice_id.invoice_line_ids',
'member_lines.account_invoice_line.invoice_id.payment_ids', 'member_lines.account_invoice_line.invoice_id.payment_ids',
'member_lines.account_invoice_line.invoice_id.payment_move_line_ids',
'member_lines.account_invoice_line.invoice_id.partner_id',
'free_member', 'free_member',
'member_lines.date_to', 'member_lines.date_from', 'member_lines.date_to', 'member_lines.date_from',
'associate_member.membership_state') 'associate_member.membership_state')
@@ -104,18 +106,22 @@ class Partner(models.Model):
if partner.membership_stop and today > partner.membership_stop: if partner.membership_stop and today > partner.membership_stop:
res[partner.id] = 'free' if partner.free_member else 'old' res[partner.id] = 'free' if partner.free_member else 'old'
continue continue
if partner.associate_member:
res_state = partner.associate_member._membership_state()
res[partner.id] = res_state[partner.associate_member.id]
continue
s = 4 s = 4
if partner.member_lines: if partner.member_lines:
for mline in partner.member_lines: for mline in partner.member_lines:
if (mline.date_to or '0000-00-00') >= today and (mline.date_from or '0000-00-00') <= today: if (mline.date_to or '0000-00-00') >= today and (mline.date_from or '0000-00-00') <= today:
if mline.account_invoice_line.invoice_id: if mline.account_invoice_line.invoice_id.partner_id == partner:
mstate = mline.account_invoice_line.invoice_id.state mstate = mline.account_invoice_line.invoice_id.state
if mstate == 'paid': if mstate == 'paid':
s = 0 s = 0
inv = mline.account_invoice_line.invoice_id inv = mline.account_invoice_line.invoice_id
for payment in inv.payment_ids: for ml in inv.payment_move_line_ids:
if any(payment.invoice_ids.filtered(lambda inv: inv.type == 'out_refund')): if any(ml.invoice_id.filtered(lambda inv: inv.type == 'out_refund')):
s = 2 s = 2
break break
elif mstate == 'open' and s != 0: elif mstate == 'open' and s != 0:
@@ -144,9 +150,6 @@ class Partner(models.Model):
res[partner.id] = 'none' res[partner.id] = 'none'
if partner.free_member and s != 0: if partner.free_member and s != 0:
res[partner.id] = 'free' res[partner.id] = 'free'
if partner.associate_member:
res_state = partner.associate_member._membership_state()
res[partner.id] = res_state[partner.associate_member.id]
return res return res
@api.one @api.one
+22 -7
View File
@@ -180,7 +180,7 @@ class PosOrder(models.Model):
} }
@api.model @api.model
def _get_account_move_line_group_data_type_key(self, data_type, values): def _get_account_move_line_group_data_type_key(self, data_type, values, options={}):
""" """
Return a tuple which will be used as a key for grouping account Return a tuple which will be used as a key for grouping account
move lines in _create_account_move_line method. move lines in _create_account_move_line method.
@@ -195,10 +195,16 @@ class PosOrder(models.Model):
values['analytic_account_id'], values['analytic_account_id'],
values['debit'] > 0) values['debit'] > 0)
elif data_type == 'tax': elif data_type == 'tax':
return ('tax', order_id = values.pop('order_id', False)
values['partner_id'], tax_key = ('tax',
values['tax_line_id'], values['partner_id'],
values['debit'] > 0) values['tax_line_id'],
values['debit'] > 0)
if options.get('rounding_method') == 'round_globally':
tax_key = ('tax',
values['tax_line_id'],
order_id)
return tax_key
elif data_type == 'counter_part': elif data_type == 'counter_part':
return ('counter_part', return ('counter_part',
values['partner_id'], values['partner_id'],
@@ -308,7 +314,7 @@ class PosOrder(models.Model):
'move_id': move.id, 'move_id': move.id,
}) })
key = self._get_account_move_line_group_data_type_key(data_type, values) key = self._get_account_move_line_group_data_type_key(data_type, values, {'rounding_method': rounding_method})
if not key: if not key:
return return
@@ -322,6 +328,14 @@ class PosOrder(models.Model):
current_value['quantity'] = current_value.get('quantity', 0.0) + values.get('quantity', 0.0) current_value['quantity'] = current_value.get('quantity', 0.0) + values.get('quantity', 0.0)
current_value['credit'] = current_value.get('credit', 0.0) + values.get('credit', 0.0) current_value['credit'] = current_value.get('credit', 0.0) + values.get('credit', 0.0)
current_value['debit'] = current_value.get('debit', 0.0) + values.get('debit', 0.0) current_value['debit'] = current_value.get('debit', 0.0) + values.get('debit', 0.0)
if key[0] == 'tax' and rounding_method == 'round_globally':
if current_value['debit'] - current_value['credit'] > 0:
current_value['debit'] = current_value['debit'] - current_value['credit']
current_value['credit'] = 0
else:
current_value['credit'] = current_value['credit'] - current_value['debit']
current_value['debit'] = 0
else: else:
grouped_data[key].append(values) grouped_data[key].append(values)
@@ -381,7 +395,8 @@ class PosOrder(models.Model):
'credit': ((tax['amount'] > 0) and tax['amount']) or 0.0, 'credit': ((tax['amount'] > 0) and tax['amount']) or 0.0,
'debit': ((tax['amount'] < 0) and -tax['amount']) or 0.0, 'debit': ((tax['amount'] < 0) and -tax['amount']) or 0.0,
'tax_line_id': tax['id'], 'tax_line_id': tax['id'],
'partner_id': partner_id 'partner_id': partner_id,
'order_id': order.id
}) })
# round tax lines per order # round tax lines per order
+10 -5
View File
@@ -862,14 +862,19 @@ class Task(models.Model):
groups = super(Task, self)._notify_get_groups(message, groups) groups = super(Task, self)._notify_get_groups(message, groups)
self.ensure_one() self.ensure_one()
new_group = (
'group_project_user',
lambda partner: bool(partner.user_ids) and any(user.has_group('project.group_project_user') for user in partner.user_ids),
{},
)
if not self.user_id and not self.stage_id.fold: if not self.user_id and not self.stage_id.fold:
take_action = self._notify_get_action_link('assign') take_action = self._notify_get_action_link('assign')
project_actions = [{'url': take_action, 'title': _('I take it')}] project_actions = [{'url': take_action, 'title': _('I take it')}]
new_group = ( new_group[2]['actions'] = project_actions
'group_project_user', lambda partner: bool(partner.user_ids) and any(user.has_group('project.group_project_user') for user in partner.user_ids), {
'actions': project_actions, groups = [new_group] + groups
})
groups = [new_group] + groups
for group_name, group_method, group_data in groups: for group_name, group_method, group_data in groups:
if group_name == 'customer': if group_name == 'customer':
+1 -1
View File
@@ -350,7 +350,7 @@ class PurchaseOrder(models.Model):
'sequence': max(line.product_id.seller_ids.mapped('sequence')) + 1 if line.product_id.seller_ids else 1, 'sequence': max(line.product_id.seller_ids.mapped('sequence')) + 1 if line.product_id.seller_ids else 1,
'product_uom': line.product_uom.id, 'product_uom': line.product_uom.id,
'min_qty': 0.0, 'min_qty': 0.0,
'price': self.currency_id._convert(line.price_unit, currency, line.company_id, line.date_order or fields.Date.today()), 'price': self.currency_id._convert(line.price_unit, currency, line.company_id, line.date_order or fields.Date.today(), round=False),
'currency_id': currency.id, 'currency_id': currency.id,
'delay': 0, 'delay': 0,
} }
+9
View File
@@ -36,6 +36,15 @@ class StockMove(models.Model):
#rslt += invoices.mapped('refund_invoice_ids') #rslt += invoices.mapped('refund_invoice_ids')
return rslt return rslt
def _assign_picking_post_process(self, new=False):
super(StockMove, self)._assign_picking_post_process(new=new)
if new and self.sale_line_id and self.sale_line_id.order_id:
self.picking_id.message_post_with_view(
'mail.message_origin_link',
values={'self': self.picking_id, 'origin': self.sale_line_id.order_id},
subtype_id=self.env.ref('mail.mt_note').id)
class ProcurementGroup(models.Model): class ProcurementGroup(models.Model):
_inherit = 'procurement.group' _inherit = 'procurement.group'
+7 -1
View File
@@ -3,6 +3,7 @@
from ast import literal_eval from ast import literal_eval
import babel import babel
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
import itertools
import json import json
from odoo import http, fields, _ from odoo import http, fields, _
@@ -88,7 +89,12 @@ class SaleTimesheetController(http.Controller):
# #
# Time Repartition (per employee per billable types) # Time Repartition (per employee per billable types)
# #
employees = projects.mapped('tasks.user_id.employee_ids') | request.env['account.analytic.line'].search([('project_id', 'in', projects.ids)]).mapped('employee_id') user_ids = request.env['project.task'].sudo().search_read([('project_id', 'in', projects.ids), ('user_id', '!=', False)], ['user_id'])
user_ids = [user_id['user_id'][0] for user_id in user_ids]
employee_ids = request.env['res.users'].sudo().search_read([('id', 'in', user_ids)], ['employee_ids'])
# flatten the list of list
employee_ids = list(itertools.chain.from_iterable([employee_id['employee_ids'] for employee_id in employee_ids]))
employees = request.env['hr.employee'].sudo().browse(employee_ids) | request.env['account.analytic.line'].search([('project_id', 'in', projects.ids)]).mapped('employee_id')
repartition_domain = [('project_id', 'in', projects.ids), ('employee_id', '!=', False), ('timesheet_invoice_type', '!=', False)] # force billable type repartition_domain = [('project_id', 'in', projects.ids), ('employee_id', '!=', False), ('timesheet_invoice_type', '!=', False)] # force billable type
repartition_data = request.env['account.analytic.line'].read_group(repartition_domain, ['employee_id', 'timesheet_invoice_type', 'unit_amount'], ['employee_id', 'timesheet_invoice_type'], lazy=False) repartition_data = request.env['account.analytic.line'].read_group(repartition_domain, ['employee_id', 'timesheet_invoice_type', 'unit_amount'], ['employee_id', 'timesheet_invoice_type'], lazy=False)
+8 -3
View File
@@ -8,7 +8,7 @@ from dateutil.relativedelta import relativedelta
import json import json
from odoo import api, fields, models, _ from odoo import api, fields, models, _
from odoo.exceptions import UserError from odoo.exceptions import AccessError, UserError
from odoo.release import version from odoo.release import version
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT as DF from odoo.tools import DEFAULT_SERVER_DATE_FORMAT as DF
@@ -33,8 +33,13 @@ class CrmTeam(models.Model):
team_id = self.env['crm.team'].browse(self.env.context.get('default_team_id')) team_id = self.env['crm.team'].browse(self.env.context.get('default_team_id'))
if not team_id: if not team_id:
default_team_id = self.env.ref('sales_team.team_sales_department', raise_if_not_found=False) default_team_id = self.env.ref('sales_team.team_sales_department', raise_if_not_found=False)
if default_team_id and (self.env.context.get('default_type') != 'lead' or default_team_id.use_leads): if default_team_id:
team_id = default_team_id try:
default_team_id.check_access_rule('read')
except AccessError:
return self.env['crm.team']
if self.env.context.get('default_type') != 'lead' or default_team_id.use_leads and default_team_id.active:
team_id = default_team_id
return team_id return team_id
def _get_default_favorite_user_ids(self): def _get_default_favorite_user_ids(self):
+4 -1
View File
@@ -720,7 +720,7 @@ class StockMove(models.Model):
recompute = True recompute = True
picking = Picking.create(move._get_new_picking_values()) picking = Picking.create(move._get_new_picking_values())
move.write({'picking_id': picking.id}) move.write({'picking_id': picking.id})
move._assign_picking_post_process(new=recompute)
# If this method is called in batch by a write on a one2many and # If this method is called in batch by a write on a one2many and
# at some point had to create a picking, some next iterations could # at some point had to create a picking, some next iterations could
# try to find back the created picking. As we look for it by searching # try to find back the created picking. As we look for it by searching
@@ -730,6 +730,9 @@ class StockMove(models.Model):
move.recompute() move.recompute()
return True return True
def _assign_picking_post_process(self, new=False):
pass
def _get_new_picking_values(self): def _get_new_picking_values(self):
""" Prepares a new picking for this move as it could not be assigned to """ Prepares a new picking for this move as it could not be assigned to
another picking. This method is designed to be inherited. """ another picking. This method is designed to be inherited. """
+2
View File
@@ -121,6 +121,8 @@ class StockScrap(models.Model):
def action_validate(self): def action_validate(self):
self.ensure_one() self.ensure_one()
if self.product_id.type != 'product':
return self.do_scrap()
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
available_qty = sum(self.env['stock.quant']._gather(self.product_id, available_qty = sum(self.env['stock.quant']._gather(self.product_id,
self.location_id, self.location_id,
+2 -2
View File
@@ -11,11 +11,11 @@
<div> <div>
<span><strong>Customer Address:</strong></span> <span><strong>Customer Address:</strong></span>
</div> </div>
<div t-if="o.move_lines and o.move_lines[0].partner_id and not o.partner_id" name="partner_header"> <div t-if="o.move_lines and o.move_lines[0].partner_id" name="partner_header">
<div t-field="o.move_lines[0].partner_id" <div t-field="o.move_lines[0].partner_id"
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True}'/> t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True}'/>
</div> </div>
<div t-if="o.partner_id" name="partner_header"> <div t-if="not (o.move_lines and o.move_lines[0].partner_id) and o.partner_id" name="partner_header">
<div t-field="o.partner_id" <div t-field="o.partner_id"
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True}'/> t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True}'/>
</div> </div>
@@ -56,7 +56,7 @@ return AbstractRenderer.extend({
on_attach_callback: function () { on_attach_callback: function () {
this._super.apply(this, arguments); this._super.apply(this, arguments);
this.isInDOM = true; this.isInDOM = true;
this._renderGraph(); this._render();
}, },
/** /**
* @override * @override
@@ -43,7 +43,7 @@ var ListController = BasicController.extend({
this.toolbarActions = params.toolbarActions || {}; this.toolbarActions = params.toolbarActions || {};
this.editable = params.editable; this.editable = params.editable;
this.noLeaf = params.noLeaf; this.noLeaf = params.noLeaf;
this.selectedRecords = []; // there is no selected record by default this.selectedRecords = params.selectedRecords || [];
}, },
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
@@ -177,6 +177,25 @@ var ListController = BasicController.extend({
this._toggleSidebar(); this._toggleSidebar();
} }
}, },
/**
* Overrides to update the list of selected records
*
* @override
*/
update: function (params, options) {
var self = this;
if (options && options.keepSelection) {
// filter out removed records from selection
var res_ids = this.model.get(this.handle).res_ids;
this.selectedRecords = _.filter(this.selectedRecords, function (id) {
return _.contains(res_ids, self.model.get(id).res_id);
});
} else {
this.selectedRecords = [];
}
params.selectedRecords = this.selectedRecords;
return this._super.apply(this, arguments);
},
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
// Private // Private
@@ -339,7 +358,6 @@ var ListController = BasicController.extend({
* @returns {Deferred} * @returns {Deferred}
*/ */
_update: function () { _update: function () {
this.selectedRecords = [];
this._toggleSidebar(); this._toggleSidebar();
return this._super.apply(this, arguments); return this._super.apply(this, arguments);
}, },
@@ -544,7 +562,7 @@ var ListController = BasicController.extend({
_onToggleGroup: function (event) { _onToggleGroup: function (event) {
this.model this.model
.toggleGroup(event.data.group.id) .toggleGroup(event.data.group.id)
.then(this.update.bind(this, {}, {reload: false})); .then(this.update.bind(this, {}, {keepSelection: true, reload: false}));
}, },
}); });
@@ -60,7 +60,7 @@ var ListRenderer = BasicRenderer.extend({
return py.parse(py.tokenize(value)); return py.parse(py.tokenize(value));
}).value(); }).value();
this.hasSelectors = params.hasSelectors; this.hasSelectors = params.hasSelectors;
this.selection = []; this.selection = params.selectedRecords || [];
this.pagers = []; // instantiated pagers (only for grouped lists) this.pagers = []; // instantiated pagers (only for grouped lists)
this.editable = params.editable; this.editable = params.editable;
}, },
@@ -81,7 +81,9 @@ var ListRenderer = BasicRenderer.extend({
*/ */
updateState: function (state, params) { updateState: function (state, params) {
this._processColumns(params.columnInvisibleFields || {}); this._processColumns(params.columnInvisibleFields || {});
this.selection = []; if (params.selectedRecords) {
this.selection = params.selectedRecords;
}
return this._super.apply(this, arguments); return this._super.apply(this, arguments);
}, },
@@ -35,6 +35,7 @@ var ListView = BasicView.extend({
*/ */
init: function (viewInfo, params) { init: function (viewInfo, params) {
this._super.apply(this, arguments); this._super.apply(this, arguments);
var selectedRecords = []; // there is no selected records by default
var mode = this.arch.attrs.editable && !params.readonly ? "edit" : "readonly"; var mode = this.arch.attrs.editable && !params.readonly ? "edit" : "readonly";
@@ -43,11 +44,13 @@ var ListView = BasicView.extend({
this.controllerParams.toolbarActions = viewInfo.toolbar; this.controllerParams.toolbarActions = viewInfo.toolbar;
this.controllerParams.noLeaf = !!this.loadParams.context.group_by_no_leaf; this.controllerParams.noLeaf = !!this.loadParams.context.group_by_no_leaf;
this.controllerParams.mode = mode; this.controllerParams.mode = mode;
this.controllerParams.selectedRecords = selectedRecords;
this.rendererParams.arch = this.arch; this.rendererParams.arch = this.arch;
this.rendererParams.hasSelectors = this.rendererParams.hasSelectors =
'hasSelectors' in params ? params.hasSelectors : true; 'hasSelectors' in params ? params.hasSelectors : true;
this.rendererParams.editable = params.readonly ? false : this.arch.attrs.editable; this.rendererParams.editable = params.readonly ? false : this.arch.attrs.editable;
this.rendererParams.selectedRecords = selectedRecords;
this.loadParams.limit = this.loadParams.limit || 80; this.loadParams.limit = this.loadParams.limit || 80;
this.loadParams.type = 'list'; this.loadParams.type = 'list';
@@ -190,6 +190,25 @@ QUnit.module('Views', {
}); });
QUnit.test('no content helper', function (assert) { QUnit.test('no content helper', function (assert) {
assert.expect(2);
this.data.foo.records = [];
var graph = createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '<graph string="Gloups">' +
'<field name="product_id"/>' +
'</graph>',
});
assert.strictEqual(graph.$('div.o_graph_svg_container svg.nvd3-svg').length, 0,
"should not contain a div with a svg element");
assert.strictEqual(graph.$('div.o_view_nocontent').length, 1,
"should display the no content helper");
graph.destroy();
});
QUnit.test('no content helper after update', function (assert) {
var done = assert.async(); var done = assert.async();
assert.expect(4); assert.expect(4);
@@ -663,6 +663,45 @@ QUnit.module('Views', {
list.destroy(); list.destroy();
}); });
QUnit.test('selection is kept on render without reload', function (assert) {
assert.expect(5);
var list = createView({
View: ListView,
model: 'foo',
data: this.data,
groupBy: ['foo'],
viewOptions: {hasSidebar: true},
arch: '<tree>' +
'<field name="foo"/>' +
'<field name="int_field" sum="Sum"/>' +
'</tree>',
});
// open blip grouping and check all lines
list.$('.o_group_header:contains("blip (2)")').click();
list.$('.o_data_row input').click();
assert.strictEqual(true, list.sidebar.$el.is(':visible'),
"element checked so sidebar")
// open yop grouping and verify blip are still checked
list.$('.o_group_header:contains("yop (1)")').click()
assert.strictEqual(2, list.$('.o_data_row input:checked').length,
"opening a grouping does not uncheck others");
assert.strictEqual(true, list.sidebar.$el.is(':visible'),
"element checked so sidebar")
// close and open blip grouping and verify blip are unchecked
list.$('.o_group_header:contains("blip (2)")').click();
list.$('.o_group_header:contains("blip (2)")').click();
assert.strictEqual(0, list.$('.o_data_row input:checked').length,
"opening and closing a grouping uncheck its elements");
assert.strictEqual(false, list.sidebar.$el.is(':visible'),
"no element checked so no sidebar")
list.destroy();
});
QUnit.test('aggregates are computed correctly', function (assert) { QUnit.test('aggregates are computed correctly', function (assert) {
assert.expect(4); assert.expect(4);
+3 -1
View File
@@ -88,7 +88,9 @@ class Users(models.Model):
params['forum_id'] = forum_id params['forum_id'] = forum_id
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
token_url = base_url + '/forum/validate_email?%s' % urls.url_encode(params) token_url = base_url + '/forum/validate_email?%s' % urls.url_encode(params)
activation_template.sudo().with_context(token_url=token_url).send_mail(self.id, force_send=True) with self._cr.savepoint():
activation_template.sudo().with_context(token_url=token_url).send_mail(
self.id, force_send=True, raise_exception=True)
return True return True
@api.one @api.one
+2 -2
View File
@@ -260,8 +260,8 @@
<t t-esc="question.child_count"/> <t t-esc="question.child_count"/>
<t t-if="question.post_type == 'question'"> <t t-if="question.post_type == 'question'">
<t t-if="question.child_count&lt;=1">View</t> <t t-if="question.child_count&lt;=1">Answer</t>
<t t-else="">Views</t> <t t-else="">Answers</t>
</t> </t>
<t t-if="question.post_type != 'question'"> <t t-if="question.post_type != 'question'">
<t t-if="question.child_count&lt;=1">Comment</t> <t t-if="question.child_count&lt;=1">Comment</t>
+2 -2
View File
@@ -8,11 +8,11 @@ declaration.
Signed, Signed,
Stijn Houben stijn.houben@dynapps.be https://github.com/stijnh92 Pieter Paulussen pieter.paulussen@dynapps.be https://github.com/PieterPaulussen
List of contributors: List of contributors:
Stijn Houben stijn.houben@dynapps.be https://github.com/stijnh92 Pieter Paulussen pieter.paulussen@dynapps.be https://github.com/PieterPaulussen
Raf Ven raf.ven@dynapps.be https://github.com/rven Raf Ven raf.ven@dynapps.be https://github.com/rven
Kurt Schmitz kurt.schmitz@dynapps.be https://github.com/kurt-schmitz Kurt Schmitz kurt.schmitz@dynapps.be https://github.com/kurt-schmitz
Rod Schouteden rod.schouteden@dynapps.be https://github.com/schout-it Rod Schouteden rod.schouteden@dynapps.be https://github.com/schout-it
+15
View File
@@ -0,0 +1,15 @@
France, 2018-06-20
JANUS agrees to the terms of the Odoo Corporate Contributor License
Agreement v1.0.
I declare that I am authorized and able to make this agreement and sign this
declaration.
Signed,
Martin Duvergey martin.duvergey@gpsante.fr https://github.com/mduvergey
List of contributors:
Martin Duvergey martin.duvergey@gpsante.fr https://github.com/mduvergey
+15
View File
@@ -0,0 +1,15 @@
South Africa, 2018-08-20
Paradigm Digital (Pty) Ltd agrees to the terms of the Odoo Corporate Contributor License
Agreement v1.0.
I declare that I am authorized and able to make this agreement and sign this
declaration.
Signed,
Habib Ayob habib@paradigmdigital.co.za https://github.com/paradigmdigital
List of contributors:
Habib Ayob habib@paradigmdigital.co.za https://github.com/h4818
+11
View File
@@ -0,0 +1,11 @@
Ukraine, 2018-06-11
I hereby agree to the terms of the Odoo Individual Contributor License
Agreement v1.0.
I declare that I am authorized and able to make this agreement and sign this
declaration.
Signed,
Paziuk Oleksandr o.paziuk@mobilunity.com https://github.com/oleksandrpaziuk
+24
View File
@@ -202,6 +202,30 @@ class TestORM(TransactionCase):
group_user.write({'users': [(3, user.id)]}) group_user.write({'users': [(3, user.id)]})
self.assertTrue(user.share) self.assertTrue(user.share)
@mute_logger('odoo.models')
def test_unlink_with_property(self):
""" Verify that unlink removes the related ir.property as unprivileged user """
user = self.env['res.users'].create({
'name': 'Justine Bridou',
'login': 'saucisson',
'groups_id': [4, self.ref('base.group_partner_manager')],
})
p1 = self.env['res.partner'].sudo(user).create({'name': 'Zorro'})
p1_prop = self.env['ir.property'].sudo(user).create({
'name': 'Slip en laine',
'res_id': 'res.partner,{}'.format(p1.id),
'fields_id': self.env['ir.model.fields'].search([
('model', '=', 'res.partner'), ('name', '=', 'ref')], limit=1).id,
'value_text': 'Nain poilu',
'type': 'char',
})
# Unlink with unprivileged user
p1.unlink()
# ir.property is deleted
self.assertEqual(
p1_prop.exists(), self.env['ir.property'], 'p1_prop should have been deleted')
class TestInherits(TransactionCase): class TestInherits(TransactionCase):
""" test the behavior of the orm for models that use _inherits; """ test the behavior of the orm for models that use _inherits;
+1 -2
View File
@@ -2948,9 +2948,8 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
# Delete the records' properties. # Delete the records' properties.
with self.env.norecompute(): with self.env.norecompute():
self.env['ir.property'].search([('res_id', 'in', refs)]).unlink()
self.check_access_rule('unlink') self.check_access_rule('unlink')
self.env['ir.property'].search([('res_id', 'in', refs)]).sudo().unlink()
cr = self._cr cr = self._cr
Data = self.env['ir.model.data'].sudo().with_context({}) Data = self.env['ir.model.data'].sudo().with_context({})
+1
View File
@@ -48,6 +48,7 @@ class _Cleaner(clean.Cleaner):
'float', 'vertical-align', 'display', 'float', 'vertical-align', 'display',
'padding', 'padding-top', 'padding-left', 'padding-bottom', 'padding-right', 'padding', 'padding-top', 'padding-left', 'padding-bottom', 'padding-right',
'margin', 'margin-top', 'margin-left', 'margin-bottom', 'margin-right', 'margin', 'margin-top', 'margin-left', 'margin-bottom', 'margin-right',
'white-space',
# box model # box model
'border', 'border-color', 'border-radius', 'border-style', 'border-width', 'border-top', 'border', 'border-color', 'border-radius', 'border-style', 'border-width', 'border-top',
'height', 'width', 'max-width', 'min-width', 'min-height', 'height', 'width', 'max-width', 'min-width', 'min-height',