diff --git a/base_accounting_kit/__manifest__.py b/base_accounting_kit/__manifest__.py index 9e8aa5780..565817502 100644 --- a/base_accounting_kit/__manifest__.py +++ b/base_accounting_kit/__manifest__.py @@ -21,7 +21,7 @@ ############################################################################# { 'name': 'Odoo 18 Full Accounting Kit for Community', - 'version': '18.0.1.0.0', + 'version': '18.0.1.0.1', 'category': 'Accounting', 'summary': """Odoo 18 Accounting, Odoo 18 Accounting Reports, Odoo18 Accounting, Odoo Accounting, Odoo18 Financial Reports, Odoo18 Asset, Odoo18 Profit and Loss, PDC, Followups, Odoo18, Accounting, Odoo Apps, Reports""", 'description': """ Odoo 18 Accounting, The module used to manage the Full @@ -92,15 +92,22 @@ 'views/account_bank_statement_views.xml', 'views/account_bank_statement_line_views.xml', 'views/account_payment_view.xml', + 'wizard/account_lock_date_views.xml', ], 'assets': { 'web.assets_backend': [ 'base_accounting_kit/static/src/scss/style.scss', 'base_accounting_kit/static/src/scss/bank_rec_widget.css', + 'base_accounting_kit/static/src/js/bank_reconcile_form_list_widget.js', + 'base_accounting_kit/static/src/js/KanbanController.js', + 'base_accounting_kit/static/src/js/ListController.js', + 'base_accounting_kit/static/src/js/bank_reconcile_form_lines_widget.js', + 'base_accounting_kit/static/src/xml/bank_rec_widget.xml', + 'base_accounting_kit/static/src/xml/bank_reconcile_widget.xml', ] }, 'license': 'LGPL-3', - 'images': ['static/description/banner.jpg'], + 'images': ['static/description/banner.png'], 'installable': True, 'auto_install': False, 'application': True, diff --git a/base_accounting_kit/doc/RELEASE_NOTES.md b/base_accounting_kit/doc/RELEASE_NOTES.md index 41a16b19a..10ddd5fa8 100644 --- a/base_accounting_kit/doc/RELEASE_NOTES.md +++ b/base_accounting_kit/doc/RELEASE_NOTES.md @@ -4,3 +4,9 @@ #### Version 18.0.1.0.0 #### ADD - Initial commit for Odoo 18 Full Accounting Kit for Community + +#### 22.10.2024 +#### Version 18.0.1.0.1 +#### UPDT +- Added the reconciliation widget and lock dates. + diff --git a/base_accounting_kit/models/account_bank_statement_line.py b/base_accounting_kit/models/account_bank_statement_line.py index 3442b6756..c4af11558 100755 --- a/base_accounting_kit/models/account_bank_statement_line.py +++ b/base_accounting_kit/models/account_bank_statement_line.py @@ -79,12 +79,20 @@ class AccountBankStatementLine(models.Model): """Ensure the current recordset holds a single record and mark it as reconciled.""" self.ensure_one() self.is_reconciled = True + return { + 'type': 'ir.actions.client', + 'tag': 'reload', + } def button_reset(self): """Reset the current bank statement line if it is in a 'reconciled' state.""" self.ensure_one() if self.bank_state == 'reconciled': self.action_undo_reconciliation() + return { + 'type': 'ir.actions.client', + 'tag': 'reload', + } def button_to_check(self, async_action=True): """Ensure the current recordset holds a single record, validate the bank @@ -93,11 +101,19 @@ class AccountBankStatementLine(models.Model): if self.bank_state == 'valid': self.button_validation(async_action=async_action) self.move_id.to_check = True + return { + 'type': 'ir.actions.client', + 'tag': 'reload', + } def button_set_as_checked(self): """Mark the associated move as 'not to check' by setting 'to_check' to False.""" self.ensure_one() self.move_id.to_check = False + return { + 'type': 'ir.actions.client', + 'tag': 'reload', + } @api.model def get_statement_line(self, record_id): diff --git a/base_accounting_kit/models/account_journal.py b/base_accounting_kit/models/account_journal.py index 3f8a05266..1376f84c1 100644 --- a/base_accounting_kit/models/account_journal.py +++ b/base_accounting_kit/models/account_journal.py @@ -55,7 +55,56 @@ class AccountJournal(models.Model): def action_open_reconcile(self): """Open the reconciliation view based on the type of the account journal.""" self.ensure_one() - + if self.type in ('bank', 'cash'): + views = [ + (self.env.ref( + 'base_accounting_kit.account_bank_statement_line_view_kanban').id, + 'kanban'), + (self.env.ref( + 'base_accounting_kit.account_bank_statement_line_view_tree').id, + 'list'), # Include tree view + ] + context = { + 'default_journal_id': self.id, + 'search_default_journal_id': self.id, + } + kanban_first = True + name = None + extra_domain = None + return { + 'name': name or _("Bank Reconciliation"), + 'type': 'ir.actions.act_window', + 'res_model': 'account.bank.statement.line', + 'context': context, + 'search_view_id': [ + self.env.ref( + 'base_accounting_kit.account_bank_statement_line_view_search').id, + 'search'], + 'view_mode': 'kanban,list' if kanban_first else 'list,kanban', + 'views': views if kanban_first else views[::-1], + 'domain': [('state', '!=', 'cancel')] + (extra_domain or []), + 'help': _(""" +

