diff options
| author | Christophe Simonis <chs@odoo.com> | 2017-01-10 13:52:44 +0100 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2017-01-10 13:52:44 +0100 |
| commit | a2c1df8b9ca731107dd2d67cc0b2dd39d0c7ad64 (patch) | |
| tree | 5de4c414a1c137c674ca004fbec6a29fc1a10db5 /addons | |
| parent | d38e00031f975db4a3d3e80250d5142444a3239b (diff) | |
| parent | 557feb950cb74656c92e59a62847c9e414c9b41f (diff) | |
[MERGE] forward port branch saas-14 up to 557feb950cb
Diffstat (limited to 'addons')
42 files changed, 470 insertions, 129 deletions
diff --git a/addons/account/models/chart_template.py b/addons/account/models/chart_template.py index 2f5f40df280..37bcefb2675 100644 --- a/addons/account/models/chart_template.py +++ b/addons/account/models/chart_template.py @@ -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 diff --git a/addons/account/views/res_config_view.xml b/addons/account/views/res_config_view.xml index b387d32d3fe..14b4f4000a0 100644 --- a/addons/account/views/res_config_view.xml +++ b/addons/account/views/res_config_view.xml @@ -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> diff --git a/addons/account/views/web_planner_data.xml b/addons/account/views/web_planner_data.xml index b3569deb76b..abd637d87d5 100644 --- a/addons/account/views/web_planner_data.xml +++ b/addons/account/views/web_planner_data.xml @@ -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> diff --git a/addons/account_accountant/views/res_config_view.xml b/addons/account_accountant/views/res_config_view.xml index dff65902427..a356583bc93 100644 --- a/addons/account_accountant/views/res_config_view.xml +++ b/addons/account_accountant/views/res_config_view.xml @@ -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> diff --git a/addons/auth_ldap/views/auth_ldap_config_settings_views.xml b/addons/auth_ldap/views/auth_ldap_config_settings_views.xml index 67d14215027..344aeda46b1 100644 --- a/addons/auth_ldap/views/auth_ldap_config_settings_views.xml +++ b/addons/auth_ldap/views/auth_ldap_config_settings_views.xml @@ -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> diff --git a/addons/auth_oauth/views/res_config_views.xml b/addons/auth_oauth/views/res_config_views.xml index 4f0912afa18..0bc57df9127 100644 --- a/addons/auth_oauth/views/res_config_views.xml +++ b/addons/auth_oauth/views/res_config_views.xml @@ -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> diff --git a/addons/barcodes/static/src/js/form_view_barcode_handler.js b/addons/barcodes/static/src/js/form_view_barcode_handler.js index 7f2614f0ef1..524ec1513c3 100644 --- a/addons/barcodes/static/src/js/form_view_barcode_handler.js +++ b/addons/barcodes/static/src/js/form_view_barcode_handler.js @@ -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); } }, diff --git a/addons/base_gengo/views/res_config_views.xml b/addons/base_gengo/views/res_config_views.xml index a78bf51c07f..66546b2d1e5 100644 --- a/addons/base_gengo/views/res_config_views.xml +++ b/addons/base_gengo/views/res_config_views.xml @@ -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> diff --git a/addons/google_calendar/models/google_calendar.py b/addons/google_calendar/models/google_calendar.py index 7a5fda8575e..007bbffeaf9 100644 --- a/addons/google_calendar/models/google_calendar.py +++ b/addons/google_calendar/models/google_calendar.py @@ -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: diff --git a/addons/l10n_es/data/account_account_template_data.xml b/addons/l10n_es/data/account_account_template_data.xml index 4c86cb55442..455d8132f04 100644 --- a/addons/l10n_es/data/account_account_template_data.xml +++ b/addons/l10n_es/data/account_account_template_data.xml @@ -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"> diff --git a/addons/l10n_mx/__manifest__.py b/addons/l10n_mx/__manifest__.py index 4d804ad96a6..8ec93d7a7e3 100644 --- a/addons/l10n_mx/__manifest__.py +++ b/addons/l10n_mx/__manifest__.py @@ -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", ], } diff --git a/addons/l10n_mx/data/fiscal_position_data.xml b/addons/l10n_mx/data/fiscal_position_data.xml new file mode 100644 index 00000000000..cc22638112f --- /dev/null +++ b/addons/l10n_mx/data/fiscal_position_data.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> diff --git a/addons/l10n_mx/models/__init__.py b/addons/l10n_mx/models/__init__.py index 5376a315b04..f2b298f3cb9 100644 --- a/addons/l10n_mx/models/__init__.py +++ b/addons/l10n_mx/models/__init__.py @@ -4,3 +4,4 @@ from . import chart_template from . import account +from . import res_config diff --git a/addons/l10n_mx/models/res_config.py b/addons/l10n_mx/models/res_config.py new file mode 100644 index 00000000000..913a422cf4b --- /dev/null +++ b/addons/l10n_mx/models/res_config.py @@ -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') diff --git a/addons/l10n_mx/views/res_config_view.xml b/addons/l10n_mx/views/res_config_view.xml new file mode 100644 index 00000000000..d5f59f6937f --- /dev/null +++ b/addons/l10n_mx/views/res_config_view.xml @@ -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> diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index a6e20c46bd2..bf145c9d59e 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -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): diff --git a/addons/payment_paypal/models/payment.py b/addons/payment_paypal/models/payment.py index 6aaff94a631..60c5f57ea51 100644 --- a/addons/payment_paypal/models/payment.py +++ b/addons/payment_paypal/models/payment.py @@ -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)) diff --git a/addons/payment_paypal/tests/test_paypal.py b/addons/payment_paypal/tests/test_paypal.py index 7cbe98f1a6b..c6a44dec41f 100644 --- a/addons/payment_paypal/tests/test_paypal.py +++ b/addons/payment_paypal/tests/test_paypal.py @@ -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') diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js index 5f61deb5e0c..f9cf49864d2 100644 --- a/addons/point_of_sale/static/src/js/screens.js +++ b/addons/point_of_sale/static/src/js/screens.js @@ -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() }; diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py index b88542f04e6..25fe10b5315 100644 --- a/addons/sale/models/sale.py +++ b/addons/sale/models/sale.py @@ -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 diff --git a/addons/stock/models/stock_quant.py b/addons/stock/models/stock_quant.py index f52f6dc331d..9c95a6682ac 100644 --- a/addons/stock/models/stock_quant.py +++ b/addons/stock/models/stock_quant.py @@ -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, diff --git a/addons/web/static/src/js/framework/domain_utils.js b/addons/web/static/src/js/framework/domain_utils.js new file mode 100644 index 00000000000..8cefb7cabbe --- /dev/null +++ b/addons/web/static/src/js/framework/domain_utils.js @@ -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, +}; +}); diff --git a/addons/web/static/src/js/views/form_widgets.js b/addons/web/static/src/js/views/form_widgets.js index a3c7bcaaff3..78d8ea7c5d1 100644 --- a/addons/web/static/src/js/views/form_widgets.js +++ b/addons/web/static/src/js/views/form_widgets.js @@ -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, { diff --git a/addons/web/static/src/js/views/list_view_editable.js b/addons/web/static/src/js/views/list_view_editable.js index 3f5d39e60e1..ef9bf1a6062 100644 --- a/addons/web/static/src/js/views/list_view_editable.js +++ b/addons/web/static/src/js/views/list_view_editable.js @@ -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(); } diff --git a/addons/web/static/src/js/views/search_view.js b/addons/web/static/src/js/views/search_view.js index 9f985fc3f49..65dbb4557e7 100644 --- a/addons/web/static/src/js/views/search_view.js +++ b/addons/web/static/src/js/views/search_view.js @@ -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'); diff --git a/addons/web/static/src/js/widgets/domain_selector.js b/addons/web/static/src/js/widgets/domain_selector.js index 5f4287a8d8d..15e967ff4d4 100644 --- a/addons/web/static/src/js/widgets/domain_selector.js +++ b/addons/web/static/src/js/widgets/domain_selector.js @@ -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]; diff --git a/addons/web/static/src/js/widgets/domain_selector_dialog.js b/addons/web/static/src/js/widgets/domain_selector_dialog.js new file mode 100644 index 00000000000..47bfa4e1359 --- /dev/null +++ b/addons/web/static/src/js/widgets/domain_selector_dialog.js @@ -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) + ); + }, +}); +}); diff --git a/addons/web/static/src/less/domain_selector.less b/addons/web/static/src/less/domain_selector.less index 7dced900b21..f3917526ec5 100644 --- a/addons/web/static/src/less/domain_selector.less +++ b/addons/web/static/src/less/domain_selector.less @@ -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 { diff --git a/addons/web/static/src/less/form_view_layout.less b/addons/web/static/src/less/form_view_layout.less index 281cf0d217e..fc5522035e5 100644 --- a/addons/web/static/src/less/form_view_layout.less +++ b/addons/web/static/src/less/form_view_layout.less @@ -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 diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index f6ac60c4d7e..2ee261a3bdd 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -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> diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml index 533e8e8b23a..c0a9b2c372f 100644 --- a/addons/web/views/webclient_templates.xml +++ b/addons/web/views/webclient_templates.xml @@ -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> diff --git a/addons/web_editor/static/src/js/backend.js b/addons/web_editor/static/src/js/backend.js index 9c6f1df5e8c..ce8643f04de 100644 --- a/addons/web_editor/static/src/js/backend.js +++ b/addons/web_editor/static/src/js/backend.js @@ -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() ); } diff --git a/addons/web_editor/static/src/js/rte.summernote.js b/addons/web_editor/static/src/js/rte.summernote.js index 1789ed69e7e..9f908a245bc 100644 --- a/addons/web_editor/static/src/js/rte.summernote.js +++ b/addons/web_editor/static/src/js/rte.summernote.js @@ -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 diff --git a/addons/web_kanban/static/src/js/kanban_view.js b/addons/web_kanban/static/src/js/kanban_view.js index 4917c24b241..7cce7617da9 100644 --- a/addons/web_kanban/static/src/js/kanban_view.js +++ b/addons/web_kanban/static/src/js/kanban_view.js @@ -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': diff --git a/addons/web_kanban/static/src/less/kanban_view.less b/addons/web_kanban/static/src/less/kanban_view.less index 877559ddf1f..0640fe9f3e3 100644 --- a/addons/web_kanban/static/src/less/kanban_view.less +++ b/addons/web_kanban/static/src/less/kanban_view.less @@ -65,7 +65,9 @@ } .o_dropdown_kanban { - float: right; + position: absolute; + top: 5px; + right: 10px; @media (max-width: @screen-xs-max) { visibility: visible; } diff --git a/addons/website_crm/views/website_crm_config_settings_views.xml b/addons/website_crm/views/website_crm_config_settings_views.xml index 8f8c4e9dbe6..a141b4e4509 100644 --- a/addons/website_crm/views/website_crm_config_settings_views.xml +++ b/addons/website_crm/views/website_crm_config_settings_views.xml @@ -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"/> diff --git a/addons/website_livechat/views/res_config.xml b/addons/website_livechat/views/res_config.xml index b50e2f6c1cf..03189ce1d70 100644 --- a/addons/website_livechat/views/res_config.xml +++ b/addons/website_livechat/views/res_config.xml @@ -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"/> diff --git a/addons/website_mass_mailing/static/src/js/website_mass_mailing.js b/addons/website_mass_mailing/static/src/js/website_mass_mailing.js index a9e8ea86273..ae499fe2438 100644 --- a/addons/website_mass_mailing/static/src/js/website_mass_mailing.js +++ b/addons/website_mass_mailing/static/src/js/website_mass_mailing.js @@ -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'); }); }); }); diff --git a/addons/website_portal_sale/controllers/main.py b/addons/website_portal_sale/controllers/main.py index 40d840029a6..e421a9ce8b0 100644 --- a/addons/website_portal_sale/controllers/main.py +++ b/addons/website_portal_sale/controllers/main.py @@ -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: diff --git a/addons/website_sale_delivery/views/website_config_settings_views.xml b/addons/website_sale_delivery/views/website_config_settings_views.xml index 61cde1a3e9e..f36e98a110a 100644 --- a/addons/website_sale_delivery/views/website_config_settings_views.xml +++ b/addons/website_sale_delivery/views/website_config_settings_views.xml @@ -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> diff --git a/addons/website_slides/view/res_config.xml b/addons/website_slides/view/res_config.xml index d035371572b..955b92e5eae 100644 --- a/addons/website_slides/view/res_config.xml +++ b/addons/website_slides/view/res_config.xml @@ -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"/> diff --git a/addons/website_twitter/views/website_twitter_settings_views.xml b/addons/website_twitter/views/website_twitter_settings_views.xml index 230be8a19b2..224e9f34c29 100644 --- a/addons/website_twitter/views/website_twitter_settings_views.xml +++ b/addons/website_twitter/views/website_twitter_settings_views.xml @@ -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"/> |
