[MERGE] forward port branch saas-14 up to 557feb950c
This commit is contained in:
@@ -350,6 +350,26 @@ class AccountChartTemplate(models.Model):
|
||||
new_xml_id = str(company.id)+'_'+template_xmlid.name
|
||||
return ir_model_data._update(model, template_xmlid.module, vals, xml_id=new_xml_id, store=True, noupdate=True, mode='init', res_id=False)
|
||||
|
||||
def _get_account_vals(self, company, account_template, code_acc, tax_template_ref):
|
||||
""" This method generates a dictionnary of all the values for the account that will be created.
|
||||
"""
|
||||
self.ensure_one()
|
||||
tax_ids = []
|
||||
for tax in account_template.tax_ids:
|
||||
tax_ids.append(tax_template_ref[tax.id])
|
||||
val = {
|
||||
'name': account_template.name,
|
||||
'currency_id': account_template.currency_id and account_template.currency_id.id or False,
|
||||
'code': code_acc,
|
||||
'user_type_id': account_template.user_type_id and account_template.user_type_id.id or False,
|
||||
'reconcile': account_template.reconcile,
|
||||
'note': account_template.note,
|
||||
'tax_ids': [(6, 0, tax_ids)],
|
||||
'company_id': company.id,
|
||||
'tag_ids': [(6, 0, [t.id for t in account_template.tag_ids])],
|
||||
}
|
||||
return val
|
||||
|
||||
@api.multi
|
||||
def generate_account(self, tax_template_ref, acc_template_ref, code_digits, company):
|
||||
""" This method for generating accounts from templates.
|
||||
@@ -365,25 +385,11 @@ class AccountChartTemplate(models.Model):
|
||||
account_tmpl_obj = self.env['account.account.template']
|
||||
acc_template = account_tmpl_obj.search([('nocreate', '!=', True), ('chart_template_id', '=', self.id)], order='id')
|
||||
for account_template in acc_template:
|
||||
tax_ids = []
|
||||
for tax in account_template.tax_ids:
|
||||
tax_ids.append(tax_template_ref[tax.id])
|
||||
|
||||
code_main = account_template.code and len(account_template.code) or 0
|
||||
code_acc = account_template.code or ''
|
||||
if code_main > 0 and code_main <= code_digits:
|
||||
code_acc = str(code_acc) + (str('0'*(code_digits-code_main)))
|
||||
vals = {
|
||||
'name': account_template.name,
|
||||
'currency_id': account_template.currency_id and account_template.currency_id.id or False,
|
||||
'code': code_acc,
|
||||
'user_type_id': account_template.user_type_id and account_template.user_type_id.id or False,
|
||||
'reconcile': account_template.reconcile,
|
||||
'note': account_template.note,
|
||||
'tax_ids': [(6, 0, tax_ids)],
|
||||
'company_id': company.id,
|
||||
'tag_ids': [(6, 0, [t.id for t in account_template.tag_ids])],
|
||||
}
|
||||
vals = self._get_account_vals(company, account_template, code_acc, tax_template_ref)
|
||||
new_account = self.create_record_with_xmlid(company, account_template, 'account.account', vals)
|
||||
acc_template_ref[account_template.id] = new_account
|
||||
return acc_template_ref
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<h2>Invoicing</h2>
|
||||
<div class="row mt16 o_settings_container">
|
||||
<div class="row mt16 o_settings_container" id="invoicing_settings">
|
||||
<div class="col-xs-12 col-md-6 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="group_proforma_invoices"/>
|
||||
@@ -176,11 +176,6 @@
|
||||
<div class="text-muted">
|
||||
Mail your invoices in one-click using <a href="https://www.docsaway.com/">Docsaway</a>
|
||||
</div>
|
||||
<div id="print_docsway_message" class="content-group" attrs="{'invisible': [('module_print_docsaway', '=', False)]}">
|
||||
<div class="text-warning mt16 mb4">
|
||||
Save this page and come back here to set up the feature.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
<th>Terms & Conditions</th>
|
||||
<td></td>
|
||||
<td>
|
||||
<a class="fa fa-arrow-right" t-attf-href="#{prepare_backend_url('sales_team.action_sale_config', 'form')}&id=#{company_id.id}" target="_blank">
|
||||
<a class="fa fa-arrow-right" t-attf-href="#{prepare_backend_url('sales_team.action_sale_config', 'form')}" target="_blank">
|
||||
Change
|
||||
</a>
|
||||
</td>
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="id" string="Cash Basis"/>
|
||||
<div class="text-muted">
|
||||
Declare taxes at payment date.
|
||||
Journal used to declare taxes at payment date
|
||||
</div>
|
||||
<field name="tax_cash_basis_journal_id"/>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<field name="inherit_id" ref="base_setup.base_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<div name='auth_ldap_right_pane' position="inside">
|
||||
<div>
|
||||
<div attrs="{'invisible': [('module_auth_ldap','=',False)]}">
|
||||
<button type="action" name="%(auth_ldap.action_ldap_installer)d" string="LDAP Server" icon="fa-arrow-right" class="btn-link"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
<field name="inherit_id" ref="base_setup.base_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<div id="msg_module_auth_oauth" position="replace">
|
||||
<div class="content-group">
|
||||
<div class="content-group" attrs="{'invisible': [('module_auth_oauth','=',False)]}">
|
||||
<div class="mt16">
|
||||
<button type="action" name="%(auth_oauth.action_oauth_provider)d" string="OAuth Providers" icon="fa-arrow-right" class="btn-link"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="module_auth_oauth" position="after">
|
||||
<div class="col-xs-12 col-md-6 o_setting_box">
|
||||
<div class="col-xs-12 col-md-6 o_setting_box" attrs="{'invisible': [('module_auth_oauth','=',False)]}">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="auth_oauth_google_enabled"/>
|
||||
</div>
|
||||
|
||||
@@ -28,6 +28,8 @@ var FormViewBarcodeHandler = common.AbstractField.extend(BarcodeHandlerMixin, {
|
||||
this.__quantity_listener = _.bind(this._set_quantity_listener, this);
|
||||
BarcodeHandlerMixin.init.apply(this, arguments);
|
||||
|
||||
this.process_barcode_mutex = new utils.Mutex();
|
||||
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
@@ -153,25 +155,35 @@ var FormViewBarcodeHandler = common.AbstractField.extend(BarcodeHandlerMixin, {
|
||||
else if (this.form_view.get('actual_mode') === 'view')
|
||||
this._display_no_edit_mode_warning();
|
||||
else {
|
||||
// Call hook method possibly implemented by subclass
|
||||
this.pre_onchange_hook(barcode).then(function(proceed) {
|
||||
if (proceed === true) {
|
||||
// Wait for hypothetical ongoing onchange to finish
|
||||
self.form_view.onchanges_mutex.exec(function() {
|
||||
// A real onchange is triggered when a value actually changes (which can correspond
|
||||
// to a widget's blur event per example). Commit the value of fields before
|
||||
// programmatically triggering an onchange to be consistent with this.
|
||||
var mutex_commit_value = new utils.Mutex();
|
||||
_.each(self.form_view.fields, function(field) {
|
||||
mutex_commit_value.exec(_.bind(field.commit_value, field));
|
||||
});
|
||||
return mutex_commit_value.def.then(function(){
|
||||
// Trigger the barcode onchange
|
||||
self.set_value(barcode);
|
||||
});
|
||||
});
|
||||
var process_barcode = function () {
|
||||
// this function can be passed to `Mutex.exec` in order to make sure
|
||||
// that every ongoing onchanges in the form view are done
|
||||
var form_onchanges_mutex = function () {
|
||||
return self.form_view.onchanges_mutex.def;
|
||||
}
|
||||
});
|
||||
|
||||
// before setting the barcode field with the received barcode, we commit
|
||||
// every fields of the form view and we wait for their hypothetical ongoing
|
||||
// onchanges to finish
|
||||
var commit_mutex = new utils.Mutex();
|
||||
_.each(self.form_view.fields, function (field) {
|
||||
commit_mutex.exec(function () {
|
||||
return field.commit_value();
|
||||
});
|
||||
commit_mutex.exec(form_onchanges_mutex);
|
||||
});
|
||||
|
||||
return commit_mutex.def.then(function () {
|
||||
return self.pre_onchange_hook(barcode).then(function (proceed) {
|
||||
if (proceed) {
|
||||
self.set_value(barcode); // set the barcode field with the received one
|
||||
return form_onchanges_mutex(); // wait for its onchange to finish
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.process_barcode_mutex.exec(process_barcode);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -7,32 +7,32 @@
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@name='base_gengo_right_pane']" position="inside">
|
||||
<div name="base_gengo_right_pane" position="replace">
|
||||
<label for="id" string="Public Key"/>
|
||||
<div name="gengo_public_key">
|
||||
<label for="id" string="Public Key" attrs="{'invisible': [('module_base_gengo','=',False)]}"/>
|
||||
<div name="gengo_public_key" attrs="{'invisible': [('module_base_gengo','=',False)]}">
|
||||
<div name="gengo_public_key">
|
||||
<field name="gengo_public_key" nolabel="1" placeholder="Add Gengo login Public Key..."/>
|
||||
</div>
|
||||
</div>
|
||||
<label for="id" string="Private Key"/>
|
||||
<div name="gengo_private_key">
|
||||
<label for="id" string="Private Key" attrs="{'invisible': [('module_base_gengo','=',False)]}"/>
|
||||
<div name="gengo_private_key" attrs="{'invisible': [('module_base_gengo','=',False)]}">
|
||||
<div name="gengo_private_key">
|
||||
<field name="gengo_private_key" password="True" nolabel="1" placeholder="Add Gengo login Private Key..."/>
|
||||
</div>
|
||||
</div>
|
||||
<div name="gengo_auto_approve">
|
||||
<div name="gengo_auto_approve" attrs="{'invisible': [('module_base_gengo','=',False)]}">
|
||||
<div name="gengo_auto_approve">
|
||||
<field name="gengo_auto_approve"/>
|
||||
<label for="id" string="Auto Approve Translation"/>
|
||||
</div>
|
||||
</div>
|
||||
<div name="gengo_sandbox">
|
||||
<div name="gengo_sandbox" attrs="{'invisible': [('module_base_gengo','=',False)]}">
|
||||
<div name="gengo_sandbox">
|
||||
<field name="gengo_sandbox"/>
|
||||
<label for="id" string="Sandbox Mode"/>
|
||||
</div>
|
||||
</div>
|
||||
<label for="id" string="Comment"/>
|
||||
<div name="gengo_comment">
|
||||
<label for="id" string="Comment" attrs="{'invisible': [('module_base_gengo','=',False)]}"/>
|
||||
<div name="gengo_comment" attrs="{'invisible': [('module_base_gengo','=',False)]}">
|
||||
<div name="gengo_comment">
|
||||
<field name="gengo_comment" nolabel="1" placeholder="Add your comments here for translator...."/>
|
||||
</div>
|
||||
|
||||
@@ -402,6 +402,20 @@ class GoogleCalendar(models.AbstractModel):
|
||||
data_json = json.dumps(data)
|
||||
return self.env['google.service']._do_request(url, data_json, headers, type='PUT')
|
||||
|
||||
def create_from_google(self, event, partner_id):
|
||||
context_tmp = dict(self._context, NewMeeting=True)
|
||||
res = self.with_context(context_tmp).update_from_google(False, event.GG.event, "create")
|
||||
event.OE.event_id = res
|
||||
meeting = self.env['calendar.event'].browse(res)
|
||||
attendee_record = self.env['calendar.attendee'].search([('partner_id', '=', partner_id), ('event_id', '=', res)])
|
||||
attendee_record.with_context(context_tmp).write({'oe_synchro_date': meeting.oe_update_date, 'google_internal_event_id': event.GG.event['id']})
|
||||
if meeting.recurrency:
|
||||
attendees = self.env['calendar.attendee'].sudo().search([('google_internal_event_id', '=ilike', '%s\_%%' % event.GG.event['id'])])
|
||||
excluded_recurrent_event_ids = set(attendee.event_id for attendee in attendees)
|
||||
for event in excluded_recurrent_event_ids:
|
||||
event.write({'recurrent_id': meeting.id, 'recurrent_id_date': event.start, 'user_id': meeting.user_id.id})
|
||||
return event
|
||||
|
||||
def update_from_google(self, event, single_event_dict, type):
|
||||
""" Update an event in Odoo with information from google calendar
|
||||
:param event : record od calendar.event to update
|
||||
@@ -786,13 +800,8 @@ class GoogleCalendar(models.AbstractModel):
|
||||
if isinstance(actToDo, NothingToDo):
|
||||
continue
|
||||
elif isinstance(actToDo, Create):
|
||||
context_tmp = {'newMeeting': True}
|
||||
if actSrc == 'GG':
|
||||
res = recs.with_context(context_tmp).update_from_google(False, event.GG.event, "create")
|
||||
event.OE.event_id = res
|
||||
meeting = CalendarEvent.browse(res)
|
||||
attendee_records = CalendarAttendee.search([('partner_id', '=', my_partner_id), ('event_id', '=', res)])
|
||||
attendee_records.with_context(context_tmp).write({'oe_synchro_date': meeting.oe_update_date, 'google_internal_event_id': event.GG.event['id']})
|
||||
self.create_from_google(event, my_partner_id)
|
||||
elif actSrc == 'OE':
|
||||
raise "Should be never here, creation for OE is done before update !"
|
||||
#TODO Add to batch
|
||||
@@ -817,6 +826,12 @@ class GoogleCalendar(models.AbstractModel):
|
||||
main_ev = CalendarAttendee.with_context(context_novirtual).search([('google_internal_event_id', '=', event.GG.event['id'].rsplit('_', 1)[0])], limit=1)
|
||||
event_to_synchronize[base_event][0][1].OE.event_id = main_ev.event_id.id
|
||||
|
||||
if event_to_synchronize[base_event][0][1].OE.event_id:
|
||||
parent_event['id'] = "%s-%s" % (event_to_synchronize[base_event][0][1].OE.event_id, new_google_event_id)
|
||||
res = self.update_from_google(parent_event, event.GG.event, "copy")
|
||||
else:
|
||||
self.create_from_google(event, my_partner_id)
|
||||
|
||||
parent_event['id'] = "%s-%s" % (event_to_synchronize[base_event][0][1].OE.event_id, new_google_event_id)
|
||||
res = recs.update_from_google(parent_event, event.GG.event, "copy")
|
||||
else:
|
||||
|
||||
@@ -1568,28 +1568,28 @@
|
||||
<field name="code">4700</field>
|
||||
<field name="reconcile" eval="True"/>
|
||||
<field name="name">Hacienda Pública, deudora por IVA</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="chart_template_id" ref="l10n_es.account_chart_template_common"/>
|
||||
</record>
|
||||
<record id="pgc_4708_child" model="account.account.template">
|
||||
<field name="code">4708</field>
|
||||
<field name="reconcile" eval="True"/>
|
||||
<field name="name">Hacienda Pública, deudora por subvenciones concedidas</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_expenses"/>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="chart_template_id" ref="l10n_es.account_chart_template_common"/>
|
||||
</record>
|
||||
<record id="pgc_4709_child" model="account.account.template">
|
||||
<field name="code">4709</field>
|
||||
<field name="reconcile" eval="True"/>
|
||||
<field name="name">Hacienda Pública, deudora por devolución de impuestos</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_expenses"/>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="chart_template_id" ref="l10n_es.account_chart_template_common"/>
|
||||
</record>
|
||||
<record id="pgc_471_child" model="account.account.template">
|
||||
<field name="code">471</field>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="name">Organismos de la Seguridad Social, deudores</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_expenses"/>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="chart_template_id" ref="l10n_es.account_chart_template_common"/>
|
||||
</record>
|
||||
<record id="pgc_472_child" model="account.account.template">
|
||||
@@ -1603,77 +1603,77 @@
|
||||
<field name="code">473</field>
|
||||
<field name="reconcile" eval="True"/>
|
||||
<field name="name">Hacienda Pública, retenciones y pagos a cuenta</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_expenses"/>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="chart_template_id" ref="l10n_es.account_chart_template_common"/>
|
||||
</record>
|
||||
<record id="pgc_4740_child" model="account.account.template">
|
||||
<field name="code">4740</field>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="name">Activos por diferencias temporarias deducibles</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_expenses"/>
|
||||
<field name="user_type_id" ref="account.data_account_type_non_current_assets"/>
|
||||
<field name="chart_template_id" ref="l10n_es.account_chart_template_common"/>
|
||||
</record>
|
||||
<record id="pgc_4742_child" model="account.account.template">
|
||||
<field name="code">4742</field>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="name">Derechos por deducciones y bonificaciones pendientes de aplicar</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_expenses"/>
|
||||
<field name="user_type_id" ref="account.data_account_type_non_current_assets"/>
|
||||
<field name="chart_template_id" ref="l10n_es.account_chart_template_common"/>
|
||||
</record>
|
||||
<record id="pgc_4745_child" model="account.account.template">
|
||||
<field name="code">4745</field>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="name">Crédito por pérdidas a compensar del ejercicio</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_expenses"/>
|
||||
<field name="user_type_id" ref="account.data_account_type_non_current_assets"/>
|
||||
<field name="chart_template_id" ref="l10n_es.account_chart_template_common"/>
|
||||
</record>
|
||||
<record id="pgc_4750_child" model="account.account.template">
|
||||
<field name="code">4750</field>
|
||||
<field name="reconcile" eval="True"/>
|
||||
<field name="name">Hacienda Pública, acreedora por IVA</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_expenses"/>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="chart_template_id" ref="l10n_es.account_chart_template_common"/>
|
||||
</record>
|
||||
<record id="pgc_4751_child" model="account.account.template">
|
||||
<field name="code">4751</field>
|
||||
<field name="reconcile" eval="True"/>
|
||||
<field name="name">Hacienda Pública, acreedora por retenciones practicadas</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_expenses"/>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="chart_template_id" ref="l10n_es.account_chart_template_common"/>
|
||||
</record>
|
||||
<record id="pgc_4752_child" model="account.account.template">
|
||||
<field name="code">4752</field>
|
||||
<field name="reconcile" eval="True"/>
|
||||
<field name="name">Hacienda Pública, acreedora por impuesto sobre sociedades</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_expenses"/>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="chart_template_id" ref="l10n_es.account_chart_template_common"/>
|
||||
</record>
|
||||
<record id="pgc_4758_child" model="account.account.template">
|
||||
<field name="code">4758</field>
|
||||
<field name="reconcile" eval="True"/>
|
||||
<field name="name">Hacienda Pública, acreedora por subvenciones a reintegrar</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_expenses"/>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="chart_template_id" ref="l10n_es.account_chart_template_common"/>
|
||||
</record>
|
||||
<record id="pgc_476_child" model="account.account.template">
|
||||
<field name="code">476</field>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="name">Organismos de la Seguridad Social, acreedores</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_expenses"/>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="chart_template_id" ref="l10n_es.account_chart_template_common"/>
|
||||
</record>
|
||||
<record id="pgc_477_child" model="account.account.template">
|
||||
<field name="code">477</field>
|
||||
<field name="reconcile" eval="True"/>
|
||||
<field name="name">Hacienda Pública. IVA repercutido</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_expenses"/>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="chart_template_id" ref="l10n_es.account_chart_template_common"/>
|
||||
</record>
|
||||
<record id="pgc_479_child" model="account.account.template">
|
||||
<field name="code">479</field>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="name">Pasivos por diferencias temporarias imponibles</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_expenses"/>
|
||||
<field name="user_type_id" ref="account.data_account_type_non_current_liabilities"/>
|
||||
<field name="chart_template_id" ref="l10n_es.account_chart_template_common"/>
|
||||
</record>
|
||||
<record id="pgc_480_child" model="account.account.template">
|
||||
|
||||
@@ -40,5 +40,7 @@ With this module you will have:
|
||||
"data/account_data.xml",
|
||||
"data/account_tax_data.xml",
|
||||
"data/account_chart_template_data.yml",
|
||||
"data/fiscal_position_data.xml",
|
||||
"views/res_config_view.xml",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" ?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="account_fiscal_position_personasmoralesdelrgimengeneral0" model="account.fiscal.position">
|
||||
<field name="name">Personas morales del régimen general</field>
|
||||
<field name="note">Es para las sociedades mercantiles, asociaciones civiles de profesionales que:
|
||||
|
||||
- Realicen actividades lucrativas,
|
||||
- Sociedades cooperativas de producción,
|
||||
- Instituciones de crédito tales como bancos y casas de bolsa,
|
||||
- Organismos descentralizados que comercialicen bienes o servicios, entre otras.
|
||||
|
||||
|
||||
http://www.sat.gob.mx/6_406.html
|
||||
|
||||
última modificación :
|
||||
16/febrero/2010, 12:56, información vigente.</field>
|
||||
</record>
|
||||
|
||||
<record id="account_fiscal_position_personasmoralesconfinesnolucrativos0" model="account.fiscal.position">
|
||||
<field name="name">Personas morales con fines no lucrativos</field>
|
||||
<field name="note">Es para las personas morales cuya finalidad no es obtener una ganancia económica o lucro, por ejemplo:
|
||||
|
||||
- Instituciones de asistencia o de beneficencia
|
||||
- Asociaciones o sociedades civiles con fines políticos o deportivos
|
||||
- Asociaciones religiosas
|
||||
- Asociaciones patronales; sindicatos obreros; cámaras de comercio e industria
|
||||
- Asociaciones o sociedades civiles de enseñanza; de investigación científica o tecnológica
|
||||
- Sociedades cooperativas de consumo o sociedades mutualistas.
|
||||
- Colegios de profesionales
|
||||
- Partidos políticos
|
||||
- Asociaciones de padres de familia
|
||||
- Asociaciones civiles de colonos o las que administren inmuebles en condominio
|
||||
|
||||
|
||||
http://www.sat.gob.mx/6_404.html
|
||||
|
||||
última modificación :
|
||||
16/febrero/2010, 12:57, información vigente.</field>
|
||||
</record>
|
||||
|
||||
<record id="account_fiscal_position_asociacionesreligiosas0" model="account.fiscal.position">
|
||||
<field name="name">Asociaciones Religiosas</field>
|
||||
<field name="note">Son congregaciones que tienen como fin el ejercer culto a una divinidad; para efectos fiscales, se considerarán a las iglesias y demás agrupaciones que obtengan su correspondiente registro constitutivo ante la Secretaría de Gobernación, en los términos de la Ley de Asociaciones Religiosas y Culto Público, ya que es a través de este registro como se obtiene la personalidad jurídica.
|
||||
|
||||
|
||||
http://www.sat.gob.mx/sitio_internet/asistencia_contribuyente/principiantes/permonl/68_1813.html
|
||||
|
||||
última modificación :
|
||||
10/abril/2012, 18:15, información vigente.</field>
|
||||
</record>
|
||||
|
||||
<record id="account_fiscal_position_personasmoralesdelrgimensimplificado0" model="account.fiscal.position">
|
||||
<field name="name">Personas morales del régimen simplificado</field>
|
||||
<field name="note">Deben tributar en el régimen simplificado las siguientes personas morales:
|
||||
|
||||
- Las dedicadas exclusivamente al autotransporte terrestre de carga o de pasajeros.
|
||||
|
||||
- Las que se dediquen exclusivamente a actividades:
|
||||
1) Agrícolas
|
||||
2) Ganaderas
|
||||
3) Silvícolas
|
||||
4) Pesqueras
|
||||
|
||||
- Las constituidas como empresas integradoras.
|
||||
|
||||
|
||||
http://www.sat.gob.mx/6_405.html
|
||||
última modificación :
|
||||
24/agosto/2009, 12:03, información vigente
|
||||
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="account_fiscal_position_personafisicaconactividadempresarial0" model="account.fiscal.position">
|
||||
<field name="name">Persona física con actividad empresarial</field>
|
||||
<field name="note">Es el régimen conforme al cual pueden pagar sus impuestos todas las personas físicas que se dediquen a las siguientes actividades
|
||||
|
||||
- Comerciales.
|
||||
|
||||
- Industriales, que consisten en la extracción, conservación o transformación de materias primas, acabado de productos y la elaboración de satisfactores;
|
||||
|
||||
- Agrícolas, que comprenden las actividades de siembra, cultivo, cosecha y la primera enajenación de los productos obtenidos, que no hayan sido objeto de transformación industrial;
|
||||
|
||||
- Ganaderas, que son las consistentes en la cría y engorda de ganado, aves de corral y animales, así como la primera enajenación de sus productos, que no hayan sido objeto de transformación industrial;
|
||||
|
||||
- De pesca, que incluyen la cría, cultivo, fomento y cuidado de la reproducción de toda clase de especies marinas y de agua dulce, incluida la acuacultura, así como la captura y extracción de las mismas y la primera enajenación de sus productos, que no hayan sido objeto de transformación industrial;
|
||||
|
||||
- Silvícolas, que son las de cultivo de los bosques o montes, así como la cría, conservación, restauración, fomento y aprovechamiento de la vegetación de los mismos y la primera enajenación de sus productos, que no hayan sido objeto de transformación industrial.
|
||||
|
||||
|
||||
http://www.sat.gob.mx/sitio_internet/asistencia_contribuyente/principiantes/empresarial/11_428.html
|
||||
última modificación :
|
||||
12/septiembre/2005, 15:58, información vigente
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="account_fiscal_position_foreign" model="account.fiscal.position">
|
||||
<field name="name">Foreign Customer</field>
|
||||
</record>
|
||||
|
||||
<record id="account_fiscal_position0_purchase" model="account.fiscal.position.tax">
|
||||
<field name="tax_src_id" ref="l10n_mx.1_tax14"/>
|
||||
<field name="tax_dest_id" ref="l10n_mx.1_tax13"/>
|
||||
<field name="position_id" ref="account_fiscal_position_foreign"/>
|
||||
</record>
|
||||
|
||||
<record id="account_fiscal_position0_sale" model="account.fiscal.position.tax">
|
||||
<field name="tax_src_id" ref="l10n_mx.1_tax12"/>
|
||||
<field name="tax_dest_id" ref="l10n_mx.1_tax9"/>
|
||||
<field name="position_id" ref="account_fiscal_position_foreign"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -4,3 +4,4 @@
|
||||
|
||||
from . import chart_template
|
||||
from . import account
|
||||
from . import res_config
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class AccountConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
module_l10n_mx_edi = fields.Boolean('Mexican Electronic Invoicing')
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="account_config_settings_account_mx_inherit" model="ir.ui.view">
|
||||
<field name="name">account.config.settings.form.inherit</field>
|
||||
<field name="model">account.config.settings</field>
|
||||
<field name="inherit_id" ref="account.account_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr=".//div[@id='invoicing_settings']" position="inside">
|
||||
<div class="col-xs-12 col-md-6 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="module_l10n_mx_edi" class="oe_inline" widget="upgrade_boolean"/>
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="module_l10n_mx_edi"/>
|
||||
<div class="text-muted">
|
||||
Create your electronic invoices automatically (CFDI format)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -271,8 +271,8 @@ class MrpProduction(models.Model):
|
||||
boms, lines = production.bom_id.explode(production.product_id, factor, picking_type=production.bom_id.picking_type_id)
|
||||
production._generate_raw_moves(lines)
|
||||
# Check for all draft moves whether they are mto or not
|
||||
self._adjust_procure_method()
|
||||
self.move_raw_ids.action_confirm()
|
||||
production._adjust_procure_method()
|
||||
production.move_raw_ids.action_confirm()
|
||||
return True
|
||||
|
||||
def _generate_finished_moves(self):
|
||||
|
||||
@@ -4,6 +4,9 @@ import json
|
||||
import logging
|
||||
import urlparse
|
||||
|
||||
import dateutil.parser
|
||||
import pytz
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.addons.payment.models.payment_acquirer import ValidationError
|
||||
from odoo.addons.payment_paypal.controllers.main import PaypalController
|
||||
@@ -193,7 +196,16 @@ class TxPaypal(models.Model):
|
||||
}
|
||||
if status in ['Completed', 'Processed']:
|
||||
_logger.info('Validated Paypal payment for tx %s: set as done' % (self.reference))
|
||||
res.update(state='done', date_validate=data.get('payment_date', fields.datetime.now()))
|
||||
try:
|
||||
# dateutil and pytz don't recognize abbreviations PDT/PST
|
||||
tzinfos = {
|
||||
'PST': -8 * 3600,
|
||||
'PDT': -7 * 3600,
|
||||
}
|
||||
date_validate = dateutil.parser.parse(data.get('payment_date'), tzinfos=tzinfos).astimezone(pytz.utc)
|
||||
except:
|
||||
date_validate = fields.Datetime.now()
|
||||
res.update(state='done', date_validate=date_validate)
|
||||
return self.write(res)
|
||||
elif status in ['Pending', 'Expired']:
|
||||
_logger.info('Received notification for Paypal payment %s: set as pending' % (self.reference))
|
||||
|
||||
@@ -187,4 +187,4 @@ class PaypalForm(PaypalCommon):
|
||||
# check
|
||||
self.assertEqual(tx.state, 'done', 'paypal: wrong state after receiving a valid pending notification')
|
||||
self.assertEqual(tx.acquirer_reference, '08D73520KX778924N', 'paypal: wrong txn_id after receiving a valid pending notification')
|
||||
self.assertEqual(tx.date_validate, '2013-11-18 03:21:19', 'paypal: wrong validation date')
|
||||
self.assertEqual(tx.date_validate, '2013-11-18 11:21:19', 'paypal: wrong validation date')
|
||||
|
||||
@@ -1432,6 +1432,7 @@ var ReceiptScreenWidget = ScreenWidget.extend({
|
||||
print_xml: function() {
|
||||
var env = {
|
||||
widget: this,
|
||||
order: this.pos.get_order(),
|
||||
receipt: this.pos.get_order().export_for_printing(),
|
||||
paymentlines: this.pos.get_order().get_paymentlines()
|
||||
};
|
||||
|
||||
@@ -502,10 +502,14 @@ class SaleOrder(models.Model):
|
||||
res = {}
|
||||
currency = self.currency_id or self.company_id.currency_id
|
||||
for line in self.order_line:
|
||||
base_tax = 0
|
||||
for tax in line.tax_id:
|
||||
group = tax.tax_group_id
|
||||
res.setdefault(group, 0.0)
|
||||
res[group] += tax.compute_all(line.price_reduce, quantity=line.product_uom_qty)['taxes'][0]['amount']
|
||||
amount = tax.compute_all(line.price_reduce + base_tax, quantity=line.product_uom_qty)['taxes'][0]['amount']
|
||||
res[group] += amount
|
||||
if tax.include_base_amount:
|
||||
base_tax += amount
|
||||
res = sorted(res.items(), key=lambda l: l[0].sequence)
|
||||
res = map(lambda l: (l[0].name, l[1]), res)
|
||||
return res
|
||||
|
||||
@@ -44,7 +44,7 @@ class Quant(models.Model):
|
||||
lot_id = fields.Many2one(
|
||||
'stock.production.lot', 'Lot/Serial Number',
|
||||
index=True, ondelete="restrict", readonly=True)
|
||||
cost = fields.Float('Unit Cost')
|
||||
cost = fields.Float('Unit Cost', group_operator='avg')
|
||||
owner_id = fields.Many2one(
|
||||
'res.partner', 'Owner',
|
||||
index=True, readonly=True,
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
odoo.define("web.domainUtils", function (require) {
|
||||
"use strict";
|
||||
|
||||
var pyeval = require("web.pyeval");
|
||||
|
||||
function domainToString(domain) {
|
||||
if (_.isString(domain)) return domain;
|
||||
return JSON.stringify(domain || []).replace(/false/g, "False").replace(/true/g, "True");
|
||||
}
|
||||
function stringToDomain(domain) {
|
||||
if (!_.isString(domain)) return domain;
|
||||
return pyeval.eval("domain", domain || "[]");
|
||||
}
|
||||
|
||||
return {
|
||||
domainToString: domainToString,
|
||||
stringToDomain: stringToDomain,
|
||||
};
|
||||
});
|
||||
@@ -11,6 +11,7 @@ var Priority = require('web.Priority');
|
||||
var ProgressBar = require('web.ProgressBar');
|
||||
var Dialog = require('web.Dialog');
|
||||
var DomainSelector = require("web.DomainSelector");
|
||||
var DomainSelectorDialog = require("web.DomainSelectorDialog");
|
||||
var common = require('web.form_common');
|
||||
var formats = require('web.formats');
|
||||
var framework = require('web.framework');
|
||||
@@ -400,11 +401,19 @@ var FieldDomain = common.AbstractField.extend(common.ReinitializeFieldMixin).ext
|
||||
e.preventDefault();
|
||||
this._showSelection();
|
||||
},
|
||||
"click .o_form_field_domain_dialog_button": function (e) {
|
||||
e.preventDefault();
|
||||
this.openDomainDialog();
|
||||
},
|
||||
},
|
||||
custom_events: {
|
||||
"domain_changed": function (e) {
|
||||
if (this.options.in_dialog) return;
|
||||
this.set_value(this.domainSelector.getDomain(), true);
|
||||
},
|
||||
"domain_selected": function (e) {
|
||||
this.set_value(e.data.domain);
|
||||
},
|
||||
},
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
@@ -412,6 +421,7 @@ var FieldDomain = common.AbstractField.extend(common.ReinitializeFieldMixin).ext
|
||||
this.valid = true;
|
||||
this.debug = session.debug;
|
||||
this.options = _.defaults(this.options || {}, {
|
||||
in_dialog: false,
|
||||
model: undefined, // this option is mandatory !
|
||||
fs_filters: {}, // Field selector filters (to only show a subset of available fields @see FieldSelector)
|
||||
});
|
||||
@@ -457,7 +467,7 @@ var FieldDomain = common.AbstractField.extend(common.ReinitializeFieldMixin).ext
|
||||
this.domainSelector.destroy();
|
||||
}
|
||||
this.domainSelector = new DomainSelector(this, this.model, domain, {
|
||||
readonly: this.get("effective_readonly"),
|
||||
readonly: this.get("effective_readonly") || this.options.in_dialog,
|
||||
fs_filters: this.options.fs_filters,
|
||||
debugMode: session.debug,
|
||||
});
|
||||
@@ -492,6 +502,13 @@ var FieldDomain = common.AbstractField.extend(common.ReinitializeFieldMixin).ext
|
||||
disable_multiple_selection: true,
|
||||
}).open();
|
||||
},
|
||||
openDomainDialog: function () {
|
||||
new DomainSelectorDialog(this, this.model, this.get("value"), {
|
||||
readonly: this.get("effective_readonly"),
|
||||
fs_filters: this.options.fs_filters,
|
||||
debugMode: session.debug,
|
||||
}).open();
|
||||
},
|
||||
});
|
||||
|
||||
var FieldDate = common.AbstractField.extend(common.ReinitializeFieldMixin, {
|
||||
|
||||
@@ -641,7 +641,8 @@ ListView.include(/** @lends instance.web.ListView# */{
|
||||
if (saveInfo.created) {
|
||||
return self.start_edition();
|
||||
}
|
||||
var record = self.records[next_record](saveInfo.record);
|
||||
var options = { wraparound: !self.is_action_enabled('create') };
|
||||
var record = self.records[next_record](saveInfo.record, options);
|
||||
if (record === undefined) {
|
||||
return self.start_edition();
|
||||
}
|
||||
|
||||
@@ -466,7 +466,7 @@ var SearchView = View.extend({
|
||||
*/
|
||||
select_completion: function (e, ui) {
|
||||
e.preventDefault();
|
||||
if(ui.item.facet.values && ui.item.facet.values.length && String(ui.item.facet.values[0].value).trim() !== "") {
|
||||
if(ui.item.facet && ui.item.facet.values && ui.item.facet.values.length && String(ui.item.facet.values[0].value).trim() !== "") {
|
||||
this.query.add(ui.item.facet);
|
||||
} else {
|
||||
this.query.trigger('add');
|
||||
|
||||
@@ -3,9 +3,9 @@ odoo.define("web.DomainSelector", function (require) {
|
||||
|
||||
var core = require("web.core");
|
||||
var datepicker = require("web.datepicker");
|
||||
var domainUtils = require("web.domainUtils");
|
||||
var formats = require ("web.formats");
|
||||
var ModelFieldSelector = require("web.ModelFieldSelector");
|
||||
var pyeval = require("web.pyeval");
|
||||
var Widget = require("web.Widget");
|
||||
|
||||
var _t = core._t;
|
||||
@@ -55,7 +55,7 @@ var DomainNode = Widget.extend({
|
||||
},
|
||||
/// A DomainNode needs a model and domain to work. It can also receives a set of options
|
||||
/// @param model - a string with the model name
|
||||
/// @param domain - an array of the prefix representation of the domain
|
||||
/// @param domain - an array of the prefix representation of the domain (or a string which represents it)
|
||||
/// @param options - an object with possible values:
|
||||
/// - readonly, a boolean to indicate if the widget is readonly or not (default to true)
|
||||
/// - fs_filters, an object with a series of filters to use for the field selector (see @FieldSelector)
|
||||
@@ -109,7 +109,7 @@ var DomainTree = DomainNode.extend({
|
||||
/// @see DomainTree._addFlattenedChildren
|
||||
init: function (parent, model, domain, options) {
|
||||
this._super.apply(this, arguments);
|
||||
this._initialize(domain);
|
||||
this._initialize(domainUtils.stringToDomain(domain));
|
||||
},
|
||||
/// @see DomainTree.init
|
||||
_initialize: function (domain) {
|
||||
@@ -265,8 +265,9 @@ var DomainSelector = DomainTree.extend({
|
||||
/// the widget is re-rendered and notifies the parents. If not, a warning is shown to the
|
||||
/// user and the input is ignored.
|
||||
"change .o_domain_debug_input": function (e) {
|
||||
var domain;
|
||||
try {
|
||||
var domain = pyeval.eval("domain", $(e.currentTarget).val() || "[]");
|
||||
domain = domainUtils.stringToDomain($(e.currentTarget).val());
|
||||
} catch (err) {
|
||||
this.do_warn(_t("Syntax error"), _t("The domain you entered is not properly formed"));
|
||||
return;
|
||||
@@ -292,12 +293,7 @@ var DomainSelector = DomainTree.extend({
|
||||
// Display technical domain if in debug mode
|
||||
this.$debugInput = this.$(".o_domain_debug_input");
|
||||
if (this.$debugInput.length) {
|
||||
var strValue = this.getDomain() || "[]";
|
||||
if (!_.isString(strValue)) { // TODO there is no "pyuneval" function
|
||||
strValue = JSON.stringify(strValue);
|
||||
strValue = strValue.replace(/false/g, "False").replace(/true/g, "True");
|
||||
}
|
||||
this.$debugInput.val(strValue);
|
||||
this.$debugInput.val(domainUtils.domainToString(this.getDomain()));
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -328,6 +324,7 @@ var DomainLeaf = DomainNode.extend({
|
||||
init: function (parent, model, domain, options) {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
domain = domainUtils.stringToDomain(domain);
|
||||
this.chain = domain[0][0];
|
||||
this.operator = domain[0][1];
|
||||
this.value = domain[0][2];
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
odoo.define("web.DomainSelectorDialog", function (require) {
|
||||
"use strict";
|
||||
|
||||
var core = require("web.core");
|
||||
var Dialog = require("web.Dialog");
|
||||
var DomainSelector = require("web.DomainSelector");
|
||||
|
||||
var _t = core._t;
|
||||
|
||||
return Dialog.extend({
|
||||
init: function (parent, model, domain, options) {
|
||||
this.model = model;
|
||||
this.options = _.extend({
|
||||
readonly: true,
|
||||
fs_filters: {},
|
||||
debugMode: false,
|
||||
}, options || {});
|
||||
|
||||
var buttons;
|
||||
if (this.options.readonly) {
|
||||
buttons = [
|
||||
{text: _t("Close"), close: true},
|
||||
];
|
||||
} else {
|
||||
buttons = [
|
||||
{text: _t("Save"), classes: "btn-primary", close: true, click: function () {
|
||||
this.trigger_up("domain_selected", {domain: this.domainSelector.getDomain()});
|
||||
}},
|
||||
{text: _t("Discard"), close: true},
|
||||
];
|
||||
}
|
||||
|
||||
this._super(parent, _.extend({}, {
|
||||
title: _t("Domain"),
|
||||
buttons: buttons,
|
||||
}, options || {}));
|
||||
|
||||
this.domainSelector = new DomainSelector(this, model, domain, options);
|
||||
},
|
||||
start: function () {
|
||||
this.$el.css("overflow", "visible").closest(".modal-dialog").css("height", "auto"); // This restores default modal height (bootstrap) and allows field selector to overflow
|
||||
return $.when(
|
||||
this._super.apply(this, arguments),
|
||||
this.domainSelector.appendTo(this.$el)
|
||||
);
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -24,7 +24,10 @@
|
||||
&.o_domain_selector {
|
||||
&.o_edit_mode {
|
||||
position: relative;
|
||||
padding-right: @o-domain-selector-panel-space;
|
||||
|
||||
> .o_domain_node_children_container {
|
||||
padding-right: @o-domain-selector-panel-space;
|
||||
}
|
||||
}
|
||||
|
||||
> .o_domain_node_children_container {
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
// Title
|
||||
.oe_title {
|
||||
> h1, > h2, > h3 {
|
||||
width: 100%; // Needed because inline-block when is a hx.o_row
|
||||
width: 100%; // Needed because inline-block when is a hx.o_row
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
line-height: inherit;
|
||||
@@ -276,6 +276,20 @@
|
||||
padding: 0 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.o_form_field_domain {
|
||||
> .o_form_field_domain_panel {
|
||||
margin-top: 8px;
|
||||
}
|
||||
&.o_inline_mode.o_edit_mode {
|
||||
position: relative;
|
||||
|
||||
> .o_form_field_domain_panel {
|
||||
.o-position-absolute(0, 0);
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Overriden style when form view in modal
|
||||
|
||||
@@ -670,13 +670,16 @@
|
||||
t-att-maxlength="widget.field.size"/>
|
||||
</t>
|
||||
|
||||
<div t-name="FieldDomain" class="o_form_field_domain">
|
||||
<div t-name="FieldDomain" t-attf-class="o_form_field_domain#{widget.get('effective_readonly') ? '' : ' o_edit_mode'}#{widget.options.in_dialog ? '' : ' o_inline_mode'}">
|
||||
<!-- domain selector will be instantiated here -->
|
||||
<div class="mt8">
|
||||
<div class="o_form_field_domain_panel">
|
||||
<i class="fa fa-arrow-right"/>
|
||||
<span><span class="o_domain_records_count"/> Records Selected</span>
|
||||
<button class="btn btn-default btn-xs o_domain_show_selection_button" type="button">See selection</button>
|
||||
<button class="btn btn-xs btn-default o_domain_show_selection_button" type="button">
|
||||
<span class="o_domain_records_count"/> record(s)
|
||||
</button>
|
||||
<span class="text-warning o_domain_error_message hidden"><i class="fa fa-exclamation-triangle"/> Invalid domain</span>
|
||||
<button t-if="widget.options.in_dialog && !widget.get('effective_readonly')"
|
||||
class="btn btn-sm btn-primary o_form_field_domain_dialog_button">Edit domain</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -684,7 +687,7 @@
|
||||
<div t-if="!widget.readonly && !widget.noControlPanel" class="btn-group btn-group-sm pull-right o_domain_node_control_panel">
|
||||
<button class="btn btn-default o_domain_delete_node_button"><i class="fa fa-minus"/></button>
|
||||
<button class="btn btn-default o_domain_add_node_button"><i class="fa fa-plus"/></button>
|
||||
<button class="btn btn-default o_domain_add_node_button" t-att-data-branch="1"><i class="fa fa-ellipsis-h"/></button>
|
||||
<button class="btn btn-default o_domain_add_node_button" data-branch="1"><i class="fa fa-ellipsis-h"/></button>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="DomainTree.OperatorSelector">
|
||||
@@ -712,9 +715,9 @@
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div>
|
||||
<t t-if="widget.children.length === 1">Match the records with the following rule:</t>
|
||||
<t t-if="widget.children.length === 1">Match records with the following rule:</t>
|
||||
<t t-else="">
|
||||
<span>Match the records with</span>
|
||||
<span>Match records with</span>
|
||||
<t t-call="DomainTree.OperatorSelector"/>
|
||||
<span>of the following rules:</span>
|
||||
</t>
|
||||
|
||||
@@ -134,6 +134,7 @@
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.ba-bbq/jquery.ba-bbq.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/web/static/src/js/framework/data_model.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/framework/domain_utils.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/framework/formats.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/framework/view.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/framework/pyeval.js"></script>
|
||||
@@ -155,6 +156,7 @@
|
||||
<script type="text/javascript" src="/web/static/src/js/widgets/debug_manager.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/widgets/data_export.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/widgets/date_picker.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/widgets/domain_selector_dialog.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/widgets/domain_selector.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/widgets/loading.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/widgets/model_field_selector.js"></script>
|
||||
|
||||
@@ -161,6 +161,11 @@ var FieldTextHtmlSimple = widget.extend({
|
||||
return !this.get('value') || this.get('value') === "<p><br/></p>" || !this.get('value').match(/\S/);
|
||||
},
|
||||
commit_value: function () {
|
||||
/* Switch to WYSIWYG mode if currently in code view */
|
||||
if (session.debug) {
|
||||
var layoutInfo = this.$textarea.data('layoutInfo');
|
||||
$.summernote.pluginEvents.codeview(undefined, undefined, layoutInfo, false);
|
||||
}
|
||||
if (this.options['style-inline']) {
|
||||
transcoder.class_to_style(this.$content);
|
||||
transcoder.font_to_img(this.$content);
|
||||
@@ -377,6 +382,11 @@ var FieldTextHtml = widget.extend({
|
||||
this._dirty_flag = false;
|
||||
return this.editor.save();
|
||||
} else if (this._dirty_flag && this.editor && this.editor.buildingBlock) {
|
||||
/* Switch to WYSIWYG mode if currently in code view */
|
||||
if (session.debug) {
|
||||
var layoutInfo = this.editor.rte.editable().data('layoutInfo');
|
||||
$.summernote.pluginEvents.codeview(undefined, undefined, layoutInfo, false);
|
||||
}
|
||||
this.editor.buildingBlock.clean_for_save();
|
||||
this.internal_set_value( this.$content.html() );
|
||||
}
|
||||
|
||||
@@ -511,12 +511,32 @@ function prettify_html(html) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
$.summernote.pluginEvents.codeview = function (event, editor, layoutInfo) {
|
||||
|
||||
/*
|
||||
* This override when clicking on the 'Code View' button has two aims:
|
||||
*
|
||||
* - have our own code view implementation for FieldTextHtml
|
||||
* - add an 'enable' paramater to call the function directly and allow us to
|
||||
* disable (false) or enable (true) the code view mode.
|
||||
*/
|
||||
$.summernote.pluginEvents.codeview = function (event, editor, layoutInfo, enable) {
|
||||
if (layoutInfo === undefined) {
|
||||
return;
|
||||
}
|
||||
if (layoutInfo.toolbar) {
|
||||
// if editor inline (FieldTextHtmlSimple)
|
||||
var is_activated = $.summernote.eventHandler.modules.codeview.isActivated(layoutInfo);
|
||||
if (is_activated === enable) {
|
||||
return;
|
||||
}
|
||||
return eventHandler.modules.codeview.toggle(layoutInfo);
|
||||
} else {
|
||||
// if editor iframe (FieldTextHtml)
|
||||
var $editor = layoutInfo.editor();
|
||||
var $textarea = $editor.prev('textarea');
|
||||
if ($textarea.is('textarea') === enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$textarea.length) {
|
||||
// init and create texarea
|
||||
|
||||
@@ -710,24 +710,19 @@ var KanbanView = View.extend({
|
||||
var context = {};
|
||||
context['default_' + this.group_by_field] = column.id;
|
||||
var name = event.data.value;
|
||||
this.dataset.name_create(name, context).then(function on_success (data) {
|
||||
this.dataset.name_create(name).done(function(data) {
|
||||
add_record(data[0]);
|
||||
}, function on_fail (event) {
|
||||
}).fail(function(error, event) {
|
||||
event.preventDefault();
|
||||
var popup = new form_common.SelectCreatePopup(this);
|
||||
popup.select_element(
|
||||
self.model,
|
||||
{
|
||||
title: _t("Create: "),
|
||||
initial_view: "form",
|
||||
disable_multiple_selection: true
|
||||
},
|
||||
[],
|
||||
{"default_name": name}
|
||||
);
|
||||
popup.on("elements_selected", self, function(element_ids) {
|
||||
add_record(element_ids[0]);
|
||||
});
|
||||
var dialog = new form_common.FormViewDialog(self, {
|
||||
res_model: self.model,
|
||||
context: _.extend({"default_name": name}, context),
|
||||
title: _t("Create"),
|
||||
disable_multiple_selection: true,
|
||||
on_selected: function(element_ids) {
|
||||
add_record(element_ids[0]);
|
||||
}
|
||||
}).open();
|
||||
});
|
||||
|
||||
function add_record(id) {
|
||||
@@ -817,8 +812,20 @@ function transform_qweb_template (node, fvg, many2manys) {
|
||||
} else if (fields_registry.contains(ftype)) {
|
||||
// do nothing, the kanban record will handle it
|
||||
} else {
|
||||
node.tag = qweb.prefix;
|
||||
node.attrs[qweb.prefix + '-esc'] = 'record.' + node.attrs.name + '.value';
|
||||
if (node.attrs.bold) {
|
||||
node.tag = 'strong';
|
||||
var children_node = {
|
||||
attrs: {},
|
||||
children: [],
|
||||
tag: qweb.prefix,
|
||||
};
|
||||
children_node.attrs[qweb.prefix + '-esc'] = 'record.' + node.attrs.name + '.value';
|
||||
node.children.push(children_node);
|
||||
} else {
|
||||
node.tag = qweb.prefix;
|
||||
node.attrs[qweb.prefix + '-esc'] = 'record.' + node.attrs.name + '.value';
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case 'button':
|
||||
|
||||
@@ -65,7 +65,9 @@
|
||||
}
|
||||
|
||||
.o_dropdown_kanban {
|
||||
float: right;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 10px;
|
||||
@media (max-width: @screen-xs-max) {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<field name="inherit_id" ref="website.website_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<div id="msg_contact_form_install_setting" position="after">
|
||||
<div class="content-group mt16">
|
||||
<div class="content-group mt16" attrs="{'invisible': [('module_website_crm', '=', False)]}">
|
||||
<div class="row">
|
||||
<label class="col-md-3 o_light_label" string="Sales Channel" for="crm_default_team_id"/>
|
||||
<field name="crm_default_team_id" class="oe_inline"/>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<field name="inherit_id" ref="website.website_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<div id="msg_module_website_livechat" position="replace">
|
||||
<div class="content-group mt16">
|
||||
<div class="content-group mt16" attrs="{'invisible': [('module_website_livechat', '=', False)]}">
|
||||
<div class="row">
|
||||
<label class="col-md-3 o_light_label" string="Channel" for="channel_id"/>
|
||||
<field name="channel_id" class="oe_inline"/>
|
||||
|
||||
@@ -124,8 +124,11 @@ animation.registry.newsletter_popup = animation.Class.extend({
|
||||
|
||||
odoo.define('mass_mailing.unsubscribe', function (require) {
|
||||
var ajax = require('web.ajax');
|
||||
var core = require('web.core');
|
||||
require('web_editor.base'); // wait for implicit dependencies to load
|
||||
|
||||
var _t = core._t;
|
||||
|
||||
if (!$('.o_unsubscribe_form').length) {
|
||||
return $.Deferred().reject("DOM doesn't contain '.o_unsubscribe_form'");
|
||||
}
|
||||
@@ -148,10 +151,10 @@ odoo.define('mass_mailing.unsubscribe', function (require) {
|
||||
|
||||
ajax.jsonRpc('/mail/mailing/unsubscribe', 'call', {'opt_in_ids': checked_ids, 'opt_out_ids': unchecked_ids, 'email': email, 'mailing_id': mailing_id})
|
||||
.then(function(result) {
|
||||
$('.alert-info').html('Your changes has been saved.').removeClass('alert-info').addClass('alert-success');
|
||||
$('.alert-info').html(_t('Your changes have been saved.')).removeClass('alert-info').addClass('alert-success');
|
||||
})
|
||||
.fail(function() {
|
||||
$('.alert-info').html('You changes has not been saved, try again later.').removeClass('alert-info').addClass('alert-warning');
|
||||
$('.alert-info').html(_t('Your changes have not been saved, try again later.')).removeClass('alert-info').addClass('alert-warning');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -217,7 +217,7 @@ class website_account(website_account):
|
||||
partner = request.env['res.users'].browse(request.uid).partner_id
|
||||
invoices = request.env['account.invoice'].sudo().search_count([('partner_id', '=', partner.id), ('state', 'not in', ['draft', 'cancel'])])
|
||||
if invoices:
|
||||
if data.get('vat', partner.vat) != partner.vat:
|
||||
if (data.get('vat', partner.vat) or False) != partner.vat:
|
||||
error['vat'] = 'error'
|
||||
error_message.append(_('Changing VAT number is not allowed once invoices have been issued for your account. Please contact us directly for this operation.'))
|
||||
if data.get('name', partner.name) != partner.name:
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<field name="inherit_id" ref="website_sale.website_config_settings_view_form_inherit_website_sale"/>
|
||||
<field name="arch" type="xml">
|
||||
<div id="msg_delivery_method_setting" position="replace">
|
||||
<div class="content-group">
|
||||
<div class="content-group" attrs="{'invisible': [('module_website_sale_delivery', '=', False)]}">
|
||||
<div class="mt16">
|
||||
<button type="action" name="%(delivery.action_delivery_carrier_form)d" string="Delivery Methods" class="btn-link" icon="fa-arrow-right"/>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<field name="inherit_id" ref="website.website_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<div id="msg_slides_install_setting" position="replace">
|
||||
<div class="content-group" groups="base.group_no_one">
|
||||
<div class="content-group" groups="base.group_no_one" attrs="{'invisible': [('module_website_slides', '=', False)]}">
|
||||
<div class="row mt16">
|
||||
<label class="col-md-3 o_light_label" for="website_slide_google_app_key"/>
|
||||
<field name="website_slide_google_app_key" class="oe_inline"/>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<field name="inherit_id" ref="website.website_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<div id="msg_twitter_roller_install_setting" position="replace">
|
||||
<div class="content-group">
|
||||
<div class="content-group" attrs="{'invisible': [('module_website_twitter', '=', False)]}">
|
||||
<div class="mt16">
|
||||
<div class="row">
|
||||
<label class="col-md-3 o_light_label" string="API Key" for="twitter_api_key"/>
|
||||
|
||||
@@ -54,7 +54,7 @@ Business objects
|
||||
by Odoo based on their configuration
|
||||
|
||||
Data files
|
||||
XML or CSV files declaring metadata (views or workflows), configuration
|
||||
XML or CSV files declaring metadata (views or reports), configuration
|
||||
data (modules parameterization), demonstration data and more
|
||||
|
||||
Web controllers
|
||||
|
||||
@@ -17,7 +17,6 @@ Index: addons/openacademy/__manifest__.py
|
||||
@@ -31,6 +31,7 @@
|
||||
'views/openacademy.xml',
|
||||
'views/partner.xml',
|
||||
'views/session_workflow.xml',
|
||||
+ 'views/session_board.xml',
|
||||
'reports.xml',
|
||||
],
|
||||
|
||||
@@ -6,9 +6,9 @@ Index: addons/openacademy/__manifest__.py
|
||||
--- addons.orig/openacademy/__manifest__.py 2014-08-29 08:39:43.975536806 +0200
|
||||
+++ addons/openacademy/__manifest__.py 2014-08-29 08:39:52.000000000 +0200
|
||||
@@ -31,6 +31,7 @@
|
||||
'templates.xml',
|
||||
'views/openacademy.xml',
|
||||
'views/partner.xml',
|
||||
'views/session_workflow.xml',
|
||||
+ 'reports.xml',
|
||||
],
|
||||
# only loaded in demonstration mode
|
||||
|
||||
@@ -738,6 +738,65 @@ you to make your alias easily configurable from the record's form view.
|
||||
res = super(BusinessExpense, self).message_new(msg, custom_values=defaults)
|
||||
return res
|
||||
|
||||
.. _reference/mixins/mail/activities:
|
||||
|
||||
Activities tracking
|
||||
-------------------
|
||||
|
||||
Activities are actions users have to take on a document like making a phone call
|
||||
or organizing a meeting. Activities come with the mail module as they are
|
||||
integrated in the Chatter but are *not bundled with mail.thread*. Activities
|
||||
are records of the ``mail.activity`` class, which have a type (``mail.activity.type``),
|
||||
name, description, scheduled time (among others). Pending activities are visible
|
||||
above the message history in the chatter widget.
|
||||
|
||||
You can integrate activities using the ``mail.activity.mixin`` class on your object
|
||||
and the specific widgets to display them (via the field ``activity_ids``) in the form
|
||||
view and kanban view of your records (``mail_activity`` and ``kanban_activity``
|
||||
widgets, respectively).
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
Organizing a business trip is a tedious process and tracking needed activities
|
||||
like ordering plane tickets or a cab for the airport could be useful. To do so,
|
||||
we will add the activities mixin on our model and display the next planned activities
|
||||
in the message history of our trip.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class BusinessTrip(models.Model):
|
||||
_name = 'business.trip'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_description = 'Business Trip'
|
||||
|
||||
name = fields.Char()
|
||||
# [...]
|
||||
|
||||
We modify the form view of our trips to display their next activites:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<record id="businness_trip_form" model="ir.ui.view">
|
||||
<field name="name">business.trip.form</field>
|
||||
<field name="model">business.trip</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Business Trip">
|
||||
<!-- Your usual form view goes here -->
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="activity_ids" widget="mail_activity"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
You can find concrete examples of integration in the following models:
|
||||
|
||||
* ``crm.lead`` in the CRM (*crm*) Application
|
||||
* ``sale.order`` in the Sales (*sale*) Application
|
||||
* ``project.task`` in the Project (*poject*) Application
|
||||
|
||||
|
||||
.. _reference/mixins/website:
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# set server timezone in UTC before time module imported
|
||||
__import__('os').environ['TZ'] = 'UTC'
|
||||
__import__('pkg_resources').declare_namespace('odoo.addons')
|
||||
import odoo
|
||||
|
||||
|
||||
+5
-8
@@ -27,15 +27,12 @@ multi_process = False
|
||||
#----------------------------------------------------------
|
||||
# libc UTC hack
|
||||
#----------------------------------------------------------
|
||||
# Make sure the OpenERP server runs in UTC. This is especially necessary
|
||||
# under Windows as under Linux it seems the real import of time is
|
||||
# sufficiently deferred so that setting the TZ environment variable
|
||||
# in odoo.cli.server was working.
|
||||
# Make sure the OpenERP server runs in UTC.
|
||||
import os
|
||||
os.environ['TZ'] = 'UTC' # Set the timezone...
|
||||
import time # ... *then* import time.
|
||||
del os
|
||||
del time
|
||||
os.environ['TZ'] = 'UTC' # Set the timezone
|
||||
import time
|
||||
if hasattr(time, 'tzset'):
|
||||
time.tzset()
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Shortcuts
|
||||
|
||||
@@ -123,6 +123,31 @@ def get_view_arch_from_file(filename, xmlid):
|
||||
_logger.warning("Could not find view arch definition in file '%s' for xmlid '%s'", filename, xmlid)
|
||||
return None
|
||||
|
||||
def add_text_before(node, text):
|
||||
""" Add text before ``node`` in its XML tree. """
|
||||
if text is None:
|
||||
return
|
||||
prev = node.getprevious()
|
||||
if prev is not None:
|
||||
prev.tail = (prev.tail or "") + text
|
||||
else:
|
||||
parent = node.getparent()
|
||||
parent.text = (parent.text or "") + text
|
||||
|
||||
def add_text_inside(node, text):
|
||||
""" Add text inside ``node``. """
|
||||
if text is None:
|
||||
return
|
||||
if len(node):
|
||||
node[-1].tail = (node[-1].tail or "") + text
|
||||
else:
|
||||
node.text = (node.text or "") + text
|
||||
|
||||
def remove_element(node):
|
||||
""" Remove ``node`` but not its tail, from its XML tree. """
|
||||
add_text_before(node, node.tail)
|
||||
node.getparent().remove(node)
|
||||
|
||||
xpath_utils = etree.FunctionNamespace(None)
|
||||
xpath_utils['hasclass'] = _hasclass
|
||||
|
||||
@@ -555,21 +580,26 @@ actual arch.
|
||||
node.set(attribute, value)
|
||||
elif attribute in node.attrib:
|
||||
del node.attrib[attribute]
|
||||
else:
|
||||
sib = node.getnext()
|
||||
elif pos == 'inside':
|
||||
add_text_inside(node, spec.text)
|
||||
for child in spec:
|
||||
if pos == 'inside':
|
||||
node.append(child)
|
||||
elif pos == 'after':
|
||||
if sib is None:
|
||||
node.addnext(child)
|
||||
node = child
|
||||
else:
|
||||
sib.addprevious(child)
|
||||
elif pos == 'before':
|
||||
node.addprevious(child)
|
||||
else:
|
||||
self.raise_view_error(_("Invalid position attribute: '%s'") % pos, inherit_id)
|
||||
node.append(child)
|
||||
elif pos == 'after':
|
||||
# add a sentinel element right after node, insert content of
|
||||
# spec before the sentinel, then remove the sentinel element
|
||||
sentinel = E.sentinel()
|
||||
node.addnext(sentinel)
|
||||
add_text_before(sentinel, spec.text)
|
||||
for child in spec:
|
||||
sentinel.addprevious(child)
|
||||
remove_element(sentinel)
|
||||
elif pos == 'before':
|
||||
add_text_before(node, spec.text)
|
||||
for child in spec:
|
||||
node.addprevious(child)
|
||||
else:
|
||||
self.raise_view_error(_("Invalid position attribute: '%s'") % pos, inherit_id)
|
||||
|
||||
else:
|
||||
attrs = ''.join([
|
||||
' %s="%s"' % (attr, spec.get(attr))
|
||||
|
||||
@@ -736,6 +736,66 @@ class TestViews(ViewCase):
|
||||
),
|
||||
string="Replacement title", version="7.0"))
|
||||
|
||||
def test_view_inheritance_text_inside(self):
|
||||
""" Test view inheritance when adding elements and text. """
|
||||
view1 = self.View.create({
|
||||
'name': "alpha",
|
||||
'model': 'ir.ui.view',
|
||||
'arch': '<form string="F">(<div/>)</form>',
|
||||
})
|
||||
view2 = self.View.create({
|
||||
'name': "beta",
|
||||
'model': 'ir.ui.view',
|
||||
'inherit_id': view1.id,
|
||||
'arch': '<div position="inside">a<p/>b<p/>c</div>',
|
||||
})
|
||||
view = self.View.with_context(check_view_ids=view2.ids).fields_view_get(view1.id)
|
||||
self.assertEqual(view['type'], 'form')
|
||||
self.assertEqual(
|
||||
view['arch'],
|
||||
'<form string="F">(<div>a<p/>b<p/>c</div>)</form>',
|
||||
)
|
||||
|
||||
def test_view_inheritance_text_after(self):
|
||||
""" Test view inheritance when adding elements and text. """
|
||||
view1 = self.View.create({
|
||||
'name': "alpha",
|
||||
'model': 'ir.ui.view',
|
||||
'arch': '<form string="F">(<div/>)</form>',
|
||||
})
|
||||
view2 = self.View.create({
|
||||
'name': "beta",
|
||||
'model': 'ir.ui.view',
|
||||
'inherit_id': view1.id,
|
||||
'arch': '<div position="after">a<p/>b<p/>c</div>',
|
||||
})
|
||||
view = self.View.with_context(check_view_ids=view2.ids).fields_view_get(view1.id)
|
||||
self.assertEqual(view['type'], 'form')
|
||||
self.assertEqual(
|
||||
view['arch'],
|
||||
'<form string="F">(<div/>a<p/>b<p/>c)</form>',
|
||||
)
|
||||
|
||||
def test_view_inheritance_text_before(self):
|
||||
""" Test view inheritance when adding elements and text. """
|
||||
view1 = self.View.create({
|
||||
'name': "alpha",
|
||||
'model': 'ir.ui.view',
|
||||
'arch': '<form string="F">(<div/>)</form>',
|
||||
})
|
||||
view2 = self.View.create({
|
||||
'name': "beta",
|
||||
'model': 'ir.ui.view',
|
||||
'inherit_id': view1.id,
|
||||
'arch': '<div position="before">a<p/>b<p/>c</div>',
|
||||
})
|
||||
view = self.View.with_context(check_view_ids=view2.ids).fields_view_get(view1.id)
|
||||
self.assertEqual(view['type'], 'form')
|
||||
self.assertEqual(
|
||||
view['arch'],
|
||||
'<form string="F">(a<p/>b<p/>c<div/>)</form>',
|
||||
)
|
||||
|
||||
def test_view_inheritance_divergent_models(self):
|
||||
view1 = self.View.create({
|
||||
'name': "bob",
|
||||
|
||||
+3
-2
@@ -1247,8 +1247,9 @@ class BaseModel(object):
|
||||
"""
|
||||
|
||||
field = E.field(name=self._rec_name_fallback())
|
||||
div = E.div(field, {'class': "oe_kanban_card oe_kanban_global_click"})
|
||||
kanban_box = E.t(div, {'t-name': "kanban-box"})
|
||||
content_div = E.div(field, {'class': "o_kanban_card_content"})
|
||||
card_div = E.div(content_div, {'t-attf-class': "oe_kanban_card oe_kanban_global_click"})
|
||||
kanban_box = E.t(card_div, {'t-name': "kanban-box"})
|
||||
templates = E.templates(kanban_box)
|
||||
return E.kanban(templates, string=self._description)
|
||||
|
||||
|
||||
@@ -364,7 +364,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
||||
for (model,) in cr.fetchall():
|
||||
if model in registry:
|
||||
env[model]._check_removed_columns(log=True)
|
||||
else:
|
||||
elif _logger.isEnabledFor(logging.INFO): # more an info that a warning...
|
||||
_logger.warning("Model %s is declared but cannot be loaded! (Perhaps a module was partially removed or renamed)", model)
|
||||
|
||||
# Cleanup orphan records
|
||||
@@ -402,8 +402,10 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
||||
if update_module:
|
||||
View = env['ir.ui.view']
|
||||
for model in registry:
|
||||
if not View._validate_custom_views(model):
|
||||
_logger.warning('invalid custom view(s) for model %s', model)
|
||||
try:
|
||||
View._validate_custom_views(model)
|
||||
except Exception as e:
|
||||
_logger.warning('invalid custom view(s) for model %s: %s', model, tools.ustr(e))
|
||||
|
||||
if report.failures:
|
||||
_logger.error('At least one test failed when loading the modules.')
|
||||
|
||||
Reference in New Issue
Block a user