diff --git a/CHANGELOG b/CHANGELOG
index 96b2931c18..3f98124cb3 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,5 @@
+* Add Journal Cash reporting
+
Version 3.6.0 - 2015-04-20
* Bug fixes (see mercurial logs for details)
* Add support for PyPy
diff --git a/__init__.py b/__init__.py
index fedf7d5cc7..8a718fc64c 100644
--- a/__init__.py
+++ b/__init__.py
@@ -41,6 +41,7 @@ def register():
JournalView,
JournalViewColumn,
Journal,
+ OpenJournalCashStart,
JournalPeriod,
Move,
Reconciliation,
@@ -90,6 +91,7 @@ def register():
OpenAgedBalance,
ClosePeriod,
ReOpenPeriod,
+ OpenJournalCash,
CloseJournalPeriod,
ReOpenJournalPeriod,
OpenJournal,
diff --git a/journal.py b/journal.py
index 0e3a9eb53a..b3e498e7ba 100644
--- a/journal.py
+++ b/journal.py
@@ -1,16 +1,22 @@
# 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 sql import Null
from sql.conditionals import Case
+from sql.aggregate import Sum
from trytond.model import ModelView, ModelSQL, fields, Unique
-from trytond.wizard import Wizard, StateTransition
+from trytond.wizard import Wizard, StateTransition, StateView, StateAction
+from trytond.wizard import Button
from trytond import backend
-from trytond.pyson import Eval, Bool
+from trytond.pyson import Eval, Bool, PYSONEncoder
from trytond.transaction import Transaction
from trytond.pool import Pool
+from trytond.tools import reduce_ids, grouped_slice
__all__ = ['JournalType', 'JournalView', 'JournalViewColumn', 'Journal',
+ 'OpenJournalCash', 'OpenJournalCashStart',
'JournalPeriod', 'CloseJournalPeriod', 'ReOpenJournalPeriod']
STATES = {
@@ -128,6 +134,17 @@ class Journal(ModelSQL, ModelView):
& (Eval('context', {}).get('company', -1) != -1)),
'invisible': ~Eval('context', {}).get('company', -1),
}, depends=['type']))
+ debit = fields.Function(fields.Numeric('Debit',
+ digits=(16, Eval('currency_digits', 2)),
+ depends=['currency_digits']), 'get_debit_credit_balance')
+ credit = fields.Function(fields.Numeric('Credit',
+ digits=(16, Eval('currency_digits', 2)),
+ depends=['currency_digits']), 'get_debit_credit_balance')
+ balance = fields.Function(fields.Numeric('Balance',
+ digits=(16, Eval('currency_digits', 2)),
+ depends=['currency_digits']), 'get_debit_credit_balance')
+ currency_digits = fields.Function(fields.Integer('Currency Digits'),
+ 'get_currency_digits')
@classmethod
def __setup__(cls):
@@ -181,6 +198,110 @@ class Journal(ModelSQL, ModelView):
(cls._rec_name,) + tuple(clause[1:]),
]
+ @classmethod
+ def get_currency_digits(cls, journals, name):
+ pool = Pool()
+ Company = pool.get('company.company')
+ company_id = Transaction().context.get('company')
+ if company_id:
+ company = Company(company_id)
+ digits = company.currency.digits
+ else:
+ digits = 2
+ return dict.fromkeys([j.id for j in journals], digits)
+
+ @classmethod
+ def get_debit_credit_balance(cls, journals, names):
+ pool = Pool()
+ MoveLine = pool.get('account.move.line')
+ Move = pool.get('account.move')
+ context = Transaction().context
+ cursor = Transaction().cursor
+
+ result = {}
+ ids = [j.id for j in journals]
+ for name in ['debit', 'credit', 'balance']:
+ result[name] = dict.fromkeys(ids, 0)
+
+ line = MoveLine.__table__()
+ move = Move.__table__()
+ where = ((move.date >= context['start_date'])
+ & (move.date <= context['end_date']))
+ for sub_journals in grouped_slice(journals):
+ sub_journals = list(sub_journals)
+ red_sql = reduce_ids(move.journal, [j.id for j in sub_journals])
+ accounts = None
+ for journal in sub_journals:
+ credit_account = (journal.credit_account.id
+ if journal.credit_account else None)
+ debit_account = (journal.debit_account.id
+ if journal.debit_account else None)
+ clause = ((move.journal == journal.id)
+ & (((line.credit != Null)
+ & (line.account == credit_account))
+ | ((line.debit != Null)
+ & (line.account == debit_account))))
+ if accounts is None:
+ accounts = clause
+ else:
+ accounts |= clause
+
+ query = line.join(move, 'LEFT', condition=line.move == move.id
+ ).select(move.journal, Sum(line.debit), Sum(line.credit),
+ where=where & red_sql & accounts,
+ group_by=move.journal)
+ cursor.execute(*query)
+ for journal_id, debit, credit in cursor.fetchall():
+ # SQLite uses float for SUM
+ if not isinstance(debit, Decimal):
+ debit = Decimal(str(debit))
+ if not isinstance(credit, Decimal):
+ credit = Decimal(str(credit))
+ result['debit'][journal_id] = debit
+ result['credit'][journal_id] = credit
+ result['balance'][journal_id] = debit - credit
+ return result
+
+
+class OpenJournalCash(Wizard):
+ 'Open Journal Cash'
+ __name__ = 'account.journal.open_cash'
+ start = StateView('account.journal.open_cash.start',
+ 'account.journal_open_cash_start_view_form', [
+ Button('Cancel', 'end', 'tryton-cancel'),
+ Button('Open', 'open_', 'tryton-ok', default=True),
+ ])
+ open_ = StateAction('account.act_journal_open_cash')
+
+ def do_open_(self, action):
+ pool = Pool()
+ Lang = pool.get('ir.lang')
+
+ lang, = Lang.search([
+ ('code', '=', Transaction().language),
+ ])
+
+ action['pyson_context'] = PYSONEncoder().encode({
+ 'start_date': self.start.start_date,
+ 'end_date': self.start.end_date,
+ })
+ action['name'] += ' %s - %s' % (
+ Lang.strftime(self.start.start_date, lang.code, lang.date),
+ Lang.strftime(self.start.end_date, lang.code, lang.date))
+ return action, {}
+
+
+class OpenJournalCashStart(ModelView):
+ 'Open Journal Cash'
+ __name__ = 'account.journal.open_cash.start'
+ start_date = fields.Date('Start Date', required=True)
+ end_date = fields.Date('End Date', required=True)
+
+ @classmethod
+ def default_start_date(cls):
+ return Pool().get('ir.date').today()
+ default_end_date = default_start_date
+
class JournalPeriod(ModelSQL, ModelView):
'Journal - Period'
diff --git a/journal.xml b/journal.xml
index 3e7bf56ae9..cbc0bf50fe 100644
--- a/journal.xml
+++ b/journal.xml
@@ -161,6 +161,13 @@ this repository contains the full copyright notices and license terms. -->
tree
journal_tree
+
+ account.journal
+
+ tree
+ journal_list_cash
+
+
Journals
account.journal
@@ -178,6 +185,32 @@ this repository contains the full copyright notices and license terms. -->
+
+ account.journal.open_cash.start
+ form
+ journal_open_cash_start
+
+
+
+ Open Journal Cash
+ account.journal.open_cash
+
+
+
+
+ Journals Cash
+ account.journal
+
+
+
+
+
+
+
+
diff --git a/view/journal_list_cash.xml b/view/journal_list_cash.xml
new file mode 100644
index 0000000000..4d2c93de5a
--- /dev/null
+++ b/view/journal_list_cash.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
diff --git a/view/journal_open_cash_start.xml b/view/journal_open_cash_start.xml
new file mode 100644
index 0000000000..ae6f9729b4
--- /dev/null
+++ b/view/journal_open_cash_start.xml
@@ -0,0 +1,9 @@
+
+
+