Add analytic state on move line

issue5900
review25661002
This commit is contained in:
Cédric Krier
2016-09-28 14:44:59 +02:00
parent 0a50180133
commit fa7412390b
6 changed files with 233 additions and 1 deletions
+1
View File
@@ -1,3 +1,4 @@
* Add analytic state on move line
* Improve account tree creation
* Remove name, journal, reference, party and active from line
* Remove currency from account
+2 -1
View File
@@ -86,7 +86,8 @@ class Account(ModelSQL, ModelView):
mandatory = fields.Boolean('Mandatory', states={
'invisible': Eval('type') != 'root',
},
depends=['type'])
depends=['type'],
help="Make this account mandatory when filling documents")
@classmethod
def __setup__(cls):
+110
View File
@@ -1,6 +1,7 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from decimal import Decimal
from collections import defaultdict
from sql import Literal
@@ -119,6 +120,35 @@ class Line(ModelSQL, ModelView):
clause &= table.date <= Transaction().context['end_date']
return clause
@classmethod
def create(cls, vlist):
pool = Pool()
MoveLine = pool.get('account.move.line')
lines = super(Line, cls).create(vlist)
move_lines = [l.move_line for l in lines]
MoveLine.set_analytic_state(move_lines)
MoveLine.save(move_lines)
return lines
@classmethod
def write(cls, *args):
pool = Pool()
MoveLine = pool.get('account.move.line')
super(Line, cls).write(*args)
lines = sum(args[0:None:2], [])
move_lines = [l.move_line for l in lines]
MoveLine.set_analytic_state(move_lines)
MoveLine.save(move_lines)
@classmethod
def delete(cls, lines):
pool = Pool()
MoveLine = pool.get('account.move.line')
move_lines = [l.move_line for l in lines]
super(Line, cls).delete(lines)
MoveLine.set_analytic_state(move_lines)
MoveLine.save(move_lines)
@classmethod
def validate(cls, lines):
super(Line, cls).validate(lines)
@@ -138,6 +168,16 @@ class Move:
__metaclass__ = PoolMeta
__name__ = 'account.move'
@classmethod
@ModelView.button
def post(cls, moves):
pool = Pool()
MoveLine = pool.get('account.move.line')
super(Move, cls).post(moves)
lines = [l for m in moves for l in m.lines]
MoveLine.set_analytic_state(lines)
MoveLine.save(lines)
def cancel(self, default=None):
'Reverse credit/debit of analytic lines'
pool = Pool()
@@ -157,6 +197,76 @@ class MoveLine(ModelSQL, ModelView):
__name__ = 'account.move.line'
analytic_lines = fields.One2Many('analytic_account.line', 'move_line',
'Analytic Lines')
analytic_state = fields.Selection([
('draft', 'Draft'),
('valid', 'Valid'),
], 'Analytic State', readonly=True, select=True)
@classmethod
def __setup__(cls):
super(MoveLine, cls).__setup__()
cls._check_modify_exclude |= {'analytic_lines', 'analytic_state'}
@classmethod
def __register__(cls, module_name):
TableHandler = backend.get('TableHandler')
table = TableHandler(cls, module_name)
cursor = Transaction().connection.cursor()
sql_table = cls.__table__()
state_exist = table.column_exist('analytic_state')
super(MoveLine, cls).__register__(module_name)
# Migration from 4.0: add analytic_state
if not state_exist:
cursor.execute(
*sql_table.update([sql_table.analytic_state], ['valid']))
@classmethod
def default_analytic_state(cls):
return 'draft'
@classmethod
def set_analytic_state(cls, lines):
pool = Pool()
AccountType = pool.get('account.account.type')
AnalyticAccount = pool.get('analytic_account.account')
income_types = AccountType.search([
('income_statement', '=', True),
])
income_types = AccountType.search([
('parent', 'child_of', [t.id for t in income_types]),
])
income_types = set(income_types)
roots = AnalyticAccount.search([
('parent', '=', None),
])
roots = set(roots)
for line in lines:
if line.account.type not in income_types:
if not line.analytic_lines:
line.analytic_state = 'valid'
else:
line.analytic_state = 'draft'
continue
amounts = defaultdict(Decimal)
for analytic_line in line.analytic_lines:
amount = analytic_line.debit - analytic_line.credit
amounts[analytic_line.account.root] += amount
if not roots <= set(amounts.iterkeys()):
line.analytic_state = 'draft'
continue
amount = line.debit - line.credit
for analytic_amount in amounts.itervalues():
if analytic_amount != amount:
line.analytic_state = 'draft'
break
else:
line.analytic_state = 'valid'
class OpenAccount(Wizard):
+28
View File
@@ -67,5 +67,33 @@ this repository contains the full copyright notices and license terms. -->
<field name="name">move_line_form</field>
</record>
<record model="ir.ui.view" id="move_line_view_list">
<field name="model">account.move.line</field>
<field name="type">tree</field>
<field name="name">move_line_list</field>
</record>
<record model="ir.action.act_window" id="act_move_line_form_completion">
<field name="name">Analytic Lines to Complete</field>
<field name="res_model">account.move.line</field>
<field name="domain"
eval="[('analytic_state', '=', 'draft'), ('move_state', '=', 'posted')]"
pyson="1"/>
</record>
<record model="ir.action.act_window.view"
id="act_move_line_form_completion_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="move_line_view_list"/>
<field name="act_window" ref="act_move_line_form_completion"/>
</record>
<record model="ir.action.act_window.view"
id="act_move_line_form_completion_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="account.move_line_view_form"/>
<field name="act_window" ref="act_move_line_form_completion"/>
</record>
<menuitem parent="account.menu_processing"
action="act_move_line_form_completion" id="menu_completion"/>
</data>
</tryton>
+77
View File
@@ -134,6 +134,83 @@ class AnalyticAccountTestCase(ModuleTestCase):
(Decimal(30), Decimal(100)))
self.assertEqual(analytic_account.balance, Decimal(70))
@with_transaction()
def test_move_line_state(self):
"Test move line state"
pool = Pool()
Party = pool.get('party.party')
AnalyticAccount = pool.get('analytic_account.account')
AnalyticLine = pool.get('analytic_account.line')
Journal = pool.get('account.journal')
Account = pool.get('account.account')
Move = pool.get('account.move')
MoveLine = pool.get('account.move.line')
party = Party(name='Party')
party.save()
company = create_company()
with set_company(company):
root, = AnalyticAccount.create([{
'type': 'root',
'name': 'Root',
}])
analytic_account1, analytic_account2 = AnalyticAccount.create([{
'type': 'normal',
'name': 'Analytic Account 1',
'parent': root.id,
'root': root.id,
}, {
'type': 'normal',
'name': 'Analytic Account 2',
'parent': root.id,
'root': root.id,
}])
create_chart(company)
fiscalyear = get_fiscalyear(company)
fiscalyear.save()
fiscalyear.create_period([fiscalyear])
period = fiscalyear.periods[0]
journal_expense, = Journal.search([
('code', '=', 'EXP'),
])
expense, = Account.search([
('kind', '=', 'expense'),
])
payable, = Account.search([
('kind', '=', 'payable'),
])
move = Move()
move.period = period
move.journal = journal_expense
move.date = period.start_date
move.lines = [
MoveLine(account=expense, debit=Decimal(100)),
MoveLine(account=payable, credit=Decimal(100), party=party),
]
move.save()
Move.post([move])
expense_line, = [l for l in move.lines if l.account == expense]
payable_line, = [l for l in move.lines if l.account == payable]
self.assertEqual(expense_line.analytic_state, 'draft')
self.assertEqual(payable_line.analytic_state, 'valid')
expense_line.analytic_lines = [
AnalyticLine(
account=analytic_account1, name='Test',
debit=Decimal(50), journal=journal_expense,
date=period.start_date),
AnalyticLine(
account=analytic_account2, name='Test',
debit=Decimal(50), journal=journal_expense,
date=period.start_date),
]
expense_line.save()
self.assertEqual(expense_line.analytic_state, 'valid')
def suite():
suite = trytond.tests.test_tryton.suite()
+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tree>
<field name="move"/>
<field name="origin"/>
<field name="description"/>
<field name="move_description"/>
<field name="date"/>
<field name="account"/>
<field name="party"/>
<field name="debit" sum="Debit"/>
<field name="credit" sum="Credit"/>
<field name="analytic_state"/>
</tree>