2
0

add to the template: - partner - analytic account - analytic tags - taxes - originator tax

When you create a move from a template, you can now indicate the date
and the company to use.

Added tests.
This commit is contained in:
Jordi Ballester Alomar 2018-10-31 13:03:41 +01:00 committed by Abraham Anes
parent af3f17e8d2
commit 39070f31b0
10 changed files with 344 additions and 62 deletions

View File

@ -7,8 +7,8 @@
'version': '11.0.1.0.0',
'category': 'Generic Modules/Accounting',
'summary': "Templates for recurring Journal Entries",
'author': "Agile Business Group,Odoo Community Association (OCA), Aurium "
"Technologies,Vauxoo",
'author': "Agile Business Group, Odoo Community Association (OCA), Aurium "
"Technologies, Vauxoo, Eficent",
'website': 'https://github.com/OCA/account-financial-tools',
'license': 'AGPL-3',
'depends': [
@ -19,6 +19,7 @@
'security/ir.model.access.csv',
'view/move_template.xml',
'wizard/select_template.xml',
'security/account_move_template_security.xml',
],
'test': [
],

View File

@ -1,4 +1,4 @@
# Copyright 2015-2017 See manifest
# Copyright 2015-2018 See manifest
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from functools import partial
@ -10,7 +10,14 @@ from odoo.tools.safe_eval import safe_eval
class AccountDocumentTemplate(models.Model):
_name = 'account.document.template'
name = fields.Char(required=True)
name = fields.Char('Name', required=True,
)
@api.multi
def copy(self, default=None):
self.ensure_one()
default = dict(default or {}, name=_('%s (copy)') % self.name)
return super(AccountDocumentTemplate, self).copy(default)
@api.multi
def _input_lines(self):
@ -76,10 +83,13 @@ class AccountDocumentTemplate(models.Model):
class AccountDocumentTemplateLine(models.Model):
_name = 'account.document.template.line'
name = fields.Char(required=True)
sequence = fields.Integer(required=True)
name = fields.Char('Name', required=True,
)
sequence = fields.Integer('Sequence', required=True,
)
type = fields.Selection([
('computed', 'Computed'),
('input', 'User input'),
], required=True, default='input')
python_code = fields.Text()
], string='Type', required=True, default='input',
)
python_code = fields.Text('Python code')

View File

