Browse Source

Oct 22: [UPDT] Module Updated 'base_accounting_kit'

pull/346/head
Cybrosys Technologies 6 months ago
parent
commit
28a4f7ac99
  1. 11
      base_accounting_kit/__manifest__.py
  2. 6
      base_accounting_kit/doc/RELEASE_NOTES.md
  3. 16
      base_accounting_kit/models/account_bank_statement_line.py
  4. 51
      base_accounting_kit/models/account_journal.py
  5. 20
      base_accounting_kit/models/res_company.py
  6. 5
      base_accounting_kit/security/ir.model.access.csv
  7. BIN
      base_accounting_kit/static/description/assets/screenshots/loc3.png
  8. BIN
      base_accounting_kit/static/description/assets/screenshots/loc_1.png
  9. BIN
      base_accounting_kit/static/description/assets/screenshots/loc_2.png
  10. BIN
      base_accounting_kit/static/description/assets/screenshots/rec_1.png
  11. BIN
      base_accounting_kit/static/description/assets/screenshots/rec_2.png
  12. BIN
      base_accounting_kit/static/description/assets/screenshots/rec_3.png
  13. BIN
      base_accounting_kit/static/description/banner.jpg
  14. BIN
      base_accounting_kit/static/description/banner.png
  15. 179
      base_accounting_kit/static/description/index.html
  16. 129
      base_accounting_kit/static/src/js/KanbanController.js
  17. 168
      base_accounting_kit/static/src/js/ListController.js
  18. 79
      base_accounting_kit/static/src/js/bank_reconcile_form_lines_widget.js
  19. 124
      base_accounting_kit/static/src/js/bank_reconcile_form_list_widget.js
  20. 1
      base_accounting_kit/static/src/scss/style.scss
  21. 33
      base_accounting_kit/static/src/xml/bank_rec_widget.xml
  22. 124
      base_accounting_kit/static/src/xml/bank_reconcile_widget.xml
  23. 1
      base_accounting_kit/wizard/__init__.py
  24. 64
      base_accounting_kit/wizard/account_lock_date.py
  25. 46
      base_accounting_kit/wizard/account_lock_date_views.xml

11
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,

6
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.

16
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):

51
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': _("""
<p class="o_view_nocontent_smiling_face">
Nothing to do here!
</p>
<p>
No transactions matching your filters were found.
</p>
"""),
}
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"""

20
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'))

5
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

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
42 access_generate_recurring_entries generate.recurring.entries.user model_account_recurring_payments account.group_account_user 1 1 1 1
43
44
45
46
47

BIN
base_accounting_kit/static/description/assets/screenshots/loc3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

BIN
base_accounting_kit/static/description/assets/screenshots/loc_1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
base_accounting_kit/static/description/assets/screenshots/loc_2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
base_accounting_kit/static/description/assets/screenshots/rec_1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
base_accounting_kit/static/description/assets/screenshots/rec_2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
base_accounting_kit/static/description/assets/screenshots/rec_3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

BIN
base_accounting_kit/static/description/banner.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 767 KiB

BIN
base_accounting_kit/static/description/banner.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

