diff --git a/README.md b/README.md index f1a201e..93fe163 100644 --- a/README.md +++ b/README.md @@ -1,262 +1,253 @@ -# Bank Statement Reconciliation Module - -## Overview - -This module enhances Odoo's bank statement reconciliation functionality by providing an intuitive interface for reconciling bank statement lines with journal entries. - -## Features - -### Core Functionality -- **Menu Access**: Direct menu to access bank statement lines -- **Bank Journal Filtering**: Filter statement lines by specific bank journals -- **Batch Reconciliation**: Select and reconcile multiple bank lines simultaneously -- **Journal Entry Matching**: Wizard to select appropriate journal entries for reconciliation -- **Automatic Reconciliation**: Automatic creation of reconciliation journal entries - -### New Features (v17.0.1.0.0) - -#### 1. Total Selected Amount Widget - -A dynamic header widget that displays real-time totals when you select multiple bank statement lines in the list view. - -**Features:** -- **Real-time Calculation**: Automatically calculates the sum of selected lines -- **Visual Indicators**: - - Green badge for positive amounts - - Red badge for negative amounts -- **Selection Counter**: Shows how many lines are currently selected -- **Currency Display**: Shows the currency of selected lines -- **Modern UI**: Clean, Bootstrap-styled interface with icons - -**How to Use:** -1. Navigate to: `Accounting > Bank Statement Reconciliation > Bank Statement Lines` -2. Select one or more bank statement lines by clicking the checkboxes -3. The header widget will automatically appear showing: - - Number of selected lines - - Total amount of selected lines - - Currency information - -**Visual Layout:** -``` -┌─────────────────────────────────────────────────────────────┐ -│ ☑ Selected Lines: [3] │ 🧮 Total Amount: [1,234.56] USD │ -└─────────────────────────────────────────────────────────────┘ -``` - -## Installation - -1. Copy the module to your Odoo addons directory: - ```bash - cp -r bank_statement_reconciliation /path/to/odoo/addons/ - ``` - -2. Update the addons list: - - Go to `Apps` menu - - Click `Update Apps List` - - Search for "Bank Statement Reconciliation" - -3. Install the module: - - Click `Install` button - -## Configuration - -No additional configuration is required. The module works out of the box after installation. - -## Usage - -### Reconciling Bank Statement Lines - -1. **Access Bank Statement Lines** - - Navigate to: `Accounting > Bank Statement Reconciliation > Bank Statement Lines` - -2. **Filter Lines** - - Use the search bar to filter by: - - Date - - Partner - - Journal - - Amount (Income/Expense) - -3. **Select Lines for Reconciliation** - - Check the boxes next to the lines you want to reconcile - - The total amount widget will show the sum automatically - -4. **Initiate Reconciliation** - - Click the `Action` menu - - Select `Reconcile Selected Lines` - - OR click the `Reconcile` button in the form view - -5. **Match Journal Entries** - - The wizard will open showing available journal entries - - Select the appropriate journal entry to match - - Confirm the reconciliation - -## Technical Details - -### Module Structure - -``` -bank_statement_reconciliation/ -├── __init__.py -├── __manifest__.py -├── README.md -├── models/ -│ ├── __init__.py -│ ├── account_bank_statement_line.py -│ ├── bank_statement_line.py -│ └── bank_statement_selector.py -├── views/ -│ ├── bank_statement_line_views.xml -│ ├── bank_statement_selector_views.xml -│ └── menu.xml -├── wizards/ -│ ├── __init__.py -│ ├── bank_reconcile_wizard.py -│ └── bank_reconcile_wizard_views.xml -├── security/ -│ └── ir.model.access.csv -└── static/ - └── src/ - ├── js/ - │ ├── bank_statement_total_widget.js - │ ├── bank_statement_list_controller.js - │ └── bank_statement_list_view.js - └── xml/ - ├── bank_statement_total_widget.xml - └── bank_statement_list_view.xml -``` - -### JavaScript Components - -#### BankStatementTotalWidget -- **Type**: OWL Component -- **Purpose**: Displays total amount of selected lines -- **Props**: - - `resIds`: Array of selected record IDs -- **Features**: - - Reactive state management - - Automatic updates on selection change - - Currency formatting - - Visual styling based on amount (positive/negative) - -#### BankStatementListController -- **Type**: List Controller Extension -- **Purpose**: Extends standard list controller with custom functionality -- **Features**: - - Provides selected records to the widget - - Integrates widget into list view layout - -### XML Views - -#### Tree View Enhancement -- Added `js_class="bank_statement_list"` attribute -- Enabled multi-edit mode -- Added monetary widget for amount field -- Added currency field for proper formatting -- Includes sum totals in footer - -### Dependencies - -- `account`: Core accounting module -- `base`: Odoo base module -- `web`: Web framework for JavaScript components - -## Customization - -### Modifying the Widget Appearance - -Edit [`bank_statement_total_widget.xml`](static/src/xml/bank_statement_total_widget.xml) to customize: -- Layout -- Colors -- Icons -- Styling - -### Extending Functionality - -To add more calculations or features: - -1. Edit [`bank_statement_total_widget.js`](static/src/js/bank_statement_total_widget.js) -2. Add new computed properties or methods -3. Update the template to display new information - -Example - Add average calculation: -```javascript -get averageAmount() { - if (this.state.selectedCount === 0) return 0; - return this.state.totalAmount / this.state.selectedCount; -} -``` - -## Troubleshooting - -### Widget Not Appearing - -1. **Clear Browser Cache** - - Hard refresh: `Ctrl+Shift+R` (Windows/Linux) or `Cmd+Shift+R` (Mac) - -2. **Update Assets** - - Navigate to: `Settings > Technical > User Interface > Views` - - Click `Regenerate Assets Bundles` - -3. **Check JavaScript Console** - - Press `F12` to open developer tools - - Look for any JavaScript errors - -### Incorrect Totals - -1. **Verify Currency Settings** - - Ensure all selected lines use the same currency - - Check currency rounding settings - -2. **Check Data Integrity** - - Verify amount fields are populated correctly - - Check for null or undefined values - -## Support - -For issues, questions, or contributions: -- **Author**: Suherdy Yacob -- **Version**: 17.0.1.0.0 -- **Odoo Version**: 17.0 - -## Changelog - -### Version 17.0.1.0.0 -- Added total selected amount widget in list view header -- Enhanced tree view with monetary formatting -- Improved user experience with visual indicators -- Added real-time calculation of selected lines -- Implemented modern OWL-based JavaScript components - -### Initial Version -- Basic bank statement reconciliation functionality -- Wizard-based reconciliation process -- Multi-line selection support -- Journal entry matching - -## License - -This module follows the same license as Odoo. - -## Screenshots - -### Total Amount Widget in Action -When you select multiple bank statement lines, the widget appears at the top showing: -- Number of selected items -- Total sum with currency -- Color-coded amount (green for positive, red for negative) - -### List View Enhancements -- Checkboxes for multi-selection -- Amount column with monetary formatting -- Footer totals for all visible records -- Clean, modern interface - -## Future Enhancements - -Planned features for future releases: -- [ ] Support for multi-currency reconciliation -- [ ] Advanced filtering options -- [ ] Reconciliation history tracking -- [ ] Export selected lines to Excel -- [ ] Batch operations for common reconciliation patterns +# Bank Statement Reconciliation Module + +## Overview + +This module enhances Odoo's bank statement reconciliation functionality by providing an intuitive interface for reconciling bank statement lines with journal entries. + +## Features + +### Core Functionality +- **Menu Access**: Direct menu to access bank statement lines +- **Bank Journal Filtering**: Filter statement lines by specific bank journals +- **Batch Reconciliation**: Select and reconcile multiple bank lines simultaneously +- **Journal Entry Matching**: Wizard to select appropriate journal entries for reconciliation +- **Automatic Reconciliation**: Automatic creation of reconciliation journal entries + +### New Features (v17.0.1.0.0) + +#### 1. Total Selected Amount Widget + +A dynamic header widget that displays real-time totals when you select multiple bank statement lines in the list view. + +**Features:** +- **Real-time Calculation**: Automatically calculates the sum of selected lines +- **Visual Indicators**: + - Green badge for positive amounts + - Red badge for negative amounts +- **Selection Counter**: Shows how many lines are currently selected +- **Currency Display**: Shows the currency of selected lines +- **Modern UI**: Clean, Bootstrap-styled interface with icons + +**How to Use:** +1. Navigate to: `Bank Statement Reconciliation` +2. Select one or more bank statement lines by clicking the checkboxes +3. The footer will showing: + - Total amount of selected lines + +## Installation + +1. Copy the module to your Odoo addons directory: + ```bash + cp -r bank_statement_reconciliation /path/to/odoo/addons/ + ``` + +2. Update the addons list: + - Go to `Apps` menu + - Click `Update Apps List` + - Search for "Bank Statement Reconciliation" + +3. Install the module: + - Click `Install` button + +## Configuration + +No additional configuration is required. The module works out of the box after installation. + +## Usage + +### Reconciling Bank Statement Lines + +1. **Access Bank Statement Lines** + - Navigate to: `Accounting > Bank Statement Reconciliation > Bank Statement Lines` + +2. **Filter Lines** + - Use the search bar to filter by: + - Date + - Partner + - Journal + - Amount (Income/Expense) + +3. **Select Lines for Reconciliation** + - Check the boxes next to the lines you want to reconcile + - The total amount widget will show the sum automatically + +4. **Initiate Reconciliation** + - Click the `Action` menu + - Select `Reconcile Selected Lines` + - OR click the `Reconcile` button in the form view + +5. **Match Journal Entries** + - The wizard will open showing available journal entries + - Select the appropriate journal entry to match + - Confirm the reconciliation + +## Technical Details + +### Module Structure + +``` +bank_statement_reconciliation/ +├── __init__.py +├── __manifest__.py +├── README.md +├── models/ +│ ├── __init__.py +│ ├── account_bank_statement_line.py +│ ├── bank_statement_line.py +│ └── bank_statement_selector.py +├── views/ +│ ├── bank_statement_line_views.xml +│ ├── bank_statement_selector_views.xml +│ └── menu.xml +├── wizards/ +│ ├── __init__.py +│ ├── bank_reconcile_wizard.py +│ └── bank_reconcile_wizard_views.xml +├── security/ +│ └── ir.model.access.csv +└── static/ + └── src/ + ├── js/ + │ ├── bank_statement_total_widget.js + │ ├── bank_statement_list_controller.js + │ └── bank_statement_list_view.js + └── xml/ + ├── bank_statement_total_widget.xml + └── bank_statement_list_view.xml +``` + +### JavaScript Components + +#### BankStatementTotalWidget +- **Type**: OWL Component +- **Purpose**: Displays total amount of selected lines +- **Props**: + - `resIds`: Array of selected record IDs +- **Features**: + - Reactive state management + - Automatic updates on selection change + - Currency formatting + - Visual styling based on amount (positive/negative) + +#### BankStatementListController +- **Type**: List Controller Extension +- **Purpose**: Extends standard list controller with custom functionality +- **Features**: + - Provides selected records to the widget + - Integrates widget into list view layout + +### XML Views + +#### Tree View Enhancement +- Added `js_class="bank_statement_list"` attribute +- Enabled multi-edit mode +- Added monetary widget for amount field +- Added currency field for proper formatting +- Includes sum totals in footer + +### Dependencies + +- `account`: Core accounting module +- `base`: Odoo base module +- `web`: Web framework for JavaScript components + +## Customization + +### Modifying the Widget Appearance + +Edit [`bank_statement_total_widget.xml`](static/src/xml/bank_statement_total_widget.xml) to customize: +- Layout +- Colors +- Icons +- Styling + +### Extending Functionality + +To add more calculations or features: + +1. Edit [`bank_statement_total_widget.js`](static/src/js/bank_statement_total_widget.js) +2. Add new computed properties or methods +3. Update the template to display new information + +Example - Add average calculation: +```javascript +get averageAmount() { + if (this.state.selectedCount === 0) return 0; + return this.state.totalAmount / this.state.selectedCount; +} +``` + +## Troubleshooting + +### Widget Not Appearing + +1. **Clear Browser Cache** + - Hard refresh: `Ctrl+Shift+R` (Windows/Linux) or `Cmd+Shift+R` (Mac) + +2. **Update Assets** + - Navigate to: `Settings > Technical > User Interface > Views` + - Click `Regenerate Assets Bundles` + +3. **Check JavaScript Console** + - Press `F12` to open developer tools + - Look for any JavaScript errors + +### Incorrect Totals + +1. **Verify Currency Settings** + - Ensure all selected lines use the same currency + - Check currency rounding settings + +2. **Check Data Integrity** + - Verify amount fields are populated correctly + - Check for null or undefined values + +## Support + +For issues, questions, or contributions: +- **Author**: Suherdy Yacob +- **Version**: 17.0.1.0.0 +- **Odoo Version**: 17.0 + +## Changelog + +### Version 17.0.1.0.0 +- Added total selected amount widget in list view header +- Enhanced tree view with monetary formatting +- Improved user experience with visual indicators +- Added real-time calculation of selected lines +- Implemented modern OWL-based JavaScript components + +### Initial Version +- Basic bank statement reconciliation functionality +- Wizard-based reconciliation process +- Multi-line selection support +- Journal entry matching + +## License + +This module follows the same license as Odoo. + +## Screenshots + +### Total Amount Widget in Action +When you select multiple bank statement lines, the widget appears at the top showing: +- Number of selected items +- Total sum with currency +- Color-coded amount (green for positive, red for negative) + +### List View Enhancements +- Checkboxes for multi-selection +- Amount column with monetary formatting +- Footer totals for all visible records +- Clean, modern interface + +## Future Enhancements + +Planned features for future releases: +- [ ] Support for multi-currency reconciliation +- [ ] Advanced filtering options +- [ ] Reconciliation history tracking +- [ ] Export selected lines to Excel +- [ ] Batch operations for common reconciliation patterns - [ ] AI-powered suggestion for matching entries \ No newline at end of file diff --git a/__init__.py b/__init__.py index 6ed2c21..cf2479f 100644 --- a/__init__.py +++ b/__init__.py @@ -1,2 +1,2 @@ -from . import models +from . import models from . import wizards \ No newline at end of file diff --git a/__manifest__.py b/__manifest__.py index 8de75bd..77b0680 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -1,32 +1,32 @@ -{ - 'name': 'Bank Statement Reconciliation', - 'version': '17.0.1.0.0', - 'category': 'Accounting', - 'summary': 'Reconcile bank statement lines with journal entries', - 'description': """ - This module allows users to reconcile bank statement lines with journal entries. - Features: - - Menu to access bank statement lines - - Filter by bank journal - - Select multiple bank lines to reconcile - - Wizard to select journal entries for reconciliation - - Automatic creation of reconciliation journal entries - - Total selected amount widget in list view header - """, - 'author': 'Suherdy Yacob', - 'depends': [ - 'account', - 'base', - 'web', - ], - 'data': [ - 'security/ir.model.access.csv', - 'views/bank_statement_line_views.xml', - 'views/bank_statement_selector_views.xml', - 'wizards/bank_reconcile_wizard_views.xml', - 'views/menu.xml', - ], - # Tree view includes sum="Total Amount" footer for displaying totals - 'installable': True, - 'auto_install': False, +{ + 'name': 'Bank Statement Reconciliation', + 'version': '17.0.1.0.0', + 'category': 'Accounting', + 'summary': 'Reconcile bank statement lines with journal entries', + 'description': """ + This module allows users to reconcile bank statement lines with journal entries. + Features: + - Menu to access bank statement lines + - Filter by bank journal + - Select multiple bank lines to reconcile + - Wizard to select journal entries for reconciliation + - Automatic creation of reconciliation journal entries + - Total selected amount widget in list view header + """, + 'author': 'Suherdy Yacob', + 'depends': [ + 'account', + 'base', + 'web', + ], + 'data': [ + 'security/ir.model.access.csv', + 'views/bank_statement_line_views.xml', + 'views/bank_statement_selector_views.xml', + 'wizards/bank_reconcile_wizard_views.xml', + 'views/menu.xml', + ], + # Tree view includes sum="Total Amount" footer for displaying totals + 'installable': True, + 'auto_install': False, } \ No newline at end of file diff --git a/__pycache__/__init__.cpython-310.pyc b/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..172acba Binary files /dev/null and b/__pycache__/__init__.cpython-310.pyc differ diff --git a/models/__init__.py b/models/__init__.py index 96b9f9b..b794ae3 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1,3 +1,3 @@ -from . import bank_statement_line -from . import bank_statement_selector +from . import bank_statement_line +from . import bank_statement_selector from . import account_bank_statement_line \ No newline at end of file diff --git a/models/__pycache__/__init__.cpython-310.pyc b/models/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..0296347 Binary files /dev/null and b/models/__pycache__/__init__.cpython-310.pyc differ diff --git a/models/__pycache__/account_bank_statement_line.cpython-310.pyc b/models/__pycache__/account_bank_statement_line.cpython-310.pyc new file mode 100644 index 0000000..e5e3cc4 Binary files /dev/null and b/models/__pycache__/account_bank_statement_line.cpython-310.pyc differ diff --git a/models/__pycache__/bank_statement_line.cpython-310.pyc b/models/__pycache__/bank_statement_line.cpython-310.pyc new file mode 100644 index 0000000..08f6cb5 Binary files /dev/null and b/models/__pycache__/bank_statement_line.cpython-310.pyc differ diff --git a/models/__pycache__/bank_statement_selector.cpython-310.pyc b/models/__pycache__/bank_statement_selector.cpython-310.pyc new file mode 100644 index 0000000..94cb9e1 Binary files /dev/null and b/models/__pycache__/bank_statement_selector.cpython-310.pyc differ diff --git a/models/account_bank_statement_line.py b/models/account_bank_statement_line.py index 5e941bb..ff5af7c 100644 --- a/models/account_bank_statement_line.py +++ b/models/account_bank_statement_line.py @@ -1,35 +1,35 @@ -from odoo import models, fields, api -from odoo.exceptions import UserError - - -class AccountBankStatementLine(models.Model): - _inherit = 'account.bank.statement.line' - - def action_reconcile_selected_lines(self): - """Open the reconciliation wizard for selected lines""" - # Get the selected records from the context - active_ids = self.env.context.get('active_ids') - active_model = self.env.context.get('active_model') - - if active_model == 'account.bank.statement.line' and active_ids: - selected_lines = self.browse(active_ids) - else: - # If called from a single record, use self - selected_lines = self - - # Filter out already reconciled lines by checking if they have a move_id with reconciliation in the name - unreconciled_lines = selected_lines.filtered(lambda line: not line.move_id) - - if not unreconciled_lines: - raise UserError("All selected bank statement lines have already been reconciled.") - - return { - 'name': 'Select Journal Entry to Reconcile', - 'type': 'ir.actions.act_window', - 'res_model': 'bank.reconcile.wizard', - 'view_mode': 'form', - 'target': 'new', - 'context': { - 'default_bank_line_ids': unreconciled_lines.ids, - } +from odoo import models, fields, api +from odoo.exceptions import UserError + + +class AccountBankStatementLine(models.Model): + _inherit = 'account.bank.statement.line' + + def action_reconcile_selected_lines(self): + """Open the reconciliation wizard for selected lines""" + # Get the selected records from the context + active_ids = self.env.context.get('active_ids') + active_model = self.env.context.get('active_model') + + if active_model == 'account.bank.statement.line' and active_ids: + selected_lines = self.browse(active_ids) + else: + # If called from a single record, use self + selected_lines = self + + # Filter out already reconciled lines by checking if they have a move_id with reconciliation in the name + unreconciled_lines = selected_lines.filtered(lambda line: not line.move_id) + + if not unreconciled_lines: + raise UserError("All selected bank statement lines have already been reconciled.") + + return { + 'name': 'Select Journal Entry to Reconcile', + 'type': 'ir.actions.act_window', + 'res_model': 'bank.reconcile.wizard', + 'view_mode': 'form', + 'target': 'new', + 'context': { + 'default_bank_line_ids': unreconciled_lines.ids, + } } \ No newline at end of file diff --git a/models/bank_statement_line.py b/models/bank_statement_line.py index 2379e58..4f58dbe 100644 --- a/models/bank_statement_line.py +++ b/models/bank_statement_line.py @@ -1,53 +1,53 @@ -from odoo import models, fields, api -from odoo.exceptions import UserError - - -class BankStatementLine(models.Model): - _name = 'bank.statement.line' - _description = 'Bank Statement Line Selection' - - @api.model - def default_get(self, fields): - res = super().default_get(fields) - active_model = self.env.context.get('active_model') - active_ids = self.env.context.get('active_ids') - - if active_model == 'account.bank.statement.line' and active_ids: - statement_lines = self.env['account.bank.statement.line'].browse(active_ids) - res['line_ids'] = [(6, 0, statement_lines.ids)] - - return res - - journal_id = fields.Many2one('account.journal', string='Bank Journal', - domain=[('type', '=', 'bank')]) - line_ids = fields.Many2many('account.bank.statement.line', string='Bank Statement Lines') - selected_line_ids = fields.Many2many('account.bank.statement.line', - 'bank_statement_line_rel', - 'wizard_id', 'line_id', - string='Selected Bank Lines') - - @api.onchange('journal_id') - def _onchange_journal_id(self): - if self.journal_id: - statement_lines = self.env['account.bank.statement.line'].search([ - ('journal_id', '=', self.journal_id.id) - ]) - self.line_ids = statement_lines - else: - self.line_ids = False - - def action_open_reconcile_wizard(self): - """Open the reconciliation wizard""" - if not self.selected_line_ids: - raise UserError("Please select at least one bank statement line to reconcile.") - - return { - 'name': 'Select Journal Entry to Reconcile', - 'type': 'ir.actions.act_window', - 'res_model': 'bank.reconcile.wizard', - 'view_mode': 'form', - 'target': 'new', - 'context': { - 'default_bank_line_ids': self.selected_line_ids.ids, - } +from odoo import models, fields, api +from odoo.exceptions import UserError + + +class BankStatementLine(models.Model): + _name = 'bank.statement.line' + _description = 'Bank Statement Line Selection' + + @api.model + def default_get(self, fields): + res = super().default_get(fields) + active_model = self.env.context.get('active_model') + active_ids = self.env.context.get('active_ids') + + if active_model == 'account.bank.statement.line' and active_ids: + statement_lines = self.env['account.bank.statement.line'].browse(active_ids) + res['line_ids'] = [(6, 0, statement_lines.ids)] + + return res + + journal_id = fields.Many2one('account.journal', string='Bank Journal', + domain=[('type', '=', 'bank')]) + line_ids = fields.Many2many('account.bank.statement.line', string='Bank Statement Lines') + selected_line_ids = fields.Many2many('account.bank.statement.line', + 'bank_statement_line_rel', + 'wizard_id', 'line_id', + string='Selected Bank Lines') + + @api.onchange('journal_id') + def _onchange_journal_id(self): + if self.journal_id: + statement_lines = self.env['account.bank.statement.line'].search([ + ('journal_id', '=', self.journal_id.id) + ]) + self.line_ids = statement_lines + else: + self.line_ids = False + + def action_open_reconcile_wizard(self): + """Open the reconciliation wizard""" + if not self.selected_line_ids: + raise UserError("Please select at least one bank statement line to reconcile.") + + return { + 'name': 'Select Journal Entry to Reconcile', + 'type': 'ir.actions.act_window', + 'res_model': 'bank.reconcile.wizard', + 'view_mode': 'form', + 'target': 'new', + 'context': { + 'default_bank_line_ids': self.selected_line_ids.ids, + } } \ No newline at end of file diff --git a/models/bank_statement_selector.py b/models/bank_statement_selector.py index 9913086..f3eec7a 100644 --- a/models/bank_statement_selector.py +++ b/models/bank_statement_selector.py @@ -1,29 +1,29 @@ -from odoo import models, fields, api - - -class BankStatementSelector(models.TransientModel): - _name = 'bank.statement.selector' - _description = 'Bank Statement Selector' - - journal_id = fields.Many2one('account.journal', - string='Bank Journal', - domain=[('type', '=', 'bank')], - required=True) - - def action_show_statement_lines(self): - """Open the bank statement lines for the selected journal""" - action = { - 'type': 'ir.actions.act_window', - 'name': 'Bank Statement Lines', - 'res_model': 'account.bank.statement.line', - 'view_mode': 'tree,form', - 'domain': [('journal_id', '=', self.journal_id.id)], - 'context': { - 'search_default_journal_id': self.journal_id.id, - }, - 'views': [ - (self.env.ref('bank_statement_reconciliation.view_account_bank_statement_line_tree').id, 'tree'), - (self.env.ref('bank_statement_reconciliation.view_account_bank_statement_line_form').id, 'form') - ] - } +from odoo import models, fields, api + + +class BankStatementSelector(models.TransientModel): + _name = 'bank.statement.selector' + _description = 'Bank Statement Selector' + + journal_id = fields.Many2one('account.journal', + string='Bank Journal', + domain=[('type', '=', 'bank')], + required=True) + + def action_show_statement_lines(self): + """Open the bank statement lines for the selected journal""" + action = { + 'type': 'ir.actions.act_window', + 'name': 'Bank Statement Lines', + 'res_model': 'account.bank.statement.line', + 'view_mode': 'tree,form', + 'domain': [('journal_id', '=', self.journal_id.id)], + 'context': { + 'search_default_journal_id': self.journal_id.id, + }, + 'views': [ + (self.env.ref('bank_statement_reconciliation.view_account_bank_statement_line_tree').id, 'tree'), + (self.env.ref('bank_statement_reconciliation.view_account_bank_statement_line_form').id, 'form') + ] + } return action \ No newline at end of file diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv index d8cc973..508a804 100644 --- a/security/ir.model.access.csv +++ b/security/ir.model.access.csv @@ -1,4 +1,4 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_bank_statement_line,access_bank_statement_line,model_bank_statement_line,account.group_account_user,1,1,1,1 -access_bank_reconcile_wizard,access_bank_reconcile_wizard,model_bank_reconcile_wizard,account.group_account_user,1,1 +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_bank_statement_line,access_bank_statement_line,model_bank_statement_line,account.group_account_user,1,1,1,1 +access_bank_reconcile_wizard,access_bank_reconcile_wizard,model_bank_reconcile_wizard,account.group_account_user,1,1 access_bank_statement_selector,access_bank_statement_selector,model_bank_statement_selector,account.group_account_user,1,1,1,1 \ No newline at end of file diff --git a/static/src/js/bank_statement_list_controller.js b/static/src/js/bank_statement_list_controller.js index 20bea97..f8c1276 100644 --- a/static/src/js/bank_statement_list_controller.js +++ b/static/src/js/bank_statement_list_controller.js @@ -1,29 +1,29 @@ -/** @odoo-module **/ - -import { ListController } from "@web/views/list/list_controller"; -import { BankStatementTotalWidget } from "./bank_statement_total_widget"; -import { patch } from "@web/core/utils/patch"; - -patch(ListController.prototype, { - get selectedRecords() { - if (this.props.resModel === "account.bank.statement.line") { - return this.model.root.selection.map(record => record.resId); - } - return []; - } -}); - -export class BankStatementListController extends ListController { - setup() { - super.setup(); - } - - get selectedRecords() { - return this.model.root.selection.map(record => record.resId); - } -} - -BankStatementListController.components = { - ...ListController.components, - BankStatementTotalWidget, +/** @odoo-module **/ + +import { ListController } from "@web/views/list/list_controller"; +import { BankStatementTotalWidget } from "./bank_statement_total_widget"; +import { patch } from "@web/core/utils/patch"; + +patch(ListController.prototype, { + get selectedRecords() { + if (this.props.resModel === "account.bank.statement.line") { + return this.model.root.selection.map(record => record.resId); + } + return []; + } +}); + +export class BankStatementListController extends ListController { + setup() { + super.setup(); + } + + get selectedRecords() { + return this.model.root.selection.map(record => record.resId); + } +} + +BankStatementListController.components = { + ...ListController.components, + BankStatementTotalWidget, }; \ No newline at end of file diff --git a/static/src/js/bank_statement_list_view.js b/static/src/js/bank_statement_list_view.js index 84a5970..000a115 100644 --- a/static/src/js/bank_statement_list_view.js +++ b/static/src/js/bank_statement_list_view.js @@ -1,12 +1,12 @@ -/** @odoo-module **/ - -import { registry } from "@web/core/registry"; -import { listView } from "@web/views/list/list_view"; -import { BankStatementListController } from "./bank_statement_list_controller"; - -export const bankStatementListView = { - ...listView, - Controller: BankStatementListController, -}; - +/** @odoo-module **/ + +import { registry } from "@web/core/registry"; +import { listView } from "@web/views/list/list_view"; +import { BankStatementListController } from "./bank_statement_list_controller"; + +export const bankStatementListView = { + ...listView, + Controller: BankStatementListController, +}; + registry.category("views").add("bank_statement_list", bankStatementListView); \ No newline at end of file diff --git a/static/src/js/bank_statement_total_widget.js b/static/src/js/bank_statement_total_widget.js index 8af20ed..e208569 100644 --- a/static/src/js/bank_statement_total_widget.js +++ b/static/src/js/bank_statement_total_widget.js @@ -1,81 +1,81 @@ -/** @odoo-module **/ - -import { Component, useState, onWillStart, onWillUpdateProps } from "@odoo/owl"; -import { registry } from "@web/core/registry"; -import { useService } from "@web/core/utils/hooks"; - -export class BankStatementTotalWidget extends Component { - setup() { - this.orm = useService("orm"); - this.state = useState({ - totalAmount: 0, - selectedCount: 0, - currency: null, - }); - - onWillStart(async () => { - await this.updateTotals(); - }); - - onWillUpdateProps(async (nextProps) => { - await this.updateTotals(nextProps); - }); - } - - async updateTotals(props = this.props) { - const resIds = props.resIds || []; - - if (resIds.length === 0) { - this.state.totalAmount = 0; - this.state.selectedCount = 0; - this.state.currency = null; - return; - } - - try { - const records = await this.orm.searchRead( - "account.bank.statement.line", - [["id", "in", resIds]], - ["amount", "currency_id"] - ); - - let total = 0; - let currency = null; - - records.forEach(record => { - total += record.amount || 0; - if (!currency && record.currency_id) { - currency = record.currency_id; - } - }); - - this.state.totalAmount = total; - this.state.selectedCount = records.length; - this.state.currency = currency; - } catch (error) { - console.error("Error fetching bank statement totals:", error); - this.state.totalAmount = 0; - this.state.selectedCount = 0; - this.state.currency = null; - } - } - - get formattedTotal() { - if (!this.state.currency) { - return this.state.totalAmount.toFixed(2); - } - // Format with currency symbol if available - return this.state.totalAmount.toFixed(2); - } - - get displayClass() { - return this.state.totalAmount < 0 ? 'text-danger' : 'text-success'; - } -} - -BankStatementTotalWidget.template = "bank_statement_reconciliation.BankStatementTotalWidget"; -BankStatementTotalWidget.props = { - resIds: { type: Array, optional: true }, -}; - +/** @odoo-module **/ + +import { Component, useState, onWillStart, onWillUpdateProps } from "@odoo/owl"; +import { registry } from "@web/core/registry"; +import { useService } from "@web/core/utils/hooks"; + +export class BankStatementTotalWidget extends Component { + setup() { + this.orm = useService("orm"); + this.state = useState({ + totalAmount: 0, + selectedCount: 0, + currency: null, + }); + + onWillStart(async () => { + await this.updateTotals(); + }); + + onWillUpdateProps(async (nextProps) => { + await this.updateTotals(nextProps); + }); + } + + async updateTotals(props = this.props) { + const resIds = props.resIds || []; + + if (resIds.length === 0) { + this.state.totalAmount = 0; + this.state.selectedCount = 0; + this.state.currency = null; + return; + } + + try { + const records = await this.orm.searchRead( + "account.bank.statement.line", + [["id", "in", resIds]], + ["amount", "currency_id"] + ); + + let total = 0; + let currency = null; + + records.forEach(record => { + total += record.amount || 0; + if (!currency && record.currency_id) { + currency = record.currency_id; + } + }); + + this.state.totalAmount = total; + this.state.selectedCount = records.length; + this.state.currency = currency; + } catch (error) { + console.error("Error fetching bank statement totals:", error); + this.state.totalAmount = 0; + this.state.selectedCount = 0; + this.state.currency = null; + } + } + + get formattedTotal() { + if (!this.state.currency) { + return this.state.totalAmount.toFixed(2); + } + // Format with currency symbol if available + return this.state.totalAmount.toFixed(2); + } + + get displayClass() { + return this.state.totalAmount < 0 ? 'text-danger' : 'text-success'; + } +} + +BankStatementTotalWidget.template = "bank_statement_reconciliation.BankStatementTotalWidget"; +BankStatementTotalWidget.props = { + resIds: { type: Array, optional: true }, +}; + registry.category("view_widgets").add("bank_statement_total_widget", BankStatementTotalWidget); \ No newline at end of file diff --git a/static/src/js/selected_amount_badge.js b/static/src/js/selected_amount_badge.js index bd8a3a7..4bfec5c 100644 --- a/static/src/js/selected_amount_badge.js +++ b/static/src/js/selected_amount_badge.js @@ -1,32 +1,32 @@ -/** @odoo-module **/ - -import { patch } from "@web/core/utils/patch"; -import { ListController } from "@web/views/list/list_controller"; - -patch(ListController.prototype, { - /** - * Compute total amount of selected bank statement lines - */ - get selectedBankLinesTotal() { - if (this.props.resModel !== "account.bank.statement.line") { - return null; - } - - const selection = this.model.root.selection; - if (!selection || selection.length === 0) { - return null; - } - - let total = 0; - selection.forEach(record => { - const amount = record.data.amount || 0; - total += amount; - }); - - return { - count: selection.length, - total: total.toFixed(2), - currency: selection[0]?.data.currency_id?.[1] || 'IDR' - }; - } +/** @odoo-module **/ + +import { patch } from "@web/core/utils/patch"; +import { ListController } from "@web/views/list/list_controller"; + +patch(ListController.prototype, { + /** + * Compute total amount of selected bank statement lines + */ + get selectedBankLinesTotal() { + if (this.props.resModel !== "account.bank.statement.line") { + return null; + } + + const selection = this.model.root.selection; + if (!selection || selection.length === 0) { + return null; + } + + let total = 0; + selection.forEach(record => { + const amount = record.data.amount || 0; + total += amount; + }); + + return { + count: selection.length, + total: total.toFixed(2), + currency: selection[0]?.data.currency_id?.[1] || 'IDR' + }; + } }); \ No newline at end of file diff --git a/static/src/xml/bank_statement_list_view.xml b/static/src/xml/bank_statement_list_view.xml index 9cf71cc..1376bb4 100644 --- a/static/src/xml/bank_statement_list_view.xml +++ b/static/src/xml/bank_statement_list_view.xml @@ -1,10 +1,10 @@ - - - - -
- -
-
-
+ + + + +
+ +
+
+
\ No newline at end of file diff --git a/static/src/xml/bank_statement_selection_info.xml b/static/src/xml/bank_statement_selection_info.xml index 1e54796..25c4ceb 100644 --- a/static/src/xml/bank_statement_selection_info.xml +++ b/static/src/xml/bank_statement_selection_info.xml @@ -1,15 +1,15 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/static/src/xml/bank_statement_total_widget.xml b/static/src/xml/bank_statement_total_widget.xml index d20436a..ab368d7 100644 --- a/static/src/xml/bank_statement_total_widget.xml +++ b/static/src/xml/bank_statement_total_widget.xml @@ -1,19 +1,19 @@ - - - -
-
- - Selected Lines: - -
-
-
- - Total Amount: - - -
-
- + + + +
+
+ + Selected Lines: + +
+
+
+ + Total Amount: + + +
+
+ \ No newline at end of file diff --git a/static/src/xml/selected_amount_badge.xml b/static/src/xml/selected_amount_badge.xml index 9107333..f8889d4 100644 --- a/static/src/xml/selected_amount_badge.xml +++ b/static/src/xml/selected_amount_badge.xml @@ -1,11 +1,11 @@ - - - - -
- - Total: Rp -
-
-
+ + + + +
+ + Total: Rp +
+
+
\ No newline at end of file diff --git a/views/bank_statement_line_views.xml b/views/bank_statement_line_views.xml index 218c8ec..ecbdf6f 100644 --- a/views/bank_statement_line_views.xml +++ b/views/bank_statement_line_views.xml @@ -1,109 +1,109 @@ - - - - - Bank Statement Lines - account.bank.statement.line - tree,form - {'tree_view_ref': 'bank_statement_reconciliation.view_account_bank_statement_line_tree'} - -

