diff --git a/addons/account/__manifest__.py b/addons/account/__manifest__.py index 02d00494814..e78ffc3a7b2 100644 --- a/addons/account/__manifest__.py +++ b/addons/account/__manifest__.py @@ -14,7 +14,7 @@ You could use this simplified accounting in case you work with an (external) acc """, 'category': 'Accounting/Accounting', 'website': 'https://www.odoo.com/app/invoicing', - 'depends' : ['base_setup', 'product', 'analytic', 'portal', 'digest'], + 'depends': ['base_setup', 'onboarding', 'product', 'analytic', 'portal', 'digest'], 'data': [ 'security/account_security.xml', 'security/ir.model.access.csv', @@ -22,6 +22,7 @@ You could use this simplified accounting in case you work with an (external) acc 'data/digest_data.xml', 'views/account_report.xml', 'data/mail_template_data.xml', + 'data/onboarding_data.xml', 'views/account_payment_view.xml', 'wizard/account_automatic_entry_wizard_views.xml', 'wizard/account_unreconcile_view.xml', @@ -59,7 +60,6 @@ You could use this simplified accounting in case you work with an (external) acc 'views/account_journal_dashboard_view.xml', 'views/account_portal_templates.xml', 'views/report_payment_receipt_templates.xml', - 'views/account_onboarding_templates.xml', 'data/service_cron.xml', 'views/account_incoterms_view.xml', 'data/account_incoterms_data.xml', @@ -94,6 +94,7 @@ You could use this simplified accounting in case you work with an (external) acc 'account/static/src/components/**/*', 'account/static/src/js/tours/account.js', 'account/static/src/xml/**/*', + 'account/static/src/views/**/*.js', ], 'web.assets_frontend': [ 'account/static/src/js/account_portal_sidebar.js', diff --git a/addons/account/controllers/__init__.py b/addons/account/controllers/__init__.py index 4cb0be3e577..44eea6b8e43 100644 --- a/addons/account/controllers/__init__.py +++ b/addons/account/controllers/__init__.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from . import onboarding from . import portal from . import terms diff --git a/addons/account/controllers/onboarding.py b/addons/account/controllers/onboarding.py deleted file mode 100644 index 240d7aef2f6..00000000000 --- a/addons/account/controllers/onboarding.py +++ /dev/null @@ -1,49 +0,0 @@ -from odoo import http -from odoo.http import request - - -class OnboardingController(http.Controller): - - @http.route('/account/account_invoice_onboarding', auth='user', type='json') - def account_invoice_onboarding(self, context=None): - """ Returns the `banner` for the account invoice onboarding panel. - It can be empty if the user has closed it or if he doesn't have - the permission to see it. """ - - # Forward `allowed_company_ids` in the context - # so `request.env.company` returns the company selected in the dropdown - if context: - request.update_context(**context) - company = request.env.company - if not request.env.is_admin() or \ - company.account_invoice_onboarding_state == 'closed': - return {} - - return { - 'html': request.env['ir.qweb']._render('account.account_invoice_onboarding_panel', { - 'company': company, - 'state': company.get_and_update_account_invoice_onboarding_state() - }) - } - - @http.route('/account/account_dashboard_onboarding', auth='user', type='json') - def account_dashboard_onboarding(self, context=None): - """ Returns the `banner` for the account dashboard onboarding panel. - It can be empty if the user has closed it or if he doesn't have - the permission to see it. """ - # Forward `allowed_company_ids` in the context - # so `request.env.company` returns the company selected in the dropdown - if context: - request.update_context(**context) - company = request.env.company - - if not request.env.is_admin() or \ - company.account_dashboard_onboarding_state == 'closed': - return {} - - return { - 'html': request.env['ir.qweb']._render('account.account_dashboard_onboarding_panel', { - 'company': company, - 'state': company.get_and_update_account_dashboard_onboarding_state() - }) - } diff --git a/addons/account/data/onboarding_data.xml b/addons/account/data/onboarding_data.xml new file mode 100644 index 00000000000..ca89a63521c --- /dev/null +++ b/addons/account/data/onboarding_data.xml @@ -0,0 +1,134 @@ + + + + + + + Company Data + Set your company's data for documents header/footer. + Let's start! + Looks great! + action_open_step_company_data + + onboarding_company-data.png + Onboarding Company Data + 1 + + + + Documents Layout + Customize the look of your documents. + Customize + Looks great! + action_open_step_base_document_layout + + onboarding_quotation-layout.png + Onboarding Documents Layout + 3 + + + + Create Invoice + Create your first invoice. + Create + First invoice sent! + action_open_step_create_invoice + + onboarding_default.png + Onboarding Create Invoice + 4 + + + + + Accounting Periods + Define your fiscal years & tax returns periodicity. + Configure + Step completed! + action_open_step_fiscal_year + + onboarding_accounting-periods.png + Onboarding Accounting Periods + 1 + + + + Bank Account + Connect your financial accounts in seconds. + Add a bank account + Step Completed! + action_open_step_bank_account + + onboarding_puzzle.png + Onboarding Bank Account + 2 + + + + Taxes + Set default Taxes for sales and purchase transactions. + Review + Taxes set! + action_open_step_default_taxes + + onboarding_taxes.png + Onboarding Taxes + 3 + + + + Chart of Accounts + Set up your chart of accounts and record initial balances. + Review + Chart of accounts set! + action_open_step_chart_of_accounts + + onboarding_chart-of-accounts.png + Onboarding Bank Account + 4 + + + + + Import Vendor Bills + 1000 + + + + Taxes + + Choose a default sales tax for your products. + Set taxes + Step Completed! + action_open_step_sales_tax + + onboarding_puzzle.png + Onboarding Bank Account + 100 + + + + + Invoicing Onboarding + + account_invoice + action_close_panel_account_invoice + + + + Account Dashboard Onboarding + + account_dashboard + action_close_panel_account_dashboard + + + diff --git a/addons/account/models/__init__.py b/addons/account/models/__init__.py index dedb26f5e69..76f6bb8cb6e 100644 --- a/addons/account/models/__init__.py +++ b/addons/account/models/__init__.py @@ -37,5 +37,7 @@ from . import ir_actions_report from . import ir_module from . import res_currency from . import account_report +from . import onboarding_onboarding +from . import onboarding_onboarding_step from . import template_generic_coa from . import uom_uom diff --git a/addons/account/models/account_journal_dashboard.py b/addons/account/models/account_journal_dashboard.py index 792655f8e9d..919e5128476 100644 --- a/addons/account/models/account_journal_dashboard.py +++ b/addons/account/models/account_journal_dashboard.py @@ -620,7 +620,7 @@ class account_journal(models.Model): """ This function is called by the "Import" button of Vendor Bills, visible on dashboard if no bill has been created yet. """ - self.env.company.sudo().set_onboarding_step_done('account_setup_bill_state') + self.env['onboarding.onboarding.step'].action_validate_step('account.onboarding_onboarding_step_setup_bill') new_wizard = self.env['account.tour.upload.bill'].create({}) view_id = self.env.ref('account.account_tour_upload_bill').id @@ -777,14 +777,3 @@ class account_journal(models.Model): def create_internal_transfer(self): """return action to create a internal transfer""" return self.open_payments_action('transfer', mode='form') - - ##################### - # Setup Steps Stuff # - ##################### - def mark_bank_setup_as_done_action(self): - """ Marks the 'bank setup' step as done in the setup bar and in the company.""" - self.company_id.sudo().set_onboarding_step_done('account_setup_bank_data_state') - - def unmark_bank_setup_as_done_action(self): - """ Marks the 'bank setup' step as not done in the setup bar and in the company.""" - self.company_id.account_setup_bank_data_state = 'not_done' diff --git a/addons/account/models/company.py b/addons/account/models/company.py index 6ea588e4e20..c705106109c 100644 --- a/addons/account/models/company.py +++ b/addons/account/models/company.py @@ -26,14 +26,6 @@ MONTH_SELECTION = [ ('12', 'December'), ] -ONBOARDING_STEP_STATES = [ - ('not_done', "Not done"), - ('just_done', "Just done"), - ('done', "Done"), -] -DASHBOARD_ONBOARDING_STATES = ONBOARDING_STEP_STATES + [('closed', 'Closed')] - - class ResCompany(models.Model): _name = "res.company" _inherit = ["res.company", "mail.thread"] @@ -104,20 +96,6 @@ class ResCompany(models.Model): account_opening_journal_id = fields.Many2one(string='Opening Journal', comodel_name='account.journal', related='account_opening_move_id.journal_id', help="Journal where the opening entry of this company's accounting has been posted.", readonly=False) account_opening_date = fields.Date(string='Opening Entry', default=lambda self: fields.Date.context_today(self).replace(month=1, day=1), required=True, help="That is the date of the opening entry.") - # Fields marking the completion of a setup step - account_setup_bank_data_state = fields.Selection(ONBOARDING_STEP_STATES, string="State of the onboarding bank data step", default='not_done') - account_setup_fy_data_state = fields.Selection(ONBOARDING_STEP_STATES, string="State of the onboarding fiscal year step", default='not_done') - account_setup_coa_state = fields.Selection(ONBOARDING_STEP_STATES, string="State of the onboarding charts of account step", default='not_done') - account_setup_taxes_state = fields.Selection(ONBOARDING_STEP_STATES, string="State of the onboarding Taxes step", default='not_done') - account_onboarding_invoice_layout_state = fields.Selection(ONBOARDING_STEP_STATES, string="State of the onboarding invoice layout step", default='not_done') - account_onboarding_create_invoice_state = fields.Selection(ONBOARDING_STEP_STATES, string="State of the onboarding create invoice step", compute='_compute_account_onboarding_create_invoice_state') - #this field must be there to ensure that the create_invoice_state stay complete and because we can't use a dependencies on account move - account_onboarding_create_invoice_state_flag = fields.Boolean(default=False, store=True) - account_onboarding_sale_tax_state = fields.Selection(ONBOARDING_STEP_STATES, string="State of the onboarding sale tax step", default='not_done') - - # account dashboard onboarding - account_invoice_onboarding_state = fields.Selection(DASHBOARD_ONBOARDING_STATES, string="State of the account invoice onboarding panel", default='not_done') - account_dashboard_onboarding_state = fields.Selection(DASHBOARD_ONBOARDING_STATES, string="State of the account dashboard onboarding panel", default='not_done') invoice_terms = fields.Html(string='Default Terms and Conditions', translate=True) terms_type = fields.Selection([('plain', 'Add a Note'), ('html', 'Add a link to a Web Page')], string='Terms & Conditions format', default='plain') @@ -125,8 +103,6 @@ class ResCompany(models.Model): sanitize_attributes=False, compute='_compute_invoice_terms_html', store=True, readonly=False) - account_setup_bill_state = fields.Selection(ONBOARDING_STEP_STATES, string="State of the onboarding bill step", default='not_done') - # Needed in the Point of Sale account_default_pos_receivable_account_id = fields.Many2one('account.account', string="Default PoS Receivable Account") @@ -232,17 +208,6 @@ class ResCompany(models.Model): foreign_vat_fpos = self.env['account.fiscal.position'].search([('company_id', '=', record.id), ('foreign_vat', '!=', False)]) record.account_enabled_tax_country_ids = foreign_vat_fpos.country_id + record.account_fiscal_country_id - @api.depends('account_onboarding_create_invoice_state_flag') - def _compute_account_onboarding_create_invoice_state(self): - for record in self: - if record.account_onboarding_create_invoice_state_flag: - record.account_onboarding_create_invoice_state = 'done' - elif self.env['account.move'].search([('company_id', '=', record.id), ('move_type', '=', 'out_invoice')], limit=1): - record.account_onboarding_create_invoice_state = 'just_done' - record.account_onboarding_create_invoice_state_flag = True - else: - record.account_onboarding_create_invoice_state = 'not_done' - @api.depends('terms_type') def _compute_invoice_terms_html(self): for company in self.filtered(lambda company: is_html_empty(company.invoice_terms_html) and company.terms_type == 'html'): @@ -252,40 +217,6 @@ class ResCompany(models.Model): if html: company.invoice_terms_html = html - def get_and_update_account_invoice_onboarding_state(self): - """ This method is called on the controller rendering method and ensures that the animations - are displayed only one time. """ - return self._get_and_update_onboarding_state( - 'account_invoice_onboarding_state', - self.get_account_invoice_onboarding_steps_states_names() - ) - - # YTI FIXME: Define only one method that returns {'account': [], 'sale': [], ...} - def get_account_invoice_onboarding_steps_states_names(self): - """ Necessary to add/edit steps from other modules (payment provider in this case). """ - return [ - 'base_onboarding_company_state', - 'account_onboarding_invoice_layout_state', - 'account_onboarding_create_invoice_state', - ] - - def get_and_update_account_dashboard_onboarding_state(self): - """ This method is called on the controller rendering method and ensures that the animations - are displayed only one time. """ - return self._get_and_update_onboarding_state( - 'account_dashboard_onboarding_state', - self.get_account_dashboard_onboarding_steps_states_names() - ) - - def get_account_dashboard_onboarding_steps_states_names(self): - """ Necessary to add/edit steps from other modules (account_winbooks_import in this case). """ - return [ - 'account_setup_bank_data_state', - 'account_setup_fy_data_state', - 'account_setup_coa_state', - 'account_setup_taxes_state', - ] - def get_new_account_code(self, current_code, old_prefix, new_prefix): digits = len(current_code) return new_prefix + current_code.replace(old_prefix, '', 1).lstrip('0').rjust(digits-len(new_prefix), '0') @@ -409,58 +340,17 @@ class ResCompany(models.Model): @api.model def setting_init_bank_account_action(self): - """ Called by the 'Bank Accounts' button of the setup bar.""" + """ Called by the 'Bank Accounts' button of the setup bar or from the Financial configuration menu.""" view_id = self.env.ref('account.setup_bank_account_wizard').id - return {'type': 'ir.actions.act_window', - 'name': _('Create a Bank Account'), - 'res_model': 'account.setup.bank.manual.config', - 'target': 'new', - 'view_mode': 'form', - 'views': [[view_id, 'form']], - } - - @api.model - def setting_init_fiscal_year_action(self): - """ Called by the 'Fiscal Year Opening' button of the setup bar.""" - company = self.env.company - new_wizard = self.env['account.financial.year.op'].create({'company_id': company.id}) - view_id = self.env.ref('account.setup_financial_year_opening_form').id - return { 'type': 'ir.actions.act_window', - 'name': _('Accounting Periods'), - 'view_mode': 'form', - 'res_model': 'account.financial.year.op', + 'name': _('Create a Bank Account'), + 'res_model': 'account.setup.bank.manual.config', 'target': 'new', - 'res_id': new_wizard.id, + 'view_mode': 'form', 'views': [[view_id, 'form']], } - @api.model - def setting_chart_of_accounts_action(self): - """ Called by the 'Chart of Accounts' button of the setup bar.""" - company = self.env.company - company.sudo().set_onboarding_step_done('account_setup_coa_state') - - # If an opening move has already been posted, we open the tree view showing all the accounts - if company.opening_move_posted(): - return 'account.action_account_form' - - # Then, we open will open a custom tree view allowing to edit opening balances of the account - view_id = self.env.ref('account.init_accounts_tree').id - # Hide the current year earnings account as it is automatically computed - domain = [('account_type', '!=', 'equity_unaffected'), ('company_id', '=', company.id)] - return { - 'type': 'ir.actions.act_window', - 'name': _('Chart of Accounts'), - 'res_model': 'account.account', - 'view_mode': 'tree', - 'limit': 99999999, - 'search_view_id': [self.env.ref('account.view_account_search').id], - 'views': [[view_id, 'list']], - 'domain': domain, - } - @api.model def create_op_move_if_non_existant(self): """ Creates an empty opening move in 'draft' state for the current company @@ -559,53 +449,9 @@ class ResCompany(models.Model): 'credit': debit_diff, }) - @api.model - def action_close_account_invoice_onboarding(self): - """ Mark the invoice onboarding panel as closed. """ - self.env.company.account_invoice_onboarding_state = 'closed' - - @api.model - def action_close_account_dashboard_onboarding(self): - """ Mark the dashboard onboarding panel as closed. """ - self.env.company.account_dashboard_onboarding_state = 'closed' - - @api.model - def action_open_account_onboarding_sale_tax(self): - """ Onboarding step for the invoice layout. """ - action = self.env["ir.actions.actions"]._for_xml_id("account.action_open_account_onboarding_sale_tax") - action['res_id'] = self.env.company.id - return action - - @api.model - def action_open_account_onboarding_create_invoice(self): - return self.env["ir.actions.actions"]._for_xml_id("account.action_open_account_onboarding_create_invoice") - - @api.model - def action_open_taxes_onboarding(self): - """ Called by the 'Taxes' button of the setup bar.""" - - company = self.env.company - company.sudo().set_onboarding_step_done('account_setup_taxes_state') - view_id_list = self.env.ref('account.view_onboarding_tax_tree').id - view_id_form = self.env.ref('account.view_tax_form').id - - return { - 'type': 'ir.actions.act_window', - 'name': _('Taxes'), - 'res_model': 'account.tax', - 'target': 'current', - 'views': [[view_id_list, 'list'], [view_id_form, 'form']], - 'context': {'search_default_sale': True, 'search_default_purchase': True, 'active_test': False}, - } - - def action_save_onboarding_invoice_layout(self): - """ Set the onboarding step as done """ - if bool(self.external_report_layout_id): - self.set_onboarding_step_done('account_onboarding_invoice_layout_state') - def action_save_onboarding_sale_tax(self): """ Set the onboarding step as done """ - self.set_onboarding_step_done('account_onboarding_sale_tax_state') + self.env['onboarding.onboarding.step'].action_validate_step('account.onboarding_onboarding_step_sales_tax') def get_chart_of_accounts_or_fail(self): account = self.env['account.account'].search([('company_id', '=', self.id)], limit=1) diff --git a/addons/account/models/onboarding_onboarding.py b/addons/account/models/onboarding_onboarding.py new file mode 100644 index 00000000000..9b51e26e572 --- /dev/null +++ b/addons/account/models/onboarding_onboarding.py @@ -0,0 +1,29 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, models + + +class Onboarding(models.Model): + _inherit = 'onboarding.onboarding' + + # Invoice Onboarding + @api.model + def action_close_panel_account_invoice(self): + self.action_close_panel('account.onboarding_onboarding_account_invoice') + + def _prepare_rendering_values(self): + """Compute existence of invoices for company.""" + self.ensure_one() + if self == self.env.ref('account.onboarding_onboarding_account_invoice', raise_if_not_found=False): + step = self.env.ref('account.onboarding_onboarding_step_create_invoice', raise_if_not_found=False) + if step and step.current_step_state == 'not_done': + if self.env['account.move'].search( + [('company_id', '=', self.env.company.id), ('move_type', '=', 'out_invoice')], limit=1 + ): + step.action_set_just_done() + return super()._prepare_rendering_values() + + # Dashboard Onboarding + @api.model + def action_close_panel_account_dashboard(self): + self.action_close_panel('account.onboarding_onboarding_account_dashboard') diff --git a/addons/account/models/onboarding_onboarding_step.py b/addons/account/models/onboarding_onboarding_step.py new file mode 100644 index 00000000000..df74c659eb0 --- /dev/null +++ b/addons/account/models/onboarding_onboarding_step.py @@ -0,0 +1,133 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import _, api, models + + +class OnboardingStep(models.Model): + _inherit = 'onboarding.onboarding.step' + + # COMMON STEPS + @api.model + def action_open_step_company_data(self): + """Set company's basic information.""" + action = { + 'type': 'ir.actions.act_window', + 'name': _('Set your company data'), + 'res_model': 'res.company', + 'res_id': self.env.company.id, + 'views': [(self.env.ref('account.res_company_form_view_onboarding').id, "form")], + 'target': 'new', + } + return action + + @api.model + def action_open_step_base_document_layout(self): + view_id = self.env.ref('web.view_base_document_layout').id + return { + 'name': _('Configure your document layout'), + 'type': 'ir.actions.act_window', + 'res_model': 'base.document.layout', + 'target': 'new', + 'views': [(view_id, 'form')], + } + + @api.model + def action_validate_step_base_document_layout(self): + """Set the onboarding(s) step as done only if layout is set.""" + step = self.env.ref('account.onboarding_onboarding_step_base_document_layout', raise_if_not_found=False) + if not step or not self.env.company.external_report_layout_id: + return False + return self.action_validate_step('account.onboarding_onboarding_step_base_document_layout') + + # INVOICE ONBOARDING + @api.model + def action_open_step_bank_account(self): + return self.env.company.setting_init_bank_account_action() + + @api.model + def action_open_step_create_invoice(self): + return { + 'type': 'ir.actions.act_window', + 'name': _('Create first invoice'), + 'views': [(self.env.ref("account.view_move_form").id, 'form')], + 'res_model': 'account.move', + 'context': {'default_move_type': 'out_invoice'}, + } + + # DASHBOARD ONBOARDING + @api.model + def action_open_step_fiscal_year(self): + company = self.env.company + company.create_op_move_if_non_existant() + new_wizard = self.env['account.financial.year.op'].create({'company_id': company.id}) + view_id = self.env.ref('account.setup_financial_year_opening_form').id + + return { + 'type': 'ir.actions.act_window', + 'name': _('Accounting Periods'), + 'view_mode': 'form', + 'res_model': 'account.financial.year.op', + 'target': 'new', + 'res_id': new_wizard.id, + 'views': [[view_id, 'form']], + } + + @api.model + def action_open_step_default_taxes(self): + """ Called by the 'Taxes' button of the setup bar.""" + self.action_validate_step('account.onboarding_onboarding_step_default_taxes') + + view_id_list = self.env.ref('account.view_onboarding_tax_tree').id + view_id_form = self.env.ref('account.view_tax_form').id + + return { + 'type': 'ir.actions.act_window', + 'name': _('Taxes'), + 'res_model': 'account.tax', + 'target': 'current', + 'views': [[view_id_list, 'list'], [view_id_form, 'form']], + 'context': {'search_default_sale': True, 'search_default_purchase': True, 'active_test': False}, + } + + @api.model + def action_open_step_chart_of_accounts(self): + """ Called by the 'Chart of Accounts' button of the dashboard onboarding panel.""" + company = self.env.company + self.sudo().action_validate_step('account.onboarding_onboarding_step_chart_of_accounts') + + # If an opening move has already been posted, we open the tree view showing all the accounts + if company.opening_move_posted(): + return 'account.action_account_form' + + # Otherwise, we create the opening move + company.create_op_move_if_non_existant() + + # Then, we open will open a custom tree view allowing to edit opening balances of the account + view_id = self.env.ref('account.init_accounts_tree').id + # Hide the current year earnings account as it is automatically computed + domain = [('account_type', '!=', 'equity_unaffected'), ('company_id', '=', company.id)] + return { + 'type': 'ir.actions.act_window', + 'name': _('Chart of Accounts'), + 'res_model': 'account.account', + 'view_mode': 'tree', + 'limit': 99999999, + 'search_view_id': [self.env.ref('account.view_account_search').id], + 'views': [[view_id, 'list']], + 'domain': domain, + } + + # STEPS WITHOUT PANEL + @api.model + def action_open_step_sales_tax(self): + view_id = self.env.ref('account.res_company_form_view_onboarding_sale_tax').id + + return { + 'type': 'ir.actions.act_window', + 'name': _('Sales tax'), + 'res_id': self.env.company.id, + 'res_model': 'res.company', + 'target': 'new', + 'view_mode': 'form', + 'views': [[view_id, 'form']], + } diff --git a/addons/account/static/src/js/tours/account.js b/addons/account/static/src/js/tours/account.js index 31b31678aa1..671a13b5e5d 100644 --- a/addons/account/static/src/js/tours/account.js +++ b/addons/account/static/src/js/tours/account.js @@ -14,29 +14,29 @@ registry.category("web_tour.tours").add('account_tour', { steps: [ ...stepUtils.goToAppSteps('account.menu_finance', _t('Send invoices to your customers in no time with the Invoicing app.')), { - trigger: "a.o_onboarding_step_action[data-method=action_open_base_onboarding_company]", + trigger: "a.o_onboarding_step_action[data-method=action_open_step_company_data]", content: _t("Start by checking your company's data."), position: "bottom", - skip_trigger: 'a[data-method=action_open_base_onboarding_company].o_onboarding_step_action__done', + skip_trigger: 'a[data-method=action_open_step_company_data].o_onboarding_step_action__done', }, { - trigger: "button[name=action_save_onboarding_company_step]", - extra_trigger: "a.o_onboarding_step_action[data-method=action_open_base_onboarding_company]", + trigger: "button.o_form_button_save", + extra_trigger: "a.o_onboarding_step_action[data-method=action_open_step_company_data]", content: _t("Looks good. Let's continue."), position: "bottom", - skip_trigger: 'a[data-method=action_open_base_onboarding_company].o_onboarding_step_action__done', + skip_trigger: 'a[data-method=action_open_step_company_data].o_onboarding_step_action__done', }, { - trigger: "a.o_onboarding_step_action[data-method=action_open_base_document_layout]", + trigger: "a.o_onboarding_step_action[data-method=action_open_step_base_document_layout]", content: _t("Customize your layout."), position: "bottom", - skip_trigger: 'a[data-method=action_open_base_document_layout].o_onboarding_step_action__done', + skip_trigger: 'a[data-method=action_open_step_base_document_layout].o_onboarding_step_action__done', }, { trigger: "button[name=document_layout_save]", - extra_trigger: "a.o_onboarding_step_action[data-method=action_open_base_document_layout]", + extra_trigger: "a.o_onboarding_step_action[data-method=action_open_step_base_document_layout]", content: _t("Once everything is as you want it, validate."), position: "top", - skip_trigger: 'a[data-method=action_open_base_document_layout].o_onboarding_step_action__done', + skip_trigger: 'a[data-method=action_open_step_base_document_layout].o_onboarding_step_action__done', }, { - trigger: "a.o_onboarding_step_action[data-method=action_open_account_onboarding_create_invoice]", + trigger: "a[data-method=action_open_step_create_invoice]", content: _t("Now, we'll create your first invoice."), position: "bottom", }, { diff --git a/addons/account/static/src/views/res_company_onboarding_form_controller.js b/addons/account/static/src/views/res_company_onboarding_form_controller.js new file mode 100644 index 00000000000..d6212a98e4a --- /dev/null +++ b/addons/account/static/src/views/res_company_onboarding_form_controller.js @@ -0,0 +1,12 @@ +/** @odoo-module **/ + +import OnboardingStepFormController from "@onboarding/views/form/onboarding_step_form_controller"; + +/** + * Validate the onboarding step on saving a record of another model (here `res.company`). + **/ +export default class CompanyOnboardingFormController extends OnboardingStepFormController { + get stepName() { + return "account.onboarding_onboarding_step_company_data"; + } +} diff --git a/addons/account/static/src/views/res_company_onboarding_form_view.js b/addons/account/static/src/views/res_company_onboarding_form_view.js new file mode 100644 index 00000000000..c40a897862d --- /dev/null +++ b/addons/account/static/src/views/res_company_onboarding_form_view.js @@ -0,0 +1,15 @@ +/** @odoo-module **/ + +import { formView } from "@web/views/form/form_view"; +import { registry } from "@web/core/registry"; + +import CompanyOnboardingFormController from "./res_company_onboarding_form_controller.js"; + + +const CompanyOnboardingFormView = { + ...formView, + Controller: CompanyOnboardingFormController, +}; + + +registry.category("views").add("company_onboarding_form", CompanyOnboardingFormView); diff --git a/addons/account/views/account_journal_dashboard_view.xml b/addons/account/views/account_journal_dashboard_view.xml index 924eba2b989..7bfc4e1810a 100644 --- a/addons/account/views/account_journal_dashboard_view.xml +++ b/addons/account/views/account_journal_dashboard_view.xml @@ -3,7 +3,7 @@ account.journal.dashboard.kanban account.journal - + diff --git a/addons/account/views/account_move_views.xml b/addons/account/views/account_move_views.xml index 21d0399b2d7..a1f80508f2e 100644 --- a/addons/account/views/account_move_views.xml +++ b/addons/account/views/account_move_views.xml @@ -447,7 +447,7 @@ primary - /account/account_invoice_onboarding + /onboarding/account_invoice Invoice Currency diff --git a/addons/account/views/account_onboarding_templates.xml b/addons/account/views/account_onboarding_templates.xml deleted file mode 100644 index 9d3d2efcd05..00000000000 --- a/addons/account/views/account_onboarding_templates.xml +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - Invoice Layout - Customize the look of your invoices. - - - - Customize - Looks great! - - - - - - - - Create Invoice - - Create your first invoice. - - - - Create - First invoice sent! - - - - - - - - Bank Account - - Connect your financial accounts in seconds. - - - - Add a bank account - Step Completed! - - - - - - - - Accounting Periods - Define your fiscal years & tax returns periodicity. - - - Configure - Step Completed! - - - - - - - - Chart of Accounts - Setup your chart of accounts and record initial balances. - - - Review - Chart of accounts set. - - - - - - - - Taxes - Set default Taxes for sales and purchase transactions. - - - Review - Taxes set. - - - - - - - - - - - Congratulations! You are all set. - - - - - - - - - - - - - - - - - - - - Create first invoice - account.move - form - - {'default_move_type': 'out_invoice'} - - - - - Taxes - Choose a default sales tax for your products. - Set taxes - - - Step Completed! - - - - - - - account.invoice.layout.onboarding.form - res.company - primary - 1000 - - - Choose a default sales tax for your products. - - - - - - - - Sales tax - res.company - form - - new - - diff --git a/addons/account/views/res_company_views.xml b/addons/account/views/res_company_views.xml index 65b5637a91c..b74c7d32d47 100644 --- a/addons/account/views/res_company_views.xml +++ b/addons/account/views/res_company_views.xml @@ -33,4 +33,35 @@ + + + res.company.form.view.onboarding + res.company + + primary + 1000 + + + + company_onboarding_form + + + + + + res.company.form.view.onboarding.sale.tax + res.company + 1000 + + + Choose a default sales tax for your products. + + + + + + diff --git a/addons/account/wizard/base_document_layout.py b/addons/account/wizard/base_document_layout.py index ab76e69aaa6..873f663175c 100644 --- a/addons/account/wizard/base_document_layout.py +++ b/addons/account/wizard/base_document_layout.py @@ -5,7 +5,9 @@ class BaseDocumentLayout(models.TransientModel): _inherit = 'base.document.layout' def document_layout_save(self): + """Save layout and onboarding step progress, return super() result""" res = super(BaseDocumentLayout, self).document_layout_save() - for wizard in self: - wizard.company_id.action_save_onboarding_invoice_layout() + if step := self.env.ref('account.onboarding_onboarding_step_base_document_layout', raise_if_not_found=False): + for company_id in self.company_id: + step.with_company(company_id).action_set_just_done() return res diff --git a/addons/account/wizard/setup_wizards.py b/addons/account/wizard/setup_wizards.py index d4da41cc8a2..3028f131fe7 100644 --- a/addons/account/wizard/setup_wizards.py +++ b/addons/account/wizard/setup_wizards.py @@ -60,7 +60,7 @@ class FinancialYearOpeningWizard(models.TransientModel): return super().write(vals) def action_save_onboarding_fiscal_year(self): - self.env.company.sudo().set_onboarding_step_done('account_setup_fy_data_state') + return self.env['onboarding.onboarding.step'].action_validate_step('account.onboarding_onboarding_step_fiscal_year') class SetupBarBankConfigWizard(models.TransientModel): @@ -146,7 +146,7 @@ class SetupBarBankConfigWizard(models.TransientModel): selected_journal.name = record.new_journal_name def validate(self): - """ Called by the validation button of this wizard. Serves as an + """Called by the validation button of this wizard. Serves as an extension hook in account_bank_statement_import. """ - self.linked_journal_id.mark_bank_setup_as_done_action() + return self.env["onboarding.onboarding.step"].action_validate_step("account.onboarding_onboarding_step_bank_account") diff --git a/addons/account_payment/__manifest__.py b/addons/account_payment/__manifest__.py index dbcec3696bc..eda961eafea 100644 --- a/addons/account_payment/__manifest__.py +++ b/addons/account_payment/__manifest__.py @@ -9,6 +9,7 @@ 'auto_install': ['account'], 'data': [ 'data/ir_config_parameter.xml', + 'data/onboarding_data.xml', 'security/ir.model.access.csv', 'security/ir_rules.xml', @@ -19,7 +20,6 @@ 'views/account_move_views.xml', 'views/account_journal_views.xml', 'views/account_payment_views.xml', - 'views/onboarding_templates.xml', 'views/payment_provider_views.xml', 'views/payment_transaction_views.xml', diff --git a/addons/account_payment/data/onboarding_data.xml b/addons/account_payment/data/onboarding_data.xml new file mode 100644 index 00000000000..20e1503e099 --- /dev/null +++ b/addons/account_payment/data/onboarding_data.xml @@ -0,0 +1,17 @@ + + + + + Online Payments + Enable credit & debit card payments supported by Stripe. + Activate Stripe + action_open_step_payment_provider + + onboarding_bank-account.png + Onboarding Online Payments + + + + diff --git a/addons/account_payment/models/__init__.py b/addons/account_payment/models/__init__.py index b3efe32217f..db619b3b29c 100644 --- a/addons/account_payment/models/__init__.py +++ b/addons/account_payment/models/__init__.py @@ -5,6 +5,6 @@ from . import account_move from . import account_payment from . import account_payment_method from . import account_payment_method_line +from . import onboarding_onboarding_step from . import payment_provider from . import payment_transaction -from . import res_company diff --git a/addons/account_payment/models/onboarding_onboarding_step.py b/addons/account_payment/models/onboarding_onboarding_step.py new file mode 100644 index 00000000000..59bb6d2a668 --- /dev/null +++ b/addons/account_payment/models/onboarding_onboarding_step.py @@ -0,0 +1,20 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, models + + +class OnboardingStep(models.Model): + _inherit = 'onboarding.onboarding.step' + + @api.model + def action_open_step_payment_provider(self): + self.env.company.payment_onboarding_payment_method = 'stripe' + menu = self.env.ref('account_payment.payment_provider_menu', raise_if_not_found=False) + menu_id = menu.id if menu else None + return self.env.company._run_payment_onboarding_step(menu_id) + + @api.model + def action_validate_step_payment_provider(self): + validation_response = super().action_validate_step_payment_provider() + self.action_validate_step("account_payment.onboarding_onboarding_step_payment_provider") + return validation_response diff --git a/addons/account_payment/models/res_company.py b/addons/account_payment/models/res_company.py deleted file mode 100644 index 2ed14af310c..00000000000 --- a/addons/account_payment/models/res_company.py +++ /dev/null @@ -1,19 +0,0 @@ -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import api, models - - -class ResCompany(models.Model): - _inherit = 'res.company' - - @api.model - def action_open_payment_onboarding(self): - self.env.company.payment_onboarding_payment_method = 'stripe' - menu = self.env.ref('account_payment.payment_provider_menu', raise_if_not_found=False) - menu_id = menu and menu.id - return self._run_payment_onboarding_step(menu_id) - - def get_account_invoice_onboarding_steps_states_names(self): - """ Override of `account` to add the state of the payment onboarding step. """ - steps = super().get_account_invoice_onboarding_steps_states_names() - return steps + ['payment_provider_onboarding_state'] diff --git a/addons/account_payment/views/onboarding_templates.xml b/addons/account_payment/views/onboarding_templates.xml deleted file mode 100644 index c689f85c2df..00000000000 --- a/addons/account_payment/views/onboarding_templates.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - Online Payments - Enable credit & debit card payments supported by Stripe - - - Activate Stripe - Online payments enabled - - - - - - - diff --git a/addons/onboarding/__manifest__.py b/addons/onboarding/__manifest__.py index 6ec2a67c99a..65fb4f35d48 100644 --- a/addons/onboarding/__manifest__.py +++ b/addons/onboarding/__manifest__.py @@ -13,9 +13,8 @@ This module allows to manage onboardings and their progress 'depends': ['base'], 'installable': True, 'data': [ - 'data/onboarding_data.xml', - 'views/onboarding_views.xml', 'views/onboarding_templates.xml', + 'views/onboarding_views.xml', 'views/onboarding_menus.xml', 'security/ir.model.access.csv', ], @@ -23,6 +22,12 @@ This module allows to manage onboardings and their progress 'web.assets_backend': [ 'onboarding/static/src/**/*', ], + "web.dark_mode_variables": [ + ('before', 'onboarding/static/src/scss/onboarding.variables.scss', 'onboarding/static/src/scss/onboarding.variables.dark.scss'), + ], + 'web._assets_primary_variables': [ + 'onboarding/static/src/scss/onboarding.variables.scss', + ] }, 'license': 'LGPL-3', } diff --git a/addons/onboarding/data/onboarding_data.xml b/addons/onboarding/data/onboarding_data.xml deleted file mode 100644 index 8c18ac54bfa..00000000000 --- a/addons/onboarding/data/onboarding_data.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - onboarding.onboarding.step - - - - - - - - - - - diff --git a/addons/onboarding/models/onboarding_onboarding.py b/addons/onboarding/models/onboarding_onboarding.py index 7acba62d45e..4eac4da3329 100644 --- a/addons/onboarding/models/onboarding_onboarding.py +++ b/addons/onboarding/models/onboarding_onboarding.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import api, fields, models +from odoo import _, api, fields, models from odoo.addons.onboarding.models.onboarding_progress import ONBOARDING_PROGRESS_STATES @@ -15,6 +15,10 @@ class Onboarding(models.Model): route_name = fields.Char('One word name', required=True) step_ids = fields.Many2many('onboarding.onboarding.step', string='Onboarding steps') + text_completed = fields.Char( + 'Message at completion', default=_('Nice work! Your configuration is done.'), + help='Text shown on onboarding when completed') + is_per_company = fields.Boolean( 'Should be done per company?', compute='_compute_is_per_company', readonly=True, store=False, ) @@ -124,6 +128,7 @@ class Onboarding(models.Model): 'close_model': 'onboarding.onboarding', 'steps': self.step_ids, 'state': self.current_progress_id._get_and_update_onboarding_state(), + 'text_completed': self.text_completed, } return values diff --git a/odoo/addons/base/static/src/scss/onboarding.scss b/addons/onboarding/static/src/scss/onboarding.scss similarity index 93% rename from odoo/addons/base/static/src/scss/onboarding.scss rename to addons/onboarding/static/src/scss/onboarding.scss index 758391ecc96..88c2e9e6b2b 100644 --- a/odoo/addons/base/static/src/scss/onboarding.scss +++ b/addons/onboarding/static/src/scss/onboarding.scss @@ -11,8 +11,10 @@ $o-onboarding-step-width: map-get($container-max-widths, 'lg') / 4 !default; } .o_onboarding_completed_message { + opacity: 0; box-shadow: 0 -7px 20px -5px rgba(#000, 0.3); - animation: o_onboarding_slideInUp $o-onboarding-base-time ease-out; + animation: o_onboarding_slideInUpDownOut; + animation-duration: 6s; } // = Step Design @@ -114,6 +116,11 @@ $o-onboarding-step-width: map-get($container-max-widths, 'lg') / 4 !default; } } + @keyframes o_onboarding_slideInUpDownOut { + 25%, 85% { transform: translate3d(0, -80%, 0); opacity: 1; } + 0%, 100% { transform: translate3d(0, 0, 0); } + } + @keyframes o_onboarding_zoomIn { from { transform: scale3d(0.8, 0.8, 0.8); diff --git a/odoo/addons/base/static/src/scss/onboarding.variables.dark.scss b/addons/onboarding/static/src/scss/onboarding.variables.dark.scss similarity index 100% rename from odoo/addons/base/static/src/scss/onboarding.variables.dark.scss rename to addons/onboarding/static/src/scss/onboarding.variables.dark.scss diff --git a/odoo/addons/base/static/src/scss/onboarding.variables.scss b/addons/onboarding/static/src/scss/onboarding.variables.scss similarity index 100% rename from odoo/addons/base/static/src/scss/onboarding.variables.scss rename to addons/onboarding/static/src/scss/onboarding.variables.scss diff --git a/addons/onboarding/tests/__init__.py b/addons/onboarding/tests/__init__.py index f01d0da0642..efa3fd75052 100644 --- a/addons/onboarding/tests/__init__.py +++ b/addons/onboarding/tests/__init__.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import case from . import test_onboarding from . import test_onboarding_concurrency diff --git a/addons/onboarding/tests/case.py b/addons/onboarding/tests/case.py new file mode 100644 index 00000000000..3fa1aa8f0d4 --- /dev/null +++ b/addons/onboarding/tests/case.py @@ -0,0 +1,40 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.tests.common import TransactionCase + + +class TransactionCaseOnboarding(TransactionCase): + def assert_step_is_done(self, step, also_with_company=None): + self.assertIn( + step.current_progress_step_id.step_state, {'done', 'just_done'}, + f'Expected done-like current state for step {step.id} for {step.env.company.name}') + self.assertEqual(step.current_progress_step_id.step_state, step.current_step_state) + if also_with_company: + self.assert_step_is_done(step.with_company(also_with_company)) + + def assert_step_is_not_done(self, step, also_with_company=None): + self.assertIn( + step.current_progress_step_id.step_state, {'not_done', False}, + f'Expected "not_done" current state for step {step.id} for {step.env.company.name}') + self.assertEqual(step.current_step_state, 'not_done') + if also_with_company: + self.assert_step_is_not_done(step.with_company(also_with_company)) + + def assert_onboarding_is_done(self, onboarding, also_with_company=None): + self.assertIn( + onboarding.current_progress_id.onboarding_state, {'done', 'just_done'}, + f'Expected done-like current state for onboarding "{onboarding.name}" ' + f'for "{onboarding.env.company.name}"') + self.assertEqual(onboarding.current_progress_id.onboarding_state, + onboarding.current_onboarding_state) + if also_with_company: + self.assert_onboarding_is_done(onboarding.with_company(also_with_company)) + + def assert_onboarding_is_not_done(self, onboarding, also_with_company=None): + self.assertIn( + onboarding.current_progress_id.onboarding_state, {'not_done', False}, + f'Expected `"not_done"` or `False` current state for onboarding {onboarding.name} ' + f'for {onboarding.env.company.name}') + self.assertEqual(onboarding.current_onboarding_state, 'not_done') + if also_with_company: + self.assert_onboarding_is_not_done(onboarding.with_company(also_with_company)) diff --git a/addons/onboarding/tests/common.py b/addons/onboarding/tests/common.py index 50a08c2a81a..acd18d6ab1c 100644 --- a/addons/onboarding/tests/common.py +++ b/addons/onboarding/tests/common.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo.tests.common import TransactionCase +from odoo.addons.onboarding.tests.case import TransactionCaseOnboarding -class TestOnboardingCommon(TransactionCase): +class TestOnboardingCommon(TransactionCaseOnboarding): @classmethod def setUpClass(cls): super().setUpClass() @@ -53,41 +53,6 @@ class TestOnboardingCommon(TransactionCase): # Create progress records as would happen through the controller (cls.onboarding_1 + cls.onboarding_2).with_company(cls.company_1)._search_or_create_progress() - def assert_step_is_done(self, step, also_with_company=None): - self.assertIn( - step.current_progress_step_id.step_state, {'done', 'just_done'}, - f'Expected done-like current state for step {step.id} for {step.env.company}') - self.assertEqual(step.current_progress_step_id.step_state, step.current_step_state) - if also_with_company: - self.assert_step_is_done(step.with_company(also_with_company)) - - def assert_step_is_not_done(self, step, also_with_company=None): - self.assertIn( - step.current_progress_step_id.step_state, {'not_done', False}, - f'Expected "not_done" current state for step {step.id} for {step.env.company}') - self.assertEqual(step.current_step_state, 'not_done') - if also_with_company: - self.assert_step_is_not_done(step.with_company(also_with_company)) - - def assert_onboarding_is_done(self, onboarding, also_with_company=None): - self.assertIn( - onboarding.current_progress_id.onboarding_state, {'done', 'just_done'}, - f'Expected done-like current state for onboarding {onboarding.name} ' - f'for {onboarding.env.company}') - self.assertEqual(onboarding.current_progress_id.onboarding_state, - onboarding.current_onboarding_state) - if also_with_company: - self.assert_onboarding_is_done(onboarding.with_company(also_with_company)) - - def assert_onboarding_is_not_done(self, onboarding, also_with_company=None): - self.assertIn( - onboarding.current_progress_id.onboarding_state, {'not_done', False}, - f'Expected `"not_done"` or `False` current state for onboarding {onboarding.name} ' - f'for {onboarding.env.company}') - self.assertEqual(onboarding.current_onboarding_state, 'not_done') - if also_with_company: - self.assert_onboarding_is_not_done(onboarding.with_company(also_with_company)) - def activate_company(self, company): self.onboarding_1_step_1 = self.onboarding_1_step_1.with_company(company) self.onboarding_1_step_2 = self.onboarding_1_step_2.with_company(company) diff --git a/odoo/addons/base/views/onboarding_views.xml b/addons/onboarding/views/onboarding_templates.xml similarity index 64% rename from odoo/addons/base/views/onboarding_views.xml rename to addons/onboarding/views/onboarding_templates.xml index 4b27d26b233..6b57a5540c2 100644 --- a/odoo/addons/base/views/onboarding_views.xml +++ b/addons/onboarding/views/onboarding_templates.xml @@ -1,5 +1,27 @@ - + + + + + + + + + + + + + + + onboarding.onboarding.step + + + + + + + @@ -13,8 +35,8 @@ @@ -79,44 +99,4 @@ - - - - - Company Data - - Set your company's data for documents header/footer. - - - - Let's start! - - - - - - - - base.company.onboarding.form - res.company - - primary - 1000 - - - - - - - - - Set your company data - res.company - form - - new - diff --git a/addons/payment/__manifest__.py b/addons/payment/__manifest__.py index 5f948030f51..1e3e73d42fb 100644 --- a/addons/payment/__manifest__.py +++ b/addons/payment/__manifest__.py @@ -5,8 +5,9 @@ 'version': '2.0', 'category': 'Hidden', 'summary': "The payment engine used by payment provider modules.", - 'depends': ['portal'], + 'depends': ['onboarding', 'portal'], 'data': [ + 'data/onboarding_data.xml', 'data/payment_method_data.xml', 'data/payment_provider_data.xml', 'data/payment_cron.xml', diff --git a/addons/payment/data/onboarding_data.xml b/addons/payment/data/onboarding_data.xml new file mode 100644 index 00000000000..ffe9c0588a3 --- /dev/null +++ b/addons/payment/data/onboarding_data.xml @@ -0,0 +1,10 @@ + + + + + + Online Payments + 99 + + + diff --git a/addons/payment/models/__init__.py b/addons/payment/models/__init__.py index ffb32ae6268..b91b37f64ae 100644 --- a/addons/payment/models/__init__.py +++ b/addons/payment/models/__init__.py @@ -5,5 +5,6 @@ from . import payment_method from . import payment_provider from . import payment_token from . import payment_transaction +from . import onboarding_onboarding_step from . import res_company from . import res_partner diff --git a/addons/payment/models/onboarding_onboarding_step.py b/addons/payment/models/onboarding_onboarding_step.py new file mode 100644 index 00000000000..242e00d8082 --- /dev/null +++ b/addons/payment/models/onboarding_onboarding_step.py @@ -0,0 +1,12 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, models + + +class OnboardingStep(models.Model): + _inherit = 'onboarding.onboarding.step' + + @api.model + def action_validate_step_payment_provider(self): + """Override to validate other steps as well.""" + return self.action_validate_step("payment.onboarding_onboarding_step_payment_provider") diff --git a/addons/payment/models/res_company.py b/addons/payment/models/res_company.py index 271e0b62b47..6ca7745ed01 100644 --- a/addons/payment/models/res_company.py +++ b/addons/payment/models/res_company.py @@ -6,10 +6,6 @@ from odoo import api, fields, models class ResCompany(models.Model): _inherit = 'res.company' - payment_provider_onboarding_state = fields.Selection( - string="State of the onboarding payment provider step", - selection=[('not_done', "Not done"), ('just_done', "Just done"), ('done', "Done")], - default='not_done') payment_onboarding_payment_method = fields.Selection( string="Selected onboarding payment method", selection=[ @@ -56,15 +52,3 @@ class ResCompany(models.Model): modules_sudo = self.env['ir.module.module'].sudo().search([('name', 'in', module_names)]) STATES = ['installed', 'to install', 'to upgrade'] modules_sudo.filtered(lambda m: m.state not in STATES).button_immediate_install() - - def _mark_payment_onboarding_step_as_done(self): - """ Mark the payment onboarding step as done. - - :return: None - """ - self.set_onboarding_step_done('payment_provider_onboarding_state') - - def get_account_invoice_onboarding_steps_states_names(self): - """ Override of account. """ - steps = super().get_account_invoice_onboarding_steps_states_names() - return steps + ['payment_provider_onboarding_state'] diff --git a/addons/payment/wizards/payment_onboarding_wizard.py b/addons/payment/wizards/payment_onboarding_wizard.py index 73edbc9abc4..90d539cae4a 100644 --- a/addons/payment/wizards/payment_onboarding_wizard.py +++ b/addons/payment/wizards/payment_onboarding_wizard.py @@ -134,13 +134,10 @@ class PaymentWizard(models.TransientModel): if payment_method == 'stripe': return self._start_stripe_onboarding() - # the user clicked `apply` and not cancel so we can assume this step is done. - self._set_payment_provider_onboarding_step_done() + # the user clicked `apply` and not cancel, so we can assume this step is done. + self.env['onboarding.onboarding.step'].sudo().action_validate_step_payment_provider() return {'type': 'ir.actions.act_window_close'} - def _set_payment_provider_onboarding_step_done(self): - self.env.company.sudo().set_onboarding_step_done('payment_provider_onboarding_state') - def _start_stripe_onboarding(self): """ Start Stripe Connect onboarding. """ menu = self.env.ref('account_payment.payment_provider_menu', False) diff --git a/addons/payment_stripe/controllers/onboarding.py b/addons/payment_stripe/controllers/onboarding.py index d6795924668..8c9dda8091d 100644 --- a/addons/payment_stripe/controllers/onboarding.py +++ b/addons/payment_stripe/controllers/onboarding.py @@ -12,7 +12,7 @@ class OnboardingController(http.Controller): @http.route(_onboarding_return_url, type='http', methods=['GET'], auth='user') def stripe_return_from_onboarding(self, provider_id, menu_id): - """ Redirect the user to the provider form of the onboarded Stripe account. + """Redirect the user to the provider form of the onboarded Stripe account. The user is redirected to this route by Stripe after or during (if the user clicks on a dedicated button) the onboarding. @@ -23,7 +23,9 @@ class OnboardingController(http.Controller): `ir.ui.menu` id """ stripe_provider = request.env['payment.provider'].browse(int(provider_id)) - stripe_provider.company_id._mark_payment_onboarding_step_as_done() + request.env['onboarding.onboarding.step'].with_company( + stripe_provider.company_id + ).action_validate_step_payment_provider() action = request.env.ref('payment_stripe.action_payment_provider_onboarding') get_params_string = url_encode({'action': action.id, 'id': provider_id, 'menu_id': menu_id}) return request.redirect(f'/web?#{get_params_string}') diff --git a/addons/payment_stripe/models/payment_provider.py b/addons/payment_stripe/models/payment_provider.py index 8f179dd2680..0cac7956a71 100644 --- a/addons/payment_stripe/models/payment_provider.py +++ b/addons/payment_stripe/models/payment_provider.py @@ -134,7 +134,7 @@ class PaymentProvider(models.Model): self.ensure_one() if self.state == 'enabled': - self.company_id._mark_payment_onboarding_step_as_done() + self.env["onboarding.onboarding.step"].action_validate_step_payment_provider() action = {'type': 'ir.actions.act_window_close'} else: # Account creation diff --git a/addons/sale/__manifest__.py b/addons/sale/__manifest__.py index 2732d84d9c4..cc198bd4956 100644 --- a/addons/sale/__manifest__.py +++ b/addons/sale/__manifest__.py @@ -31,6 +31,7 @@ This module contains all the common features of Sales Management and eCommerce. 'data/mail_message_subtype_data.xml', 'data/mail_template_data.xml', 'data/ir_config_parameter.xml', # Needs mail_template_data + 'data/onboarding_data.xml', 'wizard/account_accrued_orders_wizard_views.xml', 'wizard/mass_cancel_orders_views.xml', @@ -51,7 +52,6 @@ This module contains all the common features of Sales Management and eCommerce. 'views/product_product_views.xml', 'views/product_views.xml', 'views/res_partner_views.xml', - 'views/sale_onboarding_views.xml', 'views/sale_order_line_views.xml', 'views/sale_portal_templates.xml', 'views/utm_campaign_views.xml', diff --git a/addons/sale/controllers/__init__.py b/addons/sale/controllers/__init__.py index 85653bbd9df..400dd86ae7f 100644 --- a/addons/sale/controllers/__init__.py +++ b/addons/sale/controllers/__init__.py @@ -2,5 +2,4 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import catalog -from . import onboarding from . import portal diff --git a/addons/sale/controllers/onboarding.py b/addons/sale/controllers/onboarding.py deleted file mode 100644 index 2d90f083a4a..00000000000 --- a/addons/sale/controllers/onboarding.py +++ /dev/null @@ -1,31 +0,0 @@ -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo.http import request, route, Controller - - -class OnboardingController(Controller): - - @route('/sales/sale_quotation_onboarding_panel', auth='user', type='json') - def sale_quotation_onboarding(self, context=None): - """ Returns the `banner` for the sale onboarding panel. - It can be empty if the user has closed it or if he doesn't have - the permission to see it. """ - - # Forward `allowed_company_ids` in the context - # so `request.env.company` returns the company selected in the dropdown - if context: - request.update_context(**context) - company = request.env.company - if not request.env.is_admin() or \ - company.sale_quotation_onboarding_state == 'closed': - return {} - - return { - 'html': request.env['ir.qweb']._render( - 'sale.sale_quotation_onboarding_panel', - { - 'company': company, - 'state': company._get_and_update_sale_quotation_onboarding_state() - } - ) - } diff --git a/addons/sale/data/onboarding_data.xml b/addons/sale/data/onboarding_data.xml new file mode 100644 index 00000000000..53f51924e7e --- /dev/null +++ b/addons/sale/data/onboarding_data.xml @@ -0,0 +1,41 @@ + + + + + + Order Confirmation + Choose between electronic signatures or online payments. + Set payments + action_open_step_sale_order_confirmation + + onboarding_default.png + Onboarding Order Confirmation + 6 + + + + Sample Quotation + Send a quotation to test the customer portal. + Send sample + action_open_step_sample_quotation + + onboarding_sample-quotation.png + Onboarding Sample Quotation + 7 + + + + + Sale Quotation Onboarding + + sale_quotation + action_close_panel_sale_quotation + + + + diff --git a/addons/sale/models/__init__.py b/addons/sale/models/__init__.py index a55d995593a..6a8faf82e87 100644 --- a/addons/sale/models/__init__.py +++ b/addons/sale/models/__init__.py @@ -5,6 +5,8 @@ from . import analytic from . import account_move from . import account_move_line from . import crm_team +from . import onboarding_onboarding +from . import onboarding_onboarding_step from . import payment_provider from . import payment_transaction from . import product_product diff --git a/addons/sale/models/onboarding_onboarding.py b/addons/sale/models/onboarding_onboarding.py new file mode 100644 index 00000000000..bae39078945 --- /dev/null +++ b/addons/sale/models/onboarding_onboarding.py @@ -0,0 +1,12 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, models + + +class Onboarding(models.Model): + _inherit = 'onboarding.onboarding' + + # Sale Quotation Onboarding + @api.model + def action_close_panel_sale_quotation(self): + self.action_close_panel('sale.onboarding_onboarding_sale_quotation') diff --git a/addons/sale/models/onboarding_onboarding_step.py b/addons/sale/models/onboarding_onboarding_step.py new file mode 100644 index 00000000000..68d34759929 --- /dev/null +++ b/addons/sale/models/onboarding_onboarding_step.py @@ -0,0 +1,100 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import base64 + +from odoo import _, api, Command, models +from odoo.modules.module import get_resource_path +from odoo.tools import file_open + + +class OnboardingStep(models.Model): + _inherit = 'onboarding.onboarding.step' + + @api.model + def action_open_step_sale_order_confirmation(self): + self.env.company.get_chart_of_accounts_or_fail() + action = { + 'type': 'ir.actions.act_window', + 'name': _('Choose how to confirm quotations'), + 'res_model': 'sale.payment.provider.onboarding.wizard', + 'view_mode': 'form', + 'views': [(self.env.ref('payment.payment_provider_onboarding_wizard_form').id, 'form')], + 'target': 'new', + } + return action + + @api.model + def _get_sample_sales_order(self): + """ Get a sample quotation or create one if it does not exist. """ + # use current user as partner + partner = self.env.user.partner_id + company_id = self.env.company.id + # is there already one? + sample_sales_order = self.env['sale.order'].search([ + ('company_id', '=', company_id), + ('partner_id', '=', partner.id), + ('state', '=', 'draft'), + ], limit=1) + if not sample_sales_order: + # take any existing product or create one + product = self.env['product.product'].search([], limit=1) + if not product: + default_image_path = get_resource_path('product', 'static/img', 'product_product_13-image.jpg') + with file_open(default_image_path, 'rb') as default_image_stream: + product = self.env['product.product'].create({ + 'name': _('Sample Product'), + 'active': False, + 'image_1920': base64.b64encode(default_image_stream.read()), + }) + product.product_tmpl_id.active = False + sample_sales_order = self.env['sale.order'].create({ + 'partner_id': partner.id, + 'order_line': [ + Command.create({ + 'name': _('Sample Order Line'), + 'product_id': product.id, + 'product_uom_qty': 10, + 'price_unit': 123, + }) + ] + }) + return sample_sales_order + + @api.model + def action_open_step_sample_quotation(self): + """ Onboarding step for sending a sample quotation. Open a window to compose an email, + with the edi_invoice_template message loaded by default. """ + sample_sales_order = self._get_sample_sales_order() + template = self.env.ref('sale.email_template_edi_sale', False) + + self.env['mail.compose.message'].with_context( + mark_so_as_sent=True, + default_email_layout_xmlid='mail.mail_notification_layout_with_responsible_signature', + proforma=self.env.context.get('proforma', False), + ).create({ + 'res_ids': sample_sales_order.ids, + 'template_id': template.id if template else False, + 'model': sample_sales_order._name, + 'composition_mode': 'comment', + })._action_send_mail() + + self.action_validate_step('sale.onboarding_onboarding_step_sample_quotation') + sale_quotation_onboarding = self.env.ref('sale.onboarding_onboarding_sale_quotation', raise_if_not_found=False) + if sale_quotation_onboarding: + sale_quotation_onboarding.action_close() + + view_id = self.env.ref('sale.view_order_form').id + action = self.env['ir.actions.actions']._for_xml_id('sale.action_orders') + action.update({ + 'view_mode': 'form', + 'views': [[view_id, 'form']], + 'target': 'main', + }) + return action + + @api.model + def action_validate_step_payment_provider(self): + validation_response = super().action_validate_step_payment_provider() + if self.env.company.sale_onboarding_payment_method: # Set if the flow is/was done from the Sales panel + return self.action_validate_step('sale.onboarding_onboarding_step_sale_order_confirmation') + return validation_response diff --git a/addons/sale/models/res_company.py b/addons/sale/models/res_company.py index c22bd3768ff..d2bec4cf369 100644 --- a/addons/sale/models/res_company.py +++ b/addons/sale/models/res_company.py @@ -1,11 +1,6 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. -import base64 -from odoo import api, fields, models, _ -from odoo.fields import Command -from odoo.modules.module import get_module_resource - -from odoo.addons.account.models.company import DASHBOARD_ONBOARDING_STATES, ONBOARDING_STEP_STATES +from odoo import fields, models class ResCompany(models.Model): @@ -39,18 +34,6 @@ class ResCompany(models.Model): ) # sale onboarding - sale_quotation_onboarding_state = fields.Selection( - selection=DASHBOARD_ONBOARDING_STATES, - string="State of the sale onboarding panel", - default='not_done') - sale_onboarding_order_confirmation_state = fields.Selection( - selection=ONBOARDING_STEP_STATES, - string="State of the onboarding confirmation order step", - default='not_done') - sale_onboarding_sample_quotation_state = fields.Selection( - selection=ONBOARDING_STEP_STATES, - string="State of the onboarding sample quotation step", - default='not_done') sale_onboarding_payment_method = fields.Selection( selection=[ ('digital_signature', "Sign online"), @@ -60,104 +43,3 @@ class ResCompany(models.Model): ('manual', "Manual Payment"), ], string="Sale onboarding selected payment method") - - @api.model - def action_close_sale_quotation_onboarding(self): - """ Mark the onboarding panel as closed. """ - self.env.company.sale_quotation_onboarding_state = 'closed' - - @api.model - def action_open_sale_onboarding_payment_provider(self): - """ Called by onboarding panel above the quotation list.""" - self.env.company.get_chart_of_accounts_or_fail() - return self.env['ir.actions.actions']._for_xml_id('sale.action_open_sale_payment_provider_onboarding_wizard') - - def _mark_payment_onboarding_step_as_done(self): - """ Override of payment to mark the sale onboarding step as done. - - The payment onboarding step of Sales is only marked as done if it was started from Sales. - This prevents incorrectly marking the step as done if another module's payment onboarding - step was marked as done. - - :return: None - """ - super()._mark_payment_onboarding_step_as_done() - if self.sale_onboarding_payment_method: # The onboarding step was started from Sales - self.set_onboarding_step_done('sale_onboarding_order_confirmation_state') - - def _get_sample_sales_order(self): - """ Get a sample quotation or create one if it does not exist. """ - # use current user as partner - partner = self.env.user.partner_id - company_id = self.env.company.id - # is there already one? - sample_sales_order = self.env['sale.order'].search([ - ('company_id', '=', company_id), - ('partner_id', '=', partner.id), - ('state', '=', 'draft'), - ], limit=1) - if not sample_sales_order: - # take any existing product or create one - product = self.env['product.product'].search([], limit=1) - if not product: - default_image_path = get_module_resource('product', 'static/img', 'product_product_13-image.jpg') - product = self.env['product.product'].create({ - 'name': _("Sample Product"), - 'active': False, - 'image_1920': base64.b64encode(open(default_image_path, 'rb').read()) - }) - product.product_tmpl_id.write({'active': False}) - sample_sales_order = self.env['sale.order'].create({ - 'partner_id': partner.id, - 'order_line': [ - Command.create({ - 'name': _("Sample Order Line"), - 'product_id': product.id, - 'product_uom_qty': 10, - 'price_unit': 123, - }) - ] - }) - return sample_sales_order - - @api.model - def action_open_sale_onboarding_sample_quotation(self): - """ Onboarding step for sending a sample quotation. Open a window to compose an email, - with the edi_invoice_template message loaded by default. """ - sample_sales_order = self._get_sample_sales_order() - template = self.env.ref('sale.email_template_edi_sale', False) - - self.env['mail.compose.message'].with_context( - mark_so_as_sent=True, - default_email_layout_xmlid='mail.mail_notification_layout_with_responsible_signature', - proforma=self.env.context.get('proforma', False), - force_email=True, - ).create({ - 'res_ids': sample_sales_order.ids, - 'template_id': template.id if template else False, - 'model': sample_sales_order._name, - 'composition_mode': 'comment', - })._action_send_mail() - - self.set_onboarding_step_done('sale_onboarding_sample_quotation_state') - - self.action_close_sale_quotation_onboarding() - - action = self.env['ir.actions.actions']._for_xml_id('sale.action_orders') - action.update({ - 'views': [[self.env.ref('sale.view_order_form').id, 'form']], - 'view_mode': 'form', - 'target': 'main', - }) - return action - - def _get_and_update_sale_quotation_onboarding_state(self): - """ This method is called on the controller rendering method and ensures that the animations - are displayed only one time. """ - steps = [ - 'base_onboarding_company_state', - 'account_onboarding_invoice_layout_state', - 'sale_onboarding_order_confirmation_state', - 'sale_onboarding_sample_quotation_state', - ] - return self._get_and_update_onboarding_state('sale_quotation_onboarding_state', steps) diff --git a/addons/sale/static/src/img/sale_quotation_onboarding_bg.jpg b/addons/sale/static/src/img/sale_quotation_onboarding_bg.jpg deleted file mode 100644 index 3c0f7a340c7..00000000000 Binary files a/addons/sale/static/src/img/sale_quotation_onboarding_bg.jpg and /dev/null differ diff --git a/addons/sale/static/src/js/tours/sale.js b/addons/sale/static/src/js/tours/sale.js index 6390a8b89b2..f170c7d1109 100644 --- a/addons/sale/static/src/js/tours/sale.js +++ b/addons/sale/static/src/js/tours/sale.js @@ -23,58 +23,58 @@ registry.category("web_tour.tours").add("sale_tour", { position: "bottom", edition: "enterprise" }, { - trigger: 'a.o_onboarding_step_action.btn[data-method=action_open_base_onboarding_company]', + trigger: 'a.o_onboarding_step_action.btn[data-method=action_open_step_company_data]', extra_trigger: ".o_sale_order", content: _t("Start by checking your company's data."), position: "bottom", - skip_trigger: 'a[data-method=action_open_base_onboarding_company].o_onboarding_step_action__done', + skip_trigger: 'a[data-method=action_open_step_company_data].o_onboarding_step_action__done', }, { trigger: 'input[id=street_0]', content: _t("Complete your company's data"), position: "bottom", - skip_trigger: 'a[data-method=action_open_base_onboarding_company].o_onboarding_step_action__done', + skip_trigger: 'a[data-method=action_open_step_company_data].o_onboarding_step_action__done', }, { - trigger: ".modal-content button[name='action_save_onboarding_company_step']", + trigger: ".modal-content button.o_form_button_save", content: _t("Looks good. Let's continue."), position: "left", - skip_trigger: 'a[data-method=action_open_base_onboarding_company].o_onboarding_step_action__done', + skip_trigger: 'a[data-method=action_open_step_company_data].o_onboarding_step_action__done', }, stepUtils.showAppsMenuItem(), { trigger: ".o_app[data-menu-xmlid='sale.sale_menu_root']", - skip_trigger: 'a[data-method=action_open_base_onboarding_company].o_onboarding_step_action__done', + skip_trigger: 'a[data-method=action_open_step_company_data].o_onboarding_step_action__done', edition: "community", auto: true, }, { trigger: ".o_app[data-menu-xmlid='sale.sale_menu_root']", - skip_trigger: 'a[data-method=action_open_base_onboarding_company].o_onboarding_step_action__done', + skip_trigger: 'a[data-method=action_open_step_company_data].o_onboarding_step_action__done', edition: "enterprise", auto: true, }, { - trigger: 'a.o_onboarding_step_action.btn[data-method=action_open_base_document_layout]', + trigger: 'a.o_onboarding_step_action.btn[data-method=action_open_step_base_document_layout]', extra_trigger: ".o_sale_order", content: _t("Customize your quotes and orders."), position: "bottom", - skip_trigger: 'a[data-method=action_open_base_document_layout].o_onboarding_step_action__done', + skip_trigger: 'a[data-method=action_open_step_base_document_layout].o_onboarding_step_action__done', }, { trigger: "button[name='document_layout_save']", extra_trigger: ".o_sale_order", content: _t("Good job, let's continue."), position: "top", // dot NOT move to bottom, it would cause a resize flicker - skip_trigger: 'a[data-method=action_open_base_document_layout].o_onboarding_step_action__done', + skip_trigger: 'a[data-method=action_open_step_base_document_layout].o_onboarding_step_action__done', }, { - trigger: 'a.o_onboarding_step_action.btn[data-method=action_open_sale_onboarding_payment_provider]', + trigger: 'a.o_onboarding_step_action.btn[data-method=action_open_step_sale_order_confirmation]', extra_trigger: ".o_sale_order", content: _t("To speed up order confirmation, we can activate electronic signatures or payments."), position: "bottom", - skip_trigger: 'a[data-method=action_open_sale_onboarding_payment_provider].o_onboarding_step_action__done', + skip_trigger: 'a[data-method=action_open_step_sale_order_confirmation].o_onboarding_step_action__done', }, { trigger: "button[name='add_payment_methods']", extra_trigger: ".o_sale_order", content: _t("Lets keep electronic signature for now."), position: "bottom", - skip_trigger: 'a[data-method=action_open_sale_onboarding_payment_provider].o_onboarding_step_action__done', + skip_trigger: 'a[data-method=action_open_step_sale_order_confirmation].o_onboarding_step_action__done', }, { - trigger: 'a.o_onboarding_step_action.btn[data-method=action_open_sale_onboarding_sample_quotation]', + trigger: 'a.o_onboarding_step_action.btn[data-method=action_open_step_sample_quotation]', extra_trigger: ".o_sale_order", content: _t("Now, we'll create a sample quote."), position: "bottom", diff --git a/addons/sale/tests/__init__.py b/addons/sale/tests/__init__.py index 2a21d3536a3..1c2958aa0b9 100644 --- a/addons/sale/tests/__init__.py +++ b/addons/sale/tests/__init__.py @@ -5,6 +5,7 @@ from . import test_access_rights from . import test_accrued_sale_orders from . import test_common from . import test_controllers +from . import test_onboarding from . import test_onchange from . import test_payment_flow from . import test_reinvoice diff --git a/addons/sale/tests/test_onboarding.py b/addons/sale/tests/test_onboarding.py new file mode 100644 index 00000000000..7b52c90ea45 --- /dev/null +++ b/addons/sale/tests/test_onboarding.py @@ -0,0 +1,30 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.onboarding.tests.case import TransactionCaseOnboarding + + +class TestOnboarding(TransactionCaseOnboarding): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.account_payment_provider_step = cls.env.ref( + "account_payment.onboarding_onboarding_step_payment_provider" + ) + cls.sale_quotation_order_confirmation_step = cls.env.ref( + "sale.onboarding_onboarding_step_sale_order_confirmation" + ) + + def test_payment_provider_account_doesnt_validate_sales(self): + self.assert_step_is_not_done(self.sale_quotation_order_confirmation_step) + self.env["onboarding.onboarding.step"].action_validate_step_payment_provider() + self.assert_step_is_not_done(self.sale_quotation_order_confirmation_step) + + # Set field as in payment_provider_onboarding_wizard's add_payment_method override + self.env.company.sale_onboarding_payment_method = "stripe" + self.env["onboarding.onboarding.step"].action_validate_step_payment_provider() + self.assert_step_is_done(self.sale_quotation_order_confirmation_step) + + def test_payment_provider_sales_validates_account(self): + self.assert_step_is_not_done(self.account_payment_provider_step) + self.env["onboarding.onboarding.step"].action_validate_step_payment_provider() + self.assert_step_is_done(self.account_payment_provider_step) diff --git a/addons/sale/tests/test_sale_onboarding.py b/addons/sale/tests/test_sale_onboarding.py index 148a52b669d..08fb153e097 100644 --- a/addons/sale/tests/test_sale_onboarding.py +++ b/addons/sale/tests/test_sale_onboarding.py @@ -15,4 +15,4 @@ class TestOnboarding(HttpCase): ('state', '=', 'draft') ]).state = 'cancel' self.env['product.product'].search([]).active = False - self.env.company._get_sample_sales_order() + self.env['onboarding.onboarding.step']._get_sample_sales_order() diff --git a/addons/sale/views/sale_onboarding_views.xml b/addons/sale/views/sale_onboarding_views.xml deleted file mode 100644 index de3cac7ac12..00000000000 --- a/addons/sale/views/sale_onboarding_views.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - Quotation Layout - Customize the look of your quotations. - - - - Looks great! - Customize - - - - - - - - Order Confirmation - Choose between electronic signatures or online payments. - - - Set payments - - - - - - - - Sample Quotation - Send a quotation to test the customer portal. - - - Send sample - - - - - - - - - - - - - - - - - - - - Choose how to confirm quotations - sale.payment.provider.onboarding.wizard - form - - new - - diff --git a/addons/sale/views/sale_order_views.xml b/addons/sale/views/sale_order_views.xml index 7f539755604..794fe169cf9 100644 --- a/addons/sale/views/sale_order_views.xml +++ b/addons/sale/views/sale_order_views.xml @@ -215,7 +215,7 @@ primary - /sales/sale_quotation_onboarding_panel + /onboarding/sale_quotation diff --git a/addons/sale/wizard/payment_provider_onboarding_wizard.py b/addons/sale/wizard/payment_provider_onboarding_wizard.py index 9ecab2fdf66..d3bc79f187d 100644 --- a/addons/sale/wizard/payment_provider_onboarding_wizard.py +++ b/addons/sale/wizard/payment_provider_onboarding_wizard.py @@ -20,11 +20,6 @@ class PaymentWizard(models.TransientModel): ('paypal', "PayPal"), ('manual', "Custom payment instructions"), ], default=_get_default_payment_method) - # - - def _set_payment_provider_onboarding_step_done(self): - """ Override. """ - self.env.company.sudo().set_onboarding_step_done('sale_onboarding_order_confirmation_state') def add_payment_methods(self): self.env.company.sale_onboarding_payment_method = self.payment_method diff --git a/addons/sale_management/tests/test_sale_ui.py b/addons/sale_management/tests/test_sale_ui.py index 9d73a4400c3..96a9b420d26 100644 --- a/addons/sale_management/tests/test_sale_ui.py +++ b/addons/sale_management/tests/test_sale_ui.py @@ -11,8 +11,8 @@ class TestUi(HttpCase): self.start_tour("/web", 'sale_tour', login="admin", step_delay=100) def test_02_sale_tour_company_onboarding_done(self): - self.env.company.set_onboarding_step_done('base_onboarding_company_state') - self.start_tour("/web", 'sale_tour', login="admin", step_delay=100) + self.env["onboarding.onboarding.step"].action_validate_step("account.onboarding_onboarding_step_company_data") + self.start_tour("/web", "sale_tour", login="admin", step_delay=100) def test_03_sale_quote_tour(self): self.env['res.partner'].create({'name': 'Agrolait', 'email': 'agro@lait.be'}) diff --git a/addons/web/__manifest__.py b/addons/web/__manifest__.py index 209168aea8c..48e07bab124 100644 --- a/addons/web/__manifest__.py +++ b/addons/web/__manifest__.py @@ -205,7 +205,6 @@ This module provides the core of the Odoo Web Client. 'web/static/src/legacy/scss/ace.scss', 'web/static/src/legacy/scss/fields.scss', 'web/static/src/legacy/scss/views.scss', - 'base/static/src/scss/onboarding.scss', 'web/static/src/legacy/scss/attachment_preview.scss', 'web/static/src/legacy/scss/base_document_layout.scss', 'web/static/src/legacy/scss/fields_extra.scss', @@ -505,9 +504,6 @@ This module provides the core of the Odoo Web Client. ('include', 'web.assets_backend'), 'web/static/src/**/*.dark.scss', ], - "web.dark_mode_variables": [ - ('before', 'base/static/src/scss/onboarding.variables.scss', 'base/static/src/scss/onboarding.variables.dark.scss'), - ], # --------------------------------------------------------------------- # SUB BUNDLES @@ -525,7 +521,6 @@ This module provides the core of the Odoo Web Client. 'web._assets_primary_variables': [ 'web/static/src/scss/primary_variables.scss', 'web/static/src/**/*.variables.scss', - 'base/static/src/scss/onboarding.variables.scss', ], 'web._assets_secondary_variables': [ 'web/static/src/scss/secondary_variables.scss', diff --git a/addons/web/models/base_document_layout.py b/addons/web/models/base_document_layout.py index 47260ba25be..4921124cbbf 100644 --- a/addons/web/models/base_document_layout.py +++ b/addons/web/models/base_document_layout.py @@ -246,14 +246,6 @@ class BaseDocumentLayout(models.TransientModel): return tools.rgb_to_hex(primary), tools.rgb_to_hex(secondary) - @api.model - def action_open_base_document_layout(self, action_ref=None): - if not action_ref: - action_ref = 'web.action_base_document_layout_configurator' - res = self.env["ir.actions.actions"]._for_xml_id(action_ref) - self.env[res["res_model"]].check_access_rights('write') - return res - def document_layout_save(self): # meant to be overridden return self.env.context.get('report_action') or {'type': 'ir.actions.act_window_close'} diff --git a/addons/website_sale/models/res_company.py b/addons/website_sale/models/res_company.py index cc9df3497d6..8e4b6998a0a 100644 --- a/addons/website_sale/models/res_company.py +++ b/addons/website_sale/models/res_company.py @@ -1,21 +1,12 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import api, fields, models +from odoo import models class ResCompany(models.Model): _inherit = 'res.company' - website_sale_onboarding_payment_provider_state = fields.Selection([('not_done', "Not done"), ('just_done', "Just done"), ('done', "Done")], string="State of the website sale onboarding payment provider step", default='not_done') - - @api.model - def action_open_website_sale_onboarding_payment_provider(self): - """ Called by onboarding panel above the quotation list.""" - self.env.company.payment_onboarding_payment_method = 'stripe' - menu_id = self.env.ref('website.menu_website_dashboard').id - return self._run_payment_onboarding_step(menu_id) - def _get_default_pricelist_vals(self): """ Override of product. Called at company creation or activation of the pricelist setting. diff --git a/odoo/addons/base/__manifest__.py b/odoo/addons/base/__manifest__.py index 366f62fe37c..bd4488558c6 100644 --- a/odoo/addons/base/__manifest__.py +++ b/odoo/addons/base/__manifest__.py @@ -72,7 +72,6 @@ The kernel of Odoo, needed for all installation. 'views/ir_property_views.xml', 'views/res_config_settings_views.xml', 'views/report_paperformat_views.xml', - 'views/onboarding_views.xml', 'security/ir.model.access.csv', ], 'demo': [ diff --git a/odoo/addons/base/models/res_company.py b/odoo/addons/base/models/res_company.py index 8c62bd1f974..b91e16035f1 100644 --- a/odoo/addons/base/models/res_company.py +++ b/odoo/addons/base/models/res_company.py @@ -68,8 +68,6 @@ class Company(models.Model): company_registry = fields.Char(related='partner_id.company_registry', string="Company ID", readonly=False) paperformat_id = fields.Many2one('report.paperformat', 'Paper format', default=lambda self: self.env.ref('base.paperformat_euro', raise_if_not_found=False)) external_report_layout_id = fields.Many2one('ir.ui.view', 'Document Template') - base_onboarding_company_state = fields.Selection([ - ('not_done', "Not done"), ('just_done', "Just done"), ('done', "Done")], string="State of the onboarding company step", default='not_done') font = fields.Selection([("Lato", "Lato"), ("Roboto", "Roboto"), ("Open_Sans", "Open Sans"), ("Montserrat", "Montserrat"), ("Oswald", "Oswald"), ("Raleway", "Raleway"), ('Tajawal', 'Tajawal')], default="Lato") primary_color = fields.Char() secondary_color = fields.Char() @@ -284,40 +282,6 @@ class Company(models.Model): return (self.env['ir.actions.report'].search([('report_name', '=', report_name)], limit=1) .report_action(docids)) - @api.model - def action_open_base_onboarding_company(self): - """ Onboarding step for company basic information. """ - action = self.env["ir.actions.actions"]._for_xml_id("base.action_open_base_onboarding_company") - action['res_id'] = self.env.company.id - return action - - def set_onboarding_step_done(self, step_name): - if self[step_name] == 'not_done': - self[step_name] = 'just_done' - - def _get_and_update_onboarding_state(self, onboarding_state, steps_states): - """ Needed to display onboarding animations only one time. """ - old_values = {} - all_done = True - for step_state in steps_states: - old_values[step_state] = self[step_state] - if self[step_state] == 'just_done': - self[step_state] = 'done' - all_done = all_done and self[step_state] == 'done' - - if all_done: - if self[onboarding_state] == 'not_done': - # string `onboarding_state` instead of variable name is not an error - old_values['onboarding_state'] = 'just_done' - else: - old_values['onboarding_state'] = 'done' - self[onboarding_state] = 'done' - return old_values - - def action_save_onboarding_company_step(self): - if bool(self.street): - self.set_onboarding_step_done('base_onboarding_company_state') - @api.model def _get_main_company(self): try: diff --git a/odoo/addons/base/populate/res_company.py b/odoo/addons/base/populate/res_company.py index b4671dc6f19..23e6dc3ccbd 100644 --- a/odoo/addons/base/populate/res_company.py +++ b/odoo/addons/base/populate/res_company.py @@ -29,8 +29,6 @@ class Partner(models.Model): ('name', populate.constant('company_{counter}')), ('sequence', populate.randint(0, 100)), ('company_registry', populate.iterate([False, 'company_registry_{counter}'])), - ('base_onboarding_company_state', populate.iterate( - [False] + [e[0] for e in type(self).base_onboarding_company_state.selection])), ('primary_color', populate.iterate([False, '', '#ff7755'])), ('secondary_color', populate.iterate([False, '', '#ffff55'], seed='primary_color')), ('currency_id', populate.iterate(active_currencies)),