179
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</span>
">Odoo 18 Full Accounting Kit</span>
</h1>
</div>
<div class="col-lg-12 d-flex justify-content-center align-items-center"
@ -336,7 +336,7 @@
</p>
</div>
</div>
<div class="col-lg-4">
<div class="col-lg-4">
<div class="mb-4 d-flex flex-column justify-content-center gap-3"
style="border-radius: 12px;
border: 1px solid #B6BCCD;
@ -357,7 +357,48 @@
</p>
</div>
</div>
<div class="col-lg-4">
<div class="mb-4 d-flex flex-column justify-content-center gap-3"
style="border-radius: 12px;
border: 1px solid #B6BCCD;
background: #FFF;padding:32px ">
<div class="d-flex justify-content-center align-items-center"
style="background-color:#7847D9; border-radius:8px !important; height:42px; width:42px">
<img src="./assets/icons/feature-icon.svg"
class="img-responsive" height="26px"
width="26px">
</div>
<h5 class="m-0"
style="color:#000 !important; font-weight:bold">
Reconciliation Widget.
</h5>
<p class="m-0"
style="font-size:0.9rem; color:var(--text-color-light); font-size: 16px; font-weight: 400;">
In the Account Dashboard we can reconcile by clicking the button Reconcile.
</p>
</div>
</div>
<div class="col-lg-4">
<div class="mb-4 d-flex flex-column justify-content-center gap-3"
style="border-radius: 12px;
border: 1px solid #B6BCCD;
background: #FFF;padding:32px ">
<div class="d-flex justify-content-center align-items-center"
style="background-color:#7847D9; border-radius:8px !important; height:42px; width:42px">
<img src="./assets/icons/feature-icon.svg"
class="img-responsive" height="26px"
width="26px">
</div>
<h5 class="m-0"
style="color:#000 !important; font-weight:bold">
Lock Dates.
</h5>
<p class="m-0"
style="font-size:0.9rem; color:var(--text-color-light); font-size: 16px; font-weight: 400;">
In the Lock Dates wizard we can set the different Lock Dates.
</p>
</div>
</div>
</div>
</div>
<!--code -->
@ -366,7 +407,7 @@
<div class="wrapper-info"
style="display: flex; flex-direction: column; gap: 20px;">
<span class="wrapper-subtitle"
style="font-size: 40px; font-weight: 700; color: #fff;line-height: 60px; text-transform: capitalize; width: 450px;">Odoo 18 Full Accounting Kit for Community</span>
style="font-size: 40px; font-weight: 700; color: #fff;line-height: 60px; text-transform: capitalize; width: 450px;">Odoo 18 Full Accounting Kit</span>
<h3 class="wrapper-details"
style="font-size: 20px; font-weight: 400; color: #fff; line-height: 32px; ">
Are you ready to make your business more
@ -605,9 +646,7 @@
<div class="col-md-12">
<h1 style="font-weight:bold; font-size:calc(1.1rem + 1vw); line-height:120%; text-align:center; text-transform:capitalize; font-size: 40px;
font-weight: 700;">
<span style="color:#121212; font-size:calc(1.1rem + 1vw)"> Manage
</span>
<span style="color:#121212; font-size:calc(1.1rem + 1vw)"> Manage</span>
<span style="color: var(--primary-color); font-size:calc(1.1rem + 1vw)"> Post dated checks.</span>
</h1>
</div>
@ -629,6 +668,86 @@
</div>
</div>
<div class="position-relative mb-4"
style="border-radius:10px; background-color:#f4f4f4">
<div class="p-md-5 p-3 position-relative">
<div class="row">
<div class="col-md-12">
<h1 style="font-weight:bold; font-size:calc(1.1rem + 1vw); line-height:120%; text-align:center; text-transform:capitalize; font-size: 40px;
font-weight: 700;">
<span style="color:#121212; font-size:calc(1.1rem + 1vw)"> Reconciliation</span>
<span style="color: var(--primary-color); font-size:calc(1.1rem + 1vw)"> Widget.</span>
</h1>
</div>
<!-- <div class="col-md-12 mb-4">-->
<!-- <p style="font-weight:400; font-size:16px; line-height:150%; text-align:center; color:var(&#45;&#45;text-color-light)">-->
<!-- Can reconcile the entries.-->
<!-- </p>-->
<!-- </div>-->
<div class="col-md-12 text-center">
<div class="d-inline-block p-3 shadow-sm"
style="background-color:#fff; border-radius:10px">
<img alt="" class="img-fluid"
loading="lazy"
src="./assets/screenshots/rec_1.png"
style="min-height: 1px;">
</div>
<div class="d-inline-block p-3 shadow-sm"
style="background-color:#fff; border-radius:10px;margin-top:10px;">
<img alt="" class="img-fluid"
loading="lazy"
src="./assets/screenshots/rec_2.png"
style="min-height: 1px;">
</div>
<div class="d-inline-block p-3 shadow-sm"
style="background-color:#fff; border-radius:10px;margin-top:10px;">
<img alt="" class="img-fluid"
loading="lazy"
src="./assets/screenshots/rec_3.png"
style="min-height: 1px;">
</div>
</div>
</div>
</div>
</div>
<div class="position-relative mb-4"
style="border-radius:10px; background-color:#f4f4f4">
<div class="p-md-5 p-3 position-relative">
<div class="row">
<div class="col-md-12">
<h1 style="font-weight:bold; font-size:calc(1.1rem + 1vw); line-height:120%; text-align:center; text-transform:capitalize; font-size: 40px;
font-weight: 700;">
<span style="color:#121212; font-size:calc(1.1rem + 1vw)"> Lock</span>
<span style="color: var(--primary-color); font-size:calc(1.1rem + 1vw)"> Dates.</span>
</h1>
</div>
<div class="col-md-12 text-center">
<div class="d-inline-block p-3 shadow-sm"
style="background-color:#fff; border-radius:10px">
<img alt="" class="img-fluid"
loading="lazy"
src="./assets/screenshots/loc_1.png"
style="min-height: 1px;">
</div>
<div class="d-inline-block p-3 shadow-sm"
style="background-color:#fff; border-radius:10px;margin-top:10px;">
<img alt="" class="img-fluid"
loading="lazy"
src="./assets/screenshots/loc_2.png"
style="min-height: 1px;">
</div>
<div class="d-inline-block p-3 shadow-sm"
style="background-color:#fff; border-radius:10px;margin-top:10px;">
<img alt="" class="img-fluid"
loading="lazy"
src="./assets/screenshots/loc3.png"
style="min-height: 1px;">
</div>
</div>
</div>
</div>
</div>
<div class="position-relative mb-4"
style="border-radius:10px; background-color:#f4f4f4">
<div class="p-md-5 p-3 position-relative">
@ -1103,7 +1222,7 @@
</div>
</div>
<div class="col-md-6 col-sm-12 p-3">
<div class="col-md-6 col-sm-12 p-3">
<div class="d-flex flex-column align-items-start h-100"
style="padding:30px; border-radius:12px; background-color:#faf8ff">
<div class="d-flex align-items-center justify-content-center">
@ -1133,6 +1252,21 @@
</div>
</div>
</div>
<div class="col-md-6 col-sm-12 p-3">
<div class="d-flex flex-column align-items-start h-100"
style="padding:30px; border-radius:12px; background-color:#faf8ff">
<div class="d-flex align-items-center justify-content-center">
<div class="d-flex align-items-center justify-content-center "
style="width:36px; height:36px; border-radius:50%; background-color:#7847D9 ; margin-right:10px">
<i class="fa fa-star "
style="color:#fff; font-size:14px"></i>
</div>
<p style="color:#1A202C; font-weight:600; font-size:1.2rem; margin-bottom:2px">
Reconciliation Widget.
</p>
</div>
</div>
</div>
</div>
</div>
@ -1244,6 +1378,35 @@
class="tab-pane fade show" id="releases"
role="tabpanel">
<!-- Release Notes -->
<div class="row pt-5 m-0">
<div class="col-md-3">
<h4 style="font-size:16px; font-weight:600; color:#514F4F; margin:0; line-height:26px;">
Latest Release 18.0.1.0.1
</h4>
<span style="font-size:14px; color:#7A7979; display:block; margin-bottom:20px;">
22nd October, 2024
</span>
</div>
<div class="col-md-8">
<div style="padding:0 0 40px">
<div style="margin:0 0 10px">
<div style="display:inline-block; padding:0px 8px; color:#514F4F; background-color:#FFD8D8; border-radius:20px">
Updt
</div>
</div>
<div class="d-flex m-0"
style="color:#7A7979;">
<ul class="pl-3 mb-0">
<li>
Added reconciliation widget and lock date
</li>
</ul>
</div>
</div>
<div style="padding:0 0 0; border-bottom:1px solid #E3E3E3">
</div>
</div>
</div>
<div class="row pt-5 m-0">
<div class="col-md-3">
<h4 style="font-size:16px; font-weight:600; color:#514F4F; margin:0; line-height:26px;">

