diff options
| author | qdp-odoo <qdp@odoo.com> | 2015-08-19 15:44:49 +0200 |
|---|---|---|
| committer | qdp-odoo <qdp@odoo.com> | 2015-08-19 15:44:59 +0200 |
| commit | 6cb6f43c2beffaccd871ac93f19e11aeeb92d131 (patch) | |
| tree | 94d062f1160c8f80621ec0e9c1b39ad02aef1a3c | |
| parent | 256e5bd23a5b6f792fc35381413ef948b24e1dc7 (diff) | |
[REF] base: simplified res.partner.bank and res.bank objects for a greater usability + [IMP] account: bank journals are now the bank accounts of a company
56 files changed, 681 insertions, 863 deletions
diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index b1592ba5bec..f5322dc2a2c 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -41,12 +41,12 @@ You could use this simplified accounting in case you work with an (external) acc 'views/product_view.xml', 'views/account_analytic_view.xml', 'views/company_view.xml', - 'views/account_bank_view.xml', 'views/res_config_view.xml', 'views/account_tip_data.xml', 'views/account.xml', 'views/report_invoice.xml', 'report/account_invoice_report_view.xml', + 'report/inherited_layouts.xml', 'views/account_journal_dashboard_view.xml', ], 'demo': [ diff --git a/addons/account/models/__init__.py b/addons/account/models/__init__.py index a31840bb76e..b4855fb482f 100644 --- a/addons/account/models/__init__.py +++ b/addons/account/models/__init__.py @@ -5,7 +5,6 @@ import account import account_payment import account_invoice import account_bank_statement -import account_bank import account_move import chart_template import account_analytic_line diff --git a/addons/account/models/account.py b/addons/account/models/account.py index f3a1228b58d..95e1f061165 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -6,7 +6,7 @@ import math from openerp.osv import expression from openerp.tools.float_utils import float_round as round from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT -from openerp.exceptions import UserError +from openerp.exceptions import UserError, ValidationError from openerp import api, fields, models, _ @@ -189,7 +189,7 @@ class AccountJournal(models.Model): return self.env.ref('account.account_payment_method_manual_out') name = fields.Char(string='Journal Name', required=True) - code = fields.Char(string='Sequence Prefix', size=5, required=True, help="The journal entries of this journal will be named using this prefix.") + code = fields.Char(string='Short Code', size=5, required=True, help="The journal entries of this journal will be named using this prefix.") type = fields.Selection([ ('sale', 'Sale'), ('purchase', 'Purchase'), @@ -240,6 +240,13 @@ class AccountJournal(models.Model): profit_account_id = fields.Many2one('account.account', string='Profit Account', domain=[('deprecated', '=', False)], help="Used to register a profit when the ending balance of a cash register differs from what the system computes") loss_account_id = fields.Many2one('account.account', string='Loss Account', domain=[('deprecated', '=', False)], help="Used to register a loss when the ending balance of a cash register differs from what the system computes") + # Bank journals fields + bank_account_id = fields.Many2one('res.partner.bank', string="Bank Account", ondelete='restrict') + display_on_footer = fields.Boolean("Show in Invoices Footer", help="Display this bank account on the footer of printed documents like invoices and sales orders.") + bank_statements_source = fields.Selection([('manual', 'Record Manually')], string='Bank Feeds') + bank_acc_number = fields.Char(related='bank_account_id.acc_number') + bank_id = fields.Many2one('res.bank', related='bank_account_id.bank_id') + _sql_constraints = [ ('code_company_uniq', 'unique (code, name, company_id)', 'The code and name of the journal must be unique per company !'), ] @@ -253,6 +260,17 @@ class AccountJournal(models.Model): if self.default_debit_account_id and not self.default_debit_account_id.currency_id.id == self.currency_id.id: raise UserError(_('Configuration error!\nThe currency of the journal should be the same than the default debit account.')) + @api.one + @api.constrains('type', 'bank_account_id') + def _check_bank_account(self): + if self.type == 'bank' and self.bank_account_id: + if self.bank_account_id.company_id != self.company_id: + raise ValidationError(_('The bank account of a bank journal must belong to the same company (%s).') % self.company_id.name) + # A bank account can belong to a customer/supplier, in which case their partner_id is the customer/supplier. + # Or they are part of a bank journal and their partner_id must be the company's partner_id. + if self.bank_account_id.partner_id != self.company_id.partner_id: + raise ValidationError(_('The holder of a journal\'s bank account must be the company (%s).') % self.company_id.name) + @api.onchange('default_debit_account_id') def onchange_debit_account_id(self): if not self.default_credit_account_id: @@ -285,7 +303,14 @@ class AccountJournal(models.Model): if journal.refund_sequence_id: new_prefix = self._get_sequence_prefix(vals['code'], refund=True) journal.refund_sequence_id.write({'prefix': new_prefix}) - return super(AccountJournal, self).write(vals) + result = super(AccountJournal, self).write(vals) + + # Create the bank_account_id if necessary + if 'bank_acc_number' in vals: + for journal in self.filtered(lambda r: r.type == 'bank' and not r.bank_account_id): + journal.set_bank_account(vals.get('bank_acc_number'), vals.get('bank_id')) + + return result @api.model def _get_sequence_prefix(self, code, refund=False): @@ -350,56 +375,54 @@ class AccountJournal(models.Model): } @api.model - def _prepare_bank_journal(self, company, line): - ''' - This function prepares the value to use for the creation of a bank journal created through the wizard of - generating COA from templates. - - :param company: company for which the wizard is running - :param line: dictionary containing the values encoded by the user related to his bank account with keys - - acc_name (char): name of the bank account - - account_type (char): kind of liquidity journal to create. Either 'bank' or 'cash' - - currency_id (int): id of the currency related to this account if its different than the company currency (False otherwise) - :return: mapping of field names and values - :rtype: dict - ''' - # we need to loop to find next number for journal code - for num in xrange(1, 100): - # journal_code has a maximal size of 5, hence we can enforce the boundary num < 100 - journal_code = line.get('account_type', 'bank') == 'cash' and 'CSH' or 'BNK' - journal_code += str(num) - journal = self.env['account.journal'].search([('code', '=', journal_code), ('company_id', '=', company.id)], limit=1) - if not journal: - break - else: - raise UserError(_('Cannot generate an unused journal code.')) - - return { - 'name': line['acc_name'], - 'code': journal_code, - 'type': line.get('account_type', 'bank'), - 'company_id': company.id, - 'analytic_journal_id': False, - 'currency_id': line.get('currency_id', False), - 'show_on_dashboard': True, - } - - @api.model def create(self, vals): + company_id = vals.get('company_id', self.env.user.company_id.id) if vals.get('type') in ('bank', 'cash'): + # If no code provided, loop to find next available journal code + if not vals.get('code'): + for num in xrange(1, 100): + # journal_code has a maximal size of 5, hence we can enforce the boundary num < 100 + journal_code = (vals['type'] == 'cash' and 'CSH' or 'BNK') + str(num) + journal = self.env['account.journal'].search([('code', '=', journal_code), ('company_id', '=', company_id)], limit=1) + if not journal: + vals['code'] = journal_code + break + else: + raise UserError(_("Cannot generate an unused journal code. Please fill the 'Shortcode' field.")) + + # Create a default debit/credit account if not given default_account = vals.get('default_debit_account_id') or vals.get('default_credit_account_id') if not default_account: - company = self.env['res.company'].browse(vals['company_id']) + company = self.env['res.company'].browse(company_id) account_vals = self._prepare_liquidity_account(vals.get('name'), company, vals.get('currency_id'), vals.get('type')) default_account = self.env['account.account'].create(account_vals) vals['default_debit_account_id'] = default_account.id vals['default_credit_account_id'] = default_account.id + # We just need to create the relevant sequences according to the chosen options if not vals.get('sequence_id'): vals.update({'sequence_id': self.sudo()._create_sequence(vals).id}) if vals.get('refund_sequence') and not vals.get('refund_sequence_id'): vals.update({'refund_sequence_id': self.sudo()._create_sequence(vals, refund=True).id}) - return super(AccountJournal, self).create(vals) + + journal = super(AccountJournal, self).create(vals) + + # Create the bank_account_id if necessary + if journal.type == 'bank' and not journal.bank_account_id and vals.get('bank_acc_number'): + journal.set_bank_account(vals.get('bank_acc_number'), vals.get('bank_id')) + + return journal + + def set_bank_account(self, acc_number, bank_id=None): + """ Create a res.partner.bank and set it as value of the field bank_account_id """ + self.ensure_one() + self.bank_account_id = self.env['res.partner.bank'].create({ + 'acc_number': acc_number, + 'bank_id': bank_id, + 'company_id': self.company_id.id, + 'currency_id': self.currency_id.id, + 'partner_id': self.company_id.partner_id.id, + }).id @api.multi @api.depends('name', 'currency_id', 'company_id', 'company_id.currency_id') @@ -419,6 +442,19 @@ class AccountJournal(models.Model): journal.at_least_one_outbound = bool(len(self.outbound_payment_method_ids)) +class ResPartnerBank(models.Model): + _inherit = "res.partner.bank" + + journal_id = fields.One2many('account.journal', 'bank_account_id', domain=[('type', '=', 'bank')], string='Account Journal', readonly=True, + help="The accounting journal corresponding to this bank account.") + + @api.one + @api.constrains('journal_id') + def _check_journal_id(self): + if len(self.journal_id) > 1: + raise ValidationError(_('A bank account can anly belong to one journal.')) + + #---------------------------------------------------------- # Tax #---------------------------------------------------------- diff --git a/addons/account/models/account_bank.py b/addons/account/models/account_bank.py deleted file mode 100644 index 96b69d81bab..00000000000 --- a/addons/account/models/account_bank.py +++ /dev/null @@ -1,44 +0,0 @@ -# -*- coding: utf-8 -*- - -from openerp import api, fields, models - - -class Bank(models.Model): - _inherit = "res.partner.bank" - - journal_id = fields.Many2one('account.journal', string='Account Journal', - help="This journal will be created automatically for this bank account when you save the record") - - @api.model - def create(self, data): - result = super(Bank, self).create(data) - result.post_write() - return result - - @api.multi - def write(self, data): - result = super(Bank, self).write(data) - self.post_write() - return result - - @api.model - def _prepare_name(self, bank): - "Return the name to use when creating a bank journal" - name = bank.bank_name + ' ' if bank.bank_name else '' - name += bank.acc_number - return name - - @api.multi - def post_write(self): - JournalObj = self.env['account.journal'] - for bank in self: - # Create a journal for the bank account if it belongs to the company. - if bank.company_id and not bank.journal_id: - journal_vals = JournalObj._prepare_bank_journal(bank.company_id, {'acc_name': self._prepare_name(bank), 'currency_id': bank.currency_id.id, 'account_type': 'bank'}) - journal = JournalObj.create(journal_vals) - missing_vals = {'journal_id': journal.id} - if not bank.partner_id: - missing_vals['partner_id'] = bank.company_id.partner_id.id - if not bank.owner_name: - missing_vals['owner_name'] = bank.company_id.partner_id.name - bank.write(missing_vals) diff --git a/addons/account/models/account_journal_dashboard.py b/addons/account/models/account_journal_dashboard.py index 27e3bc8fb77..d4a8add628b 100644 --- a/addons/account/models/account_journal_dashboard.py +++ b/addons/account/models/account_journal_dashboard.py @@ -187,7 +187,8 @@ class account_journal(models.Model): 'sum_waiting': formatLang(self.env, sum_waiting or 0.0, currency_obj=self.currency_id or self.company_id.currency_id), 'sum_late': formatLang(self.env, sum_late or 0.0, currency_obj=self.currency_id or self.company_id.currency_id), 'currency_id': self.currency_id and self.currency_id.id or self.company_id.currency_id.id, - 'show_import': True if self.type in ['bank', 'cash'] and len(ac_bnk_stmt) == 0 and last_balance == 0 else False, + 'no_statement': bool(self.type in ['bank', 'cash'] and len(ac_bnk_stmt) == 0), + 'bank_statements_source': self.bank_statements_source, 'title': title, } @@ -220,7 +221,7 @@ class account_journal(models.Model): } @api.multi - def create_cash_bank(self): + def create_cash_statement(self): ctx = self._context.copy() ctx.update({'journal_id': self.id, 'default_journal_id': self.id, 'default_journal_type': 'cash'}) return { @@ -337,13 +338,12 @@ class account_journal(models.Model): return action @api.multi - def import_statement(self): - """return action to import bank/cash statements. This button should be called only on journals with type =='bank'""" - model = 'account.bank.statement' - action_name = 'action_account_bank_statement_import' - ir_model_obj = self.pool['ir.model.data'] - model, action_id = ir_model_obj.get_object_reference(self._cr, self._uid, 'account_bank_statement_import', action_name) - action = self.pool[model].read(self._cr, self._uid, action_id, context=self.env.context) - # Note: this drops action['context'], which is a dict stored as a string, which is not easy to update - action.update({'context': (u"{'journal_id': " + str(self.id) + u"}")}) + def create_bank_statement(self): + """return action to create a bank statements. This button should be called only on journals with type =='bank'""" + self.bank_statements_source = 'manual' + action = self.env.ref('account.action_bank_statement_tree').read()[0] + action.update({ + 'views': [[False, 'form']], + 'context': "{'default_journal_id': " + str(self.id) + "}", + }) return action diff --git a/addons/account/models/chart_template.py b/addons/account/models/chart_template.py index 12b4433bd70..d9eebe31e1c 100644 --- a/addons/account/models/chart_template.py +++ b/addons/account/models/chart_template.py @@ -751,42 +751,21 @@ class WizardMultiChartsAccounts(models.TransientModel): def _create_bank_journals_from_o2m(self, company, acc_template_ref): ''' This function creates bank journals and its accounts for each line encoded in the field bank_account_ids of the - wizard. + wizard (which is currently only used to create a default bank and cash journal when the CoA is installed). - :param company_id: the id of the company for which the wizard is running. + :param company: the company for which the wizard is running. :param acc_template_ref: the dictionary containing the mapping between the ids of account templates and the ids of the accounts that have been generated from them. - :return: True ''' self.ensure_one() - # Build a list with all the data to process - journal_data = [] - if self.bank_account_ids: - for acc in self.bank_account_ids: - vals = { - 'acc_name': acc.acc_name, - 'account_type': acc.account_type, - 'currency_id': acc.currency_id.id, - } - journal_data.append(vals) - - for line in journal_data: - if line['account_type'] == 'bank': - #create the bank account that will trigger the journal and account.account creation - res_partner_bank_vals = { - 'acc_number': line['acc_name'], - 'currency_id': line['currency_id'], - 'company_id': company.id, - 'owner_name': company.partner_id.name, - 'partner_id': company.partner_id.id, - 'footer': True - } - self.env['res.partner.bank'].create(res_partner_bank_vals) - else: - #create the cash journal that will trigger the account.account creation - vals_journal = self.env['account.journal']._prepare_bank_journal(company, line) - self.env['account.journal'].create(vals_journal) - return True + # Create the journals that will trigger the account.account creation + for acc in self.bank_account_ids: + self.env['account.journal'].create({ + 'name': acc.acc_name, + 'type': acc.account_type, + 'company_id': company.id, + 'currency_id': acc.currency_id.id, + }) class AccountBankAccountsWizard(models.TransientModel): diff --git a/addons/account/models/company.py b/addons/account/models/company.py index 0b3c0a8d1a9..d329003480b 100644 --- a/addons/account/models/company.py +++ b/addons/account/models/company.py @@ -34,6 +34,8 @@ class ResCompany(models.Model): property_stock_account_input_categ_id = fields.Many2one('account.account', oldname="property_stock_account_input_categ") property_stock_account_output_categ_id = fields.Many2one('account.account', oldname="property_stock_account_output_categ") property_stock_valuation_account_id = fields.Many2one('account.account') + bank_journal_ids = fields.One2many('account.journal', 'company_id', domain=[('type', '=', 'bank')], string='Bank Journals') + @api.multi def compute_fiscalyear_dates(self, date): diff --git a/addons/account/models/res_config.py b/addons/account/models/res_config.py index da44469b2cb..dc19a8dd3ac 100644 --- a/addons/account/models/res_config.py +++ b/addons/account/models/res_config.py @@ -174,14 +174,9 @@ class AccountConfigSettings(models.TransientModel): return True @api.multi - def open_company_form(self): - return { - 'type': 'ir.actions.act_window', - 'name': 'Configure your Company', - 'res_model': 'res.company', - 'res_id': self.company_id.id, - 'view_mode': 'form', - } + def open_bank_accounts(self): + action_rec = self.env['ir.model.data'].xmlid_to_object('account.action_account_bank_journal_form') + return action_rec.read([])[0] @api.multi def set_transfer_account(self): diff --git a/addons/account/report/inherited_layouts.xml b/addons/account/report/inherited_layouts.xml new file mode 100644 index 00000000000..290c1ba3a23 --- /dev/null +++ b/addons/account/report/inherited_layouts.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> +<data> + +<template id="external_layout_footer" inherit_id="report.external_layout_footer"> + <xpath expr="//ul[@name='financial_infos']" position="inside"> + <t t-if="not company.custom_footer"> + <t t-set="is_first" t-value="True"/> + <t t-foreach="company.bank_journal_ids" t-as="journal"> + <t t-set="bank_acc" t-value="journal.bank_account_id"/> + <t t-if="journal.display_on_footer"> + <li t-if="(is_first and company.vat) or (not is_first)">&bull;</li> + <li><span t-esc="bank_acc.bank_name"/><t t-if="bank_acc.bank_bic"> (<span t-esc="bank_acc.bank_bic"/>)</t>: <span t-esc="bank_acc.acc_number"/></li> + <t t-set="is_first" t-value="False"/> + </t> + </t> + </t> + </xpath> +</template> + +</data> +</openerp> diff --git a/addons/account/static/src/less/account_journal_dashboard.less b/addons/account/static/src/less/account_journal_dashboard.less index e0b15be344c..5e86c31d237 100644 --- a/addons/account/static/src/less/account_journal_dashboard.less +++ b/addons/account/static/src/less/account_journal_dashboard.less @@ -6,6 +6,10 @@ min-width: inherit; } + button { + margin-bottom: 5px; + } + .o_kanban_card_settings { padding: 8px; diff --git a/addons/account/test/account_bank_statement.yml b/addons/account/test/account_bank_statement.yml index cadb1f2fcd3..bd668e07a13 100644 --- a/addons/account/test/account_bank_statement.yml +++ b/addons/account/test/account_bank_statement.yml @@ -2,13 +2,6 @@ !context journal_type: 'bank' - - We create a new bank with the company as partner in order to be sure to have at least one bank journal -- - !record {model: res.partner.bank, id: test_bank}: - acc_number: BE123456789 - bank_name: BNP Test bank - partner_id: base.main_partner -- In order to test Bank Statement feature of account I create a bank statement line and confirm it and check it's move created - I select the period and journal for the bank statement diff --git a/addons/account/test/account_minimal_test.xml b/addons/account/test/account_minimal_test.xml index 2d495188db0..9760eb53757 100644 --- a/addons/account/test/account_minimal_test.xml +++ b/addons/account/test/account_minimal_test.xml @@ -237,6 +237,12 @@ <field name="refund_sequence" eval="True"/> </record> + <record id="bank_account" model="res.partner.bank"> + <field name="acc_number">987654321</field> + <field name="acc_type">bank</field> + <field name="bank_name">Bank</field> + <field name="partner_id" ref="base.main_partner"/> + </record> <record id="bank_journal" model="account.journal"> <field name="name">Bank - Test</field> <field name="code">TBNK</field> @@ -244,6 +250,7 @@ <field name="default_debit_account_id" ref="bnk"/> <field name="default_credit_account_id" ref="bnk"/> <field name="analytic_journal_id" ref="analytic.sit"/> + <field name="bank_account_id" ref="bank_account"/> </record> <record id="cash_journal" model="account.journal"> <field name="name">Cash - Test</field> @@ -271,6 +278,12 @@ <field name="show_on_dashboard" eval="False"/> </record> + <record id="bank_account_usd" model="res.partner.bank"> + <field name="acc_number">123456789</field> + <field name="acc_type">bank</field> + <field name="bank_name">Bank US</field> + <field name="partner_id" ref="base.main_partner"/> + </record> <record id="bank_journal_usd" model="account.journal"> <field name="name">USD Bank - Test</field> <field name="code">TUBK</field> @@ -278,6 +291,7 @@ <field name="default_debit_account_id" ref="usd_bnk"/> <field name="default_credit_account_id" ref="usd_bnk"/> <field name="currency_id" ref="base.USD"/> + <field name="bank_account_id" ref="bank_account_usd"/> </record> <record id="base.main_company" model="res.company"> <field name="currency_exchange_journal_id" ref="currency_diff_journal"/> diff --git a/addons/account/tests/test_account_customer_invoice.py b/addons/account/tests/test_account_customer_invoice.py index 0b79b47a0d9..565057d733d 100644 --- a/addons/account/tests/test_account_customer_invoice.py +++ b/addons/account/tests/test_account_customer_invoice.py @@ -8,13 +8,11 @@ class TestAccountCustomerInvoice(AccountTestUsers): # I will create bank detail with using manager access rights # because account manager can only create bank details. self.res_partner_bank_0 = self.env['res.partner.bank'].sudo(self.account_manager.id).create(dict( - state='bank', + acc_type='bank', company_id=self.main_company.id, partner_id=self.main_partner.id, acc_number='123456789', - footer=True, - bank=self.main_bank.id, - bank_name=self.main_bank.name, + bank_id=self.main_bank.id, )) # Test with that user which have rights to make Invoicing and payment and who is accountant. diff --git a/addons/account/tests/test_payment.py b/addons/account/tests/test_payment.py index fba65d6f54c..00fcc9d7d3c 100644 --- a/addons/account/tests/test_payment.py +++ b/addons/account/tests/test_payment.py @@ -26,15 +26,11 @@ class TestPayment(AccountingTestCase): self.account_payable = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_payable').id)], limit=1) self.account_revenue = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_revenue').id)], limit=1) - self.bank_euro = self.env['res.partner.bank'].create({'acc_number': '0123456789', 'bank_name': 'Test Bank', 'company_id': self.env.user.company_id.id}) - self.bank_journal_euro = self.bank_euro.journal_id + self.bank_journal_euro = self.env['account.journal'].create({'name': 'Bank', 'type': 'bank', 'code': 'BNK67'}) self.account_eur = self.bank_journal_euro.default_debit_account_id - self.bank_usd = self.env['res.partner.bank'].create({'acc_number': '0123456789', 'bank_name': 'Test Bank USD', 'company_id': self.env.user.company_id.id}) - self.bank_journal_usd = self.bank_usd.journal_id + self.bank_journal_usd = self.env['account.journal'].create({'name': 'Bank US', 'type': 'bank', 'code': 'BNK68', 'currency_id': self.currency_usd_id}) self.account_usd = self.bank_journal_usd.default_debit_account_id - self.account_usd.write({'currency_id': self.currency_usd_id}) - self.bank_journal_usd.write({'currency_id': self.currency_usd_id}) self.transfer_account = self.env['res.users'].browse(self.env.uid).company_id.transfer_account_id self.diff_income_account = self.env['res.users'].browse(self.env.uid).company_id.income_currency_exchange_account_id diff --git a/addons/account/tests/test_reconciliation.py b/addons/account/tests/test_reconciliation.py index e155923e20a..a11987dae07 100644 --- a/addons/account/tests/test_reconciliation.py +++ b/addons/account/tests/test_reconciliation.py @@ -28,16 +28,12 @@ class TestReconciliation(AccountingTestCase): self.account_rsa = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_payable').id)], limit=1) self.product = self.env.ref("product.product_product_4") - self.bank_euro = self.env['res.partner.bank'].create({'acc_number': 'Reconciliation test', 'bank_name': 'Test Bank', 'company_id': self.env.user.company_id.id}) - self.bank_journal_euro = self.bank_euro.journal_id + self.bank_journal_euro = self.env['account.journal'].create({'name': 'Bank', 'type': 'bank', 'code': 'BNK67'}) self.account_euro = self.bank_journal_euro.default_debit_account_id - self.bank_usd = self.env['res.partner.bank'].create({'acc_number': 'Reconciliation test USD', - 'bank_name': 'Test Bank USD', 'company_id': self.env.user.company_id.id}) - self.bank_journal_usd = self.bank_usd.journal_id + self.bank_journal_usd = self.env['account.journal'].create({'name': 'Bank US', 'type': 'bank', 'code': 'BNK68', 'currency_id': self.currency_usd_id}) self.account_usd = self.bank_journal_usd.default_debit_account_id - self.account_usd.write({'currency_id': self.currency_usd_id}) - self.bank_journal_usd.write({'currency_id': self.currency_usd_id}) + self.diff_income_account = self.env['res.users'].browse(self.env.uid).company_id.income_currency_exchange_account_id self.diff_expense_account = self.env['res.users'].browse(self.env.uid).company_id.expense_currency_exchange_account_id diff --git a/addons/account/views/account_bank_view.xml b/addons/account/views/account_bank_view.xml deleted file mode 100644 index d125251c360..00000000000 --- a/addons/account/views/account_bank_view.xml +++ /dev/null @@ -1,62 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<openerp> -<data> - - <!-- - Bank Accounts - --> - - <record id="view_partner_bank_form_inherit" model="ir.ui.view"> - <field name="name">Partner Bank Accounts - Journal</field> - <field name="model">res.partner.bank</field> - <field name="inherit_id" ref="base.view_partner_bank_form"/> - <field name="arch" type="xml"> - <group name="bank" position="after"> - <group name="accounting" col="2" colspan="2" attrs="{'invisible': [('company_id','=', False)]}"> - <separator string="Accounting Information" colspan="2"/> - <field name="journal_id"/> - <field name="currency_id" groups="base.group_multi_currency" options="{'no_create': True}"/> - </group> - </group> - </field> - </record> - - - <record id="view_partner_bank_tree_add_currency" model="ir.ui.view"> - <field name="name">Partner Bank Accounts - Add currency on tree</field> - <field name="model">res.partner.bank</field> - <field name="inherit_id" ref="base.view_partner_bank_tree"/> - <field name="arch" type="xml"> - <field name="acc_number" position="after"> - <field name="currency_id" options="{'no_create': True}" groups="base.group_multi_currency"/> - </field> - </field> - </record> - - - <record id="action_bank_tree" model="ir.actions.act_window"> - <field name="name">Bank Accounts</field> - <field name="res_model">res.partner.bank</field> - <field name="view_type">form</field> - <field name="view_mode">tree,form</field> - <field name="context" eval="{'default_partner_id':ref('base.main_partner'), 'company_hide':False, 'search_default_my_bank':1}"/> - <field name="help" type="html"> - <p class="oe_view_nocontent_create"> - Click to setup a new bank account. - </p><p> - Configure your company's bank account and select those that must - appear on the report footer. - </p><p> - If you use the accounting application of Odoo, journals and - accounts will be created automatically based on these data. - </p> - </field> - </record> - <menuitem - sequence="99" - parent="account.account_account_menu" - id="menu_action_bank_tree" - action="action_bank_tree"/> - -</data> -</openerp> diff --git a/addons/account/views/account_journal_dashboard_view.xml b/addons/account/views/account_journal_dashboard_view.xml index aa4dc1a2187..3d343f7ab84 100644 --- a/addons/account/views/account_journal_dashboard_view.xml +++ b/addons/account/views/account_journal_dashboard_view.xml @@ -71,12 +71,12 @@ <div class="o_kanban_card_manage_title"> <span>New</span> </div> - <div> + <div name="bank_cash_commands"> <t t-if="journal_type == 'bank'"> - <a type="object" name="import_statement">Import Statement</a> + <a type="object" name="create_bank_statement">Transaction</a> </t> <t t-if="journal_type == 'cash'"> - <a type="object" name="create_cash_bank">Transaction</a> + <a type="object" name="create_cash_statement">Transaction</a> </t> </div> <div> @@ -200,12 +200,30 @@ </t> <t t-name="JournalBodyBankCash"> - <t t-if="!dashboard.show_import"> - <div class="col-xs-6 o_kanban_primary_left"> - <button t-if="dashboard.number_to_reconcile > 0" type="object" name="action_open_reconcile" class="btn btn-primary btn-sm"> - Reconcile <t t-esc="dashboard.number_to_reconcile"/> Items - </button> - </div> + <!-- On the left, display : + - A button corresponding to the bank_statements_source, if it wasn't configured, a button for each of them + - If there are statements to reconcile, a link to reconcile them --> + <div class="col-xs-6 o_kanban_primary_left"> + <t t-if="journal_type == 'bank'"> + <t t-if="! dashboard.bank_statements_source"> + <div name="bank_journal_default_cta"> + <button type="object" name="create_bank_statement" class="btn btn-primary btn-sm">Create Statement</button> + </div> + </t> + <t t-if="dashboard.bank_statements_source"> + <div name="bank_journal_cta"> + <button t-if="dashboard.bank_statements_source == 'manual'" type="object" name="create_bank_statement" class="btn btn-primary btn-sm">Create Statement</button> + </div> + </t> + </t> + <button t-if="journal_type == 'cash'" type="object" name="create_cash_statement" class="btn btn-primary btn-sm">Register Transactions</button> + <br/> + <t t-if="!dashboard.no_statement and dashboard.number_to_reconcile > 0"> + <a type="object" name="action_open_reconcile" class="oe_inline"> Reconcile <t t-esc="dashboard.number_to_reconcile"/> Items</a> + </t> + </div> + <!-- On the right, show other common informations/actions --> + <t t-if="!dashboard.no_statement"> <div class="col-xs-6 o_kanban_primary_right"> <div class="row"> <div class="col-xs-6"> @@ -260,11 +278,7 @@ </t> <t t-name="JournalBodyGraph"> <div class="row"> - <div t-if="dashboard.show_import" class="col-xs-12 o_kanban_primary_left"> - <button t-if="journal_type == 'bank'" type="object" name="import_statement" class="btn btn-primary btn-sm">Import Statement</button> - <button t-if="journal_type == 'cash'" type="object" name="create_cash_bank" class="btn btn-primary btn-sm">Register Transactions</button> - </div> - <div t-if="!dashboard.show_import" class="o_kanban_graph_section"> + <div t-if="!dashboard.no_statement" class="o_kanban_graph_section"> <field name="kanban_dashboard_graph" t-att-graph_type="_.contains(['cash','bank'],journal_type) ? 'line' : 'bar'" widget="dashboard_graph"></field> </div> </div> diff --git a/addons/account/views/account_view.xml b/addons/account/views/account_view.xml index 65d1f101eed..79f7e3d36d4 100644 --- a/addons/account/views/account_view.xml +++ b/addons/account/views/account_view.xml @@ -215,6 +215,7 @@ <record id="view_account_journal_search" model="ir.ui.view"> <field name="name">account.journal.search</field> <field name="model">account.journal</field> + <field name="priority">1</field> <field name="arch" type="xml"> <search string="Search Account Journal"> <field name="name" filter_domain="['|', ('name','ilike',self), ('code','ilike',self)]" string="Journal"/> @@ -230,6 +231,7 @@ <record id="view_account_journal_form" model="ir.ui.view"> <field name="name">account.journal.form</field> <field name="model">account.journal</field> + <field name="priority">1</field> <field name="arch" type="xml"> <form string="Account Journal"> <sheet> @@ -249,7 +251,7 @@ <page name="journal_entries" string="Journal Entries"> <group> <group> - <field name="code"/> + <field name="code" attrs="{'readonly': ['|',('type', 'in', ['bank','cash']),('sequence_id','!=',False)], 'required': [('type', 'not in', ['bank', 'cash'])]}"/> <field name="refund_sequence" attrs="{'invisible': [('type', 'not in', ['sale', 'purchase'])]}"/> <field name="sequence_id" required="0" groups="base.group_no_one"/> <field name="refund_sequence_id" attrs="{'invisible': ['|',('type', 'not in', ['sale', 'purchase']), ('refund_sequence', '!=', True)]}" required="0" groups="base.group_no_one"/> @@ -279,11 +281,79 @@ </group> </group> </page> + <page name="bank_account" string="Bank Account" attrs="{'invisible': [('type', '!=', 'bank')]}"> + <group> + <group> + <field name="bank_acc_number"/> + <field name="bank_id"/> + <field name="display_on_footer"/> + </group> + <group > + <field name="bank_statements_source" widget="radio" attrs="{'required': [('type', '=', 'bank')]}"/> + </group> + </group> + </page> </notebook> </sheet> </form> </field> </record> + + <record id="view_account_bank_journal_tree" model="ir.ui.view"> + <field name="name">account.bank.journal.tree</field> + <field name="model">account.journal</field> + <field name="priority">10</field> + <field name="arch" type="xml"> + <tree string="Account Journal"> + <field name='sequence' widget='handle'/> + <field name="name"/> + <field name="display_on_footer"/> + <field name="company_id" groups="base.group_multi_company"/> + </tree> + </field> + </record> + <record id="view_account_bank_journal_form" model="ir.ui.view"> + <field name="name">account.bank.journal.form</field> + <field name="model">account.journal</field> + <field name="priority">10</field> + <field name="arch" type="xml"> + <form string="Bank Account"> + <sheet> + <field name="type" invisible="1"/> + <field name="name" invisible="1"/> + <div class="oe_title"> + <label for="bank_acc_number" required="1" class="oe_edit_only"/> + <h1><field name="bank_acc_number" required="1"/></h1> + </div> + <group col="4"> + <field name="bank_id"/> + <field name="display_on_footer"/> + <field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/> + <field name="currency_id" options="{'no_create': True}" groups="base.group_multi_currency"/> + <field name="inbound_payment_method_ids" widget="many2many_checkboxes"/> + <field name="outbound_payment_method_ids" widget="many2many_checkboxes"/> + </group> + </sheet> + </form> + </field> + </record> + <record id="action_account_bank_journal_form" model="ir.actions.act_window"> + <field name="name">Bank Accounts</field> + <field name="res_model">account.journal</field> + <field name="view_mode">tree,form</field> + <field name="view_ids" eval="[(5, 0, 0), + (0, 0, {'view_mode': 'tree', 'view_id': ref('view_account_bank_journal_tree')}), + (0, 0, {'view_mode': 'form', 'view_id': ref('view_account_bank_journal_form')})]"/> + <field name="domain">[('type', '=', 'bank')]</field> + <field name="context">{'default_type': 'bank', 'default_display_on_footer': True}</field> + <field name="help" type="html"> + <p class="oe_view_nocontent_create"> + Click to add a bank account. + </p> + </field> + </record> + <menuitem action="action_account_bank_journal_form" id="menu_action_account_bank_journal_form" parent="account_account_menu"/> + <record id="action_account_journal_form" model="ir.actions.act_window"> <field name="name">Journals</field> <field name="res_model">account.journal</field> diff --git a/addons/account/views/res_config_view.xml b/addons/account/views/res_config_view.xml index 6fc3552e650..2160a3a7d02 100644 --- a/addons/account/views/res_config_view.xml +++ b/addons/account/views/res_config_view.xml @@ -164,7 +164,7 @@ <div> <div name='company_footer'> <label for="company_footer"/> - <button name="open_company_form" type="object" + <button name="open_bank_accounts" type="object" string="Configure your company bank accounts" icon="gtk-go-forward" class="oe_inline oe_link"/> <field name="company_footer"/> diff --git a/addons/account_bank_statement_import/__init__.py b/addons/account_bank_statement_import/__init__.py index a72befb7117..65797c0f6e3 100644 --- a/addons/account_bank_statement_import/__init__.py +++ b/addons/account_bank_statement_import/__init__.py @@ -1,5 +1,6 @@ # -*- encoding: utf-8 -*- import account_bank_statement_import +import account_journal import wizard diff --git a/addons/account_bank_statement_import/account_bank_statement_import.py b/addons/account_bank_statement_import/account_bank_statement_import.py index c9ca9c19388..6ea8c4d266c 100644 --- a/addons/account_bank_statement_import/account_bank_statement_import.py +++ b/addons/account_bank_statement_import/account_bank_statement_import.py @@ -4,6 +4,7 @@ import base64 from openerp import api, fields, models, _ from openerp.exceptions import UserError +from openerp.addons.base.res.res_bank import sanitize_account_number import logging _logger = logging.getLogger(__name__) @@ -30,20 +31,17 @@ class AccountBankStatementImport(models.TransientModel): def import_file(self): """ Process the file chosen in the wizard, create bank statement(s) and go to reconciliation. """ self.ensure_one() - #set the active_id in the context, so that any extension module could - #reuse the fields chosen in the wizard if needed (see .QIF for example) - data_file = self.data_file # Let the appropriate implementation module parse the file and return the required data # The active_id is passed in context in case an implementation module requires information about the wizard state (see QIF) - currency_code, account_number, stmts_vals = self.with_context(active_id=self.ids[0])._parse_file(base64.b64decode(data_file)) + currency_code, account_number, stmts_vals = self.with_context(active_id=self.ids[0])._parse_file(base64.b64decode(self.data_file)) # Check raw data self._check_parsed_data(stmts_vals) # Try to find the currency and journal in odoo - currency, journal, bank_account = self._find_additional_data(currency_code, account_number) + currency, journal = self._find_additional_data(currency_code, account_number) # If no journal found, ask the user about creating one if not journal: # The active_id is passed in context so the wizard can call import_file again once the journal is created - return self.with_context(active_id=self.ids[0])._journal_creation_wizard(currency, account_number, bank_account) + return self.with_context(active_id=self.ids[0])._journal_creation_wizard(currency, account_number) # Prepare statement data to be used for bank statements creation stmts_vals = self._complete_stmts_vals(stmts_vals, journal, account_number) # Create the bank statements @@ -60,8 +58,8 @@ class AccountBankStatementImport(models.TransientModel): 'type': 'ir.actions.client', } - def _journal_creation_wizard(self, currency, account_number, bank_account): - """ Calls a wizard that allows the user to accept/refuse journal creation """ + def _journal_creation_wizard(self, currency, account_number): + """ Calls a wizard that allows the user to carry on with journal creation """ return { 'name': _('Journal Creation'), 'type': 'ir.actions.act_window', @@ -71,10 +69,11 @@ class AccountBankStatementImport(models.TransientModel): 'target': 'new', 'context': { 'statement_import_transient_id': self.env.context['active_id'], - 'default_currency_id': currency and currency.id or False, - 'default_account_number': account_number, - 'bank_account_id': bank_account and bank_account.id or False, + 'default_bank_acc_number': account_number, 'default_name': _('Bank') + ' ' + account_number, + 'default_currency_id': currency and currency.id or False, + 'default_type': 'bank', + 'default_bank_statements_source': 'file_import', } } @@ -118,10 +117,13 @@ class AccountBankStatementImport(models.TransientModel): raise UserError(_('This file doesn\'t contain any transaction.')) def _find_additional_data(self, currency_code, account_number): - """ Get the res.currency ID and the res.partner.bank ID """ + """ Look for a res.currency and account.journal using values extracted from the + statement and make sure it's consistent. + """ company_currency = self.env.user.company_id.currency_id journal_obj = self.env['account.journal'] currency = None + sanitized_account_number = sanitize_account_number(account_number) if currency_code: currency = self.env['res.currency'].search([('name', '=ilike', currency_code)], limit=1) @@ -130,31 +132,20 @@ class AccountBankStatementImport(models.TransientModel): if currency == company_currency: currency = False - bank_account = None - if account_number and len(account_number) > 4: - bank_account = self.env['res.partner.bank'].search([('acc_number', '=', account_number)], limit=1) - - # Find the journal from context or bank account - journal = journal_obj.browse(self._context.get('journal_id', [])) - if bank_account: - if journal: - if bank_account.journal_id and bank_account.journal_id != journal: - raise UserError(_('The account of this statement is linked to another journal.')) - if not bank_account.journal_id: - bank_account.write({'journal_id': journal.id}) - else: - if bank_account.journal_id: - journal = bank_account.journal_id - - # If importing into an existing journal + journal = journal_obj.browse(self.env.context.get('journal_id', [])) + if account_number: + # No bank account on the journal : create one from the account number of the statement + if journal and not journal.bank_account_id: + journal.set_bank_account(account_number) + # Already a bank account on the journal : check it's the same as on the statement + elif journal and journal.bank_account_id.sanitized_acc_number != sanitized_account_number: + raise UserError(_('The account of this statement (%s) is not the same as the journal (%s).') % (account_number, journal.bank_account_id.acc_number)) + # No journal passed to the wizard : try to find one using the account number of the statement + elif not journal: + journal = journal_obj.search([('bank_account_id.sanitized_acc_number', '=', sanitized_account_number)]) + + # If importing into an existing journal, its currency must be the same as the bank statement if journal: - # The bank account cannot belong to another journal - if not bank_account and account_number: - journal_account = self.env['res.partner.bank'].search([('journal_id', '=', journal.id)], limit=1) - if journal_account: - raise UserError(_('You are importing a file from account %s while the account of journal %s is %s.') % (account_number, journal.name, journal_account.acc_number)) - - # Its currency must be the same as the bank statement journal_currency = journal.currency_id if currency == None: currency = journal_currency @@ -167,19 +158,7 @@ class AccountBankStatementImport(models.TransientModel): if not journal and not account_number: raise UserError(_('Cannot find in which journal import this statement. Please manually select a journal.')) - return currency, journal, bank_account - - def _create_bank_account(self, account_number): - try: - bank_type = self.env.ref('bank.bank_normal') - bank_code = bank_type.code - except ValueError: - bank_code = 'bank' - account_number = account_number.replace(' ', '').replace('-', '') - return self.env['res.partner.bank'].create({ - 'acc_number': account_number, - 'state': bank_code, - }) + return currency, journal def _complete_stmts_vals(self, stmts_vals, journal, account_number): for st_vals in stmts_vals: @@ -188,7 +167,8 @@ class AccountBankStatementImport(models.TransientModel): for line_vals in st_vals['transactions']: unique_import_id = line_vals.get('unique_import_id') if unique_import_id: - line_vals['unique_import_id'] = (account_number and account_number + '-' or '') + unique_import_id + sanitized_account_number = sanitize_account_number(account_number) + line_vals['unique_import_id'] = (sanitized_account_number and sanitized_account_number + '-' or '') + unique_import_id if not line_vals.get('bank_account_id'): # Find the partner and his bank account or create the bank account. The partner selected during the @@ -202,9 +182,7 @@ class AccountBankStatementImport(models.TransientModel): bank_account_id = partner_bank.id partner_id = partner_bank.partner_id.id else: - #do not pass the journal_id in _create_bank_account() because we don't want to link - #that bank_account to the journal (it belongs to a partner, not to the company) - bank_account_id = self._create_bank_account(identifying_string).id + bank_account_id = self.env['res.partner.bank'].create({'acc_number': line_vals['account_number']}).id line_vals['partner_id'] = partner_id line_vals['bank_account_id'] = bank_account_id @@ -221,7 +199,7 @@ class AccountBankStatementImport(models.TransientModel): for st_vals in stmts_vals: filtered_st_lines = [] for line_vals in st_vals['transactions']: - if not 'unique_import_id' in line_vals \ + if 'unique_import_id' not in line_vals \ or not line_vals['unique_import_id'] \ or not bool(BankStatementLine.sudo().search([('unique_import_id', '=', line_vals['unique_import_id'])], limit=1)): filtered_st_lines.append(line_vals) diff --git a/addons/account_bank_statement_import/account_bank_statement_import_view.xml b/addons/account_bank_statement_import/account_bank_statement_import_view.xml index ad708d30b63..d529f678028 100644 --- a/addons/account_bank_statement_import/account_bank_statement_import_view.xml +++ b/addons/account_bank_statement_import/account_bank_statement_import_view.xml @@ -30,4 +30,22 @@ <field name="view_id" ref="account_bank_statement_import_view"/> </record> + <record id="journal_dashboard_view_inherit" model="ir.ui.view"> + <field name="name">account.journal.dashboard.kanban.inherit</field> + <field name="model">account.journal</field> + <field name="inherit_id" ref="account.account_journal_dashboard_kanban_view"/> + <field name="arch" type="xml"> + <xpath expr='//div[@name="bank_journal_default_cta"]/button' position='after'> + <br/><button type="object" name="import_statement" class="btn btn-primary btn-sm">Import Statement</button> + </xpath> + <xpath expr='//div[@name="bank_journal_cta"]' position='inside'> + <button t-if="dashboard.bank_statements_source == 'file_import'" type="object" name="import_statement" class="btn btn-primary btn-sm">Import Statement</button> + </xpath> + <xpath expr='//div[@name="bank_cash_commands"]' position="after"> + <t t-if="journal_type == 'bank'"> + <a type="object" name="import_statement">Import Statement</a> + </t> + </xpath> + </field> + </record> </odoo> diff --git a/addons/account_bank_statement_import/account_journal.py b/addons/account_bank_statement_import/account_journal.py new file mode 100644 index 00000000000..35267234bc6 --- /dev/null +++ b/addons/account_bank_statement_import/account_journal.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +from openerp import models, fields, api + +class AccountJournal(models.Model): + _inherit = "account.journal" + + bank_statements_source = fields.Selection(selection_add=[("file_import", "File Import")]) + + @api.multi + def import_statement(self): + """return action to import bank/cash statements. This button should be called only on journals with type =='bank'""" + self.bank_statements_source = 'file_import' + model = 'account.bank.statement' + action_name = 'action_account_bank_statement_import' + ir_model_obj = self.pool['ir.model.data'] + model, action_id = ir_model_obj.get_object_reference(self._cr, self._uid, 'account_bank_statement_import', action_name) + action = self.pool[model].read(self._cr, self._uid, action_id, context=self.env.context) + # Note: this drops action['context'], which is a dict stored as a string, which is not easy to update + action.update({'context': (u"{'journal_id': " + str(self.id) + u"}")}) + return action diff --git a/addons/account_bank_statement_import/demo/partner_bank.xml b/addons/account_bank_statement_import/demo/partner_bank.xml index 47e8fa76f48..09c14d42308 100644 --- a/addons/account_bank_statement_import/demo/partner_bank.xml +++ b/addons/account_bank_statement_import/demo/partner_bank.xml @@ -3,35 +3,31 @@ <data> <record id="ofx_partner_bank_1" model="res.partner.bank"> - <field name="owner_name">Agrolait</field> <field name="acc_number">00987654321</field> <field name="partner_id" ref="base.res_partner_2"></field> - <field name="state">bank</field> - <field name="bank" ref="base.res_bank_1"/> + <field name="acc_type">bank</field> + <field name="bank_id" ref="base.res_bank_1"/> </record> <record id="ofx_partner_bank_2" model="res.partner.bank"> - <field name="owner_name">China Export</field> <field name="acc_number">00987654322</field> <field name="partner_id" ref="base.res_partner_3"></field> - <field name="state">bank</field> - <field name="bank" ref="base.res_bank_1"/> + <field name="acc_type">bank</field> + <field name="bank_id" ref="base.res_bank_1"/> </record> <record id="qif_partner_bank_1" model="res.partner.bank"> - <field name="owner_name">Delta PC</field> <field name="acc_number">10987654320</field> <field name="partner_id" ref="base.res_partner_4"></field> - <field name="state">bank</field> - <field name="bank" ref="base.res_bank_1"/> + <field name="acc_type">bank</field> + <field name="bank_id" ref="base.res_bank_1"/> </record> <record id="qif_partner_bank_2" model="res.partner.bank"> - <field name="owner_name">Epic Technologies</field> <field name="acc_number">10987654322</field> <field name="partner_id" ref="base.res_partner_5"></field> - <field name="state">bank</field> - <field name="bank" ref="base.res_bank_1"/> + <field name="acc_type">bank</field> + <field name="bank_id" ref="base.res_bank_1"/> </record> </data> diff --git a/addons/account_bank_statement_import/wizard/journal_creation.py b/addons/account_bank_statement_import/wizard/journal_creation.py index 3acd180ac50..82417ecb661 100644 --- a/addons/account_bank_statement_import/wizard/journal_creation.py +++ b/addons/account_bank_statement_import/wizard/journal_creation.py @@ -6,37 +6,10 @@ class account_bank_statement_import_journal_creation(models.TransientModel): _name = 'account.bank.statement.import.journal.creation' _description = 'Import Bank Statement Journal Creation Wizard' - name = fields.Char('Journal Name', required=True) - currency_id = fields.Many2one('res.currency', 'Currency', readonly=True) - account_number = fields.Char('Account Number', readonly=True) + journal_id = fields.Many2one('account.journal', delegate=True, required=True, ondelete='cascade') @api.multi def create_journal(self): - import_wiz_obj = self.env['account.bank.statement.import'] - journal_obj = self.env['account.journal'] - company = self.env.user.company_id - wiz = self[0] - currency_id = wiz.currency_id.id - account_number = wiz.account_number - - bank_account_id = self.env.context.get('bank_account_id') - if bank_account_id: - vals = {'currency_id': currency_id, 'acc_name': account_number, 'account_type': 'bank'} - vals_journal = journal_obj._prepare_bank_journal(company, vals) - journal = journal_obj.create(vals_journal) - self.env['res.partner.bank'].browse(bank_account_id).write({'journal_id': journal.id}) - else: - #create the bank account that will trigger the journal and account.account creation - res_partner_bank_vals = { - 'acc_number': account_number, - 'currency_id': currency_id, - 'company_id': company.id, - 'owner_name': company.partner_id.name, - 'partner_id': company.partner_id.id, - 'footer': True - } - self.env['res.partner.bank'].create(res_partner_bank_vals) - - # Finish the statement import - statement_import_transient = import_wiz_obj.browse(self.env.context['statement_import_transient_id']) - return statement_import_transient.import_file() + """ Create the journal (the record is automatically created in the process of calling this method) and reprocess the statement """ + statement_import_transient = self.env['account.bank.statement.import'].browse(self.env.context['statement_import_transient_id']) + return statement_import_transient.with_context(journal_id=self.journal_id.id).import_file() diff --git a/addons/account_bank_statement_import/wizard/journal_creation.xml b/addons/account_bank_statement_import/wizard/journal_creation.xml index e64b6db7a60..10c29546bf8 100644 --- a/addons/account_bank_statement_import/wizard/journal_creation.xml +++ b/addons/account_bank_statement_import/wizard/journal_creation.xml @@ -7,12 +7,18 @@ <field name="model">account.bank.statement.import.journal.creation</field> <field name="arch" type="xml"> <form string="Journal Creation"> - <p>The account of the statement you are importing is not yet recorded in Odoo. In order to proceed with the import, you need to create a bank account and journal using this form.</p> + <p>The account of the statement you are importing is not yet recorded in Odoo. In order to proceed with the import, you need to create a bank journal for this account.</p> <p>Just click OK to create the account/journal and finish the import. If this was a mistake, hit cancel to abort the import.</p> <group> - <field name="name"/> - <field name="currency_id" options="{'no_create': True}" attrs="{'invisible': [('currency_id', '=', False)]}"/> - <field name="account_number"/> + <group> + <field name="name" string="Bank Journal Name"/> + <field name="bank_acc_number" readonly="1"/> + <field name="bank_id"/> + </group> + <group> + <field name="currency_id" readonly="1" groups="base.group_multi_currency"/> + <field name="company_id" groups="base.group_multi_company"/> + </group> </group> <footer> <button name="create_journal" string="OK" type="object" class="btn-primary"/> diff --git a/addons/base_iban/__openerp__.py b/addons/base_iban/__openerp__.py index de4b7dfee8a..d384795dc00 100644 --- a/addons/base_iban/__openerp__.py +++ b/addons/base_iban/__openerp__.py @@ -13,9 +13,8 @@ with a single statement. """, 'author': 'OpenERP SA', 'website': 'https://www.odoo.com', - 'depends': ['base'], + 'depends': ['account_accountant'], 'demo': ['demo/iban_demo.xml'], - 'data': ['base_iban_data.xml' , 'base_iban_view.xml'], 'installable': True, 'auto_install': False, } diff --git a/addons/base_iban/base_iban.py b/addons/base_iban/base_iban.py index ccc6b123daa..ba650a11b72 100644 --- a/addons/base_iban/base_iban.py +++ b/addons/base_iban/base_iban.py @@ -2,7 +2,7 @@ import re -from openerp import models, api, _ +from openerp import api, fields, models, _ from openerp.exceptions import UserError, ValidationError @@ -40,34 +40,45 @@ def validate_iban(iban): raise ValidationError(_("This IBAN does not pass the validation check, please verify it.")) -class res_partner_bank(models.Model): +class ResPartnerBank(models.Model): _inherit = "res.partner.bank" + @api.one + @api.depends('acc_type') + def _compute_acc_type(self): + try: + validate_iban(self.acc_number) + self.acc_type = 'iban' + except ValidationError: + super(ResPartnerBank, self)._compute_acc_type() + def get_bban(self): - if self.state != 'iban': + if self.acc_type != 'iban': raise UserError(_("Cannot compute the BBAN because the account number is not an IBAN.")) return get_bban_from_iban(self.acc_number) - def create(self, cr, uid, vals, context=None): - if (vals.get('state') == 'iban') and vals.get('acc_number'): + @api.model + def create(self, vals): + if (vals.get('acc_type') == 'iban') and vals.get('acc_number'): vals['acc_number'] = pretty_iban(normalize_iban(vals['acc_number'])) - return super(res_partner_bank, self).create(cr, uid, vals, context) + return super(ResPartnerBank, self).create(vals) - def write(self, cr, uid, ids, vals, context=None): - if (vals.get('state') == 'iban') and vals.get('acc_number'): + @api.model + def write(self, vals): + if (vals.get('acc_type') == 'iban') and vals.get('acc_number'): vals['acc_number'] = pretty_iban(normalize_iban(vals['acc_number'])) - return super(res_partner_bank, self).write(cr, uid, ids, vals, context) + return super(ResPartnerBank, self).write(vals) @api.one - @api.constrains('state', 'acc_number') + @api.constrains('acc_type', 'acc_number') def _check_iban(self): - if self.state == 'iban': + if self.acc_type == 'iban': validate_iban(self.acc_number) @api.one - @api.constrains('state', 'bank_bic') + @api.constrains('acc_type', 'bank_bic') def _check_bank_bic(self): - if self.state == 'iban' and not self.bank_bic: + if self.acc_type == 'iban' and not self.bank_bic: raise ValidationError(_("Please define BIC/Swift code on bank for bank type IBAN Account to make valid payments.")) diff --git a/addons/base_iban/base_iban_data.xml b/addons/base_iban/base_iban_data.xml deleted file mode 100644 index e4f520db4d5..00000000000 --- a/addons/base_iban/base_iban_data.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<openerp> - <data noupdate="1"> - - <!-- - Default bank account description - --> - <record id="bank_iban" model="res.partner.bank.type"> - <field name="name">IBAN Account</field> - <field name="code">iban</field> - <field name="format_layout">%(bank_name)s: IBAN %(acc_number)s - BIC %(bank_bic)s</field> - </record> - <record id="bank_swift_field" model="res.partner.bank.type.field"> - <field name="name">bank_bic</field> - <field name="bank_type_id" ref="bank_iban"/> - <field eval="False" name="required"/> - <field eval="False" name="readonly"/> - </record> - <record id="bank_country_field" model="res.partner.bank.type.field"> - <field name="name">country_id</field> - <field name="bank_type_id" ref="bank_iban"/> - <field eval="False" name="required"/> - <field eval="False" name="readonly"/> - </record> - <record id="bank_zip_field" model="res.partner.bank.type.field"> - <field name="name">zip</field> - <field name="bank_type_id" ref="bank_iban"/> - <field eval="False" name="required"/> - <field eval="False" name="readonly"/> - </record> - </data> -</openerp> diff --git a/addons/base_iban/base_iban_view.xml b/addons/base_iban/base_iban_view.xml deleted file mode 100644 index 30816750035..00000000000 --- a/addons/base_iban/base_iban_view.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<openerp> - <data> - - </data> -</openerp> diff --git a/addons/base_iban/demo/iban_demo.xml b/addons/base_iban/demo/iban_demo.xml index 602c2b4163c..4e1e247f410 100644 --- a/addons/base_iban/demo/iban_demo.xml +++ b/addons/base_iban/demo/iban_demo.xml @@ -3,26 +3,23 @@ <data noupdate="1"> <record id="bank_iban_asustek" model="res.partner.bank"> - <field name="state">iban</field> + <field name="acc_type">iban</field> <field name="acc_number">BE39103123456719</field> - <field name="bank_name">Crelan</field> - <field name="bank_bic">NICABEBB</field> + <field name="bank_id" ref="base.bank_crelan" /> <field name="partner_id" ref="base.res_partner_1"/> </record> <record id="bank_iban_china_export" model="res.partner.bank"> - <field name="state">iban</field> + <field name="acc_type">iban</field> <field name="acc_number">SI56191000000123438</field> - <field name="bank_name">CBC</field> - <field name="bank_bic">CREGBEBB</field> + <field name="bank_id" ref="base.bank_cbc" /> <field name="partner_id" ref="base.res_partner_3"/> </record> <record id="bank_iban_main_partner" model="res.partner.bank"> - <field name="state">iban</field> + <field name="acc_type">iban</field> <field name="acc_number">BE61310126985517</field> - <field name="bank_name">ING</field> - <field name="bank_bic">BBRUBEBB</field> + <field name="bank_id" ref="base.bank_ing" /> <field name="partner_id" ref="base.res_partner_4"/> </record> diff --git a/addons/crm/security/ir.model.access.csv b/addons/crm/security/ir.model.access.csv index 8d213a23edd..75dbdcb9268 100644 --- a/addons/crm/security/ir.model.access.csv +++ b/addons/crm/security/ir.model.access.csv @@ -11,7 +11,6 @@ access_res_partner,res.partner.crm.user,base.model_res_partner,base.group_sale_s access_res_partner_category,res.partner.category.crm.user,base.model_res_partner_category,base.group_sale_salesman,1,1,1,0
mail_mailgate_thread,mail.thread,mail.model_mail_thread,base.group_sale_salesman,1,1,1,1
access_base_action_rule_manager,base.action.rule manager,base_action_rule.model_base_action_rule,base.group_sale_manager,1,1,1,1
-access_res_partner_bank_type_crm_user,res.partner.bank.type.crm.user,base.model_res_partner_bank_type,base.group_sale_salesman,1,0,0,0
access_crm_lead_partner_manager,crm.lead.partner.manager,model_crm_lead,base.group_partner_manager,1,0,0,0
access_crm_lead_tag,crm_lead_tag,model_crm_lead_tag,base.group_user,1,0,0,0
access_crm_lead_tag_salesman,crm_lead_tag salesman,model_crm_lead_tag,base.group_sale_salesman,1,1,1,0
diff --git a/addons/hr_payroll_account/test/hr_payroll_account.yml b/addons/hr_payroll_account/test/hr_payroll_account.yml index 14390d358cc..7a7f73ce826 100644 --- a/addons/hr_payroll_account/test/hr_payroll_account.yml +++ b/addons/hr_payroll_account/test/hr_payroll_account.yml @@ -6,8 +6,8 @@ !record {model: res.partner.bank, id: res_partner_bank_0}: acc_number: '001-9876543-21' partner_id: base.res_partner_12 - state: bank - bank: base.res_bank_1 + acc_type: bank + bank_id: base.res_bank_1 - I create a new employee "John". - diff --git a/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py index b1aa436e5f3..01cda030979 100644 --- a/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py +++ b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py @@ -169,7 +169,7 @@ class hr_payslip_run(osv.osv): 'company_id': users[0].company_id.id, 'name': run.name, 'date': run.date_end, - 'bank_id': users[0].company_id.bank_ids and users[0].company_id.bank_ids[0].id or False + 'bank_id': users[0].company_id.partner_id.bank_ids and users[0].company_id.partner_id.bank_ids[0].id or False } advice_id = advice_pool.create(cr, uid, advice_data, context=context) slip_ids = [] diff --git a/addons/l10n_in_hr_payroll/test/payment_advice_batch.yml b/addons/l10n_in_hr_payroll/test/payment_advice_batch.yml index d1a1436b50b..9f3ec61dda4 100644 --- a/addons/l10n_in_hr_payroll/test/payment_advice_batch.yml +++ b/addons/l10n_in_hr_payroll/test/payment_advice_batch.yml @@ -6,8 +6,8 @@ !record {model: res.partner.bank, id: res_partner_bank_0}: acc_number: '3025632343043' partner_id: base.res_partner_21 - state: bank - bank: base.res_bank_1 + acc_type: bank + bank_id: base.res_bank_1 - I create a new employee “Rahul” - diff --git a/addons/l10n_in_hr_payroll/views/report_payrolladvice.xml b/addons/l10n_in_hr_payroll/views/report_payrolladvice.xml index 03bb91f567d..b242deae70b 100644 --- a/addons/l10n_in_hr_payroll/views/report_payrolladvice.xml +++ b/addons/l10n_in_hr_payroll/views/report_payrolladvice.xml @@ -25,7 +25,7 @@ <tr> <td> <p class="text-center"> - <strong>Payment Advice from <span t-esc="o.name"/> A/C no. <span t-esc="o.company_id.bank_ids and o.company_id.bank_ids[0].acc_number"/> form period <span t-esc="get_month(o.date)['from_name']"/> to <span t-esc="get_month(o.date)['to_name']"/></strong> + <strong>Payment Advice from <span t-esc="o.name"/> A/C no. <span t-esc="o.company_id.partner_id.bank_ids and o.company_id.partner_id.bank_ids[0].acc_number"/> form period <span t-esc="get_month(o.date)['from_name']"/> to <span t-esc="get_month(o.date)['to_name']"/></strong> </p> </td> </tr> @@ -89,4 +89,4 @@ </t> </template> </data> -</openerp>
\ No newline at end of file +</openerp> diff --git a/addons/membership/tests/test_membership.py b/addons/membership/tests/test_membership.py index d4fc16c60dc..717e166a29f 100644 --- a/addons/membership/tests/test_membership.py +++ b/addons/membership/tests/test_membership.py @@ -50,8 +50,8 @@ class TestMembership(TestMembershipCommon): 'membership: after opening the invoice, customer should be in invoiced status') # the invoice is paid -> customer goes to paid status - bank_journals = self.env['account.journal'].search([('type', '=', 'bank')]) - invoice.pay_and_reconcile(bank_journals[0], invoice.amount_total) + bank_journal = self.env['account.journal'].create({'name': 'Bank', 'type': 'bank', 'code': 'BNK67'}) + invoice.pay_and_reconcile(bank_journal, invoice.amount_total) self.assertEqual( self.partner_1.membership_state, 'paid', 'membership: after paying the invoice, customer should be in paid status') diff --git a/addons/payment_transfer/models/payment_acquirer.py b/addons/payment_transfer/models/payment_acquirer.py index b55fb055ab7..c691529aeae 100644 --- a/addons/payment_transfer/models/payment_acquirer.py +++ b/addons/payment_transfer/models/payment_acquirer.py @@ -23,10 +23,10 @@ class TransferPaymentAcquirer(osv.Model): return '/payment/transfer/feedback' def _format_transfer_data(self, cr, uid, context=None): - bank_ids = [bank.id for bank in self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.bank_ids] + company_id = self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.id # filter only bank accounts marked as visible - bank_ids = self.pool['res.partner.bank'].search(cr, uid, [('id', 'in', bank_ids), ('footer', '=', True)], context=context) - accounts = self.pool['res.partner.bank'].name_get(cr, uid, bank_ids, context=context) + journal_ids = self.pool['account.journal'].search(cr, uid, [('type', '=', 'bank'), ('display_on_footer', '=', True), ('company_id', '=', company_id)], context=context) + accounts = self.pool['account.journal'].browse(cr, uid, journal_ids, context=context).mapped('bank_account_id').name_get() bank_title = _('Bank Accounts') if len(accounts) > 1 else _('Bank Account') bank_accounts = ''.join(['<ul>'] + ['<li>%s</li>' % name for id, name in accounts] + ['</ul>']) post_msg = '''<div> diff --git a/addons/purchase/tests/test_purchase_to_invoice.py b/addons/purchase/tests/test_purchase_to_invoice.py index ff19456576d..3478df9c1d5 100644 --- a/addons/purchase/tests/test_purchase_to_invoice.py +++ b/addons/purchase/tests/test_purchase_to_invoice.py @@ -21,7 +21,7 @@ class TestPurchase(common.TransactionCase): self.env['product.product'].browse(product_id).product_tmpl_id.write({'property_account_expense_id': account_exp_id}) # Create Purchase Journal - self.env['account.journal'].create({'name': 'Purchase Journal - Test', 'code': 'PTPJ', 'type': 'purchase', 'company_id': company_id}) + self.env['account.journal'].create({'name': 'Purchase Journal - Test', 'code': 'PTPJ', 'type': 'purchase'}) # In order to test, I create new user and applied Invoicing & Payments group. user = self.env['res.users'].with_context({'no_reset_password': True}).create({ diff --git a/addons/report/views/layouts.xml b/addons/report/views/layouts.xml index 9aa9444257d..e6ade1e8d4d 100644 --- a/addons/report/views/layouts.xml +++ b/addons/report/views/layouts.xml @@ -92,16 +92,8 @@ <li t-if="company.website">Website: <span t-field="company.website"/></li> </ul> - <ul t-if="not company.custom_footer" class="list-inline"> + <ul t-if="not company.custom_footer" class="list-inline" name="financial_infos"> <li t-if="company.vat">TIN: <span t-field="company.vat"/></li> - <t t-set="is_first" t-value="True"/> - <t t-foreach="company.bank_ids" t-as="bank"> - <t t-if="bank.footer"> - <li t-if="(is_first and company.vat) or (not is_first)">&bull;</li> - <li><span t-esc="bank.bank_name"/>: (<span t-esc="bank.bank_bic"/>) <span t-esc="bank.acc_number"/></li> - <t t-set="is_first" t-value="False"/> - </t> - </t> </ul> <t t-if="company.custom_footer"> diff --git a/addons/sale/security/ir.model.access.csv b/addons/sale/security/ir.model.access.csv index bbcf14f53f4..acb83f33223 100644 --- a/addons/sale/security/ir.model.access.csv +++ b/addons/sale/security/ir.model.access.csv @@ -21,8 +21,6 @@ access_product_product_sale_user,product.product sale use,product.model_product_ access_account_tax_user,account.tax.user,account.model_account_tax,base.group_sale_salesman,1,0,0,0
access_ir_attachment_sales,ir.attachment.sales,base.model_ir_attachment,base.group_sale_salesman,1,1,1,0
access_ir_attachment_manager,ir.attachment.manager,base.model_ir_attachment,base.group_sale_manager,1,1,1,1
-access_res_partner_bank_type_user,res.partner.bank.type.user,base.model_res_partner_bank_type,base.group_sale_salesman,1,0,0,0
-access_res_partner_bank_type_field_user,res.partner.bank.type.field.user,base.model_res_partner_bank_type_field,base.group_sale_salesman,1,0,0,0
access_product_uom_user,product.uom.user,product.model_product_uom,base.group_sale_salesman,1,0,0,0
access_product_pricelist_sale_user,product.pricelist.sale.user,product.model_product_pricelist,base.group_sale_salesman,1,0,0,0
access_account_account_salesman,account_account salesman,account.model_account_account,base.group_sale_salesman,1,0,0,0
diff --git a/addons/sale/tests/test_sale_to_invoice.py b/addons/sale/tests/test_sale_to_invoice.py index c914bceeb5e..47b6a7b20bb 100644 --- a/addons/sale/tests/test_sale_to_invoice.py +++ b/addons/sale/tests/test_sale_to_invoice.py @@ -22,9 +22,6 @@ class TestSale(TestMail): group_id = IrModelData.xmlid_to_res_id('account.group_account_invoice') or False product_id = IrModelData.xmlid_to_res_id('product.product_category_3') or False company_id = IrModelData.xmlid_to_res_id('base.main_company') or False - company = self.env['res.company'].browse(company_id) - journal_vals = journal_obj._prepare_bank_journal(company, {'account_type': 'bank', 'acc_name': 'BNK'}) - journal = journal_obj.create(journal_vals) # Usefull accounts user_type_id = IrModelData.xmlid_to_res_id('account.data_account_type_revenue') diff --git a/addons/stock_dropshipping/tests/test_invoicing.py b/addons/stock_dropshipping/tests/test_invoicing.py index b93d7346bfb..ffe217b1db4 100644 --- a/addons/stock_dropshipping/tests/test_invoicing.py +++ b/addons/stock_dropshipping/tests/test_invoicing.py @@ -18,7 +18,7 @@ class TestCreateInvoice(TransactionCase): dropship_route = self.env.ref('stock_dropshipping.route_drop_shipping') # Create Sale Journal - self.env['account.journal'].create({'name': 'Purchase Journal - Test', 'code': 'DSTPJ', 'type': 'purchase', 'company_id': self.env.ref('base.main_company').id}) + self.env['account.journal'].create({'name': 'Purchase Journal - Test', 'code': 'DSTPJ', 'type': 'purchase'}) user_type_id = self.env.ref('account.data_account_type_payable') account_pay_id = self.env['account.account'].create({'code': 'X1012', 'name': 'Purchase - Test Payable Account', 'user_type_id': user_type_id.id, 'reconcile': True}) diff --git a/openerp/addons/base/__openerp__.py b/openerp/addons/base/__openerp__.py index 55137cc56f9..b601d49e64b 100644 --- a/openerp/addons/base/__openerp__.py +++ b/openerp/addons/base/__openerp__.py @@ -69,6 +69,7 @@ The kernel of OpenERP, needed for all installation. 'demo': [ 'base_demo.xml', 'res/res_currency_demo.xml', + 'res/res_bank_demo.xml', 'res/res_partner_demo.xml', 'res/res_partner_demo.yml', 'res/res_partner_image_demo.xml', diff --git a/openerp/addons/base/base_demo.xml b/openerp/addons/base/base_demo.xml index 2ca2d031037..bd538c36f45 100644 --- a/openerp/addons/base/base_demo.xml +++ b/openerp/addons/base/base_demo.xml @@ -13,6 +13,12 @@ <field name="country_id" ref="be"/> </record> + <record model="res.partner.bank" id="bank_partner_demo"> + <field name="acc_number">60-16-13 31926819</field> + <field name="acc_type">bank</field> + <field name="partner_id" ref="partner_demo" /> + </record> + <record id="main_partner" model="res.partner"> <field name="name">YourCompany</field> <field name="street">1725 Slough Ave.</field> @@ -28,12 +34,6 @@ <field name="name">YourCompany</field> </record> - <record model="res.partner.bank" id="bank_company"> - <field name="acc_number">BE123456789</field> - <field name="partner_id" ref="main_partner" /> - </record> - - <record id="user_demo" model="res.users"> <field name="partner_id" ref="base.partner_demo"/> <field name="login">demo</field> diff --git a/openerp/addons/base/res/res_bank.py b/openerp/addons/base/res/res_bank.py index fcffcbb8083..31af80d893c 100644 --- a/openerp/addons/base/res/res_bank.py +++ b/openerp/addons/base/res/res_bank.py @@ -1,215 +1,98 @@ # -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. -from openerp.osv import fields, osv -from openerp.tools.translate import _ -from openerp.exceptions import UserError +import re -class Bank(osv.osv): - _description='Bank' - _name = 'res.bank' - _order = 'name' - _columns = { - 'name': fields.char('Name', required=True), - 'street': fields.char('Street'), - 'street2': fields.char('Street2'), - 'zip': fields.char('Zip', change_default=True), - 'city': fields.char('City'), - 'state': fields.many2one("res.country.state", 'Fed. State', - domain="[('country_id', '=', country)]"), - 'country': fields.many2one('res.country', 'Country'), - 'email': fields.char('Email'), - 'phone': fields.char('Phone'), - 'fax': fields.char('Fax'), - 'active': fields.boolean('Active'), - 'bic': fields.char('Bank Identifier Code', - help="Sometimes called BIC or Swift."), - } - _defaults = { - 'active': lambda *a: 1, - } - def name_get(self, cr, uid, ids, context=None): - result = [] - for bank in self.browse(cr, uid, ids, context): - result.append((bank.id, (bank.bic and (bank.bic + ' - ') or '') + bank.name)) - return result +from openerp import api, fields, models, _ +from openerp.osv import expression +from openerp.exceptions import UserError -class res_partner_bank_type(osv.osv): - _description='Bank Account Type' - _name = 'res.partner.bank.type' - _order = 'name' - _columns = { - 'name': fields.char('Name', required=True, translate=True), - 'code': fields.char('Code', size=64, required=True), - 'field_ids': fields.one2many('res.partner.bank.type.field', 'bank_type_id', 'Type Fields'), - 'format_layout': fields.text('Format Layout', translate=True) - } - _defaults = { - 'format_layout': lambda *args: "%(bank_name)s: %(acc_number)s" - } +def sanitize_account_number(acc_number): + if acc_number: + return re.sub(r'\W+', '', acc_number).upper() + return False -class res_partner_bank_type_fields(osv.osv): - _description='Bank type fields' - _name = 'res.partner.bank.type.field' +class Bank(models.Model): + _description = 'Bank' + _name = 'res.bank' _order = 'name' - _columns = { - 'name': fields.char('Field Name', required=True, translate=True), - 'bank_type_id': fields.many2one('res.partner.bank.type', 'Bank Type', required=True, ondelete='cascade'), - 'required': fields.boolean('Required'), - 'readonly': fields.boolean('Readonly'), - 'size': fields.integer('Max. Size'), - } - -class res_partner_bank(osv.osv): - '''Bank Accounts''' - _name = "res.partner.bank" - _rec_name = "acc_number" - _description = __doc__ - _order = 'sequence' - - def _bank_type_get(self, cr, uid, context=None): - bank_type_obj = self.pool.get('res.partner.bank.type') + name = fields.Char(required=True) + street = fields.Char() + street2 = fields.Char() + zip = fields.Char() + city = fields.Char() + state = fields.Many2one('res.country.state', 'Fed. State', domain="[('country_id', '=', country)]") + country = fields.Many2one('res.country') + email = fields.Char() + phone = fields.Char() + fax = fields.Char() + active = fields.Boolean(default=True) + bic = fields.Char('Bank Identifier Code', select=True, help="Sometimes called BIC or Swift.") + + @api.multi + @api.depends('name', 'bic') + def name_get(self): result = [] - type_ids = bank_type_obj.search(cr, uid, []) - bank_types = bank_type_obj.browse(cr, uid, type_ids, context=context) - for bank_type in bank_types: - result.append((bank_type.code, bank_type.name)) + for bank in self: + name = bank.name + (bank.bic and (' - ' + bank.bic) or '') + result.append((bank.id, name)) return result - def _default_value(self, cursor, user, field, context=None): - if context is None: context = {} - if field in ('country_id', 'state_id'): - value = False - else: - value = '' - if not context.get('address'): - return value - - for address in self.pool.get('res.partner').resolve_2many_commands( - cursor, user, 'address', context['address'], ['type', field], context=context): - - if address.get('type') == 'contact': - return address.get(field, value) - elif not address.get('type'): - value = address.get(field, value) - return value - - _columns = { - 'acc_number': fields.char('Account Number', required=True), - 'bank': fields.many2one('res.bank', 'Bank'), - 'bank_bic': fields.char('Bank Identifier Code'), - 'bank_name': fields.char('Bank Name'), - 'owner_name': fields.char('Account Owner Name'), - 'street': fields.char('Street'), - 'zip': fields.char('Zip', change_default=True), - 'city': fields.char('City'), - 'country_id': fields.many2one('res.country', 'Country', - change_default=True), - 'state_id': fields.many2one("res.country.state", 'Fed. State', - change_default=True, domain="[('country_id','=',country_id)]"), - 'company_id': fields.many2one('res.company', 'Company', - ondelete='cascade', help="Only if this bank account belong to your company"), - 'partner_id': fields.many2one('res.partner', 'Account Holder', ondelete='cascade', select=True, domain=['|',('is_company','=',True),('parent_id','=',False)]), - 'state': fields.selection(_bank_type_get, 'Bank Account Type', - change_default=True), - 'sequence': fields.integer('Sequence'), - 'footer': fields.boolean("Display on Reports", help="Display this bank account on the footer of printed documents like invoices and sales orders."), - 'currency_id': fields.many2one('res.currency', string='Currency', help="Currency of the bank account and its related journal."), - } - - _defaults = { - 'owner_name': lambda obj, cursor, user, context: obj._default_value( - cursor, user, 'name', context=context), - 'street': lambda obj, cursor, user, context: obj._default_value( - cursor, user, 'street', context=context), - 'city': lambda obj, cursor, user, context: obj._default_value( - cursor, user, 'city', context=context), - 'zip': lambda obj, cursor, user, context: obj._default_value( - cursor, user, 'zip', context=context), - 'country_id': lambda obj, cursor, user, context: obj._default_value( - cursor, user, 'country_id', context=context), - 'state_id': lambda obj, cursor, user, context: obj._default_value( - cursor, user, 'state_id', context=context), - 'name': '/', - } - - def fields_get(self, cr, uid, allfields=None, context=None, write_access=True, attributes=None): - res = super(res_partner_bank, self).fields_get(cr, uid, allfields=allfields, context=context, write_access=write_access, attributes=attributes) - bank_type_obj = self.pool.get('res.partner.bank.type') - type_ids = bank_type_obj.search(cr, uid, []) - types = bank_type_obj.browse(cr, uid, type_ids) - for type in types: - for field in type.field_ids: - if field.name in res: - res[field.name].setdefault('states', {}) - res[field.name]['states'][type.code] = [ - ('readonly', field.readonly), - ('required', field.required)] - return res - - def _prepare_name_get(self, cr, uid, bank_dicts, context=None): - """ Format the name of a res.partner.bank. - This function is designed to be inherited to add replacement fields. - :param bank_dicts: a list of res.partner.bank dicts, as returned by the method read() - :return: [(id, name), ...], as returned by the method name_get() - """ - # prepare a mapping {code: format_layout} for all bank types - bank_type_obj = self.pool.get('res.partner.bank.type') - bank_types = bank_type_obj.browse(cr, uid, bank_type_obj.search(cr, uid, []), context=context) - bank_code_format = dict((bt.code, bt.format_layout) for bt in bank_types) - - res = [] - for data in bank_dicts: - name = data['acc_number'] - if data['state'] and bank_code_format.get(data['state']): - try: - if not data.get('bank_name'): - data['bank_name'] = _('BANK') - data = dict((k, v or '') for (k, v) in data.iteritems()) - name = bank_code_format[data['state']] % data - except Exception: - raise UserError(_("Bank account name formating error") + ': ' + _("Check the format_layout field set on the Bank Account Type.")) - if data.get('currency_id'): - currency_name = self.pool.get('res.currency').browse(cr, uid, data['currency_id'][0], context=context).name - name += ' (' + currency_name + ')' - res.append((data.get('id', False), name)) - return res - - def name_get(self, cr, uid, ids, context=None): - if not len(ids): - return [] - bank_dicts = self.read(cr, uid, ids, self.fields_get_keys(cr, uid, context=context), context=context) - return self._prepare_name_get(cr, uid, bank_dicts, context=context) - - def onchange_company_id(self, cr, uid, ids, company_id, context=None): - result = {} - if company_id: - c = self.pool.get('res.company').browse(cr, uid, company_id, context=context) - if c.partner_id: - r = self.onchange_partner_id(cr, uid, ids, c.partner_id.id, context=context) - r['value']['partner_id'] = c.partner_id.id - r['value']['footer'] = 1 - result = r - return result - - def onchange_bank_id(self, cr, uid, ids, bank_id, context=None): - result = {} - if bank_id: - bank = self.pool.get('res.bank').browse(cr, uid, bank_id, context=context) - result['bank_name'] = bank.name - result['bank_bic'] = bank.bic - return {'value': result} + @api.model + def name_search(self, name, args=None, operator='ilike', limit=100): + args = args or [] + domain = [] + if name: + domain = ['|', ('bic', '=ilike', name + '%'), ('name', operator, name)] + if operator in expression.NEGATIVE_TERM_OPERATORS: + domain = ['&'] + domain + banks = self.search(domain + args, limit=limit) + return banks.name_get() + +class ResPartnerBank(models.Model): + _name = 'res.partner.bank' + _rec_name = 'acc_number' + _description = 'Bank Accounts' + _order = 'sequence' - def onchange_partner_id(self, cr, uid, ids, partner_id, context=None): - result = {} - if partner_id is not False: - # be careful: partner_id may be a NewId - part = self.pool['res.partner'].browse(cr, uid, [partner_id], context=context) - result['owner_name'] = part.name - result['street'] = part.street or False - result['city'] = part.city or False - result['zip'] = part.zip or False - result['country_id'] = part.country_id.id - result['state_id'] = part.state_id.id - return {'value': result} + acc_type = fields.Char(compute='_compute_acc_type', help='Bank account type, inferred from account number') + acc_number = fields.Char('Account Number', required=True) + sanitized_acc_number = fields.Char(compute='_compute_sanitized_acc_number', string='Sanitized Account Number', readonly=True, store=True) + partner_id = fields.Many2one('res.partner', 'Account Holder', ondelete='cascade', select=True, domain=['|', ('is_company', '=', True), ('parent_id', '=', False)]) + bank_id = fields.Many2one('res.bank', string='Bank') + bank_name = fields.Char(related='bank_id.name') + bank_bic = fields.Char(related='bank_id.bic') + sequence = fields.Integer() + currency_id = fields.Many2one('res.currency', string='Currency') + company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env.user.company_id, ondelete='cascade') + + _sql_constraints = [ + ('unique_number', 'unique(sanitized_acc_number)', 'Account Number must be unique'), + ] + + @api.one + @api.depends('acc_number') + def _compute_sanitized_acc_number(self): + self.sanitized_acc_number = sanitize_account_number(self.acc_number) + + @api.one + @api.depends('acc_type') + def _compute_acc_type(self): + self.acc_type = 'bank' + + @api.model + def search(self, args, offset=0, limit=None, order=None, count=False): + pos = 0 + while pos < len(args): + if args[pos][0] == 'acc_number': + op = args[pos][1] + value = args[pos][2] + if hasattr(value, '__iter__'): + value = [sanitize_account_number(i) for i in value] + else: + value = sanitize_account_number(value) + if 'like' in op: + value = '%' + value + '%' + args[pos] = ('sanitized_acc_number', op, value) + pos += 1 + return super(ResPartnerBank, self).search(args, offset, limit, order, count=count) diff --git a/openerp/addons/base/res/res_bank_demo.xml b/openerp/addons/base/res/res_bank_demo.xml new file mode 100644 index 00000000000..5f232e38334 --- /dev/null +++ b/openerp/addons/base/res/res_bank_demo.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data noupdate="1"> + + <record model="res.bank" id="bank_ing"> + <field name="name">ING</field> + <field name="bic">BBRUBEBB</field> + </record> + + <record model="res.bank" id="bank_crelan"> + <field name="name">Crelan</field> + <field name="bic">NICABEBB</field> + </record> + + <record model="res.bank" id="bank_cbc"> + <field name="name">CBC</field> + <field name="bic">CREGBEBB</field> + </record> + + </data> +</openerp> diff --git a/openerp/addons/base/res/res_bank_view.xml b/openerp/addons/base/res/res_bank_view.xml index 8ed11eb2da1..f73f5b267b6 100644 --- a/openerp/addons/base/res/res_bank_view.xml +++ b/openerp/addons/base/res/res_bank_view.xml @@ -1,7 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> <openerp> <data> - <!-- res.bank --> + + <menuitem id="menu_config_bank_accounts" name="Bank Accounts" parent="base.menu_config_address_book" sequence="6" groups="base.group_no_one"/> + <record id="view_res_bank_form" model="ir.ui.view"> <field name="name">res.bank.form</field> <field name="model">res.bank</field> @@ -60,79 +62,21 @@ </field> </record> - <menuitem id="menu_config_bank_accounts" - name="Bank Accounts" - parent="base.menu_config_address_book" - sequence="6" - groups="base.group_no_one"/> - <menuitem action="action_res_bank_form" id="menu_action_res_bank_form" parent="base.menu_config_bank_accounts" sequence="1" groups="base.group_no_one"/> - - <record id="view_partner_bank_type_form" model="ir.ui.view"> - <field name="name">res.partner.bank.type.form</field> - <field name="model">res.partner.bank.type</field> - <field name="arch" type="xml"> - <form string="Bank Account Type"> - <group col="4"> - <field name="name"/> - <field name="code"/> - </group> - <field name="field_ids"/> - <label for="format_layout"/> - <field name="format_layout"/> - </form> - </field> - </record> - <record id="view_partner_bank_type_tree" model="ir.ui.view"> - <field name="name">res.partner.bank.type.tree</field> - <field name="model">res.partner.bank.type</field> - <field name="arch" type="xml"> - <tree string="Bank Account Type"> - <field name="name"/> - <field name="code"/> - </tree> - </field> - </record> - <record id="view_partner_bank_form" model="ir.ui.view"> <field name="name">res.partner.bank.form</field> <field name="model">res.partner.bank</field> <field name="arch" type="xml"> <form string="Bank account"> <group col="4"> - <field name="state"/> - <field name="acc_number" placeholder="Account Number"/> - <field name="company_id" groups="base.group_multi_company" on_change="onchange_company_id(company_id)" - invisible="context.get('company_hide', True)" options="{'no_create': True}"/> - <field name="footer" invisible="context.get('footer_hide', True)"/> + <field name="acc_number"/> + <field name="acc_type"/> + <field name="partner_id"/> + <field name="bank_id"/> + <field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/> + <field name="currency_id" groups="base.group_multi_currency" options="{'no_create': True}"/> </group> - <group> - <group name="owner" string="Account Owner"> - <label for="partner_id"/> - <div> - <div> - <field name="partner_id" on_change="onchange_partner_id(partner_id)"/> - </div> - <div> - <field name="street" placeholder="Street"/> - <div> - <field name="zip" class="oe_inline" placeholder="ZIP"/> - <field name="city" class="oe_inline" placeholder="City"/> - </div> - <div> - <field name="state_id" placeholder="State" options='{"no_open": True}'/> - <field name="country_id" placeholder="Country" options='{"no_open": True, "no_create": True}'/> - </div> - </div> - </div> - </group> - <group name="bank" string="Bank Information"> - <field name="bank_name" attrs="{'required': [('company_id','<>',False)]}"/> - <field name="bank_bic" placeholder="e.g. GEBABEBB" /> - </group> - </group> - </form> </field> </record> @@ -141,12 +85,10 @@ <field name="model">res.partner.bank</field> <field name="arch" type="xml"> <tree string="Bank Accounts"> - <field name="state" invisible="1"/> <field name="sequence" invisible="1"/> <field name="acc_number"/> <field name="bank_name"/> - <field name="company_id" invisible="context.get('company_hide', True)"/> - <field name="footer" invisible="context.get('footer_hide', True)"/> + <field name="company_id" groups="base.group_multi_company"/> <field name="partner_id"/> </tree> </field> @@ -157,7 +99,6 @@ <field name="arch" type="xml"> <search string="Bank Accounts"> <field name="bank_name" filter_domain="['|', ('bank_name','ilike',self), ('acc_number','ilike',self)]" string="Bank Name"/> - <filter name="my_bank" string="My Banks" domain="[('company_id','<>',False)]" help="Bank accounts belonging to one of your companies"/> <field name="company_id" invisible="context.get('company_hide', True)"/> <field name="partner_id"/> </search> @@ -171,35 +112,11 @@ <field name="help" type="html"> <p class="oe_view_nocontent_create"> Click to create a bank account. - </p><p> - Configure your company's bank accounts and select those that must appear on the report footer. - You can reorder bank accounts from the list view. - </p> - <p> - If you use the accounting application of Odoo, journals and accounts will be created automatically based on these data. </p> </field> </record> - <menuitem action="action_res_partner_bank_account_form" - id="menu_action_res_partner_bank_form" - parent="base.menu_config_bank_accounts" - groups="base.group_no_one" - sequence="2"/> - - - <record id="action_res_partner_bank_type_form" model="ir.actions.act_window"> - <field name="name">Bank Account Types</field> - <field name="res_model">res.partner.bank.type</field> - <field name="view_type">form</field> - <field name="view_mode">tree,form</field> - </record> - <menuitem action="action_res_partner_bank_type_form" - id="menu_action_res_partner_bank_typeform" - parent="base.menu_config_bank_accounts" - groups="base.group_no_one" - sequence="3"/> - + <menuitem action="action_res_partner_bank_account_form" id="menu_action_res_partner_bank_form" parent="base.menu_config_bank_accounts" groups="base.group_no_one" sequence="2"/> </data> </openerp> diff --git a/openerp/addons/base/res/res_company.py b/openerp/addons/base/res/res_company.py index 2c95d098e0f..0845b743017 100644 --- a/openerp/addons/base/res/res_company.py +++ b/openerp/addons/base/res/res_company.py @@ -80,7 +80,6 @@ class res_company(osv.osv): 'zip': fields.function(_get_address_data, fnct_inv=_set_address_data, size=24, type='char', string="Zip", multi='address'), 'city': fields.function(_get_address_data, fnct_inv=_set_address_data, size=24, type='char', string="City", multi='address'), 'state_id': fields.function(_get_address_data, fnct_inv=_set_address_data, type='many2one', relation='res.country.state', string="Fed. State", multi='address'), - 'bank_ids': fields.one2many('res.partner.bank','company_id', 'Bank Accounts', help='Bank accounts related to this company'), 'country_id': fields.function(_get_address_data, fnct_inv=_set_address_data, type='many2one', relation='res.country', string="Country", multi='address'), 'email': fields.related('partner_id', 'email', size=64, type='char', string="Email", store=True), 'phone': fields.related('partner_id', 'phone', size=64, type='char', string="Phone", store=True), @@ -94,28 +93,20 @@ class res_company(osv.osv): ('name_uniq', 'unique (name)', 'The company name must be unique !') ] - def onchange_footer(self, cr, uid, ids, custom_footer, phone, fax, email, website, vat, company_registry, bank_ids, context=None): - if custom_footer: - return {} - - # first line (notice that missing elements are filtered out before the join) - res = ' | '.join(filter(bool, [ - phone and '%s: %s' % (_('Phone'), phone), - fax and '%s: %s' % (_('Fax'), fax), - email and '%s: %s' % (_('Email'), email), - website and '%s: %s' % (_('Website'), website), - vat and '%s: %s' % (_('TIN'), vat), - company_registry and '%s: %s' % (_('Reg'), company_registry), - ])) - # second line: bank accounts - res_partner_bank = self.pool.get('res.partner.bank') - account_data = self.resolve_2many_commands(cr, uid, 'bank_ids', bank_ids, context=context) - account_names = res_partner_bank._prepare_name_get(cr, uid, account_data, context=context) - if account_names: - title = _('Bank Accounts') if len(account_names) > 1 else _('Bank Account') - res += '\n%s: %s' % (title, ', '.join(name for id, name in account_names)) - - return {'value': {'rml_footer': res, 'rml_footer_readonly': res}} + @api.onchange('custom_footer', 'phone', 'fax', 'email', 'website', 'vat', 'company_registry') + def onchange_footer(self): + if not self.custom_footer: + # first line (notice that missing elements are filtered out before the join) + res = ' | '.join(filter(bool, [ + self.phone and '%s: %s' % (_('Phone'), self.phone), + self.fax and '%s: %s' % (_('Fax'), self.fax), + self.email and '%s: %s' % (_('Email'), self.email), + self.website and '%s: %s' % (_('Website'), self.website), + self.vat and '%s: %s' % (_('TIN'), self.vat), + self.company_registry and '%s: %s' % (_('Reg'), self.company_registry), + ])) + self.rml_footer_readonly = res + self.rml_footer = res def onchange_state(self, cr, uid, ids, state_id, context=None): if state_id: diff --git a/openerp/addons/base/res/res_company_view.xml b/openerp/addons/base/res/res_company_view.xml index 6be1325a699..20a1da117b0 100644 --- a/openerp/addons/base/res/res_company_view.xml +++ b/openerp/addons/base/res/res_company_view.xml @@ -2,13 +2,6 @@ <openerp> <data> <report id="preview_report" model="res.company" name="preview.report" multi="True" rml="base/report/preview_report.rml" string="Preview Report"/> - <record id="bank_account_update" model="ir.actions.act_window"> - <field name="name">Company Bank Accounts</field> - <field name="res_model">res.partner.bank</field> - <field name="view_type">form</field> - <field name="view_mode">tree,form</field> - <field name="context" eval="{'default_partner_id':ref('base.main_partner'), 'company_hide':False, 'default_company_id':ref('base.main_company'), 'search_default_my_bank':1}"/> - </record> <record id="view_company_form" model="ir.ui.view"> <field name="name">res.company.form</field> <field name="model">res.company</field> @@ -55,27 +48,16 @@ </div> <field name="country_id" placeholder="Country" class="oe_no_button" options='{"no_open": True}' on_change="on_change_country(country_id)"/> </div> - <field name="website" widget="url" placeholder="e.g. www.odoo.com" - on_change="onchange_footer(custom_footer, phone, fax, email, website, vat, company_registry, bank_ids)"/> + <field name="website" widget="url" placeholder="e.g. www.odoo.com"/> </group> <group> - <field name="phone" on_change="onchange_footer(custom_footer, phone, fax, email, website, vat, company_registry, bank_ids)"/> - <field name="fax" on_change="onchange_footer(custom_footer, phone, fax, email, website, vat, company_registry, bank_ids)"/> - <field name="email" on_change="onchange_footer(custom_footer, phone, fax, email, website, vat, company_registry, bank_ids)"/> - <field name="vat" on_change="onchange_footer(custom_footer, phone, fax, email, website, vat, company_registry, bank_ids)"/> - <field name="company_registry" on_change="onchange_footer(custom_footer, phone, fax, email, website, vat, company_registry, bank_ids)"/> + <field name="phone"/> + <field name="fax"/> + <field name="email"/> + <field name="vat"/> + <field name="company_registry"/> </group> </group> - <group string="Bank Accounts"> - <field name="bank_ids" nolabel="1" context="{'default_company_id': active_id, 'footer_hide': False}" - on_change="onchange_footer(custom_footer, phone, fax, email, website, vat, company_registry, bank_ids)"> - <tree> - <field name="acc_number"/> - <field name="bank_name"/> - <field name="footer"/> - </tree> - </field> - </group> </page> <page name="configuration" string="Configuration"> <group> @@ -86,7 +68,7 @@ </page> <page name="report" string="Report Configuration"> <group string="Configuration"> - <field name="custom_footer" on_change="onchange_footer(custom_footer, phone, fax, email, website, vat, company_registry, bank_ids)"/> + <field name="custom_footer"/> <field name="rml_footer" attrs="{'invisible': [('custom_footer','=',False)]}"/> <field name="rml_footer_readonly" attrs="{'invisible': [('custom_footer','=',True)]}"/> </group> diff --git a/openerp/addons/base/res/res_partner_data.xml b/openerp/addons/base/res/res_partner_data.xml index 841a66d316d..607b149c2ed 100644 --- a/openerp/addons/base/res/res_partner_data.xml +++ b/openerp/addons/base/res/res_partner_data.xml @@ -30,17 +30,6 @@ <field name="shortcut">Prof.</field> </record> - <!-- Default bank account description --> - <record id="bank_normal" model="res.partner.bank.type"> - <field name="name">Normal Bank Account</field> - <field name="code">bank</field> - </record> - - <record id="bank_normal_field_bic" model="res.partner.bank.type.field"> - <field name="name">bank_bic</field> - <field name="bank_type_id" ref="bank_normal"/> - <field name="required" eval="0"/> - </record> <!-- Resource: res.request.link --> diff --git a/openerp/addons/base/security/ir.model.access.csv b/openerp/addons/base/security/ir.model.access.csv index 1dcfeb81c9c..1357acc31ac 100644 --- a/openerp/addons/base/security/ir.model.access.csv +++ b/openerp/addons/base/security/ir.model.access.csv @@ -63,10 +63,6 @@ "access_res_partner_group_user","res_partner group_user","model_res_partner","group_user",1,0,0,0 "access_res_partner_bank_group_user","res_partner_bank group_user","model_res_partner_bank","group_user",1,0,0,0 "access_res_partner_bank_group_partner_manager","res_partner_bank group_partner_manager","model_res_partner_bank","group_partner_manager",1,1,1,1 -"access_res_partner_bank_type_group_partner_manager","res_partner_bank_type group_partner_manager","model_res_partner_bank_type","group_partner_manager",1,1,1,1 -"access_res_partner_bank_type_group_user","res_partner_bank_type group_user","model_res_partner_bank_type",,1,0,0,0 -"access_res_partner_bank_type_field_group_partner_manager","res_partner_bank_type_field group_partner_manager","model_res_partner_bank_type_field","group_partner_manager",1,1,1,1 -"access_res_partner_bank_type_field_group_user","res_partner_bank_type_field group_user","model_res_partner_bank_type_field",,1,0,0,0 "access_res_partner_category_group_user","res_partner_category group_user","model_res_partner_category","group_user",1,0,0,0 "access_res_partner_category_group_partner_manager","res_partner_category group_partner_manager","model_res_partner_category","group_partner_manager",1,1,1,1 "access_res_partner_title_group_user","res_partner_title group_user","model_res_partner_title","group_partner_manager",1,1,1,1 diff --git a/openerp/addons/base/tests/__init__.py b/openerp/addons/base/tests/__init__.py index 82b79cfc693..d58e4931ec7 100644 --- a/openerp/addons/base/tests/__init__.py +++ b/openerp/addons/base/tests/__init__.py @@ -25,3 +25,4 @@ import test_translate import test_view_validation import test_views import test_xmlrpc +import test_res_partner_bank diff --git a/openerp/addons/base/tests/test_expression.py b/openerp/addons/base/tests/test_expression.py index a3a93971b59..9bf61d61473 100644 --- a/openerp/addons/base/tests/test_expression.py +++ b/openerp/addons/base/tests/test_expression.py @@ -175,8 +175,6 @@ class test_expression(common.TransactionCase): partner_bank_ids_col = partner_obj._columns.get('bank_ids') # one2many on res.partner to res.partner.bank category_id_col = partner_obj._columns.get('category_id') # many2many on res.partner to res.partner.category - # Get the first bank account type to be able to create a res.partner.bank - bank_type = bank_obj._bank_type_get(cr, uid)[0] # Get country/state data country_us_id = registry('res.country').search(cr, uid, [('code', 'like', 'US')])[0] state_ids = registry('res.country.state').search(cr, uid, [('country_id', '=', country_us_id)], limit=2) @@ -187,9 +185,9 @@ class test_expression(common.TransactionCase): p_aa = partner_obj.create(cr, uid, {'name': 'test__AA', 'parent_id': p_a, 'state_id': state_ids[0]}) p_ab = partner_obj.create(cr, uid, {'name': 'test__AB', 'parent_id': p_a, 'state_id': state_ids[1]}) p_ba = partner_obj.create(cr, uid, {'name': 'test__BA', 'parent_id': p_b, 'state_id': state_ids[0]}) - b_aa = bank_obj.create(cr, uid, {'acc_number': '__bank_test_a', 'state': bank_type[0], 'partner_id': p_aa}) - b_ab = bank_obj.create(cr, uid, {'acc_number': '__bank_test_b', 'state': bank_type[0], 'partner_id': p_ab}) - b_ba = bank_obj.create(cr, uid, {'acc_number': '__bank_test_b', 'state': bank_type[0], 'partner_id': p_ba}) + b_aa = bank_obj.create(cr, uid, {'acc_number': '123', 'acc_type': 'bank', 'partner_id': p_aa}) + b_ab = bank_obj.create(cr, uid, {'acc_number': '456', 'acc_type': 'bank', 'partner_id': p_ab}) + b_ba = bank_obj.create(cr, uid, {'acc_number': '789', 'acc_type': 'bank', 'partner_id': p_ba}) # -------------------------------------------------- # Test1: basics about the attribute @@ -203,34 +201,34 @@ class test_expression(common.TransactionCase): # Test2: one2many # -------------------------------------------------- - name_test = 'test_a' + name_test = '12' # Do: one2many without _auto_join self._reinit_mock() - partner_ids = partner_obj.search(cr, uid, [('bank_ids.acc_number', 'like', name_test)]) + partner_ids = partner_obj.search(cr, uid, [('bank_ids.sanitized_acc_number', 'like', name_test)]) # Test result self.assertEqual(set(partner_ids), set([p_aa]), - "_auto_join off: ('bank_ids.acc_number', 'like', '..'): incorrect result") + "_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..'): incorrect result") # Test produced queries self.assertEqual(len(self.query_list), 3, - "_auto_join off: ('bank_ids.acc_number', 'like', '..') should produce 3 queries (1 in res_partner_bank, 2 on res_partner)") + "_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') should produce 3 queries (1 in res_partner_bank, 2 on res_partner)") sql_query = self.query_list[0].get_sql() self.assertIn('res_partner_bank', sql_query[0], - "_auto_join off: ('bank_ids.acc_number', 'like', '..') first query incorrect main table") + "_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') first query incorrect main table") - expected = "%s::text like %s" % (unaccent('"res_partner_bank"."acc_number"'), unaccent('%s')) + expected = "%s::text like %s" % (unaccent('"res_partner_bank"."sanitized_acc_number"'), unaccent('%s')) self.assertIn(expected, sql_query[1], - "_auto_join off: ('bank_ids.acc_number', 'like', '..') first query incorrect where condition") + "_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') first query incorrect where condition") self.assertEqual(set(['%' + name_test + '%']), set(sql_query[2]), - "_auto_join off: ('bank_ids.acc_number', 'like', '..') first query incorrect parameter") + "_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') first query incorrect parameter") sql_query = self.query_list[2].get_sql() self.assertIn('res_partner', sql_query[0], - "_auto_join off: ('bank_ids.acc_number', 'like', '..') third query incorrect main table") + "_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') third query incorrect main table") self.assertIn('"res_partner"."id" in (%s)', sql_query[1], - "_auto_join off: ('bank_ids.acc_number', 'like', '..') third query incorrect where condition") + "_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') third query incorrect where condition") self.assertEqual(set([p_aa]), set(sql_query[2]), - "_auto_join off: ('bank_ids.acc_number', 'like', '..') third query incorrect parameter") + "_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') third query incorrect parameter") # Do: cascaded one2many without _auto_join self._reinit_mock() @@ -245,27 +243,27 @@ class test_expression(common.TransactionCase): # Do: one2many with _auto_join partner_bank_ids_col._auto_join = True self._reinit_mock() - partner_ids = partner_obj.search(cr, uid, [('bank_ids.acc_number', 'like', 'test_a')]) + partner_ids = partner_obj.search(cr, uid, [('bank_ids.sanitized_acc_number', 'like', '12')]) # Test result self.assertEqual(set(partner_ids), set([p_aa]), - "_auto_join on: ('bank_ids.acc_number', 'like', '..') incorrect result") + "_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') incorrect result") # Test produced queries self.assertEqual(len(self.query_list), 1, - "_auto_join on: ('bank_ids.acc_number', 'like', '..') should produce 1 query") + "_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') should produce 1 query") sql_query = self.query_list[0].get_sql() self.assertIn('"res_partner"', sql_query[0], - "_auto_join on: ('bank_ids.acc_number', 'like', '..') query incorrect main table") + "_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') query incorrect main table") self.assertIn('"res_partner_bank" as "res_partner__bank_ids"', sql_query[0], - "_auto_join on: ('bank_ids.acc_number', 'like', '..') query incorrect join") + "_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') query incorrect join") - expected = "%s::text like %s" % (unaccent('"res_partner__bank_ids"."acc_number"'), unaccent('%s')) + expected = "%s::text like %s" % (unaccent('"res_partner__bank_ids"."sanitized_acc_number"'), unaccent('%s')) self.assertIn(expected, sql_query[1], - "_auto_join on: ('bank_ids.acc_number', 'like', '..') query incorrect where condition") + "_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') query incorrect where condition") self.assertIn('"res_partner"."id"="res_partner__bank_ids"."partner_id"', sql_query[1], - "_auto_join on: ('bank_ids.acc_number', 'like', '..') query incorrect join condition") + "_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') query incorrect join condition") self.assertEqual(set(['%' + name_test + '%']), set(sql_query[2]), - "_auto_join on: ('bank_ids.acc_number', 'like', '..') query incorrect parameter") + "_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') query incorrect parameter") # Do: one2many with _auto_join, test final leaf is an id self._reinit_mock() @@ -426,7 +424,7 @@ class test_expression(common.TransactionCase): partner_child_ids_col._auto_join = True partner_bank_ids_col._auto_join = True partner_child_ids_col._domain = lambda self: ['!', ('name', '=', self._name)] - partner_bank_ids_col._domain = [('acc_number', 'like', 'a')] + partner_bank_ids_col._domain = [('sanitized_acc_number', 'like', '2')] # Do: 2 cascaded one2many with _auto_join, test final leaf is an id self._reinit_mock() partner_ids = partner_obj.search(cr, uid, ['&', (1, '=', 1), ('child_ids.bank_ids.id', 'in', [b_aa, b_ba])]) @@ -438,7 +436,7 @@ class test_expression(common.TransactionCase): # Test produced queries that domains effectively present sql_query = self.query_list[0].get_sql() - expected = "%s::text like %s" % (unaccent('"res_partner__child_ids__bank_ids"."acc_number"'), unaccent('%s')) + expected = "%s::text like %s" % (unaccent('"res_partner__child_ids__bank_ids"."sanitized_acc_number"'), unaccent('%s')) self.assertIn(expected, sql_query[1], "_auto_join on one2many with domains incorrect result") # TDE TODO: check first domain has a correct table name diff --git a/openerp/addons/base/tests/test_osv_expression.yml b/openerp/addons/base/tests/test_osv_expression.yml index 3a917528474..813acfd2669 100644 --- a/openerp/addons/base/tests/test_osv_expression.yml +++ b/openerp/addons/base/tests/test_osv_expression.yml @@ -206,7 +206,7 @@ # that don't have OpenERP has a parent company, you expect to find, # among others, the companies that don't have parent company. # - + # existing values be treated similarly if we simply check that some # existing value belongs to them. @@ -367,52 +367,14 @@ Equivalent queries, one2many. - !python {model: res.company }: | - # Search the company via its one2many (the one2many must point back at the company). company = self.browse(cr, uid, ref('ymltest_company3')) - max_currency_id = max(self.pool['res.partner.bank'].search(cr, uid, [])) - bank_ids1 = self.pool['res.partner.bank'].search(cr, uid, [('acc_number', 'not like', 'probably_unexisting_name')]) - bank_ids2 = self.pool['res.partner.bank'].search(cr, uid, [('id', 'not in', [max_currency_id + 1003])]) - bank_ids3 = self.pool['res.partner.bank'].search(cr, uid, [('id', 'not in', [])]) - assert bank_ids1 == bank_ids2 == bank_ids3, 'All 3 results should have be the same: all banks' - default_company = self.browse(cr, uid, 1) - # set manually a bank to the default company. - self.pool['res.partner.bank'].write(cr, uid, bank_ids1, {'company_id': default_company.id}) # one2many towards same model res_1 = self.search(cr, uid, [('child_ids', 'in', [x.id for x in company.child_ids])]) # any company having a child of company3 as child res_2 = self.search(cr, uid, [('child_ids', 'in', [company.child_ids[0].id])]) # any company having the first child of company3 as child - # one2many towards another model - res_3 = self.search(cr, uid, [('bank_ids', 'in', [x.id for x in default_company.bank_ids])]) # companies having a currency of main company - res_4 = self.search(cr, uid, [('bank_ids', 'in', [default_company.bank_ids[0].id])]) # companies having first currency of main company - res_5 = self.search(cr, uid, [('bank_ids', 'in', default_company.bank_ids[0].id)]) # companies having first currency of main company - # res_6 = self.search(cr, uid, [('bank_ids', 'in', [default_company.currency_ids[0].name])]) # TODO - res_7 = self.search(cr, uid, [('bank_ids', '=', default_company.bank_ids[0].acc_number)]) - res_8 = self.search(cr, uid, [('bank_ids', 'like', default_company.bank_ids[0].acc_number)]) - - res_9 = self.search(cr, uid, [('bank_ids', 'like', 'probably_unexisting_name')]) - # self.search(cr, uid, [('bank_ids', 'unexisting_op', 'probably_unexisting_name')]) # TODO expected exception assert res_1 == [ref('ymltest_company3')], "res_1" assert res_2 == [ref('ymltest_company3')], "res_2" - assert res_3 == [1], "res_3" - assert res_4 == [1], "res_4" - assert res_5 == [1], "res_5" - assert res_7 == [1], "res_7" - assert res_8 == [1], "res_8" - assert res_9 == [], "res_9" - # get the companies referenced by some currency (this is normally the main company) using a weird negative domain - res_10 = self.search(cr, uid, [('bank_ids', 'not like', 'probably_unexisting_name')]) - res_11 = self.search(cr, uid, [('bank_ids', 'not in', [max_currency_id + 1])]) - res_12 = self.search(cr, uid, [('bank_ids', '!=', False)]) - res_13 = self.search(cr, uid, [('bank_ids', 'not in', [])]) - res_10.sort() - res_11.sort() - res_12.sort() - res_13.sort() - assert res_10 == res_11 - assert res_10 == res_12 - assert res_10 == res_13 - # try testing real subsets with IN/NOT IN res_partner = self.pool.get('res.partner') res_users = self.pool.get('res.users') @@ -444,6 +406,53 @@ assert res_3 == expected assert res_4 == expected - + Equivalent queries, one2many. +- + !python {model: res.currency }: | + # Create a currency and a currency rate + res_currency = self.pool['res.currency'] + res_currency_rate = self.pool['res.currency.rate'] + currency = res_currency.create(cr, uid, {'name': 'ZZZ', 'symbol': 'ZZZ', 'rounding': 1.0}) + currency_rate = self.pool['res.currency.rate'].create(cr, uid, {'name': '2010-01-01', 'currency_id': currency, 'rate': 1.0}) + + # Search the currency via its rates one2many (the one2many must point back at the currency). + max_currency_rate_id = max(res_currency_rate.search(cr, uid, [])) + currency_rate1 = res_currency_rate.search(cr, uid, [('name', 'not like', 'probably_unexisting_name')]) + currency_rate2 = res_currency_rate.search(cr, uid, [('id', 'not in', [max_currency_rate_id + 1003])]) + currency_rate3 = res_currency_rate.search(cr, uid, [('id', 'not in', [])]) + assert currency_rate1 == currency_rate2 == currency_rate3, 'All 3 results should be the same : empty' + default_currency = self.browse(cr, uid, 1) + + # one2many towards another model + res_3 = self.search(cr, uid, [('rate_ids', 'in', [x.id for x in default_currency.rate_ids])]) # currencies having a rate of main currency + res_4 = self.search(cr, uid, [('rate_ids', 'in', [default_currency.rate_ids[0].id])]) # currencies having first rate of main currency + res_5 = self.search(cr, uid, [('rate_ids', 'in', default_currency.rate_ids[0].id)]) # currencies having first rate of main currency + # res_6 = self.search(cr, uid, [('rate_ids', 'in', [default_currency.rate_ids[0].name])]) + # res_7 = self.search(cr, uid, [('rate_ids', '=', default_currency.rate_ids[0].name)]) + # res_8 = self.search(cr, uid, [('rate_ids', 'like', default_currency.rate_ids[0].name)]) + + res_9 = self.search(cr, uid, [('rate_ids', 'like', 'probably_unexisting_name')]) + # self.search(cr, uid, [('rate_ids', 'unexisting_op', 'probably_unexisting_name')]) # TODO expected exception + assert res_3 == [1], "res_3" + assert res_4 == [1], "res_4" + assert res_5 == [1], "res_5" + # assert res_7 == [1], "res_7" + # assert res_8 == [1], "res_8" + assert res_9 == [], "res_9" + + # get the currencies referenced by some currency rates using a weird negative domain + res_10 = self.search(cr, uid, [('rate_ids', 'not like', 'probably_unexisting_name')]) + res_11 = self.search(cr, uid, [('rate_ids', 'not in', [max_currency_rate_id + 1])]) + res_12 = self.search(cr, uid, [('rate_ids', '!=', False)]) + res_13 = self.search(cr, uid, [('rate_ids', 'not in', [])]) + res_10.sort() + res_11.sort() + res_12.sort() + res_13.sort() + assert res_10 == res_11, 'res_11' + assert res_10 == res_12, 'res_12' + assert res_10 == res_13, 'res_13' +- Unaccent. Create a company with an accent in its name. - !record {model: res.company, id: ymltest_unaccent_company}: diff --git a/openerp/addons/base/tests/test_res_partner_bank.py b/openerp/addons/base/tests/test_res_partner_bank.py new file mode 100644 index 00000000000..40cd9f797bf --- /dev/null +++ b/openerp/addons/base/tests/test_res_partner_bank.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# This file is part of account_bank_statement_import, +# an Odoo module. +# +# Copyright (c) 2015 ACSONE SA/NV (<http://acsone.eu>) +# +# account_bank_statement_import is free software: +# you can redistribute it and/or modify it under the terms of the GNU +# Affero General Public License as published by the Free Software +# Foundation,either version 3 of the License, or (at your option) any +# later version. +# +# account_bank_statement_import is distributed +# in the hope that it will be useful, but WITHOUT ANY WARRANTY; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. See the GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with account_bank_statement_import_coda. +# If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## +from openerp.tests.common import TransactionCase + + +class TestResPartnerBank(TransactionCase): + """Tests acc_number + """ + + def test_sanitized_acc_number(self): + partner_bank_model = self.env['res.partner.bank'] + acc_number = " BE-001 2518823 03 " + vals = partner_bank_model.search([('acc_number', '=', acc_number)]) + self.assertEquals(0, len(vals)) + partner_bank = partner_bank_model.create({ + 'acc_number': acc_number, + 'partner_id': self.ref('base.res_partner_2'), + 'acc_type': 'bank', + }) + vals = partner_bank_model.search([('acc_number', '=', acc_number)]) + self.assertEquals(1, len(vals)) + self.assertEquals(partner_bank, vals[0]) + vals = partner_bank_model.search([('acc_number', 'in', [acc_number])]) + self.assertEquals(1, len(vals)) + self.assertEquals(partner_bank, vals[0]) + + self.assertEqual(partner_bank.acc_number, acc_number) + + # sanitaze the acc_number + sanitized_acc_number = 'BE001251882303' + vals = partner_bank_model.search( + [('acc_number', '=', sanitized_acc_number)]) + self.assertEquals(1, len(vals)) + self.assertEquals(partner_bank, vals[0]) + vals = partner_bank_model.search( + [('acc_number', 'in', [sanitized_acc_number])]) + self.assertEquals(1, len(vals)) + self.assertEquals(partner_bank, vals[0]) + self.assertEqual(partner_bank.sanitized_acc_number, + sanitized_acc_number) + + # search is case insensitive + vals = partner_bank_model.search( + [('acc_number', '=', sanitized_acc_number.lower())]) + self.assertEquals(1, len(vals)) + vals = partner_bank_model.search( + [('acc_number', '=', acc_number.lower())]) + self.assertEquals(1, len(vals)) |
