From 958749ee98eaa3ab000384618ddcb141d76bf4ab Mon Sep 17 00:00:00 2001 From: Maxime Chambreuil Date: Mon, 22 May 2017 15:35:37 -0500 Subject: [PATCH] [ADD] account_banking_reconciliation --- account_banking_reconciliation/README.rst | 207 +++++ account_banking_reconciliation/__init__.py | 7 + .../__manifest__.py | 24 + .../models/__init__.py | 7 + .../models/account_banking_reconciliation.py | 473 +++++++++++ .../models/account_move_line.py | 37 + .../report/bank_statement_detail.rml | 750 ++++++++++++++++++ .../report/bank_statement_report.xml | 19 + .../report/bank_statement_summary.rml | 428 ++++++++++ .../account_banking_reconciliation.xml | 25 + .../security/ir.model.access.csv | 5 + .../static/description/icon.png | Bin 0 -> 9455 bytes .../views/account_banking_reconciliation.xml | 230 ++++++ .../views/account_move_line.xml | 40 + 14 files changed, 2252 insertions(+) create mode 100644 account_banking_reconciliation/README.rst create mode 100644 account_banking_reconciliation/__init__.py create mode 100644 account_banking_reconciliation/__manifest__.py create mode 100644 account_banking_reconciliation/models/__init__.py create mode 100644 account_banking_reconciliation/models/account_banking_reconciliation.py create mode 100644 account_banking_reconciliation/models/account_move_line.py create mode 100644 account_banking_reconciliation/report/bank_statement_detail.rml create mode 100644 account_banking_reconciliation/report/bank_statement_report.xml create mode 100644 account_banking_reconciliation/report/bank_statement_summary.rml create mode 100644 account_banking_reconciliation/security/account_banking_reconciliation.xml create mode 100644 account_banking_reconciliation/security/ir.model.access.csv create mode 100644 account_banking_reconciliation/static/description/icon.png create mode 100644 account_banking_reconciliation/views/account_banking_reconciliation.xml create mode 100644 account_banking_reconciliation/views/account_move_line.xml diff --git a/account_banking_reconciliation/README.rst b/account_banking_reconciliation/README.rst new file mode 100644 index 00000000..8a96742f --- /dev/null +++ b/account_banking_reconciliation/README.rst @@ -0,0 +1,207 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +============================= +Bank Statement Reconciliation +============================= + +This module is designed to provide an easy method in which Odoo accounting users +can manually reconcile/validate their financial transactions from their financial +institution/transaction providers (e.g. Paypal, A financial institution, google +wallet, etc) against Odoo GL Chart of Account bank accounts. + +Users will be able to validate and indicate if a transaction has "Cleared the +Bank" using a checkmark on a new Reconcile Financial Account Statement view on +each individual financial transaction. Users will also be able to mark +transactions on a bank account for future research. + +The idea is that as a first step users will manually look at their paper statement +and line-by-line check off which financial transactions have cleared the bank in +Odoo using the new Bank Reconciliation Wizard. These changes will be displayed on +the new Reconcile Financial Account Statement tree view screen. This is the +process in which many companies reconcile (aka Audit) their bank account statements +and accounting system today and represents good segregation of duties. + +Users can save their in-process reconciliations. + +Background +---------- + +Using the search view filters - users will also be able to effectively sort, +filter the transactions on a particular GL Financial Account. This new screen +will display the journal items associated with a particular bank account. +Several of the field labels have been relabeled to a more common vernacular. + +The need for this module is driven by the following: + +* Users want to easily record whether bank transactions sent to their bank have + "cleared the bank"- definition of "cleared the bank": presented to the bank for + payment and paid by the bank - added/subtracted funds on a bank account. +* Users want the ability to validate if the bank processed the transactions them + correctly (e.g. properly encoded transaction - e.g. company sent a payment of + $20.20 to the bank. Was it processed for $20.20?). This can be considered + "Auditing the bank statement". We don't assume the bank correctly processed any + transaction. +* Users want to understand what payments they've made are still outstanding and + have not been paid by the bank. +* The financial auditing segregation standard of separating the duties of: + recording customer payments and making deposits; recording supplier payments + and writing checks; and monitoring bank account activity. This segregation of + duties is necessary to monitor and help prevent fraud. + +Assumptions +----------- + +#. Companies using Odoo have setup a one-to-one relationship between their + bank accounts and their Odoo GL accounts. Each bank account should have a + corresponding GL account that is not shared with another account. + Example: + + +----------------------+------------------------------------------------------------+ + | Odoo GL Account # | Corresponding Bank Account | + +======================+============================================================+ + | 10100 | Bank (AR) Account Checking 5434 (held at Institution A) | + +----------------------+------------------------------------------------------------+ + | 10200 | Master Bank Account 2343 (held at Institution A) | + +----------------------+------------------------------------------------------------+ + | 10300 | Bank Payable Account Checking 5678 (held at Institution A)| + +----------------------+------------------------------------------------------------+ + | 10400 | Bank Payroll Account 6656 (held at Institution B) | + +----------------------+------------------------------------------------------------+ + | 10500 | Paypal Account 3343 (held at Paypal) | + +----------------------+------------------------------------------------------------+ + | 10600 | Google Wallet Account 6788 | + +----------------------+------------------------------------------------------------+ + | 10700 | AMEX Corporate Card Account 9989 | + +----------------------+------------------------------------------------------------+ + +#. Companies have included a Non-Deposited Funds Account in their GL account + (typically in current assets in their bank account areas). This account is + used to store payments that have been recorded in Odoo - but not yet + deposited into the financial institution. (NOTE: this account is important to + have if the user "batches check deposits"- which is the process of making a + large single deposits of customer payment into the bank (e.g. $20,000USD), but + it is made up of smaller checks (e.g. 20 checks of $1,000 each). Many banks + just record the total deposit amount ($20,000) and don¬タルt provide the + breakdown of the individual checks that make up the larger deposit. This + setup approach enables users to drill down and see the individual checks that + make up a larger deposit. + +Recommendations +--------------- + +From a cash management and financial control perspective, it is recommended that +users establish the following four (4) bank accounts at their financial +institution at a minimum to handle financial transactions. (NOTE: we recommend +users place the last 4 digits of their bank account in the GL account name of the +account. It helps accountants in their management of the system): + +* Bank (AR) Account Checking 5434. This is a checking account designated as the + account where payments made to the company are deposited (e.g. a customer + payment made by check is deposited here, or a customer paying by electronic + transaction EFT/ACH is deposited into this GL). +* Master Bank Account 2343. This is the master account in which the company + keeps the majority of their funds. Often with the most limited access. +* Bank Payable Account Checking 5678. This is a checking account designated for + the company to pay their expenses from. (e.g. Company writes a check to pay a + supplier for their office supplies). +* Bank Payroll Account 6656. This is a checking account designated for a company + to pay their employees and payroll. + +Note +---- + +There has been common confusion in the Odoo community about managing bank +statements in the base Odoo system. This module hopes to alleviate this gap and +provide users with a sound alternative to maintain fiscal control, be easy to +understand, and allow for future growth. + +Why this approach? +------------------ + +Users in Odoo have several options in which to record financial transactions that +affect the balances of a bank account (GL Bank Account entries). We believe our +approach allows these to work in conjunction with each other: Import Electronic +Bank Statements to enter payments (this approach follows the philosophy that you +first find out that a transaction has occurred from your bank which is very +common in Europe due to the electronic nature of transactions). + +* Payment Order Payments (using the direct Method) - Payments are instantly recorded + and financial transactions posted into the GL +* Voucher Payments - Payments are instantly recorded and financial transactions + posted into the GL +* Sales Receipts/Refunds +* Transfers between accounts (a new module is being developed to help manage this) +* Funds moved from the Undeposited Funds GL account to a Bank Account GL account. +* Direct Journal Entries + +Usage +===== + +* Go to Accounting + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/98/10.0 + +Roadmap +======= + +* Include a wizard to pre-filter the tree view, and include additional reports for + accountants. And include the ability to execute quick entry of common statement + related transactions such as recording bank service fees (an Expense), and + Interest Income Earned on the bank account (Income), when allowed by law. + Often, companies will print their reconciliation, staple it to their bank + statement and file the documents for future review and audit. +* Include more information to add further clarity throughout Odoo for financial + transactions, and include the ability to quickly compare an imported bank + statement with a GL Bank Account's recorded transaction, including + autoreconciling them - and providing the information available for review. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smash it by providing detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Nova Point Group +* Balaji Kannan +* Bhavesh Odedra +* Sandeep Mangukiya + +Funders +------- + +The development of this module has been financially supported by: + +* Nova Point Group +* Ursa Information Systems + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/account_banking_reconciliation/__init__.py b/account_banking_reconciliation/__init__.py new file mode 100644 index 00000000..f82490e2 --- /dev/null +++ b/account_banking_reconciliation/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 Ursa Information Systems (http://www.ursainfosystems.com>) +# Copyright (C) 2011 NovaPoint Group LLC () +# Copyright (C) 2004-2010 OpenERP SA () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/account_banking_reconciliation/__manifest__.py b/account_banking_reconciliation/__manifest__.py new file mode 100644 index 00000000..abb44f01 --- /dev/null +++ b/account_banking_reconciliation/__manifest__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 Ursa Information Systems (http://www.ursainfosystems.com>) +# Copyright (C) 2011 NovaPoint Group LLC () +# Copyright (C) 2004-2010 OpenERP SA () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Bank Account Reconciliation", + "version": "10.0.1.0.0", + "license": "AGPL-3", + "category": "Accounting and Financial Management", + "author": "NovaPoint Group LLC, Ursa Information Systems, Odoo Community Association (OCA)", + "website": "http://www.novapointgroup.com", + "depends": [ + "account_voucher", + ], + "data": [ + "security/account_banking_reconciliation.xml", + "security/ir.model.access.csv", + "views/account_banking_reconciliation.xml", + "views/account_move_line.xml", + "report/bank_statement_report.xml"], + "demo": [], + "installable": False, +} diff --git a/account_banking_reconciliation/models/__init__.py b/account_banking_reconciliation/models/__init__.py new file mode 100644 index 00000000..9194bb96 --- /dev/null +++ b/account_banking_reconciliation/models/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 Ursa Information Systems (http://www.ursainfosystems.com>) +# Copyright (C) 2011 NovaPoint Group LLC () +# Copyright (C) 2004-2010 OpenERP SA () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import account_move_line, account_banking_reconciliation \ No newline at end of file diff --git a/account_banking_reconciliation/models/account_banking_reconciliation.py b/account_banking_reconciliation/models/account_banking_reconciliation.py new file mode 100644 index 00000000..d7991fd1 --- /dev/null +++ b/account_banking_reconciliation/models/account_banking_reconciliation.py @@ -0,0 +1,473 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2014 Ursa Information Systems () +# Copyright (C) 2011 NovaPoint Group LLC () +# Copyright (C) 2004-2010 OpenERP SA () +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see +# +############################################################################## +import time + +from openerp.osv import fields, osv +from openerp.tools.translate import _ +import openerp.addons.decimal_precision as dp +from operator import itemgetter + +class bank_acc_rec_statement(osv.osv): + def check_group(self, cr, uid, ids, context=None): + """Check if following security constraints are implemented for groups: + Bank Statement Preparer– they can create, view and delete any of the Bank Statements provided the Bank Statement is not in the DONE state, + or the Ready for Review state. + Bank Statement Verifier – they can create, view, edit, and delete any of the Bank Statements information at any time. + NOTE: DONE Bank Statements are only allowed to be deleted by a Bank Statement Verifier.""" + model_data_obj = self.pool.get('ir.model.data') + res_groups_obj = self.pool.get('res.groups') + group_verifier_id = model_data_obj._get_id(cr, uid, 'ursa_npg_bank_account_reconciliation', 'group_bank_stmt_verifier') + for statement in self.browse(cr, uid, ids, context=context): + if group_verifier_id: + res_id = model_data_obj.read(cr, uid, [group_verifier_id], ['res_id'])[0]['res_id'] + group_verifier = res_groups_obj.browse(cr, uid, res_id, context=context) + group_user_ids = [user.id for user in group_verifier.users] + if statement.state!='draft' and uid not in group_user_ids: + raise osv.except_osv(_('User Error !'), + _("Only a member of '%s' group may delete/edit bank statements when not in draft state!" %(group_verifier.name))) + return True + + def copy(self, cr, uid, id, default={}, context=None): + default.update({ + 'credit_move_line_ids': [], + 'debit_move_line_ids': [], + 'name': '', + }) + return super(bank_acc_rec_statement, self).copy(cr, uid, id, default=default, context=context) + + def write(self, cr, uid, ids, vals, context=None): + self.check_group(cr, uid, ids, context) # Check if the user is allowed to perform the action + return super(bank_acc_rec_statement, self).write(cr, uid, ids, vals, context=context) + + def unlink(self, cr, uid, ids, context=None): + "Reset the related account.move.line to be re-assigned later to statement." + statement_line_obj = self.pool.get('bank.acc.rec.statement.line') + self.check_group(cr, uid, ids, context) # Check if the user is allowed to perform the action + for statement in self.browse(cr, uid, ids, context=context): + statement_lines = statement.credit_move_line_ids + statement.debit_move_line_ids + statement_line_ids = map(lambda x: x.id, statement_lines) + statement_line_obj.unlink(cr, uid, statement_line_ids, context=context) # call unlink method to reset + return super(bank_acc_rec_statement, self).unlink(cr, uid, ids, context=context) + + def check_difference_balance(self, cr, uid, ids, context=None): + "Check if difference balance is zero or not." + for statement in self.browse(cr, uid, ids, context=context): + + if statement.cleared_balance_cur: + if statement.difference_cur != 0.0: + raise osv.except_osv(_('Warning!'), + _("Prior to reconciling a statement, all differences must be accounted for and the Difference balance must be zero." \ + " Please review and make necessary changes.")) + else: + if statement.difference != 0.0: + raise osv.except_osv(_('Warning!'), + _("Prior to reconciling a statement, all differences must be accounted for and the Difference balance must be zero." \ + " Please review and make necessary changes.")) + return True + + def action_cancel(self, cr, uid, ids, context=None): + "Cancel the the statement." + self.write(cr, uid, ids, {'state': 'cancel'}, context=context) + return True + + def action_review(self, cr, uid, ids, context=None): + "Change the status of statement from 'draft' to 'to_be_reviewed'." + # If difference balance not zero prevent further processing + self.check_difference_balance(cr, uid, ids, context=context) + self.write(cr, uid, ids, {'state': 'to_be_reviewed'}, context=context) + return True + + def action_process(self, cr, uid, ids, context=None): + """Set the account move lines as 'Cleared' and Assign 'Bank Acc Rec Statement ID' + for the statement lines which are marked as 'Cleared'.""" + account_move_line_obj = self.pool.get('account.move.line') + statement_line_obj = self.pool.get('bank.acc.rec.statement.line') + # If difference balance not zero prevent further processing + self.check_difference_balance(cr, uid, ids, context=context) + for statement in self.browse(cr, uid, ids, context=context): + statement_lines = statement.credit_move_line_ids + statement.debit_move_line_ids + for statement_line in statement_lines: + #Mark the move lines as 'Cleared'mand assign the 'Bank Acc Rec Statement ID' + account_move_line_obj.write(cr, uid, [statement_line.move_line_id.id], + {'cleared_bank_account': statement_line.cleared_bank_account, + 'bank_acc_rec_statement_id': statement_line.cleared_bank_account and statement.id or False + }, context=context) + + self.write(cr, uid, [statement.id], {'state': 'done', + 'verified_by_user_id': uid, + 'verified_date': time.strftime('%Y-%m-%d') + }, context=context) + return True + + def action_cancel_draft(self, cr, uid, ids, context=None): + """Reset the statement to draft and perform resetting operations.""" + account_move_line_obj = self.pool.get('account.move.line') + statement_line_obj = self.pool.get('bank.acc.rec.statement.line') + for statement in self.browse(cr, uid, ids, context=context): + statement_lines = statement.credit_move_line_ids + statement.debit_move_line_ids + line_ids = [] + statement_line_ids = [] + for statement_line in statement_lines: + statement_line_ids.append(statement_line.id) + + if statement_line.move_line_id: + line_ids.append(statement_line.move_line_id.id) # Find move lines related to statement lines + + # Reset 'Cleared' and 'Bank Acc Rec Statement ID' to False + account_move_line_obj.write(cr, uid, line_ids, {'cleared_bank_account': False, + 'bank_acc_rec_statement_id': False, + }, context=context) + # Reset 'Cleared' in statement lines + statement_line_obj.write(cr, uid, statement_line_ids, {'cleared_bank_account': False, + 'research_required': False + }, context=context) + # Reset statement + self.write(cr, uid, [statement.id], {'state': 'draft', + 'verified_by_user_id': False, + 'verified_date': False + }, context=context) + + return True + + def action_select_all(self, cr, uid, ids, context=None): + """Mark all the statement lines as 'Cleared'.""" + statement_line_obj = self.pool.get('bank.acc.rec.statement.line') + for statement in self.browse(cr, uid, ids, context=context): + statement_lines = statement.credit_move_line_ids + statement.debit_move_line_ids + statement_line_ids = map(lambda x: x.id, statement_lines) + statement_line_obj.write(cr, uid, statement_line_ids, {'cleared_bank_account': True}, context=context) + return True + + def action_unselect_all(self, cr, uid, ids, context=None): + """Reset 'Cleared' in all the statement lines.""" + statement_line_obj = self.pool.get('bank.acc.rec.statement.line') + for statement in self.browse(cr, uid, ids, context=context): + statement_lines = statement.credit_move_line_ids + statement.debit_move_line_ids + statement_line_ids = map(lambda x: x.id, statement_lines) + statement_line_obj.write(cr, uid, statement_line_ids, {'cleared_bank_account': False}, context=context) + return True + + def _get_balance(self, cr, uid, ids, name, args, context=None): + """Computed as following: + A) Deposits, Credits, and Interest Amount: Total SUM of Amts of lines with Cleared = True + Deposits, Credits, and Interest # of Items: Total of number of lines with Cleared = True + B) Checks, Withdrawals, Debits, and Service Charges Amount: + Checks, Withdrawals, Debits, and Service Charges Amount # of Items: + Cleared Balance (Total Sum of the Deposit Amount Cleared (A) – Total Sum of Checks Amount Cleared (B)) + Difference= (Ending Balance – Beginning Balance) - cleared balance = should be zero. +""" + res = {} + account_precision = self.pool.get('decimal.precision').precision_get(cr, uid, 'Account') + for statement in self.browse(cr, uid, ids, context=context): + res[statement.id] = { + 'sum_of_credits': 0.0, + 'sum_of_debits': 0.0, + 'sum_of_credits_cur': 0.0, + 'sum_of_debits_cur': 0.0, + 'sum_of_ucredits': 0.0, + 'sum_of_udebits': 0.0, + 'sum_of_ucredits_cur': 0.0, + 'sum_of_udebits_cur': 0.0, + 'cleared_balance': 0.0, + 'cleared_balance_cur': 0.0, + 'uncleared_balance': 0.0, + 'uncleared_balance_cur': 0.0, + 'difference': 0.0, + 'difference_cur': 0.0, + 'sum_of_credits_lines': 0.0, + 'sum_of_debits_lines': 0.0, + 'sum_of_ucredits_lines': 0.0, + 'sum_of_udebits_lines': 0.0 + } + for line in statement.credit_move_line_ids: + res[statement.id]['sum_of_credits'] += line.cleared_bank_account and round(line.amount, account_precision) or 0.0 + res[statement.id]['sum_of_credits_cur'] += line.cleared_bank_account and round(line.amountcur, account_precision) or 0.0 + res[statement.id]['sum_of_credits_lines'] += line.cleared_bank_account and 1.0 or 0.0 + res[statement.id]['sum_of_ucredits'] += (not line.cleared_bank_account) and round(line.amount, account_precision) or 0.0 + res[statement.id]['sum_of_ucredits_cur'] += (not line.cleared_bank_account) and round(line.amountcur, account_precision) or 0.0 + res[statement.id]['sum_of_ucredits_lines'] += (not line.cleared_bank_account) and 1.0 or 0.0 + for line in statement.debit_move_line_ids: + res[statement.id]['sum_of_debits'] += line.cleared_bank_account and round(line.amount, account_precision) or 0.0 + res[statement.id]['sum_of_debits_cur'] += line.cleared_bank_account and round(line.amountcur, account_precision) or 0.0 + res[statement.id]['sum_of_debits_lines'] += line.cleared_bank_account and 1.0 or 0.0 + res[statement.id]['sum_of_udebits'] += (not line.cleared_bank_account) and round(line.amount, account_precision) or 0.0 + res[statement.id]['sum_of_udebits_cur'] += (not line.cleared_bank_account) and round(line.amountcur, account_precision) or 0.0 + res[statement.id]['sum_of_udebits_lines'] += (not line.cleared_bank_account) and 1.0 or 0.0 + + res[statement.id]['cleared_balance'] = round(res[statement.id]['sum_of_debits'] - res[statement.id]['sum_of_credits'], account_precision) + res[statement.id]['cleared_balance_cur'] = round(res[statement.id]['sum_of_debits_cur'] - res[statement.id]['sum_of_credits_cur'], account_precision) + res[statement.id]['difference'] = round((statement.ending_balance - statement.starting_balance) - res[statement.id]['cleared_balance'], account_precision) + res[statement.id]['difference_cur'] = round((statement.ending_balance - statement.starting_balance) - res[statement.id]['cleared_balance_cur'], account_precision) + res[statement.id]['uncleared_balance'] = round(res[statement.id]['sum_of_udebits'] - res[statement.id]['sum_of_ucredits'], account_precision) + res[statement.id]['uncleared_balance_cur'] = round(res[statement.id]['sum_of_udebits_cur'] - res[statement.id]['sum_of_ucredits_cur'], account_precision) + return res + + # refresh data + def refresh_record(self, cr, uid, ids, context=None): + + retval = True + refdict = {} + + # get current state of moves in the statement + for statement in self.browse(cr, uid, ids, context=context): + + if statement.state == 'draft': + for cr_item in statement.credit_move_line_ids: + if cr_item.move_line_id and cr_item.cleared_bank_account: + refdict[cr_item.move_line_id.id] = cr_item.cleared_bank_account + + for dr_item in statement.debit_move_line_ids: + if dr_item.move_line_id and dr_item.cleared_bank_account: + refdict[dr_item.move_line_id.id] = dr_item.cleared_bank_account + + # for the statement + for statement in self.browse(cr, uid, ids, context=context): + + # process only if the statement is in draft state + if statement.state == 'draft': + account_id = statement.account_id and statement.account_id.id + ending_date = statement.ending_date + suppress_ending_date_filter = statement.suppress_ending_date_filter + vals = self.onchange_account_id(cr, uid, ids, account_id, ending_date, suppress_ending_date_filter, context=context) + + # list of credit lines + outlist = [] + for cr_item in vals['value']['credit_move_line_ids']: + cr_item['cleared_bank_account'] = refdict and refdict.get(cr_item['move_line_id'], False) or False + cr_item['research_required'] = False + + item = [0, False, cr_item] + outlist.append(item) + + # list of debit lines + inlist = [] + for dr_item in vals['value']['debit_move_line_ids']: + dr_item['cleared_bank_account'] = refdict and refdict.get(dr_item['move_line_id'], False) or False + dr_item['research_required'] = False + + item = [0, False, dr_item] + inlist.append(item) + + # write it to the record so it is visible on the form + retval = self.write(cr, uid, ids, {'last_ending_date':vals['value']['last_ending_date'],'starting_balance': vals['value']['starting_balance'], + 'credit_move_line_ids':outlist, 'debit_move_line_ids': inlist}, context=context) + + return retval + + # get starting balance for the account + def get_starting_balance(self, cr, uid, account_id, ending_date, context=None): + + result = (False,0.0) + reslist=[] + statement_obj = self.pool.get('bank.acc.rec.statement') + domain = [('account_id', '=', account_id), ('state', '=', 'done')] + statement_ids = statement_obj.search(cr, uid, domain, context=context) + + # get all statements for this account in the past + for statement in statement_obj.browse(cr, uid, statement_ids, context=context): + if statement.ending_date < ending_date: + reslist.append((statement.ending_date, statement.ending_balance)) + + # get the latest statement value + if len(reslist): + reslist = sorted(reslist,key=itemgetter(0)) + result = reslist[len(reslist)-1] + + return result + + def onchange_account_id(self, cr, uid, ids, account_id, ending_date, suppress_ending_date_filter, context=None): + account_move_line_obj = self.pool.get('account.move.line') + statement_line_obj = self.pool.get('bank.acc.rec.statement.line') + val = {'value': {'credit_move_line_ids': [], 'debit_move_line_ids': []}} + if account_id: + for statement in self.browse(cr, uid, ids, context=context): + statement_line_ids = statement_line_obj.search(cr, uid, [('statement_id', '=', statement.id)], context=context) + # call unlink method to reset and remove existing statement lines and + # mark reset field values in related move lines + statement_line_obj.unlink(cr, uid, statement_line_ids, context=context) + + # Apply filter on move lines to allow + #1. credit and debit side journal items in posted state of the selected GL account + #2. Journal items which are not cleared in previous bank statements + #3. Date less than or equal to ending date provided the 'Suppress Ending Date Filter' is not checkec + domain = [('account_id', '=', account_id), ('move_id.state', '=', 'posted'), ('cleared_bank_account', '=', False)] + if not suppress_ending_date_filter: + domain += [('date', '<=', ending_date)] + line_ids = account_move_line_obj.search(cr, uid, domain, context=context) + for line in account_move_line_obj.browse(cr, uid, line_ids, context=context): + + if not line.journal_id.centralisation: + amount_currency = (line.amount_currency < 0) and (-1*line.amount_currency) or line.amount_currency + res = { + 'ref': line.ref, + 'date': line.date, + 'partner_id': line.partner_id.id, + 'currency_id': line.currency_id.id, + 'amount': line.credit or line.debit, + 'amountcur': amount_currency, + 'name': line.name, + 'move_line_id': line.id, + 'type': line.credit and 'cr' or 'dr' + } + + if res['type'] == 'cr': + val['value']['credit_move_line_ids'].append(res) + else: + val['value']['debit_move_line_ids'].append(res) + + # look for previous statement for the account to pull ending balance as starting balance + prev_stmt=self.get_starting_balance(cr, uid, account_id, ending_date, context=context) + val['value']['last_ending_date'] = prev_stmt[0] + val['value']['starting_balance'] = prev_stmt[1] + + return val + + _name = "bank.acc.rec.statement" + _columns = { + 'name': fields.char('Name', required=True, size=64, states={'done':[('readonly', True)]}, help="This is a unique name identifying the statement (e.g. Bank X January 2012)."), + 'account_id': fields.many2one('account.account', 'Account', required=True, + states={'done':[('readonly', True)]}, domain="[('company_id', '=', company_id), ('type', '!=', 'view')]", + help="The Bank/Gl Account that is being reconciled."), + 'ending_date': fields.date('Ending Date', required=True, states={'done':[('readonly', True)]}, help="The ending date of your bank statement."), + 'last_ending_date': fields.date('Last Stmt Date', help="The previous statement date of your bank statement."), + 'starting_balance': fields.float('Starting Balance', required=True, digits_compute=dp.get_precision('Account'), help="The Starting Balance on your bank statement.", states={'done':[('readonly', True)]}), + 'ending_balance': fields.float('Ending Balance', required=True, digits_compute=dp.get_precision('Account'), help="The Ending Balance on your bank statement.", states={'done':[('readonly', True)]}), + 'company_id': fields.many2one('res.company', 'Company', required=True, readonly=True, + help="The Company for which the deposit ticket is made to"), + 'notes': fields.text('Notes'), + 'verified_date': fields.date('Verified Date', states={'done':[('readonly', True)]}, + help="Date in which Deposit Ticket was verified."), + 'verified_by_user_id': fields.many2one('res.users', 'Verified By', states={'done':[('readonly', True)]}, + help="Entered automatically by the “last user” who saved it. System generated."), + 'credit_move_line_ids': fields.one2many('bank.acc.rec.statement.line', 'statement_id', 'Credits', + domain=[('type','=','cr')], context={'default_type':'cr'}, states={'done':[('readonly', True)]}), + 'debit_move_line_ids': fields.one2many('bank.acc.rec.statement.line', 'statement_id', 'Debits', + domain=[('type','=','dr')], context={'default_type':'dr'}, states={'done':[('readonly', True)]}), + 'cleared_balance': fields.function(_get_balance, method=True, string='Cleared Balance', digits_compute=dp.get_precision('Account'), + type='float', help="Total Sum of the Deposit Amount Cleared – Total Sum of Checks, Withdrawals, Debits, and Service Charges Amount Cleared", + multi="balance"), + 'difference': fields.function(_get_balance, method=True, type='float', string='Difference', digits_compute=dp.get_precision('Account'), + help="(Ending Balance – Beginning Balance) - Cleared Balance.", multi="balance"), + 'cleared_balance_cur': fields.function(_get_balance, method=True, string='Cleared Balance (Cur)', digits_compute=dp.get_precision('Account'), + type='float', help="Total Sum of the Deposit Amount Cleared – Total Sum of Checks, Withdrawals, Debits, and Service Charges Amount Cleared", + multi="balance"), + 'difference_cur': fields.function(_get_balance, method=True, type='float', string='Difference (Cur)', digits_compute=dp.get_precision('Account'), + help="(Ending Balance – Beginning Balance) - Cleared Balance.", multi="balance"), + 'uncleared_balance': fields.function(_get_balance, method=True, string='Uncleared Balance', digits_compute=dp.get_precision('Account'), + type='float', help="Total Sum of the Deposit Amount Uncleared – Total Sum of Checks, Withdrawals, Debits, and Service Charges Amount Uncleared", + multi="balance"), + 'uncleared_balance_cur': fields.function(_get_balance, method=True, string='Unleared Balance (Cur)', digits_compute=dp.get_precision('Account'), + type='float', help="Total Sum of the Deposit Amount Uncleared – Total Sum of Checks, Withdrawals, Debits, and Service Charges Amount Uncleared", + multi="balance"), + 'sum_of_credits': fields.function(_get_balance, method=True, string='Checks, Withdrawals, Debits, and Service Charges Amount', digits_compute=dp.get_precision('Account'), + type='float', help="Total SUM of Amts of lines with Cleared = True", + multi="balance"), + 'sum_of_debits': fields.function(_get_balance, method=True, type='float', string='Deposits, Credits, and Interest Amount', digits_compute=dp.get_precision('Account'), + help="Total SUM of Amts of lines with Cleared = True", multi="balance"), + 'sum_of_credits_cur': fields.function(_get_balance, method=True, string='Checks, Withdrawals, Debits, and Service Charges Amount (Cur)', digits_compute=dp.get_precision('Account'), + type='float', help="Total SUM of Amts of lines with Cleared = True", + multi="balance"), + 'sum_of_debits_cur': fields.function(_get_balance, method=True, type='float', string='Deposits, Credits, and Interest Amount (Cur)', digits_compute=dp.get_precision('Account'), + help="Total SUM of Amts of lines with Cleared = True", multi="balance"), + 'sum_of_credits_lines': fields.function(_get_balance, method=True, string='Checks, Withdrawals, Debits, and Service Charges # of Items', + type='float', help="Total of number of lines with Cleared = True", + multi="balance"), + 'sum_of_debits_lines': fields.function(_get_balance, method=True, type='float', string='Deposits, Credits, and Interest # of Items', + help="Total of number of lines with Cleared = True", multi="balance"), + 'sum_of_ucredits': fields.function(_get_balance, method=True, string='Uncleared - Checks, Withdrawals, Debits, and Service Charges Amount', digits_compute=dp.get_precision('Account'), + type='float', help="Total SUM of Amts of lines with Cleared = False", + multi="balance"), + 'sum_of_udebits': fields.function(_get_balance, method=True, type='float', string='Uncleared - Deposits, Credits, and Interest Amount', digits_compute=dp.get_precision('Account'), + help="Total SUM of Amts of lines with Cleared = False", multi="balance"), + 'sum_of_ucredits_cur': fields.function(_get_balance, method=True, string='Uncleared - Checks, Withdrawals, Debits, and Service Charges Amount (Cur)', digits_compute=dp.get_precision('Account'), + type='float', help="Total SUM of Amts of lines with Cleared = False", + multi="balance"), + 'sum_of_udebits_cur': fields.function(_get_balance, method=True, type='float', string='Uncleared - Deposits, Credits, and Interest Amount (Cur)', digits_compute=dp.get_precision('Account'), + help="Total SUM of Amts of lines with Cleared = False", multi="balance"), + 'sum_of_ucredits_lines': fields.function(_get_balance, method=True, string='Uncleared - Checks, Withdrawals, Debits, and Service Charges # of Items', + type='float', help="Total of number of lines with Cleared = False", + multi="balance"), + 'sum_of_udebits_lines': fields.function(_get_balance, method=True, type='float', string='Uncleared - Deposits, Credits, and Interest # of Items', + help="Total of number of lines with Cleared = False", multi="balance"), + 'suppress_ending_date_filter': fields.boolean('Remove Ending Date Filter', help="If this is checked then the Statement End Date filter on the transactions below will not occur. All transactions would come over."), + 'state': fields.selection([ + ('draft','Draft'), + ('to_be_reviewed','Ready for Review'), + ('done','Done'), + ('cancel', 'Cancel') + ],'State', select=True, readonly=True), + } + _defaults = { + 'state': 'draft', + 'company_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id, + 'ending_date': time.strftime('%Y-%m-%d'), + } + _order = "ending_date desc" + _sql_constraints = [ + ('name_company_uniq', 'unique (name, company_id, account_id)', 'The name of the statement must be unique per company and G/L account!') + ] +bank_acc_rec_statement() + +class bank_acc_rec_statement_line(osv.osv): + _name = "bank.acc.rec.statement.line" + _description = "Statement Line" + _columns = { + 'name': fields.char('Name', size=64, help="Derived from the related Journal Item.", required=True), + 'ref': fields.char('Reference', size=64, help="Derived from related Journal Item."), + 'partner_id': fields.many2one('res.partner', string='Partner', help="Derived from related Journal Item."), + 'amount': fields.float('Amount', digits_compute=dp.get_precision('Account'), + help="Derived from the 'debit' amount from related Journal Item."), + 'amountcur': fields.float('Amount in Currency', digits_compute=dp.get_precision('Account'), + help="Derived from the 'amount currency' amount from related Journal Item."), + 'date': fields.date('Date', required=True, help="Derived from related Journal Item."), + 'statement_id': fields.many2one('bank.acc.rec.statement', 'Statement', required=True, ondelete='cascade'), + 'move_line_id': fields.many2one('account.move.line', 'Journal Item', help="Related Journal Item."), + 'cleared_bank_account': fields.boolean('Cleared? ', help='Check if the transaction has cleared from the bank'), + 'research_required': fields.boolean('Research Required? ', help='Check if the transaction should be researched by Accounting personal'), + 'currency_id': fields.many2one('res.currency', 'Currency', help="The optional other currency if it is a multi-currency entry."), + 'type':fields.selection([('dr','Debit'),('cr','Credit')], 'Cr/Dr'), + } + + def create(self, cr, uid, vals, context=None): + account_move_line_obj = self.pool.get('account.move.line') + # Prevent manually adding new statement line. + # This would allow only onchange method to pre-populate statement lines based on the filter rules. + if not vals.get('move_line_id', False): + raise osv.except_osv(_('Processing Error'),_('You cannot add any new bank statement line manually as of this revision!')) + account_move_line_obj.write(cr, uid, [vals['move_line_id']], {'draft_assigned_to_statement': True}, context=context) + return super(bank_acc_rec_statement_line, self).create(cr, uid, vals, context=context) + + def unlink(self, cr, uid, ids, context=None): + account_move_line_obj = self.pool.get('account.move.line') + move_line_ids = [x.move_line_id.id for x in self.browse(cr, uid, ids, context=context) if x.move_line_id] + #map(lambda x: x.move_line_id.id if x.move_line_id, self.browse(cr, uid, ids, context=context)) + # Reset field values in move lines to be added later + account_move_line_obj.write(cr, uid, move_line_ids, {'draft_assigned_to_statement': False, + 'cleared_bank_account': False, + 'bank_acc_rec_statement_id': False, + }, context=context) + return super(bank_acc_rec_statement_line, self).unlink(cr, uid, ids, context=context) + +bank_acc_rec_statement_line() + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/account_banking_reconciliation/models/account_move_line.py b/account_banking_reconciliation/models/account_move_line.py new file mode 100644 index 00000000..c6f8dea1 --- /dev/null +++ b/account_banking_reconciliation/models/account_move_line.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2011 NovaPoint Group LLC () +# Copyright (C) 2004-2010 OpenERP SA () +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.osv import fields,osv + +class account_move_line(osv.osv): + _inherit='account.move.line' + + _columns = { + 'cleared_bank_account': fields.boolean('Cleared? ', help='Check if the transaction has cleared from the bank'), + 'bank_acc_rec_statement_id': fields.many2one('bank.acc.rec.statement', 'Bank Acc Rec Statement', help="The Bank Acc Rec Statement linked with the journal item"), + 'draft_assigned_to_statement': fields.boolean('Assigned to Statement? ', help='Check if the move line is assigned to statement lines') + } + + +account_move_line() + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/account_banking_reconciliation/report/bank_statement_detail.rml b/account_banking_reconciliation/report/bank_statement_detail.rml new file mode 100644 index 00000000..a4a1619c --- /dev/null +++ b/account_banking_reconciliation/report/bank_statement_detail.rml @@ -0,0 +1,750 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [[ repeatIn(objects, 'stmt') ]] + [[ setLang(stmt.lang) ]] + +
+ [[ (not stmt.account_id.currency_id) and removeParentNode('section') or '' ]] + + + + + + + + + + + + + + Date + + + Comment + + + Partner + + + Reference + + + Cleared + + + Amount([[ company.currency_id.symbol ]]) + + + Amount([[ stmt.account_id.currency_id.symbol ]]) + + + +
+
+ [[ (stmt.account_id.currency_id) and removeParentNode('section') or '' ]] + + + + + + + + + + + + + + Date + + + Comment + + + Partner + + + Reference + + + Cleared + + + Amount([[ company.currency_id.symbol ]]) + + + Amount([[ stmt.account_id.currency_id.symbol ]]) + + + +
+
+
+ Reconciliation Detail + Period Ending [[ (stmt.ending_date) or '' ]] +
+ + + + + + +
+ [[ (not stmt.account_id.currency_id) and removeParentNode('section') or '' ]] + + + + + + + Date + + + Comment + + + Partner + + + Reference + + + Cleared + + + Amount([[ company.currency_id.symbol ]]) + + + Amount([[ stmt.account_id.currency_id.symbol ]]) + + + +
+
+ [[ stmt.account_id.currency_id and removeParentNode('section') or '' ]] + + + + + + + Date + + + Comment + + + Partner + + + Reference + + + Cleared + + + Amount([[ company.currency_id.symbol ]]) + + + +
+
+ [[ (not stmt.account_id.currency_id) and removeParentNode('section') or '' ]] + + + + Initial Balance + + + + [[ stmt.starting_balance ]] + + + + + + Cleared Transactions + + + + + + + + Deposits & Credits-[[ (stmt.sum_of_debits_lines) or '']] items + + + + + +
+
+ [[ stmt.account_id.currency_id and removeParentNode('section') or '' ]] + + + + Initial Balance + + + + [[ stmt.starting_balance ]] + + + + + + Cleared Transactions + + + + + + + + Deposits & Credits-[[ (stmt.sum_of_debits_lines) or '']] items + + + + + +
+
+ [[ (not stmt.account_id.currency_id) and removeParentNode('section') or '' ]] + [[ repeatIn(stmt.debit_move_line_ids, 'debit') ]] +
[[ (not debit['cleared_bank_account']) and removeParentNode('section') ]] + + + + + + + [[ formatLang(debit['date'],date=True) ]] + + + [[ debit['name'] ]] + + + [[ debit['partner_id'].name or '' ]] + + + [[ debit['ref'] ]] + + + [[ (debit['cleared_bank_account']) and 'X' or '' ]] + + + [[ formatLang((debit['amount'])) ]] + + + [[ formatLang((debit['amountcur'])) ]] + + + +
+
+
+ [[ stmt.account_id.currency_id and removeParentNode('section') or '' ]] + [[ repeatIn(stmt.debit_move_line_ids, 'debit') ]] +
[[ (not debit['cleared_bank_account']) and removeParentNode('section') ]] + + + + + + + [[ formatLang(debit['date'],date=True) ]] + + + [[ debit['name'] ]] + + + [[ debit['partner_id'].name or '' ]] + + + [[ debit['ref'] ]] + + + [[ (debit['cleared_bank_account']) and 'X' or '' ]] + + + [[ formatLang((debit['amount'])) ]] + + + +
+
+
+ [[ (not stmt.account_id.currency_id) and removeParentNode('section') or '' ]] + + + + Total Deposits & Credits + + + [[ (stmt.sum_of_debits) or '']] + [[ (stmt.sum_of_debits_cur) or '']] + + + + + + + Checks & Payments-[[ (stmt.sum_of_credits_lines) or '']] items + + + + + +
+
+ [[ (stmt.account_id.currency_id) and removeParentNode('section') or '' ]] + + + + Total Deposits & Credits + + + [[ (stmt.sum_of_debits) or '']] + + + + + + + Checks & Payments-[[ (stmt.sum_of_credits_lines) or '']] items + + + + + +
+
+ [[ (not stmt.account_id.currency_id) and removeParentNode('section') or '' ]] + [[ repeatIn(stmt.credit_move_line_ids, 'credit') ]] + + + + + + + [[ formatLang(credit['date'],date=True) ]] + + + [[ credit['name'] ]] + + + [[ credit['partner_id'].name or '' ]] + + + [[ credit['ref'] ]] + + + [[ (credit['cleared_bank_account']) and 'X' or '' ]] + + + [[ formatLang((credit['amount'])) ]] + + + [[ formatLang((credit['amountcur']))]] + + + +
+
+ [[ (stmt.account_id.currency_id) and removeParentNode('section') or '' ]] + [[ repeatIn(stmt.credit_move_line_ids, 'credit') ]] + + + + + + + [[ formatLang(credit['date'],date=True) ]] + + + [[ credit['name'] ]] + + + [[ credit['partner_id'].name or '' ]] + + + [[ credit['ref'] ]] + + + [[ (credit['cleared_bank_account']) and 'X' or '' ]] + + + [[ formatLang((credit['amount'])) ]] + + + +
+
+ [[ (not stmt.account_id.currency_id) and removeParentNode('section') or '' ]] + + + + Total Checks & Payments + + + [[ (stmt.sum_of_credits) or '']] + [[ (stmt.sum_of_credits_cur) or '']] + + + + + + Total Cleared Transactions + + + [[ formatLang(stmt.sum_of_debits - stmt.sum_of_credits) or '']] + [[ formatLang(stmt.sum_of_debits_cur - stmt.sum_of_credits_cur) or '']] + + + + Cleared Balance + + + [[ (stmt.cleared_balance) or '']] + [[ (stmt.cleared_balance_cur) or '']] + + + + Ending Balance + + + + [[ (stmt.ending_balance) or '']] + + +
+
+ [[ (stmt.account_id.currency_id) and removeParentNode('section') or '' ]] + + + + Total Deposits and Credits + + + [[ (stmt.sum_of_credits) or '']] + + + + + + Total Cleared Transactions + + + [[ formatLang(stmt.sum_of_debits - stmt.sum_of_credits) or '']] + + + + Cleared Balance + + + [[ (stmt.cleared_balance) or '']] + + + + Ending Balance + + + [[ (stmt.ending_balance) or '']] + + +
+
+
+
diff --git a/account_banking_reconciliation/report/bank_statement_report.xml b/account_banking_reconciliation/report/bank_statement_report.xml new file mode 100644 index 00000000..cb72d073 --- /dev/null +++ b/account_banking_reconciliation/report/bank_statement_report.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/account_banking_reconciliation/report/bank_statement_summary.rml b/account_banking_reconciliation/report/bank_statement_summary.rml new file mode 100644 index 00000000..55eb3ce0 --- /dev/null +++ b/account_banking_reconciliation/report/bank_statement_summary.rml @@ -0,0 +1,428 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [[ repeatIn(objects, 'stmt') ]] + [[ setLang(stmt.lang) ]] + + +
+ Reconciliation Summary + Period Ending [[ (stmt.ending_date) or '' ]] +
+ + + + + + +
+ [[ (not stmt.account_id.currency_id) and removeParentNode('section') or '' ]] + + + Initial Balance + + [[ stmt.starting_balance ]] + + + + + + + Cleared Transactions + + + + + + + + Deposits & Credits-[[ (stmt.sum_of_debits_lines) or '']] items + [[ (stmt.sum_of_debits) or '']] + [[ (stmt.sum_of_debits_cur) or '']] + + + + + Checks & Payments-[[ (stmt.sum_of_credits_lines) or '']] items + [[ (stmt.sum_of_credits) or '']] + [[ (stmt.sum_of_credits_cur) or '']] + + + + + + + Total Cleared Transactions + [[ formatLang(stmt.sum_of_debits - stmt.sum_of_credits) or '']] + [[ formatLang(stmt.sum_of_debits_cur - stmt.sum_of_credits_cur) or '']] + + + + + + Cleared Balance + [[ (stmt.cleared_balance) or '']] + [[ (stmt.cleared_balance_cur) or '']] + [[ (stmt.cleared_balance_cur) or '']] + + + + + Ending Balance + + [[ (stmt.ending_balance) or '']] + + +
+
+ [[ stmt.account_id.currency_id and removeParentNode('section') or '' ]] + + + Initial Balance + + [[ stmt.starting_balance ]] + + + + + + + Cleared Transactions + + + + + + + + Deposits & Credits-[[ (stmt.sum_of_debits_lines) or '']] items + + [[ (stmt.sum_of_debits) or '']] + + + + + Checks & Payments-[[ (stmt.sum_of_credits_lines) or '']] items + + [[ (stmt.sum_of_credits) or '']] + + + + + + + Total Cleared Transactions + + [[ formatLang(stmt.sum_of_debits - stmt.sum_of_credits) or '']] + + + + + + Cleared Balance + + [[ (stmt.cleared_balance) or '']] + [[ (stmt.cleared_balance) or '']] + + + + + Ending Balance + + [[ (stmt.ending_balance) or '']] + + +
+
+
+
diff --git a/account_banking_reconciliation/security/account_banking_reconciliation.xml b/account_banking_reconciliation/security/account_banking_reconciliation.xml new file mode 100644 index 00000000..eff450eb --- /dev/null +++ b/account_banking_reconciliation/security/account_banking_reconciliation.xml @@ -0,0 +1,25 @@ + + + + + + + + Bank Statement Preparer + + + + Bank Statement Verifier + + + + + + Bank Statements + + + ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + + + + \ No newline at end of file diff --git a/account_banking_reconciliation/security/ir.model.access.csv b/account_banking_reconciliation/security/ir.model.access.csv new file mode 100644 index 00000000..d630197c --- /dev/null +++ b/account_banking_reconciliation/security/ir.model.access.csv @@ -0,0 +1,5 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_bank_acc_rec_statement_preparer","access.bank.acc.rec.statement.preparer","model_bank_acc_rec_statement","group_bank_stmt_preparer",1,1,1,1 +"access_bank_acc_rec_statement_verifier","access.bank.acc.rec.statement.verifier","model_bank_acc_rec_statement","group_bank_stmt_verifier",1,1,1,1 +"access_bank_acc_rec_statement_line_preparer","access.bank.acc.rec.statement.line.preparer","model_bank_acc_rec_statement_line","group_bank_stmt_preparer",1,1,1,1 +"access_bank_acc_rec_statement_line_verifier","access.bank.acc.rec.statement.line.verifier","model_bank_acc_rec_statement_line","group_bank_stmt_verifier",1,1,1,1 diff --git a/account_banking_reconciliation/static/description/icon.png b/account_banking_reconciliation/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/account_banking_reconciliation/views/account_banking_reconciliation.xml b/account_banking_reconciliation/views/account_banking_reconciliation.xml new file mode 100644 index 00000000..63901753 --- /dev/null +++ b/account_banking_reconciliation/views/account_banking_reconciliation.xml @@ -0,0 +1,230 @@ + + + + + + + + bank.acc.rec.statement.tree + bank.acc.rec.statement + tree + 2 + + + + + + + + + + + + + bank.acc.rec.statement.form + bank.acc.rec.statement + form + +
+
+
+ + + + + + + + + + + + + +