2
0

[MIG] account_move_template from v11 to v12 (full re-write)

This commit is contained in:
Alexis de Lattre 2019-04-11 15:49:20 +02:00 committed by Abraham Anes
parent 7f35b8a9ee
commit d2da4f4276
16 changed files with 410 additions and 466 deletions

View File

@ -1,27 +1,25 @@
# Copyright 2015-2017 See manifest # Copyright 2015-2017 See manifest
# Copyright 2018 Raf Ven <raf.ven@dynapps.be> # 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 # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
{ {
'name': "Account Move Template", 'name': "Account Move Template",
'version': '11.0.1.0.0', 'version': '12.0.1.0.0',
'category': 'Generic Modules/Accounting', 'category': 'Accounting',
'summary': "Templates for recurring Journal Entries", 'summary': "Templates for recurring Journal Entries",
'author': "Agile Business Group, Odoo Community Association (OCA), Aurium " 'author': "Agile Business Group, Odoo Community Association (OCA), Aurium "
"Technologies, Vauxoo, Eficent", "Technologies, Vauxoo, Eficent, Akretion",
'website': 'https://github.com/OCA/account-financial-tools', 'website': 'https://github.com/OCA/account-financial-tools',
'license': 'AGPL-3', 'license': 'AGPL-3',
'depends': [ 'depends': [
'account', 'account',
'analytic',
], ],
'data': [ 'data': [
'security/ir.model.access.csv',
'view/move_template.xml',
'wizard/select_template.xml',
'security/account_move_template_security.xml', 'security/account_move_template_security.xml',
], 'security/ir.model.access.csv',
'test': [ 'wizard/account_move_template_run_view.xml',
'view/account_move_template.xml',
], ],
'installable': True, 'installable': True,
} }

View File

@ -1,2 +1 @@
from . import account_document_template
from . import account_move_template from . import account_move_template

View File

@ -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')

View File

