[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
This commit is contained in:
@@ -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': [
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
@@ -349,57 +374,55 @@ class AccountJournal(models.Model):
|
||||
'company_id': company.id,
|
||||
}
|
||||
|
||||
@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
|
||||
#----------------------------------------------------------
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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>
|
||||
@@ -6,6 +6,10 @@
|
||||
min-width: inherit;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.o_kanban_card_settings {
|
||||
padding: 8px;
|
||||
|
||||
|
||||
@@ -1,13 +1,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
|
||||
-
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import account_bank_statement_import
|
||||
import account_journal
|
||||
|
||||
import wizard
|
||||
|
||||
@@ -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)
|
||||
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)])
|
||||
|
||||
# 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
|
||||
# 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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
@@ -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>
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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."))
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
@@ -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".
|
||||
-
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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”
|
||||
-
|
||||
|
||||
@@ -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>
|
||||
</openerp>
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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
|
||||
|
||||
|
@@ -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')
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 _
|
||||
import re
|
||||
|
||||
from openerp import api, fields, models, _
|
||||
from openerp.osv import expression
|
||||
from openerp.exceptions import UserError
|
||||
|
||||
class Bank(osv.osv):
|
||||
_description='Bank'
|
||||
def sanitize_account_number(acc_number):
|
||||
if acc_number:
|
||||
return re.sub(r'\W+', '', acc_number).upper()
|
||||
return False
|
||||
|
||||
class Bank(models.Model):
|
||||
_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):
|
||||
|
||||
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 = []
|
||||
for bank in self.browse(cr, uid, ids, context):
|
||||
result.append((bank.id, (bank.bic and (bank.bic + ' - ') or '') + bank.name))
|
||||
for bank in self:
|
||||
name = bank.name + (bank.bic and (' - ' + bank.bic) or '')
|
||||
result.append((bank.id, name))
|
||||
return result
|
||||
|
||||
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"
|
||||
}
|
||||
@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 res_partner_bank_type_fields(osv.osv):
|
||||
_description='Bank type fields'
|
||||
_name = 'res.partner.bank.type.field'
|
||||
_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__
|
||||
class ResPartnerBank(models.Model):
|
||||
_name = 'res.partner.bank'
|
||||
_rec_name = 'acc_number'
|
||||
_description = 'Bank Accounts'
|
||||
_order = 'sequence'
|
||||
|
||||
def _bank_type_get(self, cr, uid, context=None):
|
||||
bank_type_obj = self.pool.get('res.partner.bank.type')
|
||||
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')
|
||||
|
||||
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))
|
||||
return result
|
||||
_sql_constraints = [
|
||||
('unique_number', 'unique(sanitized_acc_number)', 'Account Number must be unique'),
|
||||
]
|
||||
|
||||
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
|
||||
@api.one
|
||||
@api.depends('acc_number')
|
||||
def _compute_sanitized_acc_number(self):
|
||||
self.sanitized_acc_number = sanitize_account_number(self.acc_number)
|
||||
|
||||
for address in self.pool.get('res.partner').resolve_2many_commands(
|
||||
cursor, user, 'address', context['address'], ['type', field], context=context):
|
||||
@api.one
|
||||
@api.depends('acc_type')
|
||||
def _compute_acc_type(self):
|
||||
self.acc_type = 'bank'
|
||||
|
||||
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}
|
||||
|
||||
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}
|
||||
@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)
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
-->
|
||||
|
||||
@@ -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
|
||||
|
||||
|
@@ -25,3 +25,4 @@ import test_translate
|
||||
import test_view_validation
|
||||
import test_views
|
||||
import test_xmlrpc
|
||||
import test_res_partner_bank
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
@@ -443,6 +405,53 @@
|
||||
assert res_2 == expected
|
||||
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.
|
||||
-
|
||||
|
||||
@@ -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))
|
||||
Reference in New Issue
Block a user