129
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);

168
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 = `<br/><span id="moveLine" style="font-size: 12px; font-style: italic;font-weight: normal;color: #01666b;cursor: pointer;" data-moveId="${record.data.move_id[0]}">${record.data.move_id[1]}</span>`;
}
if (amount < 0) {
debitColumn = `<td style="font-weight: bold; display: table-cell; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; vertical-align: top;">${currencySymbol[0].symbol} ${-amount}</td>`;
} else {
creditColumn = `<td style="font-weight: bold; display: table-cell; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; vertical-align: top;">${currencySymbol[0].symbol} ${amount}</td>`;
}
const newRow = document.createElement('tr');
newRow.setAttribute('data-resId', record.resId); // Set a unique identifier for the row
if (debitColumn !== '') {
newRow.innerHTML = `<td style="font-weight: bold; display: table-cell; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; vertical-align: top;">${record.data.account_id[1]}
${moveId}<span style="font-size: 12px;font-style: italic;font-weight: normal;"> : ${record.data.name}</span></td>
<td style="font-weight: bold; display: table-cell; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; vertical-align: top;">${partnerName}</td>
<td style="font-weight: bold; display: table-cell; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; vertical-align: top;">${formattedDate}</td>
${debitColumn}
<td style="font-weight: bold; display: table-cell; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; vertical-align: top;"> </td>
<td class="o_list_remove_record">
<button class="btn fa fa-trash-o" data-resId="${record.resId}"/>
</td>`;
} else if (creditColumn !== '') {
newRow.innerHTML = `<td style="font-weight: bold; display: table-cell; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; vertical-align: top;">${record.data.account_id[1]}
${moveId}<span style="font-size: 12px;font-style: italic;font-weight: normal;"> : ${record.data.name}</span></td>
<td style="font-weight: bold; display: table-cell; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; vertical-align: top;">${partnerName}</td>
<td style="font-weight: bold; display: table-cell; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; vertical-align: top;">${formattedDate}</td>
<td style="font-weight: bold; display: table-cell; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; vertical-align: top;"> </td>
${creditColumn}
<td class="o_list_remove_record">
<button class="btn fa fa-trash-o" data-resId="${record.resId}"/>
</td>`;
}
newRow.addEventListener('click', async () => {
const allRows = this.__owl__.bdom.parentEl.ownerDocument.querySelectorAll('tr[data-resId]');
allRows.forEach(row => {
row.classList.remove('selected-row');
});
newRow.classList.add('selected-row');
if (record.resId){
const manualOpsTab = this.__owl__.bdom.parentEl.ownerDocument.querySelector('[name="manual_operations_tab"]');
if (manualOpsTab) {
manualOpsTab.click();
const accountField = this.__owl__.bdom.parentEl.ownerDocument.querySelector('[name="account_id"]');
accountField.value = record.data.account_id[1];
}
}
});
// Append the new row to the mainKanbanDiv
mainKanbanDiv.appendChild(newRow);
const deleteButtons = this.__owl__.bdom.parentEl.ownerDocument.querySelectorAll('.fa-trash-o');
deleteButtons.forEach(button => {
button.addEventListener('click', async (event) => {
const resId = event.target.getAttribute('data-resId');
await this.removeRecord(resId);
});
});
const moveLine = this.__owl__.bdom.parentEl.ownerDocument.querySelectorAll('#moveLine');
moveLine.forEach(line => {
line.addEventListener('click',async (event) => {
const moveId = event.target.getAttribute('data-moveId');
await this.ShowMoveForm(moveId);
});
});
}
this.updateResIdList();
}
async removeRecord(resId){
const mainKanbanDiv = this.__owl__.bdom.parentEl.ownerDocument.querySelector('#base_accounting_reconcile');
const rowToRemove = this.__owl__.bdom.parentEl.ownerDocument.querySelector(`tr[data-resId="${resId}"]`);
if (rowToRemove) {
mainKanbanDiv.removeChild(rowToRemove);
this.updateResIdList();
}
}
async ShowMoveForm(moveId) {
// Convert moveId from string to integer
const moveIdInt = parseInt(moveId, 10);
// Check if the conversion is successful
if (!isNaN(moveIdInt)) {
this.action.doAction({
type: 'ir.actions.act_window',
res_model: 'account.move',
res_id: moveIdInt,
views: [[false, "form"]],
target: 'current',
});
}
}
updateResIdList() {
// Get all resId values from the current rows and update the resIdList array
const rows = this.__owl__.bdom.parentEl.ownerDocument.querySelectorAll('tr[data-resId]');
this.resIdList = Array.from(rows).map(row => parseInt(row.getAttribute('data-resId'), 10));
}
}
AccountMoveLineListController.template = 'base_accounting_kit.AccountMoveLineListController';
export const AccountMoveListView = {
...listView,
Controller: AccountMoveLineListController,
};
registry.category('views').add('account_move_line_list_controller', AccountMoveListView);

