[MIG] account_move_template from v11 to v12 (full re-write)
This commit is contained in:
parent
7f35b8a9ee
commit
d2da4f4276
@ -1,27 +1,25 @@
|
||||
# Copyright 2015-2017 See manifest
|
||||
# Copyright 2018 Raf Ven <raf.ven@dynapps.be>
|
||||
# Copyright 2019 Akretion France (http://www.akretion.com/)
|
||||
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
|
||||
|
||||
{
|
||||
'name': "Account Move Template",
|
||||
'version': '11.0.1.0.0',
|
||||
'category': 'Generic Modules/Accounting',
|
||||
'version': '12.0.1.0.0',
|
||||
'category': 'Accounting',
|
||||
'summary': "Templates for recurring Journal Entries",
|
||||
'author': "Agile Business Group, Odoo Community Association (OCA), Aurium "
|
||||
"Technologies, Vauxoo, Eficent",
|
||||
"Technologies, Vauxoo, Eficent, Akretion",
|
||||
'website': 'https://github.com/OCA/account-financial-tools',
|
||||
'license': 'AGPL-3',
|
||||
'depends': [
|
||||
'account',
|
||||
'analytic',
|
||||
],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'view/move_template.xml',
|
||||
'wizard/select_template.xml',
|
||||
'security/account_move_template_security.xml',
|
||||
],
|
||||
'test': [
|
||||
'security/ir.model.access.csv',
|
||||
'wizard/account_move_template_run_view.xml',
|
||||
'view/account_move_template.xml',
|
||||
],
|
||||
'installable': True,
|
||||
}
|
||||
|
@ -1,2 +1 @@
|
||||
from . import account_document_template
|
||||
from . import account_move_template
|
||||
|
@ -1,95 +0,0 @@
|
||||
# Copyright 2015-2018 See manifest
|
||||
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
|
||||
|
||||
from functools import partial
|
||||
import re
|
||||
from odoo import models, fields, api, exceptions, _
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
|
||||
|
||||
class AccountDocumentTemplate(models.Model):
|
||||
_name = 'account.document.template'
|
||||
|
||||
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):
|
||||
count = 0
|
||||
for line in self.template_line_ids:
|
||||
if line.type == 'input':
|
||||
count += 1
|
||||
return count
|
||||
|
||||
@api.multi
|
||||
def _get_template_line(self, line_number):
|
||||
for line in self.template_line_ids:
|
||||
if line.sequence == line_number:
|
||||
return line
|
||||
return False
|
||||
|
||||
@api.multi
|
||||
def _generate_empty_lines(self):
|
||||
lines = {}
|
||||
for line in self.template_line_ids:
|
||||
lines[line.sequence] = None
|
||||
return lines
|
||||
|
||||
@api.multi
|
||||
def lines(self, line_number, computed_lines=None):
|
||||
if computed_lines is None:
|
||||
computed_lines = {}
|
||||
if computed_lines[line_number] is not None:
|
||||
return computed_lines[line_number]
|
||||
line = self._get_template_line(line_number)
|
||||
if re.match(r'L\( *' + str(line_number) + r' *\)', line.python_code):
|
||||
raise exceptions.Warning(
|
||||
_('Line %s can\'t refer to itself') % str(line_number)
|
||||
)
|
||||
try:
|
||||
recurse_lines = partial(self.lines, computed_lines=computed_lines)
|
||||
computed_lines[line_number] = safe_eval(
|
||||
line.python_code.replace('L', 'recurse_lines'),
|
||||
locals_dict={'recurse_lines': recurse_lines}
|
||||
)
|
||||
except KeyError:
|
||||
raise exceptions.Warning(
|
||||
_('Code "%s" refers to non existing line') % line.python_code)
|
||||
return computed_lines[line_number]
|
||||
|
||||
@api.multi
|
||||
def compute_lines(self, input_lines):
|
||||
if len(input_lines) != self._input_lines():
|
||||
raise exceptions.Warning(
|
||||
_('You can not add a different number of lines in this wizard '
|
||||
'you should try to create the move normally and then edit '
|
||||
'the created move. Inconsistent between input lines and '
|
||||
' filled lines for template %s') % self.name
|
||||
)
|
||||
computed_lines = self._generate_empty_lines()
|
||||
computed_lines.update(input_lines)
|
||||
for line_number in computed_lines:
|
||||
computed_lines[line_number] = self.lines(
|
||||
line_number, computed_lines)
|
||||
return computed_lines
|
||||
|
||||
|
||||
class AccountDocumentTemplateLine(models.Model):
|
||||
_name = 'account.document.template.line'
|
||||
|
||||
name = fields.Char('Name', required=True,
|
||||
)
|
||||
sequence = fields.Integer('Sequence', required=True,
|
||||
)
|
||||
type = fields.Selection([
|
||||
('computed', 'Computed'),
|
||||
('input', 'User input'),
|
||||
], string='Type', required=True, default='input',
|
||||
)
|
||||
python_code = fields.Text('Python code')
|
@ -1,79 +1,133 @@
|
||||
# Copyright 2015-2018 See manifest
|
||||
# Copyright 2015-2019 See manifest
|
||||
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
|
||||
|
||||
from odoo import models, fields, api
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools import float_round
|
||||
|
||||
|
||||
class AccountMoveTemplate(models.Model):
|
||||
_name = 'account.move.template'
|
||||
_inherit = ['account.document.template',
|
||||
# 'mail.activity.mixin', TODO: uncomment for saas-15
|
||||
'mail.thread']
|
||||
|
||||
@api.model
|
||||
def _company_get(self):
|
||||
return self.env['res.company']._company_default_get(
|
||||
object='account.move.template'
|
||||
)
|
||||
_description = 'Journal Entry Template'
|
||||
|
||||
name = fields.Char(required=True)
|
||||
company_id = fields.Many2one(
|
||||
'res.company',
|
||||
required=True,
|
||||
change_default=True,
|
||||
default=_company_get,
|
||||
)
|
||||
journal_id = fields.Many2one('account.journal', required=True)
|
||||
template_line_ids = fields.One2many(
|
||||
'account.move.template.line',
|
||||
inverse_name='template_id',
|
||||
)
|
||||
'res.company', string='Company', required=True, ondelete='cascade',
|
||||
default=lambda self: self.env['res.company']._company_default_get())
|
||||
journal_id = fields.Many2one(
|
||||
'account.journal', string='Journal', required=True)
|
||||
ref = fields.Char(string='Reference', copy=False)
|
||||
line_ids = fields.One2many(
|
||||
'account.move.template.line', inverse_name='template_id',
|
||||
string='Lines')
|
||||
|
||||
@api.multi
|
||||
def action_run_template(self):
|
||||
_sql_constraints = [(
|
||||
'name_company_unique',
|
||||
'unique(name, company_id)',
|
||||
'This name is already used by another template!'
|
||||
)]
|
||||
|
||||
def copy(self, default=None):
|
||||
self.ensure_one()
|
||||
action = self.env.ref(
|
||||
'account_move_template.action_wizard_select_template').read()[0]
|
||||
action.update({'context': {'default_template_id': self.id}})
|
||||
return action
|
||||
default = dict(default or {}, name=_('%s (copy)') % self.name)
|
||||
return super(AccountMoveTemplate, self).copy(default)
|
||||
|
||||
_sql_constraints = [
|
||||
('sequence_move_template_uniq', 'unique (name,company_id)',
|
||||
'The name of the template must be unique per company !')
|
||||
]
|
||||
def eval_computed_line(self, line, sequence2amount):
|
||||
safe_eval_dict = {}
|
||||
for seq, amount in sequence2amount.items():
|
||||
safe_eval_dict['L%d' % seq] = amount
|
||||
try:
|
||||
val = safe_eval(line.python_code, safe_eval_dict)
|
||||
sequence2amount[line.sequence] = val
|
||||
except ValueError:
|
||||
raise UserError(_(
|
||||
"Impossible to compute the formula of line with sequence %s "
|
||||
"(formula: %s). Check that the lines used in the formula "
|
||||
"really exists and have a lower sequence than the current "
|
||||
"line.") % (line.sequence, line.python_code))
|
||||
except SyntaxError:
|
||||
raise UserError(_(
|
||||
"Impossible to compute the formula of line with sequence %s "
|
||||
"(formula: %s): the syntax of the formula is wrong.")
|
||||
% (line.sequence, line.python_code))
|
||||
|
||||
def compute_lines(self, sequence2amount):
|
||||
prec = self.company_id.currency_id.rounding
|
||||
input_sequence2amount = sequence2amount.copy()
|
||||
for line in self.line_ids.filtered(lambda x: x.type == 'input'):
|
||||
if line.sequence not in sequence2amount:
|
||||
raise UserError(_(
|
||||
"You deleted a line in the wizard. This is not allowed: "
|
||||
"you should either update the template or modify the "
|
||||
"journal entry that will be generated by this wizard."))
|
||||
input_sequence2amount.pop(line.sequence)
|
||||
if input_sequence2amount:
|
||||
raise UserError(_(
|
||||
"You added a line in the wizard. This is not allowed: "
|
||||
"you should either update the template or modify "
|
||||
"the journal entry that will be generated by this wizard."))
|
||||
for line in self.line_ids.filtered(lambda x: x.type == 'computed'):
|
||||
self.eval_computed_line(line, sequence2amount)
|
||||
sequence2amount[line.sequence] = float_round(
|
||||
sequence2amount[line.sequence], precision_rounding=prec)
|
||||
return sequence2amount
|
||||
|
||||
def generate_journal_entry(self):
|
||||
'''Called by the button on the form view'''
|
||||
self.ensure_one()
|
||||
wiz = self.env['account.move.template.run'].create({
|
||||
'template_id': self.id})
|
||||
action = wiz.load_lines()
|
||||
return action
|
||||
|
||||
|
||||
class AccountMoveTemplateLine(models.Model):
|
||||
_name = 'account.move.template.line'
|
||||
_inherit = 'account.document.template.line'
|
||||
_description = 'Journal Item Template'
|
||||
_order = 'sequence, id'
|
||||
|
||||
company_id = fields.Many2one('res.company',
|
||||
related='template_id.company_id',
|
||||
readonly=True)
|
||||
journal_id = fields.Many2one('account.journal',
|
||||
related='template_id.journal_id',
|
||||
store=True, readonly=True)
|
||||
template_id = fields.Many2one(
|
||||
'account.move.template', string='Move Template', ondelete='cascade')
|
||||
name = fields.Char(string='Label')
|
||||
sequence = fields.Integer('Sequence', 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
|
||||
)
|
||||
'account.account', string='Account',
|
||||
required=True, domain=[('deprecated', '=', False)])
|
||||
partner_id = fields.Many2one(
|
||||
'res.partner', string='Partner',
|
||||
domain=['|', ('parent_id', '=', False), ('is_company', '=', True)])
|
||||
analytic_account_id = fields.Many2one(
|
||||
'account.analytic.account',
|
||||
ondelete="cascade"
|
||||
)
|
||||
analytic_tag_ids = fields.Many2many('account.analytic.tag',
|
||||
string='Analytic tags')
|
||||
'account.analytic.account', string='Analytic Account')
|
||||
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')
|
||||
tax_line_id = fields.Many2one(
|
||||
'account.tax', string='Originator Tax', ondelete='restrict')
|
||||
company_id = fields.Many2one(
|
||||
related='template_id.company_id', store=True)
|
||||
company_currency_id = fields.Many2one(
|
||||
related='template_id.company_id.currency_id',
|
||||
string='Company Currency', store=True)
|
||||
note = fields.Char()
|
||||
type = fields.Selection([
|
||||
('computed', 'Computed'),
|
||||
('input', 'User input'),
|
||||
], string='Type', required=True, default='input')
|
||||
python_code = fields.Text('Python Code')
|
||||
move_line_type = fields.Selection(
|
||||
[('cr', 'Credit'), ('dr', 'Debit')], required=True, string='Direction')
|
||||
|
||||
_sql_constraints = [
|
||||
('sequence_template_uniq', 'unique (template_id,sequence)',
|
||||
'The sequence of the line must be unique per template !')
|
||||
]
|
||||
_sql_constraints = [(
|
||||
'sequence_template_uniq',
|
||||
'unique(template_id, sequence)',
|
||||
'The sequence of the line must be unique per template!'
|
||||
)]
|
||||
|
||||
@api.constrains('type', 'python_code')
|
||||
def check_python_code(self):
|
||||
for line in self:
|
||||
if line.type == 'computed' and not line.python_code:
|
||||
raise ValidationError(_(
|
||||
"Python Code must be set for computed line with "
|
||||
"sequence %d.") % line.sequence)
|
||||
|
@ -5,6 +5,7 @@ Authors
|
||||
* Lorenzo Battistini <lorenzo.battistini@agilebg.com>
|
||||
* Paolo Chiara <p.chiara@isa.it>
|
||||
* Franco Tampieri <franco.tampieri@agilebg.com>
|
||||
* Alexis de Lattre <alexis.delattre@akretion.com> (full re-write for v12)
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
@ -1,7 +1,7 @@
|
||||
To create new templates:
|
||||
|
||||
#. Make sure that you have flagged *Show Full Accounting Features* in your
|
||||
user, and that the user has belongs to the *Billing Manager* group.
|
||||
user, and that the user belongs to the *Billing Manager* group.
|
||||
#. Go to *Invoicing / Configuration / Accounting / Journal Templates* and
|
||||
define there your template. You can choose to complete a line using a
|
||||
defined formula, based on other lines, or by requiring the user input.
|
||||
|
@ -2,10 +2,9 @@
|
||||
<odoo noupdate="1">
|
||||
|
||||
<record id="account_move_template_comp_rule" model="ir.rule">
|
||||
<field name="name">Move Template multi company rule</field>
|
||||
<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>
|
||||
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
@ -1,9 +1,3 @@
|
||||
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
|
||||
"access_account_document_template_user","account_document_template_user","model_account_document_template","account.group_account_user","1","1","1","1"
|
||||
"access_account_document_template_manager","account_document_template_manager","model_account_document_template","account.group_account_manager","1","1","1","1"
|
||||
"access_account_document_template_line_user","account_document_template_line_user","model_account_document_template_line","account.group_account_user","1","1","1","1"
|
||||
"access_account_document_template_line_manager","account_document_template_line_manager","model_account_document_template_line","account.group_account_manager","1","1","1","1"
|
||||
"access_account_move_template_user","account_move_template_user","model_account_move_template","account.group_account_user","1","1","1","1"
|
||||
"access_account_move_template_manager","account_move_template_manager","model_account_move_template","account.group_account_manager","1","1","1","1"
|
||||
"access_account_move_template_line_user","account_move_template_line_user","model_account_move_template_line","account.group_account_user","1","1","1","1"
|
||||
"access_account_move_template_line_manager","account_move_template_line_manager","model_account_move_template_line","account.group_account_manager","1","1","1","1"
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_account_move_template_user,Full access on account.move.template to accountant grp,model_account_move_template,account.group_account_user,1,1,1,1
|
||||
access_account_move_template_line_user,Full access on account.move.template.line to accountant grp,model_account_move_template_line,account.group_account_user,1,1,1,1
|
||||
|
|
@ -1,2 +1,3 @@
|
||||
|
||||
from . import test_account_move_template
|
||||
# Needs to be re-written because wizard.multi.charts.accounts
|
||||
# doesn't exist any more on v12
|
||||
# from . import test_account_move_template
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright 2018 Eficent Business and IT Consulting Services, S.L.
|
||||
# Copyright 2018-2019 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
|
||||
|
@ -1,28 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_move_template_line_tree" model="ir.ui.view">
|
||||
<record id="account_move_template_line_tree" model="ir.ui.view">
|
||||
<field name="name">account.move.template.line.tree</field>
|
||||
<field name="model">account.move.template.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="sequence"/>
|
||||
<field name="name"/>
|
||||
<field name="journal_id" domain="[('company_id','=',company_id)]"/>
|
||||
<field name="account_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="name"/>
|
||||
<field name="analytic_account_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="move_line_type"/>
|
||||
<field name="type"/>
|
||||
<field name="note"/>
|
||||
<field name="tax_line_id" invisible="1"/>
|
||||
<field name="tax_ids" widget="many2many_tags" options="{'no_create': True}"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="company_currency_id" invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_move_template_line_form" model="ir.ui.view">
|
||||
<record id="account_move_template_line_form" model="ir.ui.view">
|
||||
<field name="name">account.move.template.line.form</field>
|
||||
<field name="model">account.move.template.line</field>
|
||||
<field name="arch" type="xml">
|
||||
@ -30,10 +30,9 @@
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="sequence"/>
|
||||
<field name="journal_id" domain="[('company_id','=',company_id)]"/>
|
||||
<field name="account_id" domain="[('company_id','=',company_id)]"/>
|
||||
<field name="name"/>
|
||||
<field name="account_id" domain="[('company_id', '=', company_id)]"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
</group>
|
||||
@ -42,34 +41,41 @@
|
||||
<field name="analytic_tag_ids" widget="many2many_tags"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="type"/>
|
||||
<field name="move_line_type"/>
|
||||
<field name="type"/>
|
||||
<field name="note"/>
|
||||
</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"/>
|
||||
<field name="python_code" colspan="4" attrs="{'readonly': [('type', '!=', 'computed')]}" nolabel="1"/>
|
||||
<div class="oe_account_help">You can refer to other lines using their
|
||||
sequence number (e.g. 'L(1)' for first line).
|
||||
Examples of code: 'L(1) * 0.2'; 'L(2) - L(1)'; 'L(1)
|
||||
+ L(2) + L(3)'; '1250'</div>
|
||||
<group name="python_code" attrs="{'invisible': [('type', '!=', 'computed')]}" col="1" string="Compute Formula">
|
||||
<div class="oe_account_help">
|
||||
<p>You can refer to other lines using their sequence number e.g. <i>L1</i> for line with sequence = 1. Examples:</p>
|
||||
<ul>
|
||||
<li>L1 * 0.2</li>
|
||||
<li>L2 - L1</li>
|
||||
<li>L1 + L2 + L3</li>
|
||||
<li>1250</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<field name="python_code" nolabel="1" attrs="{'required': [('type', '=', 'computed')]}"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_move_template_form" model="ir.ui.view">
|
||||
<record id="account_move_template_form" model="ir.ui.view">
|
||||
<field name="name">account.move.template.form</field>
|
||||
<field name="model">account.move.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Journal Entry Template">
|
||||
<header>
|
||||
<button string="Test template"
|
||||
name="action_run_template"
|
||||
<button string="Generate Journal Entry"
|
||||
name="generate_journal_entry"
|
||||
class="btn-primary"
|
||||
type="object"/>
|
||||
</header>
|
||||
@ -78,58 +84,58 @@
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1><field name="name"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<field name="company_id" widget='selection' groups="base.group_multi_company"/>
|
||||
<field name="journal_id" widget='selection'/>
|
||||
<group name="main">
|
||||
<group name="main-left">
|
||||
<field name="company_id" widget="selection" groups="base.group_multi_company"/>
|
||||
<field name="journal_id" options="{'no_open': True, 'no_create': True}"/>
|
||||
</group>
|
||||
<group name="main-right">
|
||||
<field name="ref"/>
|
||||
</group>
|
||||
</group>
|
||||
<group name="lines">
|
||||
<field name="line_ids" nolabel="1" context="{'default_company_id': company_id}"/>
|
||||
</group>
|
||||
<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"/>
|
||||
<!--<field name="activity_ids"
|
||||
widget="mail_activity"/> TODO: uncomment on saas-15-->
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_move_template_tree" model="ir.ui.view">
|
||||
<record id="account_move_template_tree" model="ir.ui.view">
|
||||
<field name="name">account.move.template.tree</field>
|
||||
<field name="model">account.move.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
<field name="journal_id"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_move_template_search" model="ir.ui.view">
|
||||
<record id="account_move_template_search" model="ir.ui.view">
|
||||
<field name="name">account.move.template.search</field>
|
||||
<field name="model">account.move.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Journal Entry Template">
|
||||
<group>
|
||||
<separator orientation="vertical"/>
|
||||
<field name="name"/>
|
||||
<field name="company_id" widget="selection" groups="base.group_multi_company"/>
|
||||
<group name="groupby">
|
||||
<filter name="journal_groupby" string="Journal" context="{'group_by': 'journal_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_move_template_form" model="ir.actions.act_window">
|
||||
<record id="account_move_template_action" model="ir.actions.act_window">
|
||||
<field name="name">Journal Entry Templates</field>
|
||||
<field name="res_model">account.move.template</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="search_view_id" ref="view_move_template_search"/>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
action="action_move_template_form"
|
||||
id="menu_action_move_template_form" sequence="300"
|
||||
id="account_move_template_menu"
|
||||
action="account_move_template_action"
|
||||
parent="account.account_account_menu"
|
||||
groups="account.group_account_manager"/>
|
||||
sequence="300"/>
|
||||
|
||||
</odoo>
|
@ -1 +1 @@
|
||||
from . import select_template
|
||||
from . import account_move_template_run
|
||||
|
166
account_move_template/wizard/account_move_template_run.py
Normal file
166
account_move_template/wizard/account_move_template_run.py
Normal file
@ -0,0 +1,166 @@
|
||||
# Copyright 2015-2019 See manifest
|
||||
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
|
||||
|
||||
from odoo import fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import float_is_zero
|
||||
|
||||
|
||||
class AccountMoveTemplateRun(models.TransientModel):
|
||||
_name = "account.move.template.run"
|
||||
_description = "Wizard to generate move from template"
|
||||
|
||||
template_id = fields.Many2one('account.move.template', required=True)
|
||||
company_id = fields.Many2one(
|
||||
'res.company', required=True, readonly=True,
|
||||
default=lambda self: self.env['res.company']._company_default_get())
|
||||
partner_id = fields.Many2one(
|
||||
'res.partner', 'Override Partner',
|
||||
domain=['|', ('parent_id', '=', False), ('is_company', '=', True)])
|
||||
date = fields.Date(required=True, default=fields.Date.context_today)
|
||||
journal_id = fields.Many2one(
|
||||
'account.journal', string='Journal', readonly=True)
|
||||
ref = fields.Char(string='Reference')
|
||||
line_ids = fields.One2many(
|
||||
'account.move.template.line.run', 'wizard_id', string="Lines")
|
||||
state = fields.Selection([
|
||||
('select_template', 'Select Template'),
|
||||
('set_lines', 'Set Lines'),
|
||||
], readonly=True, default='select_template')
|
||||
|
||||
def _prepare_wizard_line(self, line):
|
||||
vals = {
|
||||
'wizard_id': self.id,
|
||||
'sequence': line.sequence,
|
||||
'name': line.name,
|
||||
'amount': 0.0,
|
||||
'account_id': line.account_id.id,
|
||||
'partner_id': line.partner_id.id or False,
|
||||
'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)],
|
||||
'note': line.note,
|
||||
}
|
||||
return vals
|
||||
|
||||
# STEP 1
|
||||
def load_lines(self):
|
||||
self.ensure_one()
|
||||
amtlro = self.env['account.move.template.line.run']
|
||||
if self.company_id != self.template_id.company_id:
|
||||
raise UserError(_(
|
||||
"The selected template (%s) is not in the same company (%s) "
|
||||
"as the current user (%s).") % (
|
||||
self.template_id.name,
|
||||
self.template_id.company_id.display_name,
|
||||
self.company_id.display_name))
|
||||
lines = self.template_id.line_ids
|
||||
for line in lines.filtered(lambda l: l.type == 'input'):
|
||||
vals = self._prepare_wizard_line(line)
|
||||
amtlro.create(vals)
|
||||
self.write({
|
||||
'journal_id': self.template_id.journal_id.id,
|
||||
'state': 'set_lines',
|
||||
})
|
||||
if not self.line_ids:
|
||||
return self.generate_move()
|
||||
action = self.env.ref(
|
||||
'account_move_template.account_move_template_run_action')
|
||||
result = action.read()[0]
|
||||
result.update({
|
||||
'res_id': self.id,
|
||||
'context': self.env.context,
|
||||
})
|
||||
return result
|
||||
|
||||
# STEP 2
|
||||
def generate_move(self):
|
||||
self.ensure_one()
|
||||
sequence2amount = {}
|
||||
for wizard_line in self.line_ids:
|
||||
sequence2amount[wizard_line.sequence] = wizard_line.amount
|
||||
prec = self.company_id.currency_id.rounding
|
||||
if all([
|
||||
float_is_zero(x, precision_rounding=prec)
|
||||
for x in sequence2amount.values()]):
|
||||
raise UserError(_("Debit and credit of all lines are null."))
|
||||
self.template_id.compute_lines(sequence2amount)
|
||||
move_vals = self._prepare_move()
|
||||
for line in self.template_id.line_ids:
|
||||
amount = sequence2amount[line.sequence]
|
||||
if not float_is_zero(amount, precision_rounding=prec):
|
||||
move_vals['line_ids'].append(
|
||||
(0, 0, self._prepare_move_line(line, amount)))
|
||||
move = self.env['account.move'].create(move_vals)
|
||||
action = self.env.ref('account.action_move_journal_line')
|
||||
result = action.read()[0]
|
||||
result.update({
|
||||
'name': _('Entry from template %s') % self.template_id.name,
|
||||
'res_id': move.id,
|
||||
'views': False,
|
||||
'view_id': False,
|
||||
'view_mode': 'form,tree,kanban',
|
||||
'context': self.env.context,
|
||||
})
|
||||
return result
|
||||
|
||||
def _prepare_move(self):
|
||||
move_vals = {
|
||||
'ref': self.ref,
|
||||
'journal_id': self.journal_id.id,
|
||||
'date': self.date,
|
||||
'company_id': self.company_id.id,
|
||||
'line_ids': [],
|
||||
}
|
||||
return move_vals
|
||||
|
||||
def _prepare_move_line(self, line, amount):
|
||||
debit = line.move_line_type == 'dr'
|
||||
values = {
|
||||
'name': line.name,
|
||||
'analytic_account_id': line.analytic_account_id.id,
|
||||
'account_id': line.account_id.id,
|
||||
'credit': not debit and amount or 0.0,
|
||||
'debit': debit and amount or 0.0,
|
||||
'partner_id': self.partner_id.id or line.partner_id.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
|
||||
|
||||
|
||||
class AccountMoveTemplateLineRun(models.TransientModel):
|
||||
_name = "account.move.template.line.run"
|
||||
_description = 'Wizard Lines to generate move from template'
|
||||
|
||||
wizard_id = fields.Many2one(
|
||||
'account.move.template.run', ondelete='cascade')
|
||||
company_id = fields.Many2one(
|
||||
related='wizard_id.company_id')
|
||||
company_currency_id = fields.Many2one(
|
||||
related='wizard_id.company_id.currency_id', string='Company Currency')
|
||||
sequence = fields.Integer('Sequence', required=True)
|
||||
name = fields.Char('Name')
|
||||
account_id = fields.Many2one(
|
||||
'account.account', required=True, readonly=True)
|
||||
analytic_account_id = fields.Many2one(
|
||||
'account.analytic.account', 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='Direction')
|
||||
amount = fields.Monetary(
|
||||
'Amount', required=True, currency_field='company_currency_id')
|
||||
note = fields.Char(readonly=True)
|
@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="account_move_template_run_form" model="ir.ui.view">
|
||||
<field name="model">account.move.template.run</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Journal Entry Template" >
|
||||
<group name="main">
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="template_id" widget="selection"
|
||||
domain="[('company_id', '=', company_id)]"
|
||||
attrs="{'readonly': [('state', '=', 'set_lines')]}" />
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="date" states="set_lines"/>
|
||||
<field name="journal_id" states="set_lines"/>
|
||||
<field name="ref" states="set_lines"/>
|
||||
<field name="partner_id" states="set_lines"/>
|
||||
</group>
|
||||
<group name="lines" states="set_lines">
|
||||
<field name="line_ids" nolabel="1">
|
||||
<tree editable="bottom">
|
||||
<field name="sequence" invisible="1"/>
|
||||
<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="note"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="company_currency_id" invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="load_lines" class="btn-primary" string="Next" type="object" states="select_template" />
|
||||
<button name="generate_move" class="btn-primary" string="Create Journal Entry" type="object" states="set_lines" />
|
||||
<button special="cancel" string="Cancel" class="btn-default"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="account_move_template_run_action" model="ir.actions.act_window">
|
||||
<field name="name">Create Entry from Template</field>
|
||||
<field name="res_model">account.move.template.run</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="account_move_template_run_menu"
|
||||
parent="account.menu_finance_entries_accounting_entries"
|
||||
action="account_move_template_run_action"
|
||||
sequence="10"/>
|
||||
</odoo>
|
@ -1,142 +0,0 @@
|
||||
# Copyright 2015-2017 See manifest
|
||||
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
|
||||
|
||||
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()
|
||||
lines = self.template_id.template_line_ids
|
||||
for line in lines.filtered(lambda l: l.type == 'input'):
|
||||
self.env['wizard.select.move.template.line'].create({
|
||||
'template_id': self.id,
|
||||
'sequence': line.sequence,
|
||||
'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')
|
||||
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):
|
||||
self.ensure_one()
|
||||
input_lines = {}
|
||||
for template_line in self.line_ids:
|
||||
input_lines[template_line.sequence] = template_line.amount
|
||||
amounts = self.template_id.compute_lines(input_lines)
|
||||
name = self.template_id.name
|
||||
partner = self.partner_id.id
|
||||
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)
|
||||
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})
|
||||
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):
|
||||
return self.env['account.move'].create({
|
||||
'ref': ref,
|
||||
'journal_id': journal_id,
|
||||
'date': self.date,
|
||||
})
|
||||
|
||||
@api.model
|
||||
def _prepare_line(self, line, amounts, partner_id):
|
||||
debit = line.move_line_type == 'dr'
|
||||
values = {
|
||||
'name': line.name,
|
||||
'journal_id': line.journal_id.id,
|
||||
'analytic_account_id': line.analytic_account_id.id,
|
||||
'account_id': line.account_id.id,
|
||||
'credit': not debit and amounts[line.sequence] or 0.0,
|
||||
'debit': debit and amounts[line.sequence] or 0.0,
|
||||
'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
|
||||
|
||||
|
||||
class WizardSelectMoveTemplateLine(models.TransientModel):
|
||||
_description = 'Template Lines'
|
||||
_name = "wizard.select.move.template.line"
|
||||
|
||||
template_id = fields.Many2one(
|
||||
'wizard.select.move.template')
|
||||
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)
|
||||
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,
|
||||
)
|
@ -1,94 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="wizard_select_template" model="ir.ui.view">
|
||||
<field name="name">Select Journal Entry Template</field>
|
||||
<field name="model">wizard.select.move.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Journal Entry Template" >
|
||||
<field name="state" invisible="1"/>
|
||||
<group>
|
||||
<group>
|
||||
<field name="template_id" widget="selection"
|
||||
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>
|
||||
<separator
|
||||
string="Modify the lines before create them or edit them after creation."
|
||||
attrs="{'invisible':[('state','!=','template_selected')]}"/>
|
||||
<field name="line_ids" nolabel="1" attrs="{'invisible':[('state','!=','template_selected')]}"/>
|
||||
<footer>
|
||||
<button name="load_template" class="btn-primary" string="Load" type="object" colspan="1" attrs="{'invisible':[('state','!=','template_selected')]}" />
|
||||
<button name="load_lines" class="btn-primary" string="Next" type="object" attrs="{'invisible':[('state','=','template_selected')]}" />
|
||||
<button special="cancel" string="Cancel" class="btn-default"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="wizard_select_template_line" model="ir.ui.view">
|
||||
<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="Journal Entry Template Line">
|
||||
<group>
|
||||
<field name="sequence" invisible="1"/>
|
||||
<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 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" 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 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>
|
||||
<field name="view_id" ref="wizard_select_template"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<act_window name="Create Journal Entry from Template"
|
||||
res_model="wizard.select.move.template"
|
||||
src_model="account.move"
|
||||
view_mode="form"
|
||||
target="new"
|
||||
key2="client_action_multi"
|
||||
id="action_wizard_select_template_by_move"
|
||||
view_id="wizard_select_template"/>
|
||||
|
||||
<menuitem name="Create Journal Entry from Template" action="action_wizard_select_template" id="menu_action_wizard_select_template" sequence="10" parent="account.menu_finance_entries_accounting_entries"/>
|
||||
</odoo>
|
Loading…
Reference in New Issue
Block a user