@ -1,4 +1,4 @@
# Copyright 2015-2017 See manifest
# Copyright 2015-2018 See manifest
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from odoo import models, fields, api
@ -35,17 +35,26 @@ class AccountMoveTemplate(models.Model):
action.update({'context': {'default_template_id': self.id}})
return action
_sql_constraints = [
('sequence_move_template_uniq', 'unique (name,company_id)',
'The name of the template must be unique per company !')
]
class AccountMoveTemplateLine(models.Model):
_name = 'account.move.template.line'
_inherit = 'account.document.template.line'
company_id = fields.Many2one('res.company',
related='template_id.company_id',
readonly=True)
journal_id = fields.Many2one('account.journal', required=True)
account_id = fields.Many2one(
'account.account',
required=True,
ondelete="cascade"
)
partner_id = fields.Many2one('res.partner', string='Partner')
move_line_type = fields.Selection(
[('cr', 'Credit'), ('dr', 'Debit')],
required=True
@ -54,6 +63,11 @@ class AccountMoveTemplateLine(models.Model):
'account.analytic.account',
ondelete="cascade"
)
analytic_tag_ids = fields.Many2many('account.analytic.tag',
string='Analytic tags')
tax_ids = fields.Many2many('account.tax', string='Taxes')
tax_line_id = fields.Many2one('account.tax', string='Originator tax',
ondelete='restrict')
template_id = fields.Many2one('account.move.template')
_sql_constraints = [

View File

@ -12,4 +12,5 @@ Contributors
* Jalal ZAHID <j.zahid@auriumtechnologies.com> (port to v10)
* Alex Comba <alex.comba@agilebg.com> (Port to V8)
* Guewen Baconnier <guewen.baconnier@camptocamp.com>
* Raf Ven <raf.ven@dynapps.be> (port to v11)
* Raf Ven <raf.ven@dynapps.be> (port to v11)
* Jordi Ballester <jordi.ballester@eficent.com> (EFICENT)

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="account_move_template_comp_rule" model="ir.rule">
<field name="name">Move Template multi company rule</field>
<field name="model_id" ref="model_account_move_template"/>
<field name="global" eval="True"/>
<field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
</record>
</odoo>

View File

@ -0,0 +1,2 @@
from . import test_account_move_template

View File

@ -0,0 +1,184 @@
# Copyright 2018 Eficent Business and IT Consulting Services, S.L.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging
from psycopg2 import IntegrityError
from odoo.tests.common import TransactionCase
from odoo.tools import mute_logger
from odoo import fields
_logger = logging.getLogger(__name__)
class TestAccountMoveTemplate(TransactionCase):
def with_context(self, *args, **kwargs):
context = dict(args[0] if args else self.env.context, **kwargs)
self.env = self.env(context=context)
return self
def _chart_of_accounts_create(self, company, chart):
_logger.debug('Creating chart of account')
self.env.user.write({
'company_ids': [(4, company.id)],
'company_id': company.id,
})
self.with_context(
company_id=company.id, force_company=company.id)
wizard = self.env['wizard.multi.charts.accounts'].create({
'company_id': company.id,
'chart_template_id': chart.id,
'code_digits': 6,
'currency_id': self.env.ref('base.EUR').id,
'transfer_account_id': chart.transfer_account_id.id,
})
wizard.onchange_chart_template_id()
wizard.execute()
return True
def setUp(self):
super(TestAccountMoveTemplate, self).setUp()
employees_group = self.env.ref('base.group_user')
multi_company_group = self.env.ref('base.group_multi_company')
account_user_group = self.env.ref('account.group_account_user')
account_manager_group = self.env.ref('account.group_account_manager')
self.company = self.env['res.company'].create({
'name': 'Test company',
})
self.company_2 = self.env['res.company'].create({
'name': 'Test company 2',
'parent_id': self.company.id,
})
self.env.user.company_ids += self.company
self.env.user.company_ids += self.company_2
self.user = self.env['res.users'].sudo(self.env.user).with_context(
no_reset_password=True).create(
{'name': 'Test User',
'login': 'test_user',
'email': 'test@oca.com',
'groups_id': [(6, 0, [employees_group.id,
account_user_group.id,
account_manager_group.id,
multi_company_group.id,
])],
'company_id': self.company.id,
'company_ids': [(4, self.company.id)],
})
self.user_type = self.env.ref('account.data_account_type_liquidity')
self.chart = self.env['account.chart.template'].search([], limit=1)
self._chart_of_accounts_create(self.company, self.chart)
account_template = self.env['account.account.template'].create({
'name': 'Test 1',
'code': 'Code_test',
'user_type_id': self.user_type.id,
})
self.env['ir.model.data'].create({
'name': account_template.name,
'module': 'account',
'model': 'account.account.template',
'res_id': account_template.id,
'noupdate': 0,
})
self.chart_2 = self.env['account.chart.template'].create({
'name': 'Test Chart',
'currency_id': self.env.ref('base.EUR').id,
'transfer_account_id': account_template.id,
})
account_template.chart_template_id = self.chart_2
self.chart_2.tax_template_ids |= self.chart.tax_template_ids
self._chart_of_accounts_create(self.company_2, self.chart_2)
self.chart.company_id = self.company
self.chart_2.company_id = self.company_2
self.account_company_1 = self.env['account.account'].search(
[('company_id', '=', self.company.id)], limit=1)
self.account_journal_1 = self.env['account.journal'].create({
'name': 'Journal Company 1',
'company_id': self.company.id,
'code': 'TST',
'type': 'general',
})
self.partner = self.env['res.partner'].create({
'name': 'Test partner',
'company_id': False,
})
self.partner2 = self.env['res.partner'].create({
'name': 'Test partner 2',
'company_id': False,
})
self.account_type = self.env['account.account.type'].create({
'name': 'Test Tax Account Type'})
self.tax_account_id = self.env['account.account'].create({
'name': 'tax account',
'code': 'TAX',
'user_type_id': self.account_type.id,
'company_id': self.company.id,
})
self.tax = self.env['account.tax'].create({
'name': 'Tax 10.0%',
'amount': 10.0,
'amount_type': 'percent',
'account_id': self.tax_account_id.id,
})
def test_create_template(self):
"""Test that I can create a template
"""
template = self.env['account.move.template'].sudo(self.user).create({
'name': 'Test Move Template',
'company_id': self.company.id,
'template_line_ids': [
(0, 0, {
'name': 'L1',
'sequence': 1,
'journal_id': self.account_journal_1.id,
'account_id': self.account_company_1.id,
'partner_id': self.partner.id,
'tax_line_id': self.tax.id,
'move_line_type': 'dr',
'type': 'input'
}),
(0, 0, {
'name': 'L2',
'sequence': 2,
'journal_id': self.account_journal_1.id,
'account_id': self.account_company_1.id,
'move_line_type': 'cr',
'tax_ids': [(4, self.tax.id)],
'type': 'input',
})
]
})
self.assertEquals(template.company_id, self.user.company_id)
template_2 = template.copy()
self.assertEquals(template_2.name, '%s (copy)' % template.name)
wiz = self.env['wizard.select.move.template'].sudo(self.user).create({
'company_id': self.company.id,
'template_id': template.id,
'partner_id': self.partner2.id,
'date': fields.Date.today(),
})
wiz.load_lines()
res = wiz.load_template()
aml = self.env['account.move.line'].search(
[('account_id', '=', self.account_company_1.id)], limit=1)
self.assertEquals(res['domain'], ([('id', 'in', aml.move_id.ids)]))
aml = self.env['account.move.line'].search(
[('name', '=', 'L1')], limit=1)
self.assertEquals(aml.tax_line_id, self.tax)
self.assertEquals(aml.partner_id, self.partner)
aml = self.env['account.move.line'].search(
[('name', '=', 'L2')], limit=1)
self.assertEquals(aml.tax_ids[0], self.tax)
with self.assertRaises(IntegrityError), mute_logger('odoo.sql_db'):
template_2.name = template.name

View File

@ -7,12 +7,17 @@
<tree>
<field name="sequence"/>
<field name="name"/>
<field name="journal_id"/>
<field name="account_id" />
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
<field name="journal_id" domain="[('company_id','=',company_id)]"/>
<field name="account_id" domain="[('company_id','=',company_id)]"/>
<field name="partner_id"/>
<field name="analytic_account_id" domain="[('company_id','=',company_id)]" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_accounting"/>
<field name="tax_line_id"/>
<field name="tax_ids" widget="many2many_tags"/>
<field name="type"/>
<field name="python_code" />
<field name="python_code"/>
<field name="move_line_type"/>
<field name="company_id" invisible="1"/>
</tree>
</field>
</record>
@ -27,14 +32,23 @@
<group>
<field name="name"/>
<field name="sequence"/>
<field name="journal_id"/>
<field name="account_id"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
<field name="journal_id" domain="[('company_id','=',company_id)]"/>
<field name="account_id" domain="[('company_id','=',company_id)]"/>
<field name="partner_id"/>
<field name="company_id" invisible="1"/>
</group>
<group groups="analytic.group_analytic_accounting" string="Analytic">
<field name="analytic_account_id" domain="[('company_id','=',company_id)]"/>
<field name="analytic_tag_ids" widget="many2many_tags"/>
</group>
<group>
<field name="type"/>
<field name="move_line_type"/>
</group>
<group string="Taxes">
<field name="tax_line_id"/>
<field name="tax_ids" widget="many2many_tags"/>
</group>
</group>
<label for="python_code" string="Python Code"
attrs="{'invisible': [('type', '!=', 'computed')]}" nolabel="1"/>
@ -67,7 +81,7 @@
<group>
<field name="company_id" widget='selection' groups="base.group_multi_company"/>
</group>
<field nolabel="1" name="template_line_ids"/>
<field nolabel="1" name="template_line_ids" context="{'default_company_id' : company_id}"/>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>

View File

@ -1,7 +1,6 @@
# Copyright 2015-2017 See manifest
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
import time
from odoo import models, fields, api, _
@ -9,12 +8,21 @@ class WizardSelectMoveTemplate(models.TransientModel):
_name = "wizard.select.move.template"
template_id = fields.Many2one('account.move.template', required=True)
company_id = fields.Many2one(
'res.company', required=True,
default=lambda self: self.env.user.company_id)
partner_id = fields.Many2one('res.partner', 'Partner')
date = fields.Date(required=True, default=fields.Date.context_today)
line_ids = fields.One2many(
'wizard.select.move.template.line', 'template_id')
state = fields.Selection(
[('template_selected', 'Template selected')])
@api.onchange('template_id', 'company_id')
def onchange_company_id(self):
template_domain = [('company_id', '=', self.company_id.id)]
return {'domain': {'template_id': template_domain}}
@api.multi
def load_lines(self):
self.ensure_one()
@ -26,22 +34,24 @@ class WizardSelectMoveTemplate(models.TransientModel):
'name': line.name,
'amount': 0.0,
'account_id': line.account_id.id,
'partner_id': line.partner_id.id or self.partner_id.id,
'move_line_type': line.move_line_type,
'tax_ids': [(6, 0, line.tax_ids.ids)],
'tax_line_id': line.tax_line_id.id,
'analytic_account_id': line.analytic_account_id.id,
'analytic_tag_ids': [(6, 0, line.analytic_tag_ids.ids)],
})
if not self.line_ids:
return self.load_template()
self.state = 'template_selected'
view_rec = self.env.ref('account_move_template.wizard_select_template')
return {
'view_type': 'form',
'view_id': [view_rec.id],
'view_mode': 'form',
'res_model': 'wizard.select.move.template',
'res_id': self.id,
'type': 'ir.actions.act_window',
'target': 'new',
'context': self.env.context,
}
action = self.env.ref(
'account_move_template.action_wizard_select_template_by_move')
result = action.read()[0]
result['res_id'] = self.id
result['view_id'] = [view_rec.id]
result['context'] = self.env.context
return result
@api.multi
def load_template(self):
@ -55,29 +65,26 @@ class WizardSelectMoveTemplate(models.TransientModel):
moves = self.env['account.move']
for journal in self.template_id.template_line_ids.mapped('journal_id'):
lines = []
move = self._create_move(name, journal.id, partner)
move = self._create_move(name, journal.id)
moves = moves + move
for line in self.template_id.template_line_ids.filtered(
lambda l, j=journal: l.journal_id == j):
lines.append((0, 0,
self._prepare_line(line, amounts, partner)))
move.write({'line_ids': lines})
return {
'domain': [('id', 'in', moves.ids)],
'name': _('Entries from template: %s') % name,
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'account.move',
'type': 'ir.actions.act_window',
'target': 'current',
}
action = self.env.ref('account.action_move_journal_line')
result = action.read()[0]
result['domain'] = [('id', 'in', moves.ids)]
result['name'] = _('Entries from template: %s') % name
result['context'] = self.env.context
return result
@api.model
def _create_move(self, ref, journal_id, partner_id):
def _create_move(self, ref, journal_id):
return self.env['account.move'].create({
'ref': ref,
'journal_id': journal_id,
'partner_id': partner_id,
'date': self.date,
})
@api.model
@ -88,11 +95,15 @@ class WizardSelectMoveTemplate(models.TransientModel):
'journal_id': line.journal_id.id,
'analytic_account_id': line.analytic_account_id.id,
'account_id': line.account_id.id,
'date': fields.Date.today(),
'credit': not debit and amounts[line.sequence] or 0.0,
'debit': debit and amounts[line.sequence] or 0.0,
'partner_id': partner_id,
'partner_id': line.partner_id.id or partner_id,
'tax_line_id': line.tax_line_id.id,
}
if line.analytic_tag_ids:
values['analytic_tag_ids'] = [(6, 0, line.analytic_tag_ids.ids)]
if line.tax_ids:
values['tax_ids'] = [(6, 0, line.tax_ids.ids)]
return values
@ -102,10 +113,30 @@ class WizardSelectMoveTemplateLine(models.TransientModel):
template_id = fields.Many2one(
'wizard.select.move.template')
sequence = fields.Integer(required=True)
name = fields.Char(required=True, readonly=True)
company_id = fields.Many2one('res.company',
related='template_id.company_id',
readonly=True)
sequence = fields.Integer('Sequence', required=True,
)
name = fields.Char('Name', required=True, readonly=True,
)
account_id = fields.Many2one(
'account.account', required=True, readonly=True)
move_line_type = fields.Selection(
[('cr', 'Credit'), ('dr', 'Debit')], required=True, readonly=True)
amount = fields.Float(required=True)
analytic_account_id = fields.Many2one(
'account.analytic.account',
ondelete="cascade", readonly=True,
)
analytic_tag_ids = fields.Many2many('account.analytic.tag',
string='Analytic tags',
readonly=True,)
tax_ids = fields.Many2many('account.tax', string='Taxes', readonly=True)
tax_line_id = fields.Many2one('account.tax', string='Originator tax',
ondelete='restrict', readonly=True)
partner_id = fields.Many2one('res.partner', readonly=True,
string='Partner')
move_line_type = fields.Selection([('cr', 'Credit'), ('dr', 'Debit')],
required=True, readonly=True,
string='Journal Item Type',
)
amount = fields.Float('Amount', required=True,
)