79
base_accounting_kit/static/src/js/bank_reconcile_form_lines_widget.js

@ -0,0 +1,79 @@
/** @odoo-module **/
import { registry } from '@web/core/registry';
import { useService } from "@web/core/utils/hooks";
import { _t } from "@web/core/l10n/translation";
const { Component, useState, useExternalListener } = owl;
export class BankReconcileFormLinesWidget extends Component {
setup(){
super.setup();
this.state = useState({statementLineResult: null,
MoveLineResult:null});
this.action = useService("action")
this.orm = useService("orm")
}
range(n){
return[...Array(Math.max(n,0)).keys()];
}
get record(){
return this.props.record;
}
async mountStatementLine(ev){
const manualOpsTab = document.querySelector('[name="manual_operations_tab"]');
if (manualOpsTab) {
manualOpsTab.click();
}
}
async onclickLink(ev){
const id = ev.currentTarget.dataset.id;
if (id) {
this.action.doAction({
type: 'ir.actions.act_window',
res_model: 'account.move',
res_id: parseInt(id),
views: [[false, "form"]],
target: 'current',
});
}
}
async removeRecord(ev){
ev.preventDefault();
var button = ev.currentTarget;
var row = button.closest('tr');
var firstRow = document.querySelector('.o_data_row:first-child');
var data_id = firstRow.dataset.id;
try {
await this.orm.call('account.bank.statement.line', 'write', [[parseInt(data_id)], {'lines_widget_json': null}]);
// Update the UI or perform any other actions as needed
} catch (error) {
console.error('Error removing lines_widget_json:', error);
// Handle the error as needed
}
row.remove();
}
getRenderValues(){
var self=this;
let data = this.props.record.context
this.orm.call('account.bank.statement.line', 'update_rowdata', [this.props.record.data.id])
let columns=[
["account",_t("Account")],
["partner",_t("Partner")],
["date",_t("Date")],
];
if(data.display_analytic_account_column){
columns.push(["analytic_account", _t("Analytic Account")]);
}
if(data.display_multi_currency_column){
columns.push(["amount_currency", _t("Amount in Currency")], ["currency", _t("Currency")]);
}
if(data.display_taxes_column){
columns.push(["taxes", _t("Taxes")]);
}
columns.push(["debit", _t("Debit")], ["credit", _t("Credit")], ["__trash", ""]);
return {...data,columns:columns}
}
}
BankReconcileFormLinesWidget.template = 'base_accounting_kit.bank_reconcile_widget_lines_widget';
export const FormLines = {
component: BankReconcileFormLinesWidget
}
registry.category("fields").add('bank_reconcile_widget_lines_widget', FormLines)

