first push message

This commit is contained in:
2026-07-01 14:41:49 +07:00
parent 6667dec2bf
commit 58b5f46cc4
2951 changed files with 316619 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-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 . import account_common_partner_report
from . import account_aged_trial_balance
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
from . import asset_depreciation_confirmation
from . import asset_modify
from . import cash_flow_report
from . import financial_report
from . import import_bank_statement
from . import kit_account_tax_report
@@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-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/>.
#
#############################################################################
import time
from dateutil.relativedelta import relativedelta
from odoo import fields, models, _
from odoo.exceptions import UserError
class AccountAgedTrialBalance(models.TransientModel):
_name = 'account.aged.trial.balance'
_inherit = 'account.common.partner.report'
_description = 'Account Aged Trial balance Report'
section_main_report_ids = fields.Many2many(string="Section Of",
comodel_name='account.report',
relation="account_aged_trail_report_section_rel",
column1="sub_report_id",
column2="main_report_id")
section_report_ids = fields.Many2many(string="Sections",
comodel_name='account.report',
relation="account_aged_trail_report_section_rel",
column1="main_report_id",
column2="sub_report_id")
name = fields.Char(string="Account Aged Trial balance Report", default="Account Aged Trial balance Report", required=True, translate=True)
journal_ids = fields.Many2many('account.journal', string='Journals',
required=True)
period_length = fields.Integer(string='Period Length (days)',
required=True, default=30)
date_from = fields.Date(default=lambda *a: time.strftime('%Y-%m-%d'))
def _print_report(self, data):
res = {}
data = self.pre_print_report(data)
data['form'].update(self.read(['period_length'])[0])
period_length = data['form']['period_length']
if period_length <= 0:
raise UserError(_('You must set a period length greater than 0.'))
if not data['form']['date_from']:
raise UserError(_('You must set a start date.'))
start = data['form']['date_from']
for i in range(5)[::-1]:
stop = start - relativedelta(days=period_length - 1)
res[str(i)] = {
'name': (i != 0 and (
str((5 - (i + 1)) * period_length) + '-' + str(
(5 - i) * period_length)) or (
'+' + str(4 * period_length))),
'stop': start.strftime('%Y-%m-%d'),
'start': (i != 0 and stop.strftime('%Y-%m-%d') or False),
}
start = stop - relativedelta(days=1)
data['form'].update(res)
return self.env.ref(
'base_accounting_kit.action_report_aged_partner_balance').with_context(
landscape=True).report_action(self, data=data)
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!--Account Aged Trial Balance Form View-->
<record id="account_aged_trial_balance_view_form" model="ir.ui.view">
<field name="name">account.aged.trial.balance.view.form</field>
<field name="model">account.aged.trial.balance</field>
<field name="arch" type="xml">
<form string="Report Options">
<group col="4">
<field name="date_from"/>
<field name="period_length"/>
<newline/>
<field name="result_selection" widget="radio"/>
<field name="target_move" widget="radio"/>
</group>
<footer>
<button name="check_report" string="Print" type="object" default_focus="1" class="oe_highlight"/>
<button string="Cancel" class="btn btn-default" special="cancel"/>
</footer>
</form>
</field>
</record>
<!--Action Account Aged Trial Balance-->
<record id="action_account_aged_balance_view" model="ir.actions.act_window">
<field name="name">Aged Partner Balance</field>
<field name="res_model">account.aged.trial.balance</field>
<field name="type">ir.actions.act_window</field>
<field name="view_mode">list,form</field>
<field name="view_id" ref="account_aged_trial_balance_view_form"/>
<field name="context">{}</field>
<field name="target">new</field>
</record>
<!--Menu Aged Partner Balance-->
<menuitem id="menu_aged_trial_balance"
name="Aged Partner Balance"
sequence="2"
action="action_account_aged_balance_view"
parent="account.menu_finance_reports"/>
</odoo>
@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-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, _
class AccountBalanceReport(models.TransientModel):
_name = 'account.balance.report'
_inherit = "account.common.account.report"
_description = 'Trial Balance Report'
section_report_ids = fields.Many2many(string="Sections",
comodel_name='account.report',
relation="account_balance_report_section_rel",
column1="main_report_id",
column2="sub_report_id")
section_main_report_ids = fields.Many2many(string="Section Of",
comodel_name='account.report',
relation="account_balance_report_section_rel",
column1="sub_report_id",
column2="main_report_id")
name = fields.Char(string="Trial Balance", default="Trial Balance", required=True, translate=True)
journal_ids = fields.Many2many('account.journal',
'account_balance_report_journal_rel',
'account_id', 'journal_id',
string='Journals', required=True,
default=[])
@api.model
def _get_report_name(self):
period_id = self._get_selected_period_id()
return self.env['consolidation.period'].browse(period_id)['display_name'] or _("Trial Balance")
def _print_report(self, data):
data = self.pre_print_report(data)
records = self.env[data['model']].browse(data.get('ids', []))
return self.env.ref(
'base_accounting_kit.action_report_trial_balance').report_action(
records, data=data)
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!--Account Balance Report Form View-->
<record id="account_report_balance_view" model="ir.ui.view">
<field name="name">account.balance.report.view.form.inherit.base.accounting.kit</field>
<field name="model">account.balance.report</field>
<field name="inherit_id" ref="base_accounting_kit.account_report_view_form"/>
<field name="arch" type="xml">
<field name="journal_ids" position="replace"/>
<xpath expr="//field[@name='target_move']" position="after">
<field name="display_account" widget="radio"/>
<newline/>
</xpath>
</field>
</record>
<!--Action Account Balance Report-->
<record id="action_account_balance_menu" model="ir.actions.act_window">
<field name="name">Trial Balance</field>
<field name="res_model">account.balance.report</field>
<field name="type">ir.actions.act_window</field>
<field name="view_mode">form</field>
<field name="view_id" ref="account_report_balance_view"/>
<field name="target">new</field>
<field name="binding_model_id" ref="account.model_account_account"/>
</record>
<!--Menu Trial Balance-->
<menuitem id="menu_Balance_report"
name="Trial Balance"
sequence="7"
action="action_account_balance_menu"
parent="base_accounting_kit.account_reports_audit"/>
</odoo>
@@ -0,0 +1,112 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-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 datetime import date
from odoo import api, fields, models, _
from odoo.exceptions import UserError
class BankBookWizard(models.TransientModel):
_name = 'account.bank.book.report'
_description = 'Account Bank Book Report'
company_id = fields.Many2one('res.company', string='Company',
readonly=True,
default=lambda self: self.env.company)
target_move = fields.Selection([('posted', 'All Posted Entries'),
('all', 'All Entries')],
string='Target Moves', required=True,
default='posted')
date_from = fields.Date(string='Start Date', default=date.today(),
required=True)
date_to = fields.Date(string='End Date', default=date.today(),
required=True)
display_account = fields.Selection(
[('all', 'All'), ('movement', 'With movements'),
('not_zero', 'With balance is not equal to 0')],
string='Display Accounts', required=True, default='movement')
sortby = fields.Selection(
[('sort_date', 'Date'), ('sort_journal_partner', 'Journal & Partner')],
string='Sort by', required=True, default='sort_date')
initial_balance = fields.Boolean(string='Include Initial Balances',
help='If you selected date, this field allow you to add a '
'row to display the amount of debit/credit/balance that '
'precedes the filter you\'ve set.')
def _get_default_account_ids(self):
journals = self.env['account.journal'].search([('type', '=', 'bank')])
accounts = []
for journal in journals:
accounts.append(journal.default_account_id.id)
return accounts
account_ids = fields.Many2many('account.account',
'account_report_bankbook_account_rel',
'report_id', 'account_id',
'Accounts',
default=_get_default_account_ids)
journal_ids = fields.Many2many('account.journal',
'account_report_bankbook_journal_rel',
'account_id', 'journal_id',
string='Journals', required=True,
default=lambda self: self.env[
'account.journal'].search([]))
@api.onchange('account_ids')
def onchange_account_ids(self):
if self.account_ids:
journals = self.env['account.journal'].search(
[('type', '=', 'bank')])
accounts = []
for journal in journals:
accounts.append(journal.default_account_id.id)
domain = {'account_ids': [('id', 'in', accounts)]}
return {'domain': domain}
def _build_contexts(self, data):
result = {}
result['journal_ids'] = 'journal_ids' in data['form'] and data['form'][
'journal_ids'] or False
result['state'] = 'target_move' in data['form'] and data['form'][
'target_move'] or ''
result['date_from'] = data['form']['date_from'] or False
result['date_to'] = data['form']['date_to'] or False
result['strict_range'] = True if result['date_from'] else False
return result
def check_report(self):
self.ensure_one()
if self.initial_balance and not self.date_from:
raise UserError(_("You must choose a Start Date"))
data = {}
data['ids'] = self.env.context.get('active_ids', [])
data['model'] = self.env.context.get('active_model', 'ir.ui.menu')
data['form'] = self.read(
['date_from', 'date_to', 'journal_ids', 'target_move',
'display_account',
'account_ids', 'sortby', 'initial_balance'])[0]
used_context = self._build_contexts(data)
data['form']['used_context'] = dict(used_context,
lang=self.env.context.get(
'lang') or 'en_US')
return self.env.ref(
'base_accounting_kit.action_report_bank_book').report_action(self,
data=data)
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!-- Account Bank Book Report Form View-->
<record id="account_bank_book_report_view_form" model="ir.ui.view">
<field name="name">account.bank.book.report.view.form</field>
<field name="model">account.bank.book.report</field>
<field name="arch" type="xml">
<form string="Bank Book Report">
<field name="company_id" invisible="1"/>
<group col="4">
<field name="target_move" widget="radio"/>
<field name="sortby" widget="radio"/>
<field name="initial_balance"/>
<newline/>
<newline/>
<field name="date_from"/>
<field name="date_to"/>
</group>
<group>
<field name="account_ids" widget="many2many_tags"/>
<field name="journal_ids" widget="many2many_tags" options="{'no_create': True}"/>
</group>
<footer>
<button name="check_report" string="Print" type="object" default_focus="1" class="oe_highlight"/>
<button string="Cancel" class="btn btn-default" special="cancel" />
</footer>
</form>
</field>
</record>
<!--Action Account Bank Book Report-->
<record id="action_account_bank_book_view" model="ir.actions.act_window">
<field name="name">Bank Book Report</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.bank.book.report</field>
<field name="view_id" ref="account_bank_book_report_view_form"/>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<!--Menu Bank Book-->
<menuitem id="account_bank_book_menu" name="Bank Book" action="action_account_bank_book_view"
parent="base_accounting_kit.account_reports_daily_reports"/>
</odoo>
@@ -0,0 +1,110 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-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 datetime import date
from odoo import api, fields, models, _
from odoo.exceptions import UserError
class CashBookWizard(models.TransientModel):
_name = 'account.cash.book.report'
_description = 'Account Cash Book Report'
company_id = fields.Many2one('res.company', string='Company',
readonly=True,
default=lambda self: self.env.company)
target_move = fields.Selection([('posted', 'All Posted Entries'),
('all', 'All Entries')], string='Target Moves', required=True,
default='posted')
date_from = fields.Date(string='Start Date', default=date.today(),
required=True)
date_to = fields.Date(string='End Date', default=date.today(),
required=True)
display_account = fields.Selection(
[('all', 'All'), ('movement', 'With movements'),
('not_zero', 'With balance is not equal to 0')],
string='Display Accounts', required=True, default='movement')
sortby = fields.Selection(
[('sort_date', 'Date'), ('sort_journal_partner', 'Journal & Partner')],
string='Sort by',
required=True, default='sort_date')
initial_balance = fields.Boolean(string='Include Initial Balances',
help='If you selected date, this field allow you to add a row to display the amount of debit/credit/balance that precedes the filter you\'ve set.')
def _get_default_account_ids(self):
journals = self.env['account.journal'].search([('type', '=', 'cash')])
accounts = []
for journal in journals:
accounts.append(journal.default_account_id.id)
return accounts
account_ids = fields.Many2many('account.account',
'account_report_cashbook_account_rel',
'report_id', 'account_id',
'Accounts',
default=_get_default_account_ids)
journal_ids = fields.Many2many('account.journal',
'account_report_cashbook_journal_rel',
'account_id', 'journal_id',
string='Journals', required=True,
default=lambda self: self.env[
'account.journal'].search([]))
@api.onchange('account_ids')
def onchange_account_ids(self):
if self.account_ids:
journals = self.env['account.journal'].search(
[('type', '=', 'cash')])
accounts = []
for journal in journals:
accounts.append(journal.default_account_id.id)
domain = {'account_ids': [('id', 'in', accounts)]}
return {'domain': domain}
def _build_contexts(self, data):
result = {}
result['journal_ids'] = 'journal_ids' in data['form'] and data['form'][
'journal_ids'] or False
result['state'] = 'target_move' in data['form'] and data['form'][
'target_move'] or ''
result['date_from'] = data['form']['date_from'] or False
result['date_to'] = data['form']['date_to'] or False
result['strict_range'] = True if result['date_from'] else False
return result
def check_report(self):
self.ensure_one()
if self.initial_balance and not self.date_from:
raise UserError(_("You must choose a Start Date"))
data = {}
data['ids'] = self.env.context.get('active_ids', [])
data['model'] = self.env.context.get('active_model', 'ir.ui.menu')
data['form'] = self.read(
['date_from', 'date_to', 'journal_ids', 'target_move',
'display_account',
'account_ids', 'sortby', 'initial_balance'])[0]
used_context = self._build_contexts(data)
data['form']['used_context'] = dict(used_context,
lang=self.env.context.get(
'lang') or 'en_US')
return self.env.ref(
'base_accounting_kit.action_report_cash_book').report_action(self,
data=data)
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!-- Account Cash Book Report Form View-->
<record id="account_cash_book_report_view_form" model="ir.ui.view">
<field name="name">account.cash.book.report.view.form</field>
<field name="model">account.cash.book.report</field>
<field name="arch" type="xml">
<form string="Cash Book Report">
<field name="company_id" invisible="1"/>
<group col="4">
<field name="target_move" widget="radio"/>
<field name="sortby" widget="radio"/>
<field name="initial_balance"/>
<newline/>
<newline/>
<field name="date_from"/>
<field name="date_to"/>
</group>
<group>
<field name="account_ids" widget="many2many_tags"/>
<field name="journal_ids" widget="many2many_tags" options="{'no_create': True}"/>
</group>
<footer>
<button name="check_report" string="Print" type="object" default_focus="1" class="oe_highlight"/>
<button string="Cancel" class="btn btn-default" special="cancel" />
</footer>
</form>
</field>
</record>
<!--Action Cash Book Report-->
<record id="action_account_cash_book_view" model="ir.actions.act_window">
<field name="name">Cash Book Report</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.cash.book.report</field>
<field name="view_id" ref="account_cash_book_report_view_form"/>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<!--Menu Cash Book-->
<menuitem id="account_cash_book_menu" name="Cash Book" action="action_account_cash_book_view"
parent="base_accounting_kit.account_reports_daily_reports"/>
</odoo>
@@ -0,0 +1,96 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-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 fields, models
from odoo.tools.misc import get_lang
class AccountingCommonPartnerReport(models.TransientModel):
_name = 'account.common.partner.report'
_inherit = "account.report"
_description = 'Account Common Partner Report'
section_main_report_ids = fields.Many2many(string="Section Of",
comodel_name='account.report',
relation="account_common_parnter_report_section_rel",
column1="sub_report_id",
column2="main_report_id")
section_report_ids = fields.Many2many(string="Sections",
comodel_name='account.report',
relation="account_common_parnter_report_section_rel",
column1="main_report_id",
column2="sub_report_id")
company_id = fields.Many2one('res.company', string='Company', required=True, readonly=True,
default=lambda self: self.env.company)
journal_ids = fields.Many2many(
comodel_name='account.journal',
string='Journals',
required=True,
default=lambda self: self.env['account.journal'].search([('company_id', '=', self.company_id.id)]),
domain="[('company_id', '=', company_id)]",
)
date_from = fields.Date(string='Start Date')
date_to = fields.Date(string='End Date')
target_move = fields.Selection([('posted', 'All Posted Entries'),
('all', 'All Entries'),
], string='Target Moves', required=True, default='posted')
result_selection = fields.Selection([('customer', 'Receivable Accounts'),
('supplier', 'Payable Accounts'),
('customer_supplier',
'Receivable and Payable Accounts')
], string="Partner's", required=True,
default='customer')
def _build_contexts(self, data):
result = {}
result['journal_ids'] = 'journal_ids' in data['form'] and data['form'][
'journal_ids'] or False
result['state'] = 'target_move' in data['form'] and data['form'][
'target_move'] or ''
result['date_from'] = data['form']['date_from'] or False
result['date_to'] = data['form']['date_to'] or False
result['strict_range'] = True if result['date_from'] else False
return result
def check_report(self):
self.ensure_one()
data = {}
data['ids'] = self.env.context.get('active_ids', [])
data['model'] = self.env.context.get('active_model', 'ir.ui.menu')
data['form'] = self.read(['date_from', 'date_to', 'journal_ids', 'target_move', 'company_id'])[0]
used_context = self._build_contexts(data)
data['form']['used_context'] = dict(used_context, lang=get_lang(self.env).code)
return self.with_context(discard_logo_check=True)._print_report(data)
def _print_report(self, data):
data['form'].update(self.read(
['date_from_cmp', 'debit_credit', 'date_to_cmp', 'filter_cmp',
'account_report_id', 'enable_filter', 'label_filter',
'target_move'])[0])
return self.env.ref(
'base_accounting_kit.action_report_cash_flow').report_action(self,
data=data,
config=False)
def pre_print_report(self, data):
data['form'].update(self.read(['result_selection'])[0])
return data
@@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-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 datetime import date
from odoo import fields, models
class DayBookWizard(models.TransientModel):
_name = 'account.day.book.report'
_description = 'Account Day Book Report'
company_id = fields.Many2one('res.company', string='Company',
readonly=True,
default=lambda self: self.env.company)
journal_ids = fields.Many2many('account.journal', string='Journals',
required=True,
default=lambda self: self.env[
'account.journal'].search([]))
target_move = fields.Selection([('posted', 'All Posted Entries'),
('all', 'All Entries')], string='Target Moves', required=True,
default='posted')
account_ids = fields.Many2many('account.account',
'account_report_daybook_account_rel',
'report_id', 'account_id',
'Accounts')
date_from = fields.Date(string='Start Date', default=date.today(),
required=True)
date_to = fields.Date(string='End Date', default=date.today(),
required=True)
def _build_contexts(self, data):
result = {}
result['journal_ids'] = 'journal_ids' in data['form'] and data['form'][
'journal_ids'] or False
result['state'] = 'target_move' in data['form'] and data['form'][
'target_move'] or ''
result['date_from'] = data['form']['date_from'] or False
result['date_to'] = data['form']['date_to'] or False
result['strict_range'] = True if result['date_from'] else False
return result
def check_report(self):
self.ensure_one()
data = {}
data['ids'] = self.env.context.get('active_ids', [])
data['model'] = self.env.context.get('active_model', 'ir.ui.menu')
data['form'] = \
self.read(['date_from', 'date_to', 'journal_ids', 'target_move',
'account_ids'])[0]
used_context = self._build_contexts(data)
data['form']['used_context'] = dict(used_context,
lang=self.env.context.get(
'lang') or 'en_US')
return self.env.ref(
'base_accounting_kit.day_book_pdf_report').report_action(self,
data=data)
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!--Account Day Book Report View Form-->
<record id="account_day_book_report_view_form" model="ir.ui.view">
<field name="name">account.day.book.report.view.form</field>
<field name="model">account.day.book.report</field>
<field name="arch" type="xml">
<form string="Cash Book Report">
<field name="company_id" invisible="1"/>
<group col="4">
<field name="target_move" widget="radio"/>
<newline/>
<field name="date_from"/>
<field name="date_to"/>
</group>
<group>
<field name="account_ids" widget="many2many_tags"/>
<field name="journal_ids" widget="many2many_tags" options="{'no_create': True}"/>
</group>
<footer>
<button name="check_report" string="Print" type="object" default_focus="1" class="oe_highlight"/>
<button string="Cancel" class="btn btn-default" special="cancel" />
</footer>
</form>
</field>
</record>
<!--Action Account Day Book Report-->
<record id="action_account_day_book_view" model="ir.actions.act_window">
<field name="name">Day Book Report</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.day.book.report</field>
<field name="view_id" ref="account_day_book_report_view_form"/>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<!--Menu Day Book-->
<menuitem id="account_day_book_menu" name="Day Book" action="action_account_day_book_view"
parent="base_accounting_kit.account_reports_daily_reports"/>
</odoo>
@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-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,
})
@@ -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="base_accounting_kit.group_account_chief"/>
<menuitem id="menu_lock_dates"
name="Lock Dates"
action="account_update_lock_date_act_window"
parent="base_accounting_kit.actions_menu"
groups="base_accounting_kit.group_account_chief"/>
</odoo>
@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-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 fields, models
class AccountPrintJournal(models.TransientModel):
_name = "account.print.journal"
_inherit = "account.common.journal.report"
_description = "Account Print Journal"
section_main_report_ids = fields.Many2many(string="Section Of",
comodel_name='account.report',
relation="account_common_print_report_section_rel",
column1="sub_report_id",
column2="main_report_id")
section_report_ids = fields.Many2many(string="Sections",
comodel_name='account.report',
relation="account_common_print_report_section_rel",
column1="main_report_id",
column2="sub_report_id")
name = fields.Char(string="Journal Audit", default="Journal Audit", required=True, translate=True)
sort_selection = fields.Selection(
[('date', 'Date'), ('move_name', 'Journal Entry Number')],
'Entries Sorted by', required=True, default='move_name')
journal_ids = fields.Many2many('account.journal', string='Journals',
required=True,
default=lambda self: self.env[
'account.journal'].search(
[('type', 'in', ['sale', 'purchase'])]))
def _print_report(self, data):
data = self.pre_print_report(data)
data['form'].update({'sort_selection': self.sort_selection})
return self.env.ref(
'base_accounting_kit.action_report_journal').with_context(
landscape=True).report_action(self, data=data)
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!--Account Print Journal Form View-->
<record id="account_common_report_inherited" model="ir.ui.view">
<field name="name">account.print.journal.view.form.inherit.base.accounting.kit</field>
<field name="model">account.print.journal</field>
<field name="inherit_id" ref="base_accounting_kit.account_report_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='target_move']" position="after">
<field name="amount_currency" groups="base.group_multi_currency"/>
<field name="sort_selection" widget="radio"/>
<newline/>
</xpath>
</field>
</record>
<!--Action Account Print Journal-->
<record id="action_account_print_journal_menu" model="ir.actions.act_window">
<field name="name">Journals Audit</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.print.journal</field>
<field name="view_mode">form</field>
<field name="view_id" ref="account_common_report_inherited"/>
<field name="target">new</field>
</record>
<!--Menu Journals Audit-->
<menuitem id="menu_print_journal"
name="Journals Audit"
sequence="9"
action="action_account_print_journal_menu"
parent="base_accounting_kit.account_reports_audit"
groups="base_accounting_kit.group_account_chief,account.group_account_user"/>
</odoo>
@@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-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 fields, models, _
from odoo.exceptions import UserError
class AccountReportGeneralLedger(models.TransientModel):
_name = "account.report.general.ledger"
_inherit = "account.common.account.report"
_description = "General Ledger Report"
section_main_report_ids = fields.Many2many(string="Section Of",
comodel_name='account.report',
relation="account_report_general_section_rel",
column1="sub_report_id",
column2="main_report_id")
section_report_ids = fields.Many2many(string="Sections",
comodel_name='account.report',
relation="account_report_general_section_rel",
column1="main_report_id",
column2="sub_report_id")
name = fields.Char(string="General Ledger", default="General Ledger", required=True, translate=True)
initial_balance = fields.Boolean(string='Include Initial Balances',
help='If you selected date, this field '
'allow you to add a row to display '
'the amount of debit/credit/balance '
'that precedes the filter you\'ve '
'set.')
sortby = fields.Selection(
[('sort_date', 'Date'), ('sort_journal_partner', 'Journal & Partner')],
string='Sort by', required=True, default='sort_date')
journal_ids = fields.Many2many('account.journal',
'account_report_general_ledger_journal_rel',
'account_id', 'journal_id',
string='Journals', required=True)
def _print_report(self, data):
data = self.pre_print_report(data)
data['form'].update(self.read(['initial_balance', 'sortby'])[0])
if data['form'].get('initial_balance') and not data['form'].get(
'date_from'):
raise UserError(_("You must define a Start Date"))
records = self.env[data['model']].browse(data.get('ids', []))
return self.env.ref(
'base_accounting_kit.action_report_general_ledger').with_context(
landscape=True).report_action(records, data=data)
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!--Account Report General Ledger Form View-->
<record id="account_report_general_ledger_view" model="ir.ui.view">
<field name="name">account.report.general.ledger.view.form.inherit.base.accounting.kit</field>
<field name="model">account.report.general.ledger</field>
<field name="inherit_id" ref="base_accounting_kit.account_report_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='target_move']" position="after">
<field name="sortby" widget="radio"/>
<field name="display_account" widget="radio"/>
<field name="initial_balance"/>
<newline/>
</xpath>
</field>
</record>
<!--Action Account Report General Ledger-->
<record id="action_account_general_ledger_menu" model="ir.actions.act_window">
<field name="name">General Ledger</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.report.general.ledger</field>
<field name="view_mode">form</field>
<field name="view_id" ref="account_report_general_ledger_view"/>
<field name="target">new</field>
<field name="binding_model_id" ref="account.model_account_account"/>
<field name="binding_type">report</field>
</record>
<!--Menu General Ledger-->
<menuitem
id="menu_general_ledger"
name="General Ledger"
sequence="3"
parent="base_accounting_kit.account_reports_audit"
action="action_account_general_ledger_menu"
groups="account.group_account_user,base_accounting_kit.group_account_chief"/>
</odoo>
@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-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 fields, models
class AccountPartnerLedger(models.TransientModel):
_name = "account.report.partner.ledger"
_inherit = "account.common.partner.report"
_description = "Account Partner Ledger"
section_main_report_ids = fields.Many2many(string="Section Of",
comodel_name='account.report',
relation="account_report_partner_section_rel",
column1="sub_report_id",
column2="main_report_id")
section_report_ids = fields.Many2many(string="Sections",
comodel_name='account.report',
relation="account_report_partner_section_rel",
column1="main_report_id",
column2="sub_report_id")
name = fields.Char(string="Partner Ledger Report", default="Partner Ledger Report", required=True, translate=True)
amount_currency = fields.Boolean("With Currency",
help="It adds the currency column on report if the "
"currency differs from the company currency.")
reconciled = fields.Boolean('Reconciled Entries')
def _print_report(self, data):
data = self.pre_print_report(data)
data['form'].update({'reconciled': self.reconciled,
'amount_currency': self.amount_currency})
return self.env.ref(
'base_accounting_kit.action_report_partnerledger').report_action(
self, data=data)
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!--Account Report Partner Ledger Form View-->
<record id="account_report_partner_ledger_view" model="ir.ui.view">
<field name="name">account.report.partner.ledger.view.form.inherit.base.accounting.kit</field>
<field name="model">account.report.partner.ledger</field>
<field name="inherit_id" ref="base_accounting_kit.account_report_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='target_move']" position="after">
<field name="result_selection"/>
<field name="amount_currency" groups="base.group_multi_currency"/>
<newline/>
<field name="reconciled"/>
<newline/>
</xpath>
</field>
</record>
<!--Action Account Report Partner Ledger-->
<record id="action_partner_leadger" model="ir.actions.act_window">
<field name="name">Partner Ledger</field>
<field name="res_model">account.report.partner.ledger</field>
<field name="type">ir.actions.act_window</field>
<field name="view_mode">form</field>
<field name="view_id" ref="account_report_partner_ledger_view"/>
<field name="target">new</field>
</record>
<!--Menu Partner Ledger-->
<menuitem name="Partner Ledger"
id="menu_partner_ledger" sequence="1"
parent="base_accounting_kit.account_reports_partner"
action="action_partner_leadger"/>
</odoo>
@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-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 fields, models, _
class AssetDepreciationConfirmationWizard(models.TransientModel):
_name = "asset.depreciation.confirmation"
_description = "Asset Depreciation Confirmation"
date = fields.Date('Account Date', required=True,
help="Choose the period for which you want to automatically "
"post the depreciation lines of running assets",
default=fields.Date.context_today)
def asset_compute(self):
self.ensure_one()
context = self._context
created_move_ids = self.env['account.asset.asset'].sudo().compute_generated_entries(self.date, asset_type=context.get('asset_type'))
moves = self.env['account.move'].browse(created_move_ids)
auto_post_draft_moves = moves.filtered(lambda move: move.state == 'draft' and move.auto_post)
auto_post_draft_moves.write({'auto_post': 'at_date'})
return {
'name': _('Created Asset Moves') if context.get('asset_type') == 'purchase' else _('Created Revenue Moves'),
'view_mode': 'list,form',
'res_model': 'account.move',
'view_id': False,
'domain': "[('id','in',[" + ','.join(str(id) for id in created_move_ids) + "])]",
'type': 'ir.actions.act_window',
}
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!--Asset Depreciation Confirmation Form View-->
<record id="asset_depreciation_confirmation_view_form" model="ir.ui.view">
<field name="name">asset.depreciation.confirmation.view.form</field>
<field name="model">asset.depreciation.confirmation</field>
<field name="arch" type="xml">
<form string="Compute Asset">
<div>
<p>
This wizard will post installment/depreciation lines for the selected month.<br/>
This will generate journal entries for all related installment lines on this period of asset/revenue recognition as well.
</p>
</div>
<group>
<field name="date"/>
</group>
<footer>
<button string="Generate Entries" name="asset_compute" type="object" class="btn-primary"/>
<button string="Cancel" class="btn-default" special="cancel"/>
</footer>
</form>
</field>
</record>
<!--Action Asset Depreciation Confirmation-->
<record id="action_asset_depreciation_confirmation_wizard" model="ir.actions.act_window">
<field name="name">Post Depreciation Lines</field>
<field name="res_model">asset.depreciation.confirmation</field>
<field name="view_mode">list,form</field>
<field name="view_id" ref="asset_depreciation_confirmation_view_form"/>
<field name="target">new</field>
<field name="context">{'asset_type': 'purchase'}</field>
</record>
</odoo>
+101
View File
@@ -0,0 +1,101 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-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 lxml import etree
from odoo import api, fields, models, _
class AssetModify(models.TransientModel):
_name = 'asset.modify'
_description = 'Modify Asset'
name = fields.Text(string='Reason', required=True)
method_number = fields.Integer(string='Number of Depreciations', required=True)
method_period = fields.Integer(string='Period Length')
method_end = fields.Date(string='Ending date')
asset_method_time = fields.Char(compute='_get_asset_method_time',
string='Asset Method Time', readonly=True)
def _get_asset_method_time(self):
if self.env.context.get('active_id'):
asset = self.env['account.asset.asset'].browse(self.env.context.get('active_id'))
self.asset_method_time = asset.method_time
@api.model
def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
result = super(AssetModify, self).fields_view_get(view_id, view_type, toolbar=toolbar, submenu=submenu)
asset_id = self.env.context.get('active_id')
active_model = self.env.context.get('active_model')
if active_model == 'account.asset.asset' and asset_id:
asset = self.env['account.asset.asset'].browse(asset_id)
doc = etree.XML(result['arch'])
if asset.method_time == 'number' and doc.xpath("//field[@name='method_end']"):
node = doc.xpath("//field[@name='method_end']")[0]
node.set('invisible', '1')
# setup_modifiers(node, result['fields']['method_end'])
elif asset.method_time == 'end' and doc.xpath("//field[@name='method_number']"):
node = doc.xpath("//field[@name='method_number']")[0]
node.set('invisible', '1')
# setup_modifiers(node, result['fields']['method_number'])
result['arch'] = etree.tostring(doc, encoding='unicode')
return result
@api.model
def default_get(self, fields):
res = super(AssetModify, self).default_get(fields)
asset_id = self.env.context.get('active_id')
asset = self.env['account.asset.asset'].browse(asset_id)
if 'name' in fields:
res.update({'name': asset.name})
if 'method_number' in fields and asset.method_time == 'number':
res.update({'method_number': asset.method_number})
if 'method_period' in fields:
res.update({'method_period': asset.method_period})
if 'method_end' in fields and asset.method_time == 'end':
res.update({'method_end': asset.method_end})
if self.env.context.get('active_id'):
active_asset = self.env['account.asset.asset'].browse(self.env.context.get('active_id'))
res['asset_method_time'] = active_asset.method_time
return res
def modify(self):
""" Modifies the duration of asset for calculating depreciation
and maintains the history of old values, in the chatter.
"""
asset_id = self.env.context.get('active_id', False)
asset = self.env['account.asset.asset'].browse(asset_id)
old_values = {
'method_number': asset.method_number,
'method_period': asset.method_period,
'method_end': asset.method_end,
}
asset_vals = {
'method_number': self.method_number,
'method_period': self.method_period,
'method_end': self.method_end,
}
asset.write(asset_vals)
asset.compute_depreciation_board()
tracked_fields = self.env['account.asset.asset'].fields_get(['method_number', 'method_period', 'method_end'])
changes, tracking_value_ids = asset._mail_track(tracked_fields, old_values)
if changes:
asset.message_post(subject=_('Depreciation board modified'), body=self.name, tracking_value_ids=tracking_value_ids)
return {'type': 'ir.actions.act_window_close'}
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!--Asset Modify Form View-->
<record model="ir.ui.view" id="asset_modify_view_form">
<field name="name">asset.modify.view.form</field>
<field name="model">asset.modify</field>
<field name="arch" type="xml">
<form string="Modify Asset">
<field name="asset_method_time" invisible="1"/>
<group string="Asset Durations to Modify" col="4">
<group colspan="2" col="2">
<field name="name"/>
<field name="method_number" invisible="asset_method_time in 'end'"/>
</group>
<group colspan="2" col="2">
<field name="method_end" invisible="asset_method_time in 'number'"/>
<label for="method_period"/>
<div>
<field name="method_period" class="oe_inline"/> months
</div>
</group>
</group>
<footer>
<button name="modify" string="Modify" type="object" class="btn-primary"/>
<button string="Cancel" class="btn-default" special="cancel"/>
</footer>
</form>
</field>
</record>
<!--Action Asset Modify-->
<record id="action_asset_modify" model="ir.actions.act_window">
<field name="name">Modify Asset</field>
<field name="res_model">asset.modify</field>
<field name="type">ir.actions.act_window</field>
<field name="view_mode">list,form</field>
<field name="view_id" ref="asset_modify_view_form"/>
<field name="target">new</field>
</record>
</odoo>
@@ -0,0 +1,128 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-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
class AccountingReport(models.TransientModel):
_name = "cash.flow.report"
_inherit = "account.report"
_description = "Cash Flow Report"
section_main_report_ids = fields.Many2many(string="Section Of",
comodel_name='account.report',
relation="account_cash_flow_report_section_rel",
column1="sub_report_id",
column2="main_report_id")
section_report_ids = fields.Many2many(string="Sections",
comodel_name='account.report',
relation="account_cash_flow_report_section_rel",
column1="main_report_id",
column2="sub_report_id")
name = fields.Char(string="Cash Flow Report", default="Cash Flow Report", required=True, translate=True)
date_from = fields.Date(string='Start Date')
date_to = fields.Date(string='End Date')
company_id = fields.Many2one('res.company', string='Company', required=True, readonly=True, default=lambda self: self.env.company)
target_move = fields.Selection([('posted', 'All Posted Entries'),
('all', 'All Entries'),
], string='Target Moves', required=True, default='posted')
journal_ids = fields.Many2many(
comodel_name='account.journal',
string='Journals',
required=True,
default=lambda self: self.env['account.journal'].search([('company_id', '=', self.company_id.id)]),
domain="[('company_id', '=', company_id)]",
)
@api.model
def _get_account_report(self):
reports = []
if self._context.get('active_id'):
menu = self.env['ir.ui.menu'].browse(
self._context.get('active_id')).name
reports = self.env['account.financial.report'].search(
[('name', 'ilike', menu)])
return reports and reports[0] or False
enable_filter = fields.Boolean(string='Enable Comparison')
account_report_id = fields.Many2one('account.financial.report',
string='Account Reports',
required=True,
default=_get_account_report)
label_filter = fields.Char(string='Column Label',
help="This label will be displayed on report to show the balance"
" computed for the given comparison filter.")
filter_cmp = fields.Selection(
[('filter_no', 'No Filters'), ('filter_date', 'Date')],
string='Filter by', required=True, default='filter_no')
date_from_cmp = fields.Date(string='Date Start')
date_to_cmp = fields.Date(string='Date End')
debit_credit = fields.Boolean(string='Display Debit/Credit Columns',
help="This option allows you to get more details about the way your balances are computed. Because it is space consuming, we do not allow to use it while doing a comparison.")
def _build_comparison_context(self, data):
result = {}
result['journal_ids'] = 'journal_ids' in data['form'] and data['form'][
'journal_ids'] or False
result['state'] = 'target_move' in data['form'] and data['form'][
'target_move'] or ''
if data['form']['filter_cmp'] == 'filter_date':
result['date_from'] = data['form']['date_from_cmp']
result['date_to'] = data['form']['date_to_cmp']
result['strict_range'] = True
return result
def _build_contexts(self, data):
result = {}
result['journal_ids'] = 'journal_ids' in data['form'] and data['form']['journal_ids'] or False
result['state'] = 'target_move' in data['form'] and data['form']['target_move'] or ''
result['date_from'] = data['form']['date_from'] or False
result['date_to'] = data['form']['date_to'] or False
result['strict_range'] = True if result['date_from'] else False
result['company_id'] = data['form']['company_id'][0] or False
return result
# @api.multi
def check_report(self):
res = super(AccountingReport, self).check_report()
data = {}
data['form'] = self.read(
['account_report_id', 'date_from_cmp', 'date_to_cmp',
'journal_ids', 'filter_cmp', 'target_move'])[0]
for field in ['account_report_id']:
if isinstance(data['form'][field], tuple):
data['form'][field] = data['form'][field][0]
comparison_context = self._build_comparison_context(data)
res['data']['form']['comparison_context'] = comparison_context
return res
def _print_report(self, data):
raise NotImplementedError()
def _print_report(self, data):
data['form'].update(self.read(
['date_from_cmp', 'debit_credit', 'date_to_cmp', 'filter_cmp',
'account_report_id', 'enable_filter', 'label_filter',
'target_move'])[0])
return self.env.ref(
'base_accounting_kit.action_report_cash_flow').report_action(self,
data=data,
config=False)
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!--Cash Flow Report Form View-->
<record id="cash_flow_report_view" model="ir.ui.view">
<field name="name">cash.flow.report.view.form.inherit.base.accounting.kit</field>
<field name="model">cash.flow.report</field>
<field name="inherit_id" ref="base_accounting_kit.account_report_view_form"/>
<field name="arch" type="xml">
<field name="company_id" position="replace"/>
<field name="target_move" position="before">
<field name="account_report_id" domain="[('parent_id','=',False)]" readonly="1" invisible="1"/>
<field name="enable_filter"/>
</field>
<field name="target_move" position="after">
<field name="debit_credit"/>
</field>
<field name="journal_ids" position="after">
<group>
<field name="company_id" position="replace" invisible="1"/>
</group>
<notebook tabpos="up" colspan="4" invisible="not enable_filter">
<page string="Comparison" name="comparison">
<group>
<field name="label_filter" required="enable_filter"/>
<field name="filter_cmp"/>
</group>
<group string="Dates" invisible="filter_cmp != 'filter_date'">
<field name="date_from_cmp" required="filter_cmp in 'filter_date'"/>
<field name="date_to_cmp" required="filter_cmp in 'filter_date'"/>
</group>
</page>
</notebook>
</field>
<field name="journal_ids" position="replace"/>
</field>
</record>
<!--Action Cash Flow Report-->
<record id="action_cash_flow_report" model="ir.actions.act_window">
<field name="name">Cash Flow Statement</field>
<field name="res_model">cash.flow.report</field>
<field name="type">ir.actions.act_window</field>
<field name="view_mode">form</field>
<field name="view_id" ref="cash_flow_report_view"/>
<field name="target">new</field>
<field name="context"
eval="{'default_account_report_id':ref('base_accounting_kit.account_financial_report_cash_flow0')}"/>
</record>
<!--Menu Cash Flow Statement-->
<menuitem id="menu_account_cash_flow_report"
name="Cash Flow Statement"
sequence="5"
action="action_cash_flow_report"
parent="base_accounting_kit.account_reports_generic_statements"/>
</odoo>
@@ -0,0 +1,438 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-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/>.
#
#############################################################################
import re
from odoo import api, models, fields
class FinancialReport(models.TransientModel):
_name = "financial.report"
_inherit = "account.report"
_description = "Financial Reports"
section_main_report_ids = fields.Many2many(string="Section Of",
comodel_name='account.report',
relation="account_financial_report_section_rel",
column1="sub_report_id",
column2="main_report_id")
section_report_ids = fields.Many2many(string="Sections",
comodel_name='account.report',
relation="account_financial_report_section_rel",
column1="main_report_id",
column2="sub_report_id")
name = fields.Char(string="Financial Report", default="Financial Report", required=True, translate=True)
target_move = fields.Selection([('posted', 'All Posted Entries'),
('all', 'All Entries'),
], string='Target Moves', required=True, default='posted')
view_format = fields.Selection([
('vertical', 'Vertical'),
('horizontal', 'Horizontal')],
default='vertical',
string="Format")
def _build_contexts(self, data):
result = {}
result['journal_ids'] = 'journal_ids' in data['form'] and data['form'][
'journal_ids'] or False
result['state'] = 'target_move' in data['form'] and data['form'][
'target_move'] or ''
result['date_from'] = data['form']['date_from'] or False
result['date_to'] = data['form']['date_to'] or False
result['strict_range'] = True if result['date_from'] else False
return result
@api.model
def _get_account_report(self):
reports = []
if self._context.get('active_id'):
menu = self.env['ir.ui.menu'].browse(
self._context.get('active_id')).name
reports = self.env['account.financial.report'].search([
('name', 'ilike', menu)])
return reports and reports[0] or False
enable_filter = fields.Boolean(
string='Enable Comparison',
default=False)
account_report_id = fields.Many2one(
'account.financial.report',
string='Account Reports',
required=True)
date_from = fields.Date(string='Start Date')
date_to = fields.Date(string='End Date')
debit_credit = fields.Boolean(
string='Display Debit/Credit Columns',
default=True,
help="This option allows you to"
" get more details about the "
"way your balances are computed."
" Because it is space consuming,"
" we do not allow to use it "
"while doing a comparison.")
company_id = fields.Many2one(
'res.company',
string='Company',
index=True,
default=lambda self: self.env.company.id)
def view_report_pdf(self):
"""This function will be executed when we click the view button
from the wizard. Based on the values provided in the wizard, this
function will print pdf report"""
self.ensure_one()
data = dict()
data['ids'] = self.env.context.get('active_ids', [])
data['model'] = self.env.context.get('active_model', 'ir.ui.menu')
data['form'] = self.read(
['date_from', 'enable_filter', 'debit_credit', 'date_to',
'account_report_id', 'target_move', 'view_format',
'company_id'])[0]
used_context = self._build_contexts(data)
data['form']['used_context'] = dict(
used_context,
lang=self.env.context.get('lang') or 'en_US')
report_lines = self.get_account_lines(data['form'])
# find the journal items of these accounts
journal_items = self.find_journal_items(report_lines, data['form'])
def set_report_level(rec):
"""This function is used to set the level of each item.
This level will be used to set the alignment in the dynamic reports."""
level = 1
if not rec['parent']:
return level
else:
for line in report_lines:
key = 'a_id' if line['type'] == 'account' else 'id'
if line[key] == rec['parent']:
return level + set_report_level(line)
# finding the root
for item in report_lines:
item['balance'] = round(item['balance'], 2)
if not item['parent']:
item['level'] = 1
parent = item
report_name = item['name']
id = item['id']
report_id = item['r_id']
else:
item['level'] = set_report_level(item)
currency = self._get_currency()
data['currency'] = currency
data['journal_items'] = journal_items
data['report_lines'] = report_lines
# checking view type
return self.env.ref(
'base_accounting_kit.financial_report_pdf').report_action(self,
data)
def _compute_account_balance(self, accounts):
""" compute the balance, debit
and credit for the provided accounts
"""
mapping = {
'balance':
"COALESCE(SUM(debit),0) - COALESCE(SUM(credit), 0)"
" as balance",
'debit': "COALESCE(SUM(debit), 0) as debit",
'credit': "COALESCE(SUM(credit), 0) as credit",
}
res = {}
for account in accounts:
res[account.id] = dict((fn, 0.0)
for fn in mapping.keys())
if accounts:
tables, where_clause, where_params = (
self.env['account.move.line']._query_get())
tables = tables.replace(
'"', '') if tables else "account_move_line"
wheres = [""]
if where_clause.strip():
wheres.append(where_clause.strip())
filters = " AND ".join(wheres)
request = ("SELECT account_id as id, " +
', '.join(mapping.values()) +
" FROM " + tables +
" WHERE account_id IN %s " +
filters +
" GROUP BY account_id")
params = (tuple(accounts._ids),) + tuple(where_params)
self.env.cr.execute(request, params)
for row in self.env.cr.dictfetchall():
res[row['id']] = row
return res
def _compute_report_balance(self, reports):
"""returns a dictionary with key=the ID of a record and
value=the credit, debit and balance amount
computed for this record. If the record is of type :
'accounts' : it's the sum of the linked accounts
'account_type' : it's the sum of leaf accounts with
such an account_type
'account_report' : it's the amount of the related report
'sum' : it's the sum of the children of this record
(aka a 'view' record)"""
res = {}
fields = ['credit', 'debit', 'balance']
for report in reports:
if report.id in res:
continue
res[report.id] = dict((fn, 0.0) for fn in fields)
if report.type == 'accounts':
# it's the sum of the linked accounts
res[report.id]['account'] = self._compute_account_balance(
report.account_ids
)
for value in \
res[report.id]['account'].values():
for field in fields:
res[report.id][field] += value.get(field)
elif report.type == 'account_type':
# it's the sum the leaf accounts
# with such an account type
accounts = self.env['account.account'].search([
('account_type', '=', report.account_type_ids)
])
if report.name == "Expenses":
accounts = self.env['account.account'].search([
('account_type', 'in', ["expense","expense_depreciation","expense_direct_cost"])
])
if report.name == "Liability":
accounts = self.env['account.account'].search([
('account_type', 'in', ["liability_payable","equity","liability_current","liability_non_current"])
])
if report.name == "Assets":
accounts = self.env['account.account'].search([
('account_type', 'in', ["asset_receivable","asset_cash","asset_current","asset_non_current","asset_prepayments","asset_fixed"])
])
res[report.id]['account'] = self._compute_account_balance(
accounts)
for value in res[report.id]['account'].values():
for field in fields:
res[report.id][field] += value.get(field)
elif report.type == 'account_report' and report.account_report_id:
# it's the amount of the linked report
res2 = self._compute_report_balance(report.account_report_id)
for key, value in res2.items():
for field in fields:
res[report.id][field] += value[field]
elif report.type == 'sum':
# it's the sum of the children of this account.report
res2 = self._compute_report_balance(report.children_ids)
for key, value in res2.items():
for field in fields:
res[report.id][field] += value[field]
return res
def get_account_lines(self, data):
lines = []
account_report = self.env['account.financial.report'].search([
('id', '=', data['account_report_id'][0])
])
child_reports = account_report._get_children_by_order()
res = self.with_context(
data.get('used_context'))._compute_report_balance(child_reports)
if data['enable_filter']:
comparison_res = self._compute_report_balance(child_reports)
for report_id, value in comparison_res.items():
res[report_id]['comp_bal'] = value['balance']
report_acc = res[report_id].get('account')
if report_acc:
for account_id, val in \
comparison_res[report_id].get('account').items():
report_acc[account_id]['comp_bal'] = val['balance']
for report in child_reports:
r_name = str(report.name)
r_name = re.sub('[^0-9a-zA-Z]+', '', r_name)
if report.parent_id:
p_name = str(report.parent_id.name)
p_name = re.sub('[^0-9a-zA-Z]+', '', p_name) + str(
report.parent_id.id)
else:
p_name = False
vals = {
'r_id': report.id,
'id': r_name + str(report.id),
'sequence': report.sequence,
'parent': p_name,
'name': report.name,
'balance': res[report.id]['balance'] * int(report.sign),
'type': 'report',
'level': bool(
report.style_overwrite) and report.style_overwrite or
report.level,
'account_type': report.type or False,
# used to underline the financial report balances
}
if data['debit_credit']:
vals['debit'] = res[report.id]['debit']
vals['credit'] = res[report.id]['credit']
if data['enable_filter']:
vals['balance_cmp'] = res[report.id]['comp_bal'] * int(
report.sign)
lines.append(vals)
if report.display_detail == 'no_detail':
# the rest of the loop is
# used to display the details of the
# financial report, so it's not needed here.
continue
if res[report.id].get('account'):
sub_lines = []
for account_id, value \
in res[report.id]['account'].items():
# if there are accounts to display,
# we add them to the lines with a level equals
# to their level in
# the COA + 1 (to avoid having them with a too low level
# that would conflicts with the level of data
# financial reports for Assets, liabilities...)
flag = False
account = self.env['account.account'].browse(account_id)
# new_r_name = str(report.name)
# new_r_name = new_r_name.replace(" ", "-") + "-"
vals = {
'account': account.id,
'a_id': account.code + re.sub('[^0-9a-zA-Z]+', 'acnt',
account.name) + str(
account.id),
'name': account.code + '-' + account.name,
'balance': value['balance'] * int(report.sign) or 0.0,
'type': 'account',
'parent': r_name + str(report.id),
'level': (
report.display_detail == 'detail_with_hierarchy' and
4),
'account_type': account.account_type,
}
if data['debit_credit']:
vals['debit'] = value['debit']
vals['credit'] = value['credit']
for company in account.company_ids:
if not company.currency_id.is_zero(
vals['debit']) or \
not company.currency_id.is_zero(
vals['credit']):
flag = True
for company in account.company_ids:
if not company.currency_id.is_zero(
vals['balance']):
flag = True
if data['enable_filter']:
vals['balance_cmp'] = value['comp_bal'] * int(
report.sign)
for company in account.company_ids:
if not company.currency_id.is_zero(
vals['balance_cmp']):
flag = True
if flag:
sub_lines.append(vals)
lines += sorted(sub_lines,
key=lambda sub_line: sub_line['name'])
return lines
def find_journal_items(self, report_lines, form):
cr = self.env.cr
journal_items = []
for i in report_lines:
if i['type'] == 'account':
account = i['account']
if form['target_move'] == 'posted':
search_query = ("select aml.id, am.id as j_id, "
"aml.account_id, aml.date, aml.name as "
"label, am.name, (aml.debit-aml.credit) as "
"balance, aml.debit, aml.credit, "
"aml.partner_id from "
"account_move_line aml "
"join account_move am on (aml.move_id=am.id"
" and am.state=%s) where aml.account_id=%s")
vals = [form['target_move']]
else:
search_query = ("select aml.id, am.id as j_id, "
"aml.account_id, aml.date, aml.name as "
"label, am.name, (aml.debit-aml.credit) as "
"balance, aml.debit, aml.credit, "
"aml.partner_id from account_move_line aml"
" join account_move am on "
"(aml.move_id=am.id) where "
"aml.account_id=%s")
vals = []
if form['date_from'] and form['date_to']:
search_query += " and aml.date>=%s and aml.date<=%s"
vals += [account, form['date_from'], form['date_to']]
elif form['date_from']:
search_query += " and aml.date>=%s"
vals += [account, form['date_from']]
elif form['date_to']:
search_query += " and aml.date<=%s"
vals += [account, form['date_to']]
else:
vals += [account]
cr.execute(search_query, tuple(vals))
items = cr.dictfetchall()
for j in items:
temp = j['id']
j['id'] = re.sub('[^0-9a-zA-Z]+', '', i['name']) + str(
temp)
j['p_id'] = str(i['a_id'])
j['type'] = 'journal_item'
journal_items.append(j)
return journal_items
@api.model
def _get_currency(self):
journal = self.env['account.journal'].browse(
self.env.context.get('default_journal_id', False))
if journal.currency_id:
return journal.currency_id.id
return self.env.company.currency_id.symbol
class ProfitLossPdf(models.AbstractModel):
""" Abstract model for generating PDF report value and send to template """
_name = 'report.base_accounting_kit.report_financial'
_description = 'Financial Report'
@api.model
def _get_report_values(self, docids, data=None):
""" Provide report values to template """
ctx = {
'data': data,
'journal_items': data['journal_items'],
'report_lines': data['report_lines'],
'account_report': data['form']['account_report_id'][1],
'currency': data['currency'],
}
return ctx
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- Financial report common form view -->
<record id="financial_report_view_form" model="ir.ui.view">
<field name="name">financial.report.view.form</field>
<field name="model">financial.report</field>
<field name="arch" type="xml">
<form>
<group>
<group string="Dates">
<field name="date_from"/>
<field name="date_to"/>
</group>
<group string="Type">
<field name="target_move" widget="radio"/>
<field name="view_format" widget="radio" invisible="1"/>
<field name="enable_filter" invisible="1"/>
<field name="debit_credit"/>
<field name="company_id" invisible="1"/>
</group>
</group>
<footer>
<button string="Print" name="view_report_pdf" type="object"
class="btn-primary"/>
<button string="Discard" class="btn-secondary"
special="cancel"/>
</footer>
</form>
</field>
</record>
<!-- Action for profit and loss -->
<record id="action_profit_and_loss_report" model="ir.actions.act_window">
<field name="name">Profit and Loss</field>
<field name="res_model">financial.report</field>
<field name="type">ir.actions.act_window</field>
<field name="view_mode">form</field>
<field name="view_id" ref="financial_report_view_form"/>
<field name="target">new</field>
<field name="context"
eval="{'default_account_report_id':ref('base_accounting_kit.account_financial_report_profitandloss0')}"/>
</record>
<!-- Action for balance sheet -->
<record id="action_balance_sheet_report" model="ir.actions.act_window">
<field name="name">Balance Sheet</field>
<field name="res_model">financial.report</field>
<field name="type">ir.actions.act_window</field>
<field name="view_mode">form</field>
<field name="view_id" ref="financial_report_view_form"/>
<field name="target">new</field>
<field name="context"
eval="{'default_account_report_id':ref('base_accounting_kit.account_financial_report_balancesheet0')}"/>
</record>
<!--Menu Balance Sheet And Profit and Loss-->
<menuitem id="account_financial_reports_profit_loss" sequence="1"
name="Profit and Loss" parent="base_accounting_kit.account_reports_generic_statements"
action="action_profit_and_loss_report"/>
<menuitem id="_account_financial_reports_balance_sheet" sequence="2"
name="Balance Sheet" parent="base_accounting_kit.account_reports_generic_statements"
action="action_balance_sheet_report"/>
</odoo>
@@ -0,0 +1,446 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Akhil Ashok (odoo@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/>.
#
###############################################################################
import base64
import codecs
import csv
import openpyxl
import os
import io
from datetime import datetime
from io import BytesIO
from odoo import fields, models, _
from odoo.exceptions import ValidationError
from ofxparse import OfxParser
from qifparse.parser import QifParser
class ImportBankStatement(models.TransientModel):
""" A class to import files as bank statement """
_name = "import.bank.statement"
_description = "Import button"
_rec_name = "file_name"
attachment = fields.Binary(string="File", required=True,
help="Choose the file to import")
file_name = fields.Char(string="File Name", help="Name of the file")
journal_id = fields.Many2one('account.journal', string="Journal ID",
help="Journal in which the file importing")
def _parse_date(self, date_str):
""" Helper to parse date from string """
if not date_str:
return fields.Date.today()
if isinstance(date_str, datetime):
return date_str.date()
# Remove potential quotes and whitespace
date_str = str(date_str).strip().strip('"').strip("'")
if not date_str:
return fields.Date.today()
# Try common formats
for fmt in ('%Y-%m-%d', '%d/%m/%Y', '%m/%d/%Y', '%d-%m-%Y', '%Y/%m/%d'):
try:
return datetime.strptime(date_str, fmt).date()
except (ValueError, TypeError):
continue
# Fallback to Odoo fields.Date.from_string
try:
res = fields.Date.from_string(date_str)
if res:
return res
except:
pass
return fields.Date.today()
def _parse_float(self, val):
""" Helper to parse float from string with currency symbols and commas """
if not val:
return 0.0
if isinstance(val, (int, float)):
return float(val)
# Remove currency symbols, quotes, commas, spaces
clean_val = str(val).strip().replace('"', '').replace("'", "").replace(',', '').replace(' ', '')
for symbol in ('$', '', '£', '¥', ''):
clean_val = clean_val.replace(symbol, '')
# Handle accounting negative format (100.0) -> -100.0
if clean_val.startswith('(') and clean_val.endswith(')'):
clean_val = '-' + clean_val[1:-1]
try:
return float(clean_val)
except (ValueError, TypeError):
return 0.0
def action_statement_import(self):
"""Function to import csv, xlsx, ofx and qif file format"""
split_tup = os.path.splitext(self.file_name)
if split_tup[1] == '.csv' or split_tup[1] == '.xlsx' or split_tup[
1] == '.ofx' or split_tup[1] == '.qif':
if split_tup[1] == '.csv':
try:
file_data = base64.b64decode(self.attachment)
file_string = file_data.decode('utf-8-sig')
f = io.StringIO(file_string)
reader = csv.DictReader(f)
fieldnames = reader.fieldnames or []
header_map = {f.strip().lower(): f for f in fieldnames}
except Exception as e:
raise ValidationError(_("Error reading CSV file: %s") % str(e))
statement_id = False
for row in reader:
if not any(row.values()):
continue
def get_field(keys):
for k in keys:
if k.lower() in header_map:
return row.get(header_map[k.lower()])
return None
# Mappings for common Odoo and generic bank headers
name = get_field(['name', 'label', 'description', 'reference', 'account', 'line_ids/payment_ref', 'payment_ref'])
amount = get_field(['amount', 'value', 'price', 'total', 'line_ids/amount'])
partner_name = get_field(['partner', 'partner_id/name', 'contact', 'payee', 'customer', 'supplier', 'line_ids/partner_id', 'partner_id'])
date_str = get_field(['date', 'transaction date', 'time', 'line_ids/date'])
starting_balance = get_field(['starting balance', 'start balance', 'opening balance', 'balance_start'])
ending_balance = get_field(['ending balance', 'balance', 'real balance', 'end balance', 'balance_end_real'])
values = [v.strip() if v else '' for v in row.values()]
keys = list(row.keys())
mapped_indices = set()
def find_column(targets, is_numeric=False, is_date=False, exclude_indices=None):
exclude_indices = exclude_indices or set()
for k, v in header_map.items():
if any(t in k for t in targets):
idx = keys.index(v)
if idx not in exclude_indices:
return row.get(v), idx
if is_numeric or is_date:
for i, val in enumerate(values):
if i in exclude_indices: continue
if is_numeric:
try:
temp = val.replace('$', '').replace(',', '').strip()
if temp:
float(temp)
return val, i
except: pass
if is_date:
try:
self._parse_date(val)
return val, i
except: pass
return None, -1
# Heuristic backups for unmapped fields
if amount is None:
amount_val, idx = find_column(['amount', 'value', 'price', 'total'], is_numeric=True)
if idx != -1:
amount = amount_val
mapped_indices.add(idx)
if date_str is None:
date_val, idx = find_column(['date', 'time'], is_date=True, exclude_indices=mapped_indices)
if idx != -1:
date_str = date_val
mapped_indices.add(idx)
if ending_balance is None:
bal_val, idx = find_column(['balance', 'ending', 'real', 'end balance'], is_numeric=True, exclude_indices=mapped_indices)
if idx != -1:
ending_balance = bal_val
mapped_indices.add(idx)
if starting_balance is None:
# Attempt to find starting balance only if multiple numeric columns exist
numeric_count = len([v for v in values if v.replace('$', '').replace(',', '').strip().replace('.', '').isdigit()])
bal_val, idx = find_column(['starting', 'start balance', 'opening'], is_numeric=(numeric_count > 2), exclude_indices=mapped_indices)
if idx != -1:
starting_balance = bal_val
mapped_indices.add(idx)
if name is None:
for i, val in enumerate(values):
if i in mapped_indices: continue
try:
float(val.replace('$', '').replace(',', '').strip())
continue
except:
try:
self._parse_date(val)
continue
except:
name = val
mapped_indices.add(i)
break
if not name and values:
name = values[0]
transaction_date = self._parse_date(date_str)
clean_amount = self._parse_float(amount)
clean_start_balance = self._parse_float(starting_balance)
clean_end_balance = self._parse_float(ending_balance)
# Ensure statement is balanced (End = Start + Amount) to prevent red state
if starting_balance is not None and ending_balance is not None:
b_start, b_end = clean_start_balance, clean_end_balance
clean_amount = b_end - b_start
elif ending_balance is not None:
b_end = clean_end_balance
b_start = b_end - clean_amount
elif starting_balance is not None:
b_start = clean_start_balance
b_end = b_start + clean_amount
else:
b_start, b_end = 0.0, clean_amount
partner = False
if partner_name:
partner_name = partner_name.strip()
if partner_name.lower() in ('bank', 'cash', 'main', 'demo', 'yourcompany'):
partner_name = False
if partner_name:
partner = self.env['res.partner'].search([('name', '=', partner_name)], limit=1)
if not partner and len(partner_name) > 1 and not partner_name.isdigit():
try:
self._parse_date(partner_name)
except:
raise ValidationError(_("Partner '%s' does not exist") % partner_name)
statement = self.env['account.bank.statement'].create({
'name': name,
'journal_id': self.journal_id.id,
'company_id': self.journal_id.company_id.id,
'date': transaction_date,
'balance_start': b_start,
'balance_end_real': b_end,
'line_ids': [(0, 0, {
'date': transaction_date,
'payment_ref': name or 'csv file',
'partner_id': partner.id if partner else False,
'journal_id': self.journal_id.id,
'amount': clean_amount,
})],
})
statement_id = statement.id
return {
'type': 'ir.actions.act_window',
'name': 'Statements',
'view_mode': 'list',
'res_model': 'account.bank.statement',
'res_id': statement.id,
}
elif split_tup[1] == '.xlsx':
# Reading xlsx file
try:
order = openpyxl.load_workbook(
filename=BytesIO(base64.b64decode(self.attachment)))
xl_order = order.active
except:
raise ValidationError(_("Choose correct file"))
for record in xl_order.iter_rows(min_row=2, max_row=None,
min_col=None,
max_col=None,
values_only=True):
line = list(record)
# Reading the content from file
if line[0] and line[1] and line[3]:
partner = self.env['res.partner'].search(
[('name', '=', line[3])])
date_obj = self._parse_date(line[2])
# Creating record
if partner:
statement = self.env[
'account.bank.statement'].create({
'name': line[0],
'line_ids': [
(0, 0, {
'date': date_obj,
'payment_ref': 'xlsx file',
'partner_id': partner.id,
'journal_id': self.journal_id.id,
'amount': line[1],
}),
],
})
else:
raise ValidationError(_("Partner not exist"))
else:
if not line[0]:
raise ValidationError(
_("Account name is not set"))
elif not line[1]:
raise ValidationError(
_("Amount is not set"))
elif not line[3]:
date_obj = self._parse_date(line[2])
# Creating record
statement = self.env[
'account.bank.statement'].create({
'name': line[0],
'line_ids': [
(0, 0, {
'date': date_obj,
'payment_ref': 'xlsx file',
'journal_id': self.journal_id.id,
'amount': line[1],
}),
],
})
return {
'type': 'ir.actions.act_window',
'name': 'Statements',
'view_mode': 'list',
'res_model': 'account.bank.statement',
'res_id': statement.id,
}
elif split_tup[1] == '.ofx':
try:
file_data = base64.b64decode(self.attachment)
ofx_file = OfxParser.parse(io.BytesIO(file_data))
except Exception as e:
raise ValidationError(_("Wrong file format or parsing error: %s") % str(e))
if not ofx_file.account or not ofx_file.account.statement:
raise ValidationError(_("No account information found in OFX file."))
statement_id = False
stmt = ofx_file.account.statement
# Standardize balance extraction
ledger_bal = getattr(stmt, 'balance', getattr(stmt, 'ledger_balance', None))
final_balance = self._parse_float(ledger_bal) if ledger_bal is not None else 0.0
for transaction in stmt.transactions:
amount = self._parse_float(transaction.amount)
if amount == 0:
continue
# Clean labels and match partners
label = (transaction.memo or transaction.name or transaction.payee or 'ofx transaction').strip()
payee_name = transaction.payee.strip() if transaction.payee else False
partner = False
if payee_name:
# Shared noise-filtering for partners
if payee_name.lower() in ('bank', 'cash', 'main', 'demo', 'yourcompany'):
payee_name = False
if payee_name:
partner = self.env['res.partner'].search([('name', '=', payee_name)], limit=1)
date = self._parse_date(transaction.date)
b_end = final_balance
b_start = b_end - amount
statement = self.env['account.bank.statement'].create({
'name': label,
'journal_id': self.journal_id.id,
'company_id': self.journal_id.company_id.id,
'date': date,
'balance_start': b_start,
'balance_end_real': b_end,
'line_ids': [(0, 0, {
'date': date,
'payment_ref': label,
'partner_id': partner.id if partner else False,
'journal_id': self.journal_id.id,
'amount': amount,
})],
})
statement_id = statement.id
if not statement_id:
raise ValidationError(_("No valid transactions found in the OFX file."))
return {
'type': 'ir.actions.act_window',
'name': 'Statements',
'view_mode': 'list',
'res_model': 'account.bank.statement',
'res_id': statement_id,
}
elif split_tup[1] == '.qif':
try:
file_data = base64.b64decode(self.attachment)
file_string = file_data.decode('utf-8-sig')
qif = QifParser().parse(io.StringIO(file_string))
except Exception as e:
raise ValidationError(_("Error parsing QIF file: %s") % str(e))
statement_id = False
for account in qif.get_accounts():
for transaction in qif.get_transactions(account):
amount = self._parse_float(transaction.amount)
if amount == 0:
continue
date = self._parse_date(transaction.date)
label = (transaction.payee or transaction.memo or 'qif transaction').strip()
payee_name = transaction.payee.strip() if transaction.payee else False
partner = False
if payee_name:
if payee_name.lower() in ('bank', 'cash', 'main', 'demo', 'yourcompany'):
payee_name = False
if payee_name:
partner = self.env['res.partner'].search([('name', '=', payee_name)], limit=1)
statement = self.env['account.bank.statement'].create({
'name': label,
'journal_id': self.journal_id.id,
'company_id': self.journal_id.company_id.id,
'date': date,
'balance_start': 0.0,
'balance_end_real': amount,
'line_ids': [(0, 0, {
'date': date,
'payment_ref': label,
'partner_id': partner.id if partner else False,
'journal_id': self.journal_id.id,
'amount': amount,
})],
})
statement_id = statement.id
if not statement_id:
raise ValidationError(_("No valid transactions found in the QIF file."))
return {
'type': 'ir.actions.act_window',
'name': 'Statements',
'view_mode': 'list',
'res_model': 'account.bank.statement',
'res_id': statement_id,
}
else:
raise ValidationError(_("Choose correct file"))
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- Wizard view to upload files -->
<record id="import_bank_statement_view_form" model="ir.ui.view">
<field name="name">import.bank.statement.view.form</field>
<field name="model">import.bank.statement</field>
<field name="arch" type="xml">
<form>
<sheet>
<p>
<center>
<i>Upload csv or xlsx or ofx or qif file format</i>
</center>
</p>
<group>
<group>
<field name="attachment" filename="file_name"/>
<field name="file_name" invisible="1"/>
<field name="journal_id" invisible="1"/>
</group>
</group>
<footer>
<button name="action_statement_import" class="oe_highlight"
string="IMPORT" type="object"
help="Import bank statement in CSV or XLSX format"/>
</footer>
</sheet>
</form>
</field>
</record>
<!-- Wizard action record -->
<record id="import_bank_statement_view_action"
model="ir.actions.act_window">
<field name="name">import.bank.statement.view.action</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">import.bank.statement</field>
<field name="view_mode">list,form</field>
<field name="view_id" ref="base_accounting_kit.import_bank_statement_view_form"/>
<field name="target">new</field>
</record>
</odoo>
@@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-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 fields, models
from odoo.tools.misc import get_lang
class AccountTaxReport(models.TransientModel):
_name = 'kit.account.tax.report'
_inherit = "account.report"
_description = 'Tax Report'
section_main_report_ids = fields.Many2many(string="Section Of",
comodel_name='account.report',
relation="account_tax_report_section_rel",
column1="sub_report_id",
column2="main_report_id")
section_report_ids = fields.Many2many(string="Sections",
comodel_name='account.report',
relation="account_tax_report_section_rel",
column1="main_report_id",
column2="sub_report_id")
company_id = fields.Many2one('res.company', string='Company', required=True, readonly=True, default=lambda self: self.env.company)
name = fields.Char(string="Tax Report", default="Tax Report",
required=True, translate=True)
date_from = fields.Date(string='Start Date')
date_to = fields.Date(string='End Date')
journal_ids = fields.Many2many(
comodel_name='account.journal',
string='Journals',
required=True,
default=lambda self: self.env['account.journal'].search([('company_id', '=', self.company_id.id)]),
domain="[('company_id', '=', company_id)]",
)
target_move = fields.Selection([('posted', 'All Posted Entries'),
('all', 'All Entries'),
], string='Target Moves', required=True, default='posted')
def _build_contexts(self, data):
result = {}
result['journal_ids'] = 'journal_ids' in data['form'] and data['form']['journal_ids'] or False
result['state'] = 'target_move' in data['form'] and data['form']['target_move'] or ''
result['date_from'] = data['form']['date_from'] or False
result['date_to'] = data['form']['date_to'] or False
result['strict_range'] = True if result['date_from'] else False
result['company_id'] = data['form']['company_id'][0] or False
return result
def check_report(self):
self.ensure_one()
data = {}
data['ids'] = self.env.context.get('active_ids', [])
data['model'] = self.env.context.get('active_model', 'ir.ui.menu')
data['form'] = self.read(['date_from', 'date_to', 'journal_ids', 'target_move', 'company_id'])[0]
used_context = self._build_contexts(data)
data['form']['used_context'] = dict(used_context, lang=get_lang(self.env).code)
return self.with_context(discard_logo_check=True)._print_report(data)
def pre_print_report(self, data):
data['form'].update(self.read(['display_account'])[0])
return data
def _print_report(self, data):
return self.env.ref(
'base_accounting_kit.action_report_account_tax').report_action(
self, data=data)
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!--Kit Account Tax Report Form View-->
<record id="accounting_tax_report_view" model="ir.ui.view">
<field name="name">kit.account.tax.report.view.form.inherit.base.accounting.kit</field>
<field name="model">kit.account.tax.report</field>
<field name="inherit_id" ref="base_accounting_kit.account_report_view_form"/>
<field name="arch" type="xml">
<field name="journal_ids" position="replace"/>
<field name="target_move" position="replace"/>
</field>
</record>
<!--Action Kit Account Tax Report-->
<record id="action_account_tax_report" model="ir.actions.act_window">
<field name="name">Tax Reports</field>
<field name="res_model">kit.account.tax.report</field>
<field name="type">ir.actions.act_window</field>
<field name="view_mode">form</field>
<field name="view_id" ref="accounting_tax_report_view"/>
<field name="context">{}</field>
<field name="target">new</field>
</record>
<!--Menu Tax Report-->
<menuitem id="menu_tax_report"
name="Tax Report"
sequence="8"
action="action_account_tax_report"
parent="base_accounting_kit.account_reports_audit"
groups="base_accounting_kit.group_account_chief,account.group_account_user"/>
</odoo>