- Select a bank journal to view its statement lines -

-
-
- - - - Reconcile Selected Lines - - - code - - action = { - 'name': 'Select Journal Entry to Reconcile', - 'type': 'ir.actions.act_window', - 'res_model': 'bank.reconcile.wizard', - 'view_mode': 'form', - 'target': 'new', - 'context': { - 'default_bank_line_ids': env.context.get('active_ids'), - } - } - - - - - - account.bank.statement.line.tree - account.bank.statement.line - - - - - - - - - - - - - - - - - - - account.bank.statement.line.form - account.bank.statement.line - -
-
-
- - - - - - - - - - - - - - - - - - - -
-
-
- - - - account.bank.statement.line.search - account.bank.statement.line - - - - - - - - - - - - - - - - - + + + + + Bank Statement Lines + account.bank.statement.line + tree,form + {'tree_view_ref': 'bank_statement_reconciliation.view_account_bank_statement_line_tree'} + +

+ Select a bank journal to view its statement lines +

+
+
+ + + + Reconcile Selected Lines + + + code + + action = { + 'name': 'Select Journal Entry to Reconcile', + 'type': 'ir.actions.act_window', + 'res_model': 'bank.reconcile.wizard', + 'view_mode': 'form', + 'target': 'new', + 'context': { + 'default_bank_line_ids': env.context.get('active_ids'), + } + } + + + + + + account.bank.statement.line.tree + account.bank.statement.line + + + + + + + + + + + + + + + + + + + account.bank.statement.line.form + account.bank.statement.line + +
+
+
+ + + + + + + + + + + + + + + + + + + +
+
+
+ + + + account.bank.statement.line.search + account.bank.statement.line + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/views/bank_statement_selector_views.xml b/views/bank_statement_selector_views.xml index 407e2cc..94a5942 100644 --- a/views/bank_statement_selector_views.xml +++ b/views/bank_statement_selector_views.xml @@ -1,29 +1,29 @@ - - - - - bank.statement.selector.form - bank.statement.selector - -
- - - -
-
-
-
-
- - - - Select Bank Journal - bank.statement.selector - form - new - - + + + + + bank.statement.selector.form + bank.statement.selector + +
+ + + +
+
+
+
+
+ + + + Select Bank Journal + bank.statement.selector + form + new + +
\ No newline at end of file diff --git a/views/menu.xml b/views/menu.xml index 87cde3f..d09a2fa 100644 --- a/views/menu.xml +++ b/views/menu.xml @@ -1,15 +1,15 @@ - - - - - - + + + + + + \ No newline at end of file diff --git a/wizards/__pycache__/__init__.cpython-310.pyc b/wizards/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..4e7101f Binary files /dev/null and b/wizards/__pycache__/__init__.cpython-310.pyc differ diff --git a/wizards/__pycache__/bank_reconcile_wizard.cpython-310.pyc b/wizards/__pycache__/bank_reconcile_wizard.cpython-310.pyc new file mode 100644 index 0000000..9e92604 Binary files /dev/null and b/wizards/__pycache__/bank_reconcile_wizard.cpython-310.pyc differ diff --git a/wizards/bank_reconcile_wizard.py b/wizards/bank_reconcile_wizard.py index 391473e..5561792 100644 --- a/wizards/bank_reconcile_wizard.py +++ b/wizards/bank_reconcile_wizard.py @@ -1,93 +1,93 @@ -from odoo import models, fields, api -from odoo.exceptions import UserError - - -class BankReconcileWizard(models.TransientModel): - _name = 'bank.reconcile.wizard' - _description = 'Bank Reconcile Wizard' - - bank_line_ids = fields.Many2many('account.bank.statement.line', - string='Selected Bank Lines', - readonly=True) - journal_entry_id = fields.Many2one('account.move', - string='Journal Entry to Reconcile', - domain=[('state', '=', 'posted')]) - journal_entry_line_id = fields.Many2one('account.move.line', - string='Journal Entry Line to Reconcile', - domain="[('move_id', '=', journal_entry_id), ('reconciled', '=', False)]") - total_bank_line_amount = fields.Float(string='Total Bank Line Amount', compute='_compute_total_bank_line_amount', store=True) - - @api.onchange('journal_entry_id') - def _onchange_journal_entry_id(self): - """Reset journal entry line when journal entry changes""" - if self.journal_entry_id: - self.journal_entry_line_id = False - else: - self.journal_entry_line_id = False - - - @api.depends('bank_line_ids') - def _compute_total_bank_line_amount(self): - """Compute total amount of selected bank lines""" - for record in self: - record.total_bank_line_amount = sum(line.amount for line in record.bank_line_ids) - - def action_reconcile(self): - """Perform the reconciliation for each selected bank line""" - if not self.journal_entry_id: - raise UserError("Please select a journal entry to reconcile.") - - if not self.journal_entry_line_id: - raise UserError("Please select a journal entry line to reconcile.") - - # Check if any of the selected bank lines are already reconciled - for bank_line in self.bank_line_ids: - if bank_line.move_id and bank_line.move_id.ref and 'Reconciliation:' in bank_line.move_id.ref: - raise UserError(f"Bank statement line '{bank_line.ref}' has already been reconciled and cannot be reconciled again.") - - # Process each selected bank line individually - for bank_line in self.bank_line_ids: - self._reconcile_single_line(bank_line, self.journal_entry_line_id) - - - return {'type': 'ir.actions.act_window_close'} - - def _reconcile_single_line(self, bank_line, journal_entry_line): - """Reconcile a single bank line with a journal entry line""" - - # Create a journal entry to balance the transaction - # This mimics the standard Odoo reconciliation widget behavior. - move = self.env['account.move'].create({ - 'journal_id': bank_line.journal_id.id, - 'date': bank_line.date, - 'ref': f'Reconciliation: {bank_line.name or "Bank Line"}', - 'move_type': 'entry', - 'line_ids': [ - (0, 0, { - 'account_id': bank_line.journal_id.default_account_id.id, - 'debit': bank_line.amount if bank_line.amount > 0 else 0, - 'credit': -bank_line.amount if bank_line.amount < 0 else 0, - 'name': f'Bank Reconciliation: {bank_line.name or ""}', - }), - (0, 0, { - 'account_id': journal_entry_line.account_id.id, - 'debit': -bank_line.amount if bank_line.amount < 0 else 0, - 'credit': bank_line.amount if bank_line.amount > 0 else 0, - 'name': f'Bank Reconciliation: {journal_entry_line.name or ""}', - }), - ], - }) - move.action_post() - - # Link the bank statement line to the new journal entry - bank_line.write({ - 'move_id': move.id, - }) - - # Find the corresponding line in the new move and reconcile with the journal entry line - move_line = move.line_ids.filtered(lambda l: l.account_id.id == journal_entry_line.account_id.id) - if move_line: - try: - (journal_entry_line + move_line).reconcile() - except: - pass +from odoo import models, fields, api +from odoo.exceptions import UserError + + +class BankReconcileWizard(models.TransientModel): + _name = 'bank.reconcile.wizard' + _description = 'Bank Reconcile Wizard' + + bank_line_ids = fields.Many2many('account.bank.statement.line', + string='Selected Bank Lines', + readonly=True) + journal_entry_id = fields.Many2one('account.move', + string='Journal Entry to Reconcile', + domain=[('state', '=', 'posted')]) + journal_entry_line_id = fields.Many2one('account.move.line', + string='Journal Entry Line to Reconcile', + domain="[('move_id', '=', journal_entry_id), ('reconciled', '=', False)]") + total_bank_line_amount = fields.Float(string='Total Bank Line Amount', compute='_compute_total_bank_line_amount', store=True) + + @api.onchange('journal_entry_id') + def _onchange_journal_entry_id(self): + """Reset journal entry line when journal entry changes""" + if self.journal_entry_id: + self.journal_entry_line_id = False + else: + self.journal_entry_line_id = False + + + @api.depends('bank_line_ids') + def _compute_total_bank_line_amount(self): + """Compute total amount of selected bank lines""" + for record in self: + record.total_bank_line_amount = sum(line.amount for line in record.bank_line_ids) + + def action_reconcile(self): + """Perform the reconciliation for each selected bank line""" + if not self.journal_entry_id: + raise UserError("Please select a journal entry to reconcile.") + + if not self.journal_entry_line_id: + raise UserError("Please select a journal entry line to reconcile.") + + # Check if any of the selected bank lines are already reconciled + for bank_line in self.bank_line_ids: + if bank_line.move_id and bank_line.move_id.ref and 'Reconciliation:' in bank_line.move_id.ref: + raise UserError(f"Bank statement line '{bank_line.ref}' has already been reconciled and cannot be reconciled again.") + + # Process each selected bank line individually + for bank_line in self.bank_line_ids: + self._reconcile_single_line(bank_line, self.journal_entry_line_id) + + + return {'type': 'ir.actions.act_window_close'} + + def _reconcile_single_line(self, bank_line, journal_entry_line): + """Reconcile a single bank line with a journal entry line""" + + # Create a journal entry to balance the transaction + # This mimics the standard Odoo reconciliation widget behavior. + move = self.env['account.move'].create({ + 'journal_id': bank_line.journal_id.id, + 'date': bank_line.date, + 'ref': f'Reconciliation: {bank_line.name or "Bank Line"}', + 'move_type': 'entry', + 'line_ids': [ + (0, 0, { + 'account_id': bank_line.journal_id.default_account_id.id, + 'debit': bank_line.amount if bank_line.amount > 0 else 0, + 'credit': -bank_line.amount if bank_line.amount < 0 else 0, + 'name': f'Bank Reconciliation: {bank_line.name or ""}', + }), + (0, 0, { + 'account_id': journal_entry_line.account_id.id, + 'debit': -bank_line.amount if bank_line.amount < 0 else 0, + 'credit': bank_line.amount if bank_line.amount > 0 else 0, + 'name': f'Bank Reconciliation: {journal_entry_line.name or ""}', + }), + ], + }) + move.action_post() + + # Link the bank statement line to the new journal entry + bank_line.write({ + 'move_id': move.id, + }) + + # Find the corresponding line in the new move and reconcile with the journal entry line + move_line = move.line_ids.filtered(lambda l: l.account_id.id == journal_entry_line.account_id.id) + if move_line: + try: + (journal_entry_line + move_line).reconcile() + except: + pass diff --git a/wizards/bank_reconcile_wizard_views.xml b/wizards/bank_reconcile_wizard_views.xml index ee25624..4c11199 100644 --- a/wizards/bank_reconcile_wizard_views.xml +++ b/wizards/bank_reconcile_wizard_views.xml @@ -1,36 +1,36 @@ - - - - - bank.reconcile.wizard.form - bank.reconcile.wizard - -
- - - - - - - - -
-
-
-
-
- - - - Select Journal Entry to Reconcile - bank.reconcile.wizard - form - new - + + + + + bank.reconcile.wizard.form + bank.reconcile.wizard + +
+ + + + + + + + +
+
+
+
+
+ + + + Select Journal Entry to Reconcile + bank.reconcile.wizard + form + new +
\ No newline at end of file