124
base_accounting_kit/static/src/js/bank_reconcile_form_list_widget.js

@ -0,0 +1,124 @@
/** @odoo-module **/
import { registry } from '@web/core/registry';
import { standardWidgetProps } from "@web/views/widgets/standard_widget_props";
import { View } from "@web/views/view";
const { Component, useSubEnv } = owl;
class FormListView extends Component {
setup(){
useSubEnv({
config:{},
});
}
get bankReconcileListViewProps(){
return{
type:'list',
display:{
controlPanel:{
"top-left":false,
"bottom-left":false,
}
},
resModel:this.props.resModel,
searchMenuTypes:["filter"],
allowSelectors:false,
searchViewId:false,
searchViewArch: `
<search>
<field name="name" string="Journal Item"/>
<field name="journal_id"/>
<field name="account_id"/>
<field name="partner_id"/>
<field name="move_id"/>
<field name="currency_id" groups="base.group_multi_currency"/>
<field name="date" string="Date"/>
<separator/>
<filter name="amount_received" string="Incoming" domain="[('balance','>',0.0)]"/>
<filter name="amount_paid" string="Outgoing" domain="[('balance','&lt;',0.0)]"/>
<separator name="inject_after"/>
<filter name="date" string="Date" date="date"/>
<filter string="Customer/Vendor" name="partner_id" domain="[]"/>
<filter string="Miscellaneous" domain="[('journal_id.type', '=', 'general')]" name="misc_filter"/>
</search>
`,
searchViewFields: {
name: {
name:"name",
string:"Journal Item",
type:"char",
store: true,
sortable: true,
searchable: true,
},
date: {
name: "date",
string: "Date",
type: "date",
store: true,
sortable: true,
searchable: true,
},
journal_id: {
name: "journal_id",
string: "Journal",
type: "many2one",
store: true,
sortable: true,
searchable: true,
},
account_id: {
name: "account_id",
string: "Account",
type: "many2one",
store: true,
sortable: true,
searchable: true,
},
partner_id: {
name: "partner_id",
string: "Partner",
type: "many2one",
store: true,
sortable: true,
searchable: true,
group_by:"partner_id",
},
currency_id: {
name: "currency_id",
string: "Currency",
type: "many2one",
store: true,
sortable: true,
searchable: true,
},
move_id: {
name:"move_id",
string:"Journal Entry",
type:"many2one",
store:true,
sortable:true,
searchable:true,
filter_domain:"['|',('move_id.name','ilike',self),('move_id.ref','ilike',self)]",
},
},
context:{
list_view_ref:"base_accounting_kit.account_move_line_view_tree",
}
}
}
}
FormListView.template = "base_accounting_kit.FormListView";
FormListView.components = { View };
FormListView.props = {
...standardWidgetProps,
resModel: { type: String },
};
export const formListView = {
component: FormListView,
extractProps: ({ attrs }) => ({
resModel: attrs.resModel,
}),
};
registry.category("view_widgets").add("form_list_view", formListView);