View File

@ -1,17 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="wizard_select_template" model="ir.ui.view">
<field name="name">Select Move Template</field>
<field name="name">Select Journal Entry Template</field>
<field name="model">wizard.select.move.template</field>
<field name="arch" type="xml">
<form string="Move Template" >
<form string="Journal Entry Template" >
<field name="state" invisible="1"/>
<group>
<group>
<field name="template_id" widget="selection"
attrs="{'invisible':[('state','=','template_selected')]}"/>
attrs="{'invisible':[('state','=','template_selected')]}"
domain="[('company_id','=',company_id)]"/>
</group>
<group>
<field name="company_id" widget="selection" groups="base.group_multi_company"/>
<field name="partner_id" attrs="{'invisible':[('state','=','template_selected')]}"/>
</group>
</group>
@ -29,37 +31,49 @@
</record>
<record id="wizard_select_template_line" model="ir.ui.view">
<field name="name">Select Move Template Line</field>
<field name="name">Select Journal Entry Template Line</field>
<field name="model">wizard.select.move.template.line</field>
<field name="arch" type="xml">
<form string="Move Template Line">
<form string="Journal Entry Template Line">
<group>
<field name="sequence" invisible="1"/>
<field name="name" />
<field name="account_id" />
<field name="move_line_type" />
<field name="amount" />
<field name="name"/>
<field name="account_id" domain="[('company_id','=',company_id)]"/>
<field name="partner_id"/>
<field name="analytic_account_id" domain="[('company_id','=',company_id)]" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_accounting"/>
<field name="tax_line_id" attrs="{'invisible': [('tax_line_id','=',False)]}"/>
<field name="tax_ids" widget="many2many_tags" attrs="{'invisible': [('tax_ids','=',[])]}"/>
<field name="move_line_type"/>
<field name="amount"/>
<field name="company_id" invisible="1"/>
</group>
</form>
</field>
</record>
<record id="wizard_select_template_line_tree" model="ir.ui.view">
<field name="name">Select Move Template Line</field>
<field name="name">Select Journal Entry Template Line</field>
<field name="model">wizard.select.move.template.line</field>
<field name="arch" type="xml">
<tree editable="bottom">
<field name="sequence" invisible="1"/>
<field name="name" />
<field name="account_id" />
<field name="name"/>
<field name="account_id" domain="[('company_id','=',company_id)]"/>
<field name="partner_id"/>
<field name="analytic_account_id" domain="[('company_id','=',company_id)]" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_accounting"/>
<field name="tax_line_id" attrs="{'invisible': [('tax_line_id','=',False)]}"/>
<field name="tax_ids" widget="many2many_tags" attrs="{'invisible': [('tax_ids','=',[])]}"/>
<field name="move_line_type" />
<field name="amount" />
<field name="company_id" invisible="1"/>
</tree>
</field>
</record>
<record id="action_wizard_select_template" model="ir.actions.act_window">
<field name="name">Select Move Template</field>
<field name="name">Select Journal Entry Template</field>
<field name="res_model">wizard.select.move.template</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
@ -67,7 +81,7 @@
<field name="target">new</field>
</record>
<act_window name="Create Move from Template"
<act_window name="Create Journal Entry from Template"
res_model="wizard.select.move.template"
src_model="account.move"
view_mode="form"