@ -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 # 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): class AccountMoveTemplate(models.Model):
_name = 'account.move.template' _name = 'account.move.template'
_inherit = ['account.document.template', _description = 'Journal Entry 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'
)
name = fields.Char(required=True)
company_id = fields.Many2one( company_id = fields.Many2one(
'res.company', 'res.company', string='Company', required=True, ondelete='cascade',
required=True, default=lambda self: self.env['res.company']._company_default_get())
change_default=True, journal_id = fields.Many2one(
default=_company_get, 'account.journal', string='Journal', required=True)
) ref = fields.Char(string='Reference', copy=False)
journal_id = fields.Many2one('account.journal', required=True) line_ids = fields.One2many(
template_line_ids = fields.One2many( 'account.move.template.line', inverse_name='template_id',
'account.move.template.line', string='Lines')
inverse_name='template_id',
)
@api.multi _sql_constraints = [(
def action_run_template(self): 'name_company_unique',
'unique(name, company_id)',
'This name is already used by another template!'
)]
def copy(self, default=None):
self.ensure_one() self.ensure_one()
action = self.env.ref( default = dict(default or {}, name=_('%s (copy)') % self.name)
'account_move_template.action_wizard_select_template').read()[0] return super(AccountMoveTemplate, self).copy(default)
action.update({'context': {'default_template_id': self.id}})
return action
_sql_constraints = [ def eval_computed_line(self, line, sequence2amount):
('sequence_move_template_uniq', 'unique (name,company_id)', safe_eval_dict = {}
'The name of the template must be unique per company !') 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): class AccountMoveTemplateLine(models.Model):
_name = 'account.move.template.line' _name = 'account.move.template.line'
_inherit = 'account.document.template.line' _description = 'Journal Item Template'
_order = 'sequence, id'
company_id = fields.Many2one('res.company', template_id = fields.Many2one(
related='template_id.company_id', 'account.move.template', string='Move Template', ondelete='cascade')
readonly=True) name = fields.Char(string='Label')
journal_id = fields.Many2one('account.journal', sequence = fields.Integer('Sequence', required=True)
related='template_id.journal_id',
store=True, readonly=True)
account_id = fields.Many2one( account_id = fields.Many2one(
'account.account', 'account.account', string='Account',
required=True, required=True, domain=[('deprecated', '=', False)])
ondelete="cascade" partner_id = fields.Many2one(
) 'res.partner', string='Partner',
partner_id = fields.Many2one('res.partner', string='Partner') domain=['|', ('parent_id', '=', False), ('is_company', '=', True)])
move_line_type = fields.Selection(
[('cr', 'Credit'), ('dr', 'Debit')],
required=True
)
analytic_account_id = fields.Many2one( analytic_account_id = fields.Many2one(
'account.analytic.account', 'account.analytic.account', string='Analytic Account')
ondelete="cascade" analytic_tag_ids = fields.Many2many(
) 'account.analytic.tag', string='Analytic Tags')
analytic_tag_ids = fields.Many2many('account.analytic.tag',
string='Analytic tags')
tax_ids = fields.Many2many('account.tax', string='Taxes') tax_ids = fields.Many2many('account.tax', string='Taxes')
tax_line_id = fields.Many2one('account.tax', string='Originator tax', tax_line_id = fields.Many2one(
ondelete='restrict') 'account.tax', string='Originator Tax', ondelete='restrict')
template_id = fields.Many2one('account.move.template') 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 = [ _sql_constraints = [(
('sequence_template_uniq', 'unique (template_id,sequence)', 'sequence_template_uniq',
'The sequence of the line must be unique per template !') '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)

View File

@ -5,6 +5,7 @@ Authors
* Lorenzo Battistini <lorenzo.battistini@agilebg.com> * Lorenzo Battistini <lorenzo.battistini@agilebg.com>
* Paolo Chiara <p.chiara@isa.it> * Paolo Chiara <p.chiara@isa.it>
* Franco Tampieri <franco.tampieri@agilebg.com> * Franco Tampieri <franco.tampieri@agilebg.com>
* Alexis de Lattre <alexis.delattre@akretion.com> (full re-write for v12)
Contributors Contributors
------------ ------------

View File

@ -1,7 +1,7 @@
To create new templates: To create new templates:
#. Make sure that you have flagged *Show Full Accounting Features* in your #. 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 #. Go to *Invoicing / Configuration / Accounting / Journal Templates* and
define there your template. You can choose to complete a line using a 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. defined formula, based on other lines, or by requiring the user input.

View File

@ -2,10 +2,9 @@
<odoo noupdate="1"> <odoo noupdate="1">
<record id="account_move_template_comp_rule" model="ir.rule"> <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="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> </record>
</odoo> </odoo>

View File

@ -1,9 +1,3 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" 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_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_document_template_manager","account_document_template_manager","model_account_document_template","account.group_account_manager","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
"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"

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_account_document_template_user access_account_move_template_user account_document_template_user Full access on account.move.template to accountant grp model_account_document_template model_account_move_template account.group_account_user 1 1 1 1
3 access_account_document_template_manager access_account_move_template_line_user account_document_template_manager Full access on account.move.template.line to accountant grp model_account_document_template model_account_move_template_line account.group_account_manager account.group_account_user 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

View File

@ -1,2 +1,3 @@
# Needs to be re-written because wizard.multi.charts.accounts
from . import test_account_move_template # doesn't exist any more on v12
# from . import test_account_move_template

View File

@ -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). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging import logging
from psycopg2 import IntegrityError from psycopg2 import IntegrityError

View File

@ -1,28 +1,28 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <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="name">account.move.template.line.tree</field>
<field name="model">account.move.template.line</field> <field name="model">account.move.template.line</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree> <tree>
<field name="sequence"/> <field name="sequence"/>
<field name="name"/> <field name="account_id" domain="[('company_id', '=', company_id)]"/>
<field name="journal_id" domain="[('company_id','=',company_id)]"/>
<field name="account_id" domain="[('company_id','=',company_id)]"/>
<field name="partner_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="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="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_id" invisible="1"/>
<field name="company_currency_id" invisible="1"/>
</tree> </tree>
</field> </field>
</record> </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="name">account.move.template.line.form</field>
<field name="model">account.move.template.line</field> <field name="model">account.move.template.line</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
@ -30,10 +30,9 @@
<sheet> <sheet>
<group> <group>
<group> <group>
<field name="name"/>
<field name="sequence"/> <field name="sequence"/>
<field name="journal_id" domain="[('company_id','=',company_id)]"/> <field name="name"/>
<field name="account_id" domain="[('company_id','=',company_id)]"/> <field name="account_id" domain="[('company_id', '=', company_id)]"/>
<field name="partner_id"/> <field name="partner_id"/>
<field name="company_id" invisible="1"/> <field name="company_id" invisible="1"/>
</group> </group>
@ -42,34 +41,41 @@
<field name="analytic_tag_ids" widget="many2many_tags"/> <field name="analytic_tag_ids" widget="many2many_tags"/>
</group> </group>
<group> <group>
<field name="type"/>
<field name="move_line_type"/> <field name="move_line_type"/>
<field name="type"/>
<field name="note"/>
</group> </group>
<group string="Taxes"> <group string="Taxes">
<field name="tax_line_id"/> <field name="tax_line_id"/>
<field name="tax_ids" widget="many2many_tags"/> <field name="tax_ids" widget="many2many_tags"/>
</group> </group>
</group> </group>
<label for="python_code" string="Python Code" <group name="python_code" attrs="{'invisible': [('type', '!=', 'computed')]}" col="1" string="Compute Formula">
attrs="{'invisible': [('type', '!=', 'computed')]}" nolabel="1"/> <div class="oe_account_help">
<field name="python_code" colspan="4" attrs="{'readonly': [('type', '!=', 'computed')]}" nolabel="1"/> <p>You can refer to other lines using their sequence number e.g. <i>L1</i> for line with sequence = 1. Examples:</p>
<div class="oe_account_help">You can refer to other lines using their <ul>
sequence number (e.g. 'L(1)' for first line). <li>L1 * 0.2</li>
Examples of code: 'L(1) * 0.2'; 'L(2) - L(1)'; 'L(1) <li>L2 - L1</li>
+ L(2) + L(3)'; '1250'</div> <li>L1 + L2 + L3</li>
<li>1250</li>
</ul>
</div>
<field name="python_code" nolabel="1" attrs="{'required': [('type', '=', 'computed')]}"/>
</group>
</sheet> </sheet>
</form> </form>
</field> </field>
</record> </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="name">account.move.template.form</field>
<field name="model">account.move.template</field> <field name="model">account.move.template</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Journal Entry Template"> <form string="Journal Entry Template">
<header> <header>
<button string="Test template" <button string="Generate Journal Entry"
name="action_run_template" name="generate_journal_entry"
class="btn-primary" class="btn-primary"
type="object"/> type="object"/>
</header> </header>
@ -78,58 +84,58 @@
<label for="name" class="oe_edit_only"/> <label for="name" class="oe_edit_only"/>
<h1><field name="name"/></h1> <h1><field name="name"/></h1>
</div> </div>
<group> <group name="main">
<field name="company_id" widget='selection' groups="base.group_multi_company"/> <group name="main-left">
<field name="journal_id" widget='selection'/> <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> </group>
<field nolabel="1" name="template_line_ids" context="{'default_company_id' : company_id}"/>
</sheet> </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> </form>
</field> </field>
</record> </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="name">account.move.template.tree</field>
<field name="model">account.move.template</field> <field name="model">account.move.template</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree> <tree>
<field name="name"/> <field name="name"/>
<field name="journal_id"/>
<field name="company_id" groups="base.group_multi_company"/> <field name="company_id" groups="base.group_multi_company"/>
</tree> </tree>
</field> </field>
</record> </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="name">account.move.template.search</field>
<field name="model">account.move.template</field> <field name="model">account.move.template</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="Journal Entry Template"> <search string="Journal Entry Template">
<group>
<separator orientation="vertical"/>
<field name="name"/> <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> </group>
</search> </search>
</field> </field>
</record> </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="name">Journal Entry Templates</field>
<field name="res_model">account.move.template</field> <field name="res_model">account.move.template</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
<field name="search_view_id" ref="view_move_template_search"/>
</record> </record>
<menuitem <menuitem
action="action_move_template_form" id="account_move_template_menu"
id="menu_action_move_template_form" sequence="300" action="account_move_template_action"
parent="account.account_account_menu" parent="account.account_account_menu"
groups="account.group_account_manager"/> sequence="300"/>
</odoo> </odoo>

View File

@ -1 +1 @@
from . import select_template from . import account_move_template_run

View 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)

View File

@ -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>

View File

@ -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,
)

View File

@ -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>