+ Nothing to do here! +

+

+ No transactions matching your filters were found. +

+ """), + } + else: + # Open reconciliation view for customers/suppliers + action_context = {'show_mode_selector': False, + 'company_ids': self.mapped('company_id').ids} + if self.type == 'sale': + action_context.update({'mode': 'customers'}) + elif self.type == 'purchase': + action_context.update({'mode': 'suppliers'}) + return { + 'type': 'ir.actions.client', + 'tag': 'manual_reconciliation_view', + 'context': action_context, + } def create_cash_statement(self): """for redirecting in to bank statement lines""" diff --git a/base_accounting_kit/models/res_company.py b/base_accounting_kit/models/res_company.py index 7e5e94d5a..07cf3fc2d 100644 --- a/base_accounting_kit/models/res_company.py +++ b/base_accounting_kit/models/res_company.py @@ -27,14 +27,13 @@ class ResCompany(models.Model): """Model for inheriting res_company.""" _inherit = "res.company" - def _validate_fiscalyear_lock(self, values): - """Validate the fiscal year lock date by checking for unposted entries and unreconciled bank statement lines.""" - if values.get('fiscalyear_lock_date'): - + def _validate_locks(self, values): + """Validate the hard lock date by checking for unposted entries and unreconciled bank statement lines.""" + if values.get('hard_lock_date'): draft_entries = self.env['account.move'].search([ ('company_id', 'in', self.ids), ('state', '=', 'draft'), - ('date', '<=', values['fiscalyear_lock_date'])]) + ('date', '<=', values['hard_lock_date'])]) if draft_entries: error_msg = _('There are still unposted entries in the ' 'period you want to lock. You should either post ' @@ -53,21 +52,20 @@ class ResCompany(models.Model): unreconciled_statement_lines = self.env['account.bank.statement.line'].search([ ('company_id', 'in', self.ids), ('is_reconciled', '=', False), - ('date', '<=', values['fiscalyear_lock_date']), + ('date', '<=', values['hard_lock_date']), ('move_id.state', 'in', ('draft', 'posted')), ]) if unreconciled_statement_lines: error_msg = _("There are still unreconciled bank statement lines in the period you want to lock." "You should either reconcile or delete them.") action_error = { - 'view_mode': 'list', + 'view_mode': 'kanban', 'name': 'Unreconciled Transactions', 'res_model': 'account.bank.statement.line', 'type': 'ir.actions.act_window', 'domain': [('id', 'in', unreconciled_statement_lines.ids)], 'views': [[self.env.ref( - 'base_accounting_kit.view_bank_statement_line_tree').id, - 'list']] + 'base_accounting_kit.account_bank_statement_line_view_kanban').id, + 'kanban']] } - raise RedirectWarning(error_msg, action_error, - _('Show Unreconciled Bank Statement Line')) + raise RedirectWarning(error_msg, action_error, _('Show Unreconciled Bank Statement Lines')) diff --git a/base_accounting_kit/security/ir.model.access.csv b/base_accounting_kit/security/ir.model.access.csv index 5d32e5cdf..b89efcea1 100644 --- a/base_accounting_kit/security/ir.model.access.csv +++ b/base_accounting_kit/security/ir.model.access.csv @@ -42,9 +42,6 @@ access_account_common_journal_report,account.common.journal.report,model_account access_account_account_type,account.account.type,model_account_account_type,account.group_account_user,1,1,1,1 +access_account_lock_date,access.account.lock.date,model_account_lock_date,account.group_account_user,1,1,1,1 access_account_recurring_entries_line,access.account.recurring.entries.line,model_account_recurring_entries_line,account.group_account_user,1,1,1,1 access_generate_recurring_entries,generate.recurring.entries.user,model_account_recurring_payments,account.group_account_user,1,1,1,1 - - - - diff --git a/base_accounting_kit/static/description/assets/screenshots/loc3.png b/base_accounting_kit/static/description/assets/screenshots/loc3.png new file mode 100644 index 000000000..819a348b9 Binary files /dev/null and b/base_accounting_kit/static/description/assets/screenshots/loc3.png differ diff --git a/base_accounting_kit/static/description/assets/screenshots/loc_1.png b/base_accounting_kit/static/description/assets/screenshots/loc_1.png new file mode 100644 index 000000000..eb3f26ee4 Binary files /dev/null and b/base_accounting_kit/static/description/assets/screenshots/loc_1.png differ diff --git a/base_accounting_kit/static/description/assets/screenshots/loc_2.png b/base_accounting_kit/static/description/assets/screenshots/loc_2.png new file mode 100644 index 000000000..632937a85 Binary files /dev/null and b/base_accounting_kit/static/description/assets/screenshots/loc_2.png differ diff --git a/base_accounting_kit/static/description/assets/screenshots/rec_1.png b/base_accounting_kit/static/description/assets/screenshots/rec_1.png new file mode 100644 index 000000000..bd2d203a3 Binary files /dev/null and b/base_accounting_kit/static/description/assets/screenshots/rec_1.png differ diff --git a/base_accounting_kit/static/description/assets/screenshots/rec_2.png b/base_accounting_kit/static/description/assets/screenshots/rec_2.png new file mode 100644 index 000000000..ed66aeed9 Binary files /dev/null and b/base_accounting_kit/static/description/assets/screenshots/rec_2.png differ diff --git a/base_accounting_kit/static/description/assets/screenshots/rec_3.png b/base_accounting_kit/static/description/assets/screenshots/rec_3.png new file mode 100644 index 000000000..a95d7bedd Binary files /dev/null and b/base_accounting_kit/static/description/assets/screenshots/rec_3.png differ diff --git a/base_accounting_kit/static/description/banner.jpg b/base_accounting_kit/static/description/banner.jpg deleted file mode 100644 index 3cb15fe01..000000000 Binary files a/base_accounting_kit/static/description/banner.jpg and /dev/null differ diff --git a/base_accounting_kit/static/description/banner.png b/base_accounting_kit/static/description/banner.png new file mode 100644 index 000000000..738d65baf Binary files /dev/null and b/base_accounting_kit/static/description/banner.png differ diff --git a/base_accounting_kit/static/description/index.html b/base_accounting_kit/static/description/index.html index cc79a478b..c4bc1caab 100644 --- a/base_accounting_kit/static/description/index.html +++ b/base_accounting_kit/static/description/index.html @@ -126,7 +126,7 @@ font-size: 46px; font-weight: 700; line-height: normal; - ">Odoo 18 Full Accounting Kit for Community + ">Odoo 18 Full Accounting Kit
-
+
+
+
+ +
+
+ Reconciliation Widget. +
+

+ In the Account Dashboard we can reconcile by clicking the button Reconcile. +

+
+
+
+
+
+ +
+
+ Lock Dates. +
+

+ In the Lock Dates wizard we can set the different Lock Dates. +

+
+
@@ -366,7 +407,7 @@
Odoo 18 Full Accounting Kit for Community + style="font-size: 40px; font-weight: 700; color: #fff;line-height: 60px; text-transform: capitalize; width: 450px;">Odoo 18 Full Accounting Kit

Are you ready to make your business more @@ -605,9 +646,7 @@

- Manage - - + Manage Post dated checks.

@@ -629,6 +668,86 @@

+
+
+
+
+

+ Reconciliation + Widget. +

+
+ + + + + +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+

+ Lock + Dates. +

+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+
@@ -1103,7 +1222,7 @@
-
+
@@ -1133,6 +1252,21 @@
+
+
+
+
+ +
+

+ Reconciliation Widget. +

+
+
+
@@ -1244,6 +1378,35 @@ class="tab-pane fade show" id="releases" role="tabpanel"> +
+
+

+ Latest Release 18.0.1.0.1 +

+ + 22nd October, 2024 + +
+
+
+
+
+ Updt +
+
+
+
    +
  • + Added reconciliation widget and lock date +
  • +
+
+
+
+
+
+

diff --git a/base_accounting_kit/static/src/js/KanbanController.js b/base_accounting_kit/static/src/js/KanbanController.js new file mode 100755 index 000000000..37acd5c3f --- /dev/null +++ b/base_accounting_kit/static/src/js/KanbanController.js @@ -0,0 +1,129 @@ +/** @odoo-module **/ +import { registry } from "@web/core/registry"; +import { useService } from "@web/core/utils/hooks"; +import { View } from "@web/views/view"; +import { kanbanView } from "@web/views/kanban/kanban_view"; +import { KanbanController } from "@web/views/kanban/kanban_controller"; +import { KanbanRenderer } from "@web/views/kanban/kanban_renderer"; +import { KanbanRecord } from "@web/views/kanban/kanban_record"; +const { useState } = owl; + +class CustomKanbanController extends KanbanController { + async setup(){ + super.setup() + this.state = useState({ + selectedStLineId: null, + linesWidgetData: null, + moveLineData: null, + }); + this.action = useService("action") + this.orm = useService("orm") + const o_bank_reconcile_status_buttons_aside_left = document.getElementsByClassName("o_bank_reconcile_status_buttons_aside_left") + } + + async openRecord(record, mode) { + this.state.moveLineData = null; + this.state.viewID = await this.orm.call('res.config.settings', 'get_view_id', []) + await this.mountStLine(record.resId); + + const statementRecord = document.querySelectorAll('.o_bank_reconcile_st_line_kanban_card'); + statementRecord.forEach(line => { + line.addEventListener('click', async (event) => { + // Remove 'div-added' class and its child divs from all elements + statementRecord.forEach(item => { + item.classList.remove('div-added'); + const childDiv = item.querySelector('.new-div'); + if (childDiv) { + item.removeChild(childDiv); + } + }); + // Add 'div-added' class and new div to the clicked record + if (!line.classList.contains('div-added')) { + const newDiv = document.createElement('div'); + newDiv.classList.add('new-div'); // Add a class to identify the new div + line.classList.add('div-added'); + line.appendChild(newDiv); + } + }); + }); + } + + async mountStLine(stLineId){ + const currentStLineId = this.state.selectedStLineId; + if (currentStLineId !== stLineId) { + this.state.selectedStLineId = stLineId; // Update selected ST Line ID + try { + const data = await this.orm.call("account.bank.statement.line", "get_statement_line", [stLineId]); + this.state.linesWidgetData = data; + } catch (error) { + console.error("Error fetching statement line data:", error); + } + try { + const data = await this.orm.call('account.bank.statement.line', 'read', [[stLineId]], { fields: ['lines_widget_json'] }); + if (data && data.length > 0 && data[0].lines_widget_json) { + const parsedData = JSON.parse(data[0].lines_widget_json); + const moveIdMatch = parsedData.move_id.match(/\((\d+),\)/); + parsedData.numeric_move_id = moveIdMatch ? parseInt(moveIdMatch[1]) : null; + this.state.moveLineData = parsedData; + } else { + console.warn("No lines_widget_json found for selected statement line."); + } + } catch (error) { + console.error("Error reading statement line:", error); + } + } + } + + get prepareFormPropsBankReconcile(){ + if (!this.state.selectedStLineId) { + return null; // or some default props + } + return { + type: "form", + viewId: this.state.viewID, + context: { + default_st_line_id: this.state.selectedStLineId, + default_lines_widget: this.state.linesWidgetData || null, + default_move_line: this.state.moveLineData || null, + }, + display: { controlPanel: false, noBreadcrumbs: true}, + mode: "edit", + resModel: "account.bank.statement.line", + resId: this.state.selectedStLineId, + } + } +} +CustomKanbanController.components = { + ...CustomKanbanController.components, View } +CustomKanbanController.template = "base_accounting_kit.CustomKanbanView"; + +export class BankCustomKanbanRenderer extends KanbanRenderer { + setup(){ + super.setup(); + } +} +export class BankReconcileKanbanRecord extends KanbanRecord { + setup(){ + super.setup(); + this.state=useState({ + Statement_record:{} + }) + } +} +BankReconcileKanbanRecord.template = "base_accounting_kit.BankReconcileKanbanRecord"; + +BankCustomKanbanRenderer.components = { + ...KanbanRenderer.components, + KanbanRecord: BankReconcileKanbanRecord, +} +BankCustomKanbanRenderer.template = "base_accounting_kit.BankRecKanbanRenderer"; + +export const customKanbanView = { + ...kanbanView, + Controller: CustomKanbanController, + Renderer: BankCustomKanbanRenderer, + searchMenuTypes: ["filter"], +}; + +// Register it to the views registry +registry.category("views").add("custom_kanban", customKanbanView); diff --git a/base_accounting_kit/static/src/js/ListController.js b/base_accounting_kit/static/src/js/ListController.js new file mode 100755 index 000000000..da17c06f3 --- /dev/null +++ b/base_accounting_kit/static/src/js/ListController.js @@ -0,0 +1,168 @@ +/** @odoo-module **/ +import { registry } from '@web/core/registry'; +import { ListController } from "@web/views/list/list_controller"; +import { listView } from "@web/views/list/list_view"; +import { useState, useRef } from "@odoo/owl"; +import { useListener, useService} from "@web/core/utils/hooks"; +export class AccountMoveLineListController extends ListController { + constructor() { + super(...arguments); + this.resIdList = []; + } + setup(){ + super.setup(); + this.state = useState({ selectedRecordId: null , + selectedRecordIds: [],}); + this.action = useService("action") + this.orm = useService("orm") + } + async openRecord(record) { + const kanban_row = this.__owl__.bdom.parentEl.ownerDocument.querySelector(`tr[data-id]`); + const data_id = parseInt(kanban_row.getAttribute('data-id')) + var data = await this.orm.call('account.bank.statement.line', + 'update_match_row_data', + [record.resId]) + await this.orm.call('account.bank.statement.line', 'write', [[data_id], { lines_widget_json: JSON.stringify(data) }]); + const rowSelector = this.__owl__.bdom.parentEl.querySelector(`tr[data-id='${record.id}']`) + if (!record.clickCount) { + record.clickCount = true + rowSelector.style.backgroundColor = "#d1ecf1"; + } else { + // Set the default background color here + record.clickCount = false; + rowSelector.style.backgroundColor = "white"; + } + + const currencySymbol = await this.orm.call('res.currency', 'read',[record.data.currency_id[0]]) + const mainKanbanDiv = this.__owl__.bdom.parentEl.ownerDocument.querySelector('#base_accounting_reconcile') + const existingRow = this.__owl__.bdom.parentEl.ownerDocument.querySelector(`tr[data-resId="${record.resId}"]`) + const stateLineRow = this.__owl__.bdom.parentEl.ownerDocument.querySelector('.statement_row') + if (stateLineRow){ + const dataIdValue = stateLineRow.getAttribute('data-id'); + if(dataIdValue == record.resId){ + mainKanbanDiv.removeChild(stateLineRow); + } + } + if (existingRow) { + mainKanbanDiv.removeChild(existingRow); + } else { + // If the row doesn't exist, create and add it + const dateObject = new Date(record.data.date); + const year = dateObject.getFullYear(); + const month = String(dateObject.getMonth() + 1).padStart(2, '0'); // Months are 0-indexed + const day = String(dateObject.getDate()).padStart(2, '0'); + const formattedDate = `${year}-${month}-${day}`; + let amount = parseFloat(record.data.amount_residual); + let debitColumn = ''; + let creditColumn = ''; + let partnerName = ''; + let moveId = ''; + + // Check if partner_id exists and is not empty + if (record.data.partner_id && record.data.partner_id[1]) { + partnerName = record.data.partner_id[1]; + } + if (record.data.move_id && record.data.move_id[1]) { + moveId = `
${record.data.move_id[1]}`; + } + + + if (amount < 0) { + debitColumn = `${currencySymbol[0].symbol} ${-amount}`; + } else { + creditColumn = `${currencySymbol[0].symbol} ${amount}`; + } + + const newRow = document.createElement('tr'); + newRow.setAttribute('data-resId', record.resId); // Set a unique identifier for the row + if (debitColumn !== '') { + newRow.innerHTML = `${record.data.account_id[1]} + ${moveId} : ${record.data.name} + ${partnerName} + ${formattedDate} + ${debitColumn} + + +