1
base_accounting_kit/static/src/scss/style.scss

@ -214,7 +214,6 @@
}
.accounts-dashboard-wrap .info-box .info-box-icon {
border-radius: .25rem;
-ms-flex-align: center;
align-items: center;

33
base_accounting_kit/static/src/xml/bank_rec_widget.xml

@ -0,0 +1,33 @@
<templates>
<t t-name="base_accounting_kit.CustomKanbanView" t-inherit="web.KanbanView"
owl="1">
<xpath expr="//Layout" position="inside">
<div class="h-100 form_view_class">
<View t-if="state.selectedStLineId"
t-props="prepareFormPropsBankReconcile"
t-key="state.selectedStLineId"/>
</div>
</xpath>
</t>
<t t-name="base_accounting_kit.BankRecKanbanRenderer"
t-inherit="web.KanbanRenderer" t-inherit-mode="primary" owl="1">
<xpath expr="//div[hasclass('o_kanban_renderer')]"
position="before">
</xpath>
<xpath expr="//div[hasclass('o_kanban_renderer')]"
position="attributes">
<attribute name="class">o_kanban_renderer o_custom_class</attribute>
</xpath>
<xpath expr="//div[hasclass('o_custom_class')]"
position="attributes">
<attribute name="style">width:30%;</attribute>
</xpath>
</t>
<t t-name="base_accounting_kit.BankReconcileKanbanRecord"
t-inherit="web.KanbanRecord" t-inherit-mode="primary" owl="1">
</t>
<t t-name="base_accounting_kit.AccountMoveLineListController"
t-inherit="web.ListView"
t-inherit-mode="primary" owl="1">
</t>
</templates>

124
base_accounting_kit/static/src/xml/bank_reconcile_widget.xml

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8" ?>
<templates id="template" xml:space="preserve">
<t t-name="base_accounting_kit.bank_reconcile_widget_lines_widget">
<t t-set="data" t-value="getRenderValues()"/>
<div class="o_list_renderer table-responsive">
<table class="table table-sm position-relative mb-0 o_list_table_ungrouped table-striped o_bank_reconcile_lines_widget_line">
<thead>
<tr>
<t t-foreach="data.columns" t-as="column"
t-key="column[0]">
<th t-esc="column[1]"/>
</t>
</tr>
</thead>
<tbody id="base_accounting_reconcile">
<t t-if="data.default_lines_widget">
<t t-foreach="data.default_lines_widget" t-as="line"
t-key="line.id">
<tr class="o_data_row"
t-on-click="mountStatementLine"
t-att-data-id="line.id">
<t t-set="account"
t-value="line.account_code+' '+line.account_name"/>
<t t-set="amount"
t-value="line.currency_symbol+' '+line.amount"/>
<td class="o_data_cell o_field_cell"
style="font-weight: bolder;"
field="account_id">
<t t-esc="account"/>
<br/>
<span class="text-muted"
style="font-style: italic;">
<t t-esc="line.payment_ref"/>
</span>
</td>
<t t-if="line.partner_id">
<td field="partner_id"
t-esc="line.partner_id[1]"
style="font-weight: bolder;"/>
</t>
<t t-else="">
<td t-esc="line.partner_id[1]"/>
</t>
<td field="date"
t-esc="line.date"
style="font-weight: bolder;"/>
<t t-if="line.amount &gt; 0">
<td field="debit"
t-esc="amount"
style="font-weight: bolder;"/>
</t>
<t t-else="">
<td t-esc="' '"/>
</t>
<t t-if="line.amount &lt; 0">
<td field="credit"
t-esc="amount"
style="font-weight: bolder;"/>
</t>
</tr>
</t>
</t>
<t t-if="data.default_move_line">
<tr class="o_data_row statement_row"
t-on-click="mountStatementLine"
t-att-data-id="data.default_move_line.id">
<t t-set="amount_residual"
t-value="data.default_move_line.currency_symbol+' '+data.default_move_line.amount_residual"/>
<t t-set="move_account"
t-value="data.default_move_line.account_code+' '+data.default_move_line.account_name"/>
<td class="o_data_cell o_field_cell"
style="font-weight: bolder;">
<t t-esc="move_account"/>
<br/>
<span style="font-size: 12px; font-style: italic;font-weight: normal;color: #01666b;cursor: pointer;"
t-att-data-id="data.default_move_line.numeric_move_id"
t-on-click="onclickLink">
<t t-esc="data.default_move_line.move_name"/>
</span>
:
<span class="text-muted"
style="font-style: italic;">
<t t-esc="data.default_move_line.name"/>
</span>
</td>
<t t-if="data.default_move_line.partner_id">
<td t-esc="data.default_move_line.partner_name"
style="font-weight: bolder;"/>
</t>
<t t-else="">
<td t-esc="data.default_move_line.partner_name"/>
</t>
<td t-esc="data.default_move_line.date"
style="font-weight: bolder;"/>
<t t-if="data.default_move_line.amount_residual &lt; 0">
<td t-esc="amount_residual"
style="font-weight: bolder;"/>
</t>
<t t-else="">
<td t-esc="' '"/>
</t>
<t t-if="data.default_move_line.amount_residual &gt; 0">
<td t-esc="amount_residual"
style="font-weight: bolder;"/>
</t>
<t t-if="this.props.record.data.bank_state!='reconciled'">
<td class="o_list_remove_record">
<button class="btn fa fa-trash-o"
style="margin-top: -4px;"
t-on-click="removeRecord"/>
</td>
</t>
</tr>
</t>
</tbody>
</table>
</div>
</t>
<t t-name="base_accounting_kit.FormListView" owl="1">
<t t-if="props.record.id">
<View t-props="bankReconcileListViewProps"/>
</t>
</t>
</templates>

1
base_accounting_kit/wizard/__init__.py

@ -25,6 +25,7 @@ from . import account_balance_report
from . import account_bank_book_report
from . import account_cash_book_report
from . import account_day_book_report
from . import account_lock_date
from . import account_print_journal
from . import account_report_general_ledger
from . import account_report_partner_ledger

64
base_accounting_kit/wizard/account_lock_date.py

@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from odoo import api, fields, models, SUPERUSER_ID, _
from odoo.exceptions import UserError
class AccountUpdateLockDate(models.TransientModel):
_name = 'account.lock.date'
_description = 'Lock date for accounting'
company_id = fields.Many2one(comodel_name='res.company', string="Company",
required=True)
sale_lock_date = fields.Date(string="Sales Lock Date", help='Prevents creating and modifying invoices up to the date.')
purchase_lock_date = fields.Date(string="Purchase Lock date", help='Prevents creating and modifying bills up to the date.')
hard_lock_date = fields.Date(string="Lock Everyone",
help="No users, including Advisers, can edit accounts prior to and "
"inclusive of this date. Use it for fiscal year locking for "
"example.")
@api.model
def default_get(self, field_list):
res = super(AccountUpdateLockDate, self).default_get(field_list)
company = self.env.company
res.update({
'company_id': company.id,
'sale_lock_date': company.sale_lock_date,
'purchase_lock_date': company.purchase_lock_date,
'hard_lock_date': company.hard_lock_date,
})
return res
def _check_execute_allowed(self):
self.ensure_one()
has_adviser_group = self.env.user.has_group(
'account.group_account_manager')
if not (has_adviser_group or self.env.uid == SUPERUSER_ID):
raise UserError(_("You are not allowed to execute this action."))
def execute(self):
self.ensure_one()
self._check_execute_allowed()
self.company_id.sudo().write({
'sale_lock_date': self.sale_lock_date,
'purchase_lock_date': self.purchase_lock_date,
'hard_lock_date': self.hard_lock_date,
})

46
base_accounting_kit/wizard/account_lock_date_views.xml

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!--Account Lock Date Form View-->
<record model="ir.ui.view" id="account_lock_date_view_form">
<field name="name">account.lock.date.view.form</field>
<field name="model">account.lock.date</field>
<field name="arch" type="xml">
<form>
<group>
<field name="company_id" invisible="1"/>
<label for="sale_lock_date"/>
<div class="d-flex">
<field name="sale_lock_date" class="oe_inline"/><span class="text-muted"><i>to lock invoices</i></span>
</div>
<label for="purchase_lock_date"/>
<div class="d-flex">
<field name="purchase_lock_date" class="oe_inline"/><span class="text-muted"><i>to lock bills</i></span>
</div>
<field name="hard_lock_date" class="oe_inline"/>
</group>
<footer>
<button string="Update" name="execute" type="object" class="btn-primary"/>
<button string="Cancel" class="btn-default" special="cancel"/>
</footer>
</form>
</field>
</record>
<!--Action Account Lock Date-->
<record model="ir.actions.act_window" id="account_update_lock_date_act_window">
<field name="name">Lock your Fiscal Period</field>
<field name="res_model">account.lock.date</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<!--Menu Lock Dates-->
<menuitem id="actions_menu"
name="Actions"
sequence="500"
parent="account.menu_finance_entries"
groups="account.group_account_manager"/>
<menuitem id="menu_lock_dates"
name="Lock Dates"
action="account_update_lock_date_act_window"
parent="base_accounting_kit.actions_menu"
groups="account.group_account_manager"/>
</odoo>
Loading…
Cancel
Save