Add reconciliation
This commit is contained in:
1 parent
c3df295908
commit
0a855a7de0
2 files changed
+357
-6
No files matched your search
@@ -4,6 +4,7 @@ from trytond.osv import fields, OSV, ExceptORM
|
||||
from trytond.wizard import Wizard, WizardOSV
|
||||
from decimal import Decimal
|
||||
import datetime
|
||||
from trytond.netsvc import LocalService
|
||||
|
||||
_MOVE_STATES = {
|
||||
'readonly': "state == 'posted'",
|
||||
@@ -195,6 +196,57 @@ class Move(OSV):
|
||||
Move()
|
||||
|
||||
|
||||
class Reconciliation(OSV):
|
||||
'Account Move Reconciliation Lines'
|
||||
_name = 'account.move.reconciliation'
|
||||
|
||||
name = fields.Char('Name', size=None, required=True)
|
||||
lines = fields.One2Many('account.move.line', 'reconciliation',
|
||||
'Lines')
|
||||
|
||||
def __init__(self):
|
||||
super(Reconciliation, self).__init__()
|
||||
self._constraints += [
|
||||
('check_lines', 'You can not create reconciliation \n' \
|
||||
'where lines are not balanced, not valid, \n' \
|
||||
'not in the same account!',
|
||||
['lines']),
|
||||
]
|
||||
|
||||
def default_name(self, cursor, user, context=None):
|
||||
sequence_obj = self.pool.get('ir.sequence')
|
||||
return sequence_obj.get(cursor, user, 'account.move.reconciliation')
|
||||
|
||||
def create(self, cursor, user, vals, context=None):
|
||||
workflow_service = LocalService('workflow')
|
||||
res = super(Reconciliation, self).create(cursor, user, vals, context=context)
|
||||
reconciliation = self.browse(cursor, user, res, context=context)
|
||||
for line in reconciliation.lines:
|
||||
workflow_service.trg_trigger(user, 'account.move.line', line.id,
|
||||
cursor)
|
||||
return res
|
||||
|
||||
def check_lines(self, cursor, user, ids):
|
||||
currency_obj = self.pool.get('account.currency')
|
||||
for reconciliation in self.browse(cursor, user, ids):
|
||||
amount = Decimal('0.0')
|
||||
account = None
|
||||
for line in reconciliation.lines:
|
||||
if line.state != 'valid':
|
||||
return False
|
||||
amount += line.debit - line.credit
|
||||
if not account:
|
||||
account = line.account
|
||||
elif account.id != line.account.id:
|
||||
return False
|
||||
if not currency_obj.is_zero(cursor, user, account.company.currency,
|
||||
amount):
|
||||
return False
|
||||
return True
|
||||
|
||||
Reconciliation()
|
||||
|
||||
|
||||
class Line(OSV):
|
||||
'Account Move Line'
|
||||
_name = 'account.move.line'
|
||||
@@ -236,7 +288,8 @@ class Line(OSV):
|
||||
('valid', 'Valid'),
|
||||
], 'State', readonly=True, required=True)
|
||||
active = fields.Boolean('Active', select=2)
|
||||
#TODO add reconcile
|
||||
reconciliation = fields.Many2One('account.move.reconciliation',
|
||||
'Reconciliation', readonly=True, ondelete='SET NULL', select=2)
|
||||
|
||||
def __init__(self):
|
||||
super(Line, self).__init__()
|
||||
@@ -349,12 +402,68 @@ class Line(OSV):
|
||||
partner_obj = self.pool.get('partner.partner')
|
||||
journal_obj = self.pool.get('account.journal')
|
||||
account_obj = self.pool.get('account.account')
|
||||
currency_obj = self.pool.get('account.currency')
|
||||
res = {}
|
||||
if (not vals.get('partner')) or vals.get('account'):
|
||||
return res
|
||||
partner = partner_obj.browse(cursor, user, vals.get('partner'),
|
||||
context=context)
|
||||
#TODO add debit/credit, account for partner, account not reconciled
|
||||
|
||||
if partner and (not vals.get('debit')) and (not vals.get('credit')):
|
||||
query = 'SELECT ' \
|
||||
'COALESCE(SUM(' \
|
||||
'(COALESCE(debit, 0) - COALESCE(credit, 0))' \
|
||||
'), 0)::NUMERIC ' \
|
||||
'FROM account_move_line ' \
|
||||
'WHERE reconciliation IS NULL ' \
|
||||
'AND partner = %s ' \
|
||||
'AND account = %s'
|
||||
cursor.execute(query, (partner.id, partner.account_receivable.id))
|
||||
amount = cursor.fetchone()[0]
|
||||
if not currency_obj.is_zero(cursor, user,
|
||||
partner.account_receivable.currency, amount):
|
||||
if amount > 0:
|
||||
res['credit'] = currency_obj.round(cursor, user,
|
||||
partner.account_receivable.currency, amount)
|
||||
res['debit'] = Decimal('0.0')
|
||||
else:
|
||||
res['credit'] = Decimal('0.0')
|
||||
res['debit'] = - currency_obj.round(cursor, user,
|
||||
partner.account_receivable.currency, amount)
|
||||
res['account'] = account_obj.name_get(cursor, user,
|
||||
partner.account_receivable.id, context=context)[0]
|
||||
else:
|
||||
cursor.execute(query, (partner.id, partner.account_payable.id))
|
||||
amount = cursor.fetchone()[0]
|
||||
if not currency_obj.is_zero(cursor, user,
|
||||
partner.account_payable.currency, amount):
|
||||
if amount > 0:
|
||||
res['credit'] = currency_obj.round(cursor, user,
|
||||
partner.account_payable.currency, amount)
|
||||
res['debit'] = Decimal('0.0')
|
||||
else:
|
||||
res['credit'] = Decimal('0.0')
|
||||
res['debit'] = - currency_obj.round(cursor, user,
|
||||
partner.account_payable.currency, amount)
|
||||
res['account'] = account_obj.name_get(cursor, user,
|
||||
partner.account_payable.id, context=context)[0]
|
||||
|
||||
if partner and vals.get('debit'):
|
||||
if vals['debit'] > 0:
|
||||
res.setdefault('account', account_obj.name_get(cursor, user,
|
||||
partner.account_receivable.id, context=context)[0])
|
||||
else:
|
||||
res.setdefault('account', account_obj.name_get(cursor, user,
|
||||
partner.account_payable.id, context=context)[0])
|
||||
|
||||
if partner and vals.get('credit'):
|
||||
if vals['credit'] > 0:
|
||||
res.setdefault('account', account_obj.name_get(cursor, user,
|
||||
partner.account_payable.id, context=context)[0])
|
||||
else:
|
||||
res.setdefault('account', account_obj.name_get(cursor, user,
|
||||
partner.account_receivable.id, context=context)[0])
|
||||
|
||||
journal_id = vals.get('journal') or context.get('journal')
|
||||
if journal_id and partner:
|
||||
journal = journal_obj.browse(cursor, user, journal_id,
|
||||
@@ -502,7 +611,9 @@ class Line(OSV):
|
||||
if line.move.state == 'posted':
|
||||
raise ExceptORM('UserError',
|
||||
'You can not modify line from a posted move!')
|
||||
#TODO add reconcile
|
||||
if line.reconciliation:
|
||||
raise ExceptORM('UserError',
|
||||
'You can not modify reconciled line!')
|
||||
journal_period = (line.journal.id, line.period.id)
|
||||
if journal_period not in journal_period_done:
|
||||
self.check_journal_period_modify(cursor, user, line.period.id,
|
||||
@@ -617,6 +728,49 @@ class Line(OSV):
|
||||
#TODO add hexmd5
|
||||
return result
|
||||
|
||||
def reconcile(self, cursor, user, ids, journal_id=False, period_id=False,
|
||||
account_id=False, context=None):
|
||||
move_obj = self.pool.get('account.move')
|
||||
currency_obj = self.pool.get('account.currency')
|
||||
reconciliation_obj = self.pool.get('account.move.reconciliation')
|
||||
ids = ids[:]
|
||||
if journal_id and period_id and account_id:
|
||||
account = None
|
||||
amount = Decimal('0.0')
|
||||
for line in self.browse(cursor, user, ids, context=context):
|
||||
amount += line.debit - line.credit
|
||||
if not account:
|
||||
account = line.account
|
||||
amount = currency_obj.round(cursor, user, account.currency, amount)
|
||||
move_id = move_obj.create(cursor, user, {
|
||||
'journal': journal_id,
|
||||
'period': period_id,
|
||||
'date': datetime.date.today(),
|
||||
'lines': [
|
||||
(0, 0, {
|
||||
'name': 'Write-Off',
|
||||
'account': account.id,
|
||||
'debit': amount < 0 and -amount or Decimal('0.0'),
|
||||
'credit': amount > 0 and amount or Decimal('0.0'),
|
||||
}),
|
||||
(0, 0, {
|
||||
'name': 'Write-Off',
|
||||
'account': account_id,
|
||||
'debit': amount > 0 and amount or Decimal('0.0'),
|
||||
'credit': amount < 0 and - amount or Decimal('0.0'),
|
||||
}),
|
||||
],
|
||||
}, context=context)
|
||||
ids += self.search(cursor, user, [
|
||||
('move', '=', move_id),
|
||||
('account', '=', account.id),
|
||||
('debit', '=', amount < 0 and -amount or Decimal('0.0')),
|
||||
('credit', '=', amount > 0 and amount or Decimal('0.0')),
|
||||
], limit=1, context=context)
|
||||
return reconciliation_obj.create(cursor, user, {
|
||||
'lines': [(4, x) for x in ids],
|
||||
}, context=context)
|
||||
|
||||
Line()
|
||||
|
||||
|
||||
@@ -773,6 +927,134 @@ class OpenAccount(Wizard):
|
||||
OpenAccount()
|
||||
|
||||
|
||||
class ReconcileLinesWriteOff(WizardOSV):
|
||||
'Reconcile Lines Write-Off'
|
||||
_name = 'account.move.reconcile_lines.writeoff'
|
||||
journal = fields.Many2One('account.journal', 'Journal', required=True)
|
||||
period = fields.Many2One('account.period', 'Period', required=True)
|
||||
account = fields.Many2One('account.account', 'Account', required=True,
|
||||
domain=[('type', '!=', 'view'), ('type', '!=', 'closed')])
|
||||
|
||||
def default_period(self, cursor, user, context=None):
|
||||
period_obj = self.pool.get('account.period')
|
||||
return period_obj.find(cursor, user, exception=False, context=context)
|
||||
|
||||
ReconcileLinesWriteOff()
|
||||
|
||||
|
||||
class ReconcileLines(Wizard):
|
||||
'Reconcile Lines'
|
||||
_name = 'account.move.reconcile_lines'
|
||||
states = {
|
||||
'init': {
|
||||
'result': {
|
||||
'type': 'choice',
|
||||
'next_state': '_check_writeoff',
|
||||
},
|
||||
},
|
||||
'writeoff': {
|
||||
'result': {
|
||||
'type': 'form',
|
||||
'object': 'account.move.reconcile_lines.writeoff',
|
||||
'state': [
|
||||
('end', 'Cancel', 'gtk-cancel'),
|
||||
('reconcile', 'Reconcile', 'gtk-ok', True),
|
||||
],
|
||||
},
|
||||
},
|
||||
'reconcile': {
|
||||
'actions': ['_reconcile'],
|
||||
'result': {
|
||||
'type': 'state',
|
||||
'state': 'end',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def _check_writeoff(self, cursor, user, data, context=None):
|
||||
line_obj = self.pool.get('account.move.line')
|
||||
currency_obj = self.pool.get('account.currency')
|
||||
|
||||
company = None
|
||||
amount = Decimal('0.0')
|
||||
for line in line_obj.browse(cursor, user, data['ids'],
|
||||
context=context):
|
||||
amount += line.debit - line.credit
|
||||
if not company:
|
||||
company = line.account.company
|
||||
if currency_obj.is_zero(cursor, user, company.currency, amount):
|
||||
return 'reconcile'
|
||||
return 'writeoff'
|
||||
|
||||
def _reconcile(self, cursor, user, data, context=None):
|
||||
line_obj = self.pool.get('account.move.line')
|
||||
|
||||
if data['form']:
|
||||
journal_id = data['form'].get('journal')
|
||||
period_id = data['form'].get('period')
|
||||
account_id = data['form'].get('account')
|
||||
else:
|
||||
journal_id = False
|
||||
period_id = False
|
||||
account_id = False
|
||||
line_obj.reconcile(cursor, user, data['ids'], journal_id, period_id,
|
||||
account_id, context=context)
|
||||
return {}
|
||||
|
||||
ReconcileLines()
|
||||
|
||||
|
||||
class OpenReconcileLinesInit(WizardOSV):
|
||||
_name = 'account.move.open_reconcile_lines.init'
|
||||
account = fields.Many2One('account.account', 'Account', required=True,
|
||||
domain=[('type', '!=', 'view'), ('reconcile', '=', True)])
|
||||
|
||||
OpenReconcileLinesInit()
|
||||
|
||||
|
||||
class OpenReconcileLines(Wizard):
|
||||
'Open Reconcile Lines'
|
||||
_name = 'account.move.open_reconcile_lines'
|
||||
states = {
|
||||
'init': {
|
||||
'result': {
|
||||
'type': 'form',
|
||||
'object': 'account.move.open_reconcile_lines.init',
|
||||
'state': [
|
||||
('end', 'Cancel', 'gtk-cancel'),
|
||||
('open', 'Open', 'gtk-ok', True),
|
||||
],
|
||||
},
|
||||
},
|
||||
'open': {
|
||||
'result': {
|
||||
'type': 'action',
|
||||
'action': '_action_open_reconcile_lines',
|
||||
'state': 'end',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def _action_open_reconcile_lines(self, cursor, user, data, context=None):
|
||||
model_data_obj = self.pool.get('ir.model.data')
|
||||
act_window_obj = self.pool.get('ir.action.act_window')
|
||||
|
||||
model_data_ids = model_data_obj.search(cursor, user, [
|
||||
('fs_id', '=', 'act_move_line_form'),
|
||||
('module', '=', 'account'),
|
||||
], limit=1, context=context)
|
||||
model_data = model_data_obj.browse(cursor, user, model_data_ids[0],
|
||||
context=context)
|
||||
res = act_window_obj.read(cursor, user, model_data.db_id, context=context)
|
||||
res['domain'] = str([
|
||||
('account', '=', data['form']['account']),
|
||||
('reconciliation', '=', False),
|
||||
])
|
||||
return res
|
||||
|
||||
OpenReconcileLines()
|
||||
|
||||
|
||||
class Partner(OSV):
|
||||
_name = 'partner.partner'
|
||||
receivable = fields.Function('get_receivable_payable',
|
||||
@@ -822,7 +1104,7 @@ class Partner(OSV):
|
||||
'AND a.type = %s ' \
|
||||
'AND l.partner IN ' \
|
||||
'(' + ','.join(['%s' for x in ids]) + ') ' \
|
||||
#TODO add reconcile clause
|
||||
'AND l.reconciliation IS NULL ' \
|
||||
'AND ' + line_query + ' ' \
|
||||
'AND a.company = %s ' \
|
||||
'GROUP BY l.partner', (name,) + tuple(ids) + (company_id,))
|
||||
@@ -868,7 +1150,7 @@ class Partner(OSV):
|
||||
'AND a.active ' \
|
||||
'AND a.type = %s ' \
|
||||
'AND l.partner IS NOT NULL ' \
|
||||
#TODO add reconcile clause
|
||||
'AND l.reconciliation IS NOT NULL ' \
|
||||
'AND ' + line_query + ' ' \
|
||||
'AND a.company = %s ' \
|
||||
'GROUP BY l.partner ' \
|
||||
|
||||
@@ -72,6 +72,39 @@
|
||||
<field name="code">account.move</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="move_reconciliation_view_form">
|
||||
<field name="model">account.move.reconciliation</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<form string="Reconciliation" col="2">
|
||||
<label name="name"/>
|
||||
<field name="name"/>
|
||||
<field name="lines" colspan="2"/>
|
||||
</form>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="move_reconciliation_view_tree">
|
||||
<field name="model">account.move.reconciliation</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<tree string="Reconciliations">
|
||||
<field name="name" select="1"/>
|
||||
</tree>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.sequence.type" id="sequence_type_account_move_reconciliation">
|
||||
<field name="name">Account Move Reconciliation</field>
|
||||
<field name="code">account.move.reconciliation</field>
|
||||
</record>
|
||||
<record model="ir.sequence" id="sequence_account_move_reconciliation">
|
||||
<field name="name">Default Account Move Reconciliation</field>
|
||||
<field name="code">account.move.reconciliation</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="move_line_view_form">
|
||||
<field name="model">account.move.line</field>
|
||||
<field name="type">form</field>
|
||||
@@ -107,7 +140,8 @@
|
||||
<field name="second_currency"/>
|
||||
<label name="move"/>
|
||||
<field name="move" required="0"/>
|
||||
<newline/>
|
||||
<label name="reconciliation"/>
|
||||
<field name="reconciliation"/>
|
||||
<label name="maturity_date"/>
|
||||
<field name="maturity_date"/>
|
||||
<label name="create_date"/>
|
||||
@@ -137,6 +171,7 @@
|
||||
<field name="account" select="2"/>
|
||||
<field name="debit" select="2" sum="Debit"/>
|
||||
<field name="credit" select="2" sum="Credit"/>
|
||||
<field name="reconciliation" select="2"/>
|
||||
<field name="state" select="2"/>
|
||||
</tree>
|
||||
]]>
|
||||
@@ -177,6 +212,40 @@
|
||||
</record>
|
||||
<menuitem parent="menu_entries" action="act_open_journal"
|
||||
icon="STOCK_INDENT" id="menu_open_journal"/>
|
||||
|
||||
<record model="ir.ui.view" id="reconcile_lines_writeoff_view_form">
|
||||
<field name="model">account.move.reconcile_lines.writeoff</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<form string="Write-Off">
|
||||
<label name="journal"/>
|
||||
<field name="journal"/>
|
||||
<label name="period"/>
|
||||
<field name="period"/>
|
||||
<label name="account"/>
|
||||
<field name="account"/>
|
||||
</form>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.action.wizard" id="act_reconcile_lines">
|
||||
<field name="name">Reconcile Lines</field>
|
||||
<field name="wiz_name">account.move.reconcile_lines</field>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_reconcile_lines_keyword">
|
||||
<field name="keyword">form_action</field>
|
||||
<field name="model">account.move.line,0</field>
|
||||
<field name="action" ref="act_reconcile_lines"/>
|
||||
</record>
|
||||
<record model="ir.action.wizard" id="act_open_reconcile_lines">
|
||||
<field name="name">Open Reconcile Lines</field>
|
||||
<field name="wiz_name">account.move.open_reconcile_lines</field>
|
||||
</record>
|
||||
<menuitem name="Reconcile Lines" parent="menu_entries"
|
||||
action="act_open_reconcile_lines" icon="STOCK_JUSTIFY_FILL"
|
||||
id="menu_open_reconcile_lines"/>
|
||||
|
||||
<record model="ir.ui.view" id="partner_view_tree">
|
||||
<field name="model">partner.partner</field>
|
||||
<field name="inherit" ref="partner.partner_view_tree"/>
|
||||
|
||||
Reference in new issue
Block a user