Files
tradon/modules/account_move_line_grouping/account.py
2026-03-14 09:42:12 +00:00

261 lines
9.4 KiB
Python

# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from sql import As, Null, Window
from sql.aggregate import BoolAnd, BoolOr, Min, Sum
from sql.conditionals import Coalesce
from sql.functions import FirstValue
from trytond.model import ModelSQL, ModelView, fields
from trytond.modules.account import MoveLineMixin
from trytond.modules.currency.fields import Monetary
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Bool, Eval, If
from trytond.transaction import Transaction
class Move(metaclass=PoolMeta):
__name__ = 'account.move'
grouped_lines = fields.One2Many(
'account.move.line.group', 'move', "Grouped Lines", readonly=True,
states={
'invisible': ~Eval('grouped_lines', []),
})
class MoveLine(metaclass=PoolMeta):
__name__ = 'account.move.line'
@classmethod
def _view_reconciliation_muted(cls):
pool = Pool()
ModelData = pool.get('ir.model.data')
muted = super()._view_reconciliation_muted()
muted.add(ModelData.get_id(
'account_move_line_grouping',
'move_line_group_view_list_move'))
return muted
class MoveLineGroup(MoveLineMixin, ModelSQL, ModelView):
__name__ = 'account.move.line.group'
move = fields.Many2One(
'account.move', "Move", readonly=True, required=True)
account = fields.Many2One(
'account.account', "Account", readonly=True,
context={
'company': Eval('company', -1),
'period': Eval('period', -1),
},
depends={'company', 'period'})
party = fields.Many2One(
'party.party', "Party", readonly=True,
context={
'company': Eval('company', -1),
},
depends={'company'})
maturity_date = fields.Date("Maturity Date", readonly=True)
debit = Monetary(
"Debit", currency='currency', digits='currency', readonly=True)
credit = Monetary(
"Credit", currency='currency', digits='currency', readonly=True)
amount_second_currency = Monetary(
"Amount Second Currency",
currency='second_currency', digits='second_currency', readonly=True)
second_currency = fields.Many2One(
'currency.currency', "Second Currency", readonly=True)
amount = fields.Function(Monetary(
"Amount", currency='amount_currency', digits='amount_currency'),
'get_amount')
amount_currency = fields.Function(fields.Many2One(
'currency.currency', "Amount Currency"), 'get_amount_currency')
delegated_amount = fields.Function(Monetary(
"Delegated Amount",
currency='amount_currency', digits='amount_currency',
states={
'invisible': ~Eval('partially_reconciled', False),
}),
'get_delegated_amount')
payable_receivable_balance = fields.Function(
Monetary(
"Payable/Receivable Balance",
currency='amount_currency', digits='amount_currency'),
'get_payable_receivable_balance')
partially_reconciled = fields.Boolean(
"Partially Reconciled", readonly=True)
reconciled = fields.Boolean("Reconciled", readonly=True)
amount_reconciled = Monetary(
"Amount Reconciled",
currency='currency', digits='currency', readonly=True)
state = fields.Selection('get_states', "State", readonly=True, sort=False)
company = fields.Function(
fields.Many2One('company.company', "Company"),
'get_move_field', searcher='search_move_field')
journal = fields.Function(fields.Many2One(
'account.journal', "Journal",
context={
'company': Eval('company', -1),
},
depends={'company'}),
'get_move_field', searcher='search_move_field')
period = fields.Function(fields.Many2One(
'account.period', "Period"),
'get_move_field', searcher='search_move_field')
date = fields.Function(fields.Date(
"Effective Date"),
'get_move_field', searcher='search_move_field')
move_origin = fields.Function(fields.Reference(
"Move Origin", selection='get_move_origin'),
'get_move_field', searcher='search_move_field')
move_description_used = fields.Function(
fields.Char("Move Description"),
'get_move_field', searcher='search_move_field')
move_state = fields.Function(fields.Selection(
'get_move_states', "Move State"),
'get_move_field', searcher='search_move_field')
lines = fields.Many2Many(
'account.move.line.group-move.line', 'group', 'line', "Lines",
readonly=True, order=[('id', 'DESC')])
currency = fields.Function(fields.Many2One(
'currency.currency', "Currency"), 'on_change_with_currency')
order_company = MoveLineMixin._order_move_field('company')
order_period = MoveLineMixin._order_move_field('period')
order_company = MoveLineMixin._order_move_field('company')
order_date = MoveLineMixin._order_move_field('date')
order_move_origin = MoveLineMixin._order_move_field('origin')
order_move_state = MoveLineMixin._order_move_field('state')
@classmethod
def __setup__(cls):
super().__setup__()
cls.__access__.add('move')
cls._order[0] = ('id', 'DESC')
@classmethod
def table_query(cls):
pool = Pool()
Line = pool.get('account.move.line')
line = Line.__table__()
std_columns = [Min(line.id).as_('id')]
grouped_columns = cls._grouped_columns(line)
aggregated_columns = cls._aggregated_columns(line)
columns = std_columns + grouped_columns + aggregated_columns
grouped_columns = [
c.expression if isinstance(c, As) else c for c in grouped_columns]
return line.select(*columns, group_by=grouped_columns)
@classmethod
def _grouped_columns(cls, line):
return [
line.move,
line.account,
line.party,
line.maturity_date,
line.second_currency,
line.state,
]
@classmethod
def _aggregated_columns(cls, line):
context = Transaction().context
if not context.get('reconciled', True):
filter_ = line.reconciliation == Null
else:
filter_ = None
return [
Coalesce(Sum(line.debit, filter_=filter_), 0).as_('debit'),
Coalesce(Sum(line.credit, filter_=filter_), 0).as_('credit'),
Sum(line.amount_second_currency, filter_=filter_).as_(
'amount_second_currency'),
BoolOr(line.reconciliation != Null).as_('partially_reconciled'),
BoolAnd(line.reconciliation != Null).as_('reconciled'),
Sum(
line.debit + line.credit,
filter_=line.reconciliation != Null).as_('amount_reconciled'),
]
@classmethod
def get_states(cls):
pool = Pool()
Line = pool.get('account.move.line')
return Line.fields_get(['state'])['state']['selection']
@fields.depends('account')
def on_change_with_currency(self, name=None):
return self.account.currency if self.account else None
def get_delegated_amount(self, name):
return self.amount_currency.round(
sum(l.delegated_amount for l in self.lines if l.delegated_amount))
@classmethod
def view_attributes(cls):
attributes = super().view_attributes()
view_ids = cls._view_reconciliation_muted()
if Transaction().context.get('view_id') in view_ids:
attributes.append(
('/tree', 'visual',
If(Bool(Eval('reconciliation')), 'muted', '')))
return attributes
@classmethod
def _view_reconciliation_muted(cls):
pool = Pool()
ModelData = pool.get('ir.model.data')
return {ModelData.get_id(
'account_move_line_grouping',
'move_line_group_view_list_payable_receivable')}
class MoveLineGroup_MoveLine(ModelSQL):
__name__ = 'account.move.line.group-move.line'
group = fields.Many2One('account.move.line.group', "Group")
line = fields.Many2One('account.move.line', "Line")
@classmethod
def table_query(cls):
pool = Pool()
Line = pool.get('account.move.line')
LineGroup = pool.get('account.move.line.group')
line = Line.__table__()
grouped_columns = LineGroup._grouped_columns(line)
window = Window(
grouped_columns,
order_by=[line.id.asc])
query = line.select(
line.id.as_('id'),
FirstValue(line.id, window=window).as_('group'),
line.id.as_('line'))
return query
class OpenAccount(metaclass=PoolMeta):
__name__ = 'account.move.open_account'
_readonly = True
def do_open_(self, action):
pool = Pool()
Action = pool.get('ir.action')
ModelData = pool.get('ir.model.data')
context = Transaction().context
if context.get('action_id') == ModelData.get_id(
'account_move_line_grouping', 'act_open_account'):
action_id = Action.get_action_id(
ModelData.get_id(
'account_move_line_grouping', 'act_move_line_group_form'))
action = Action(action_id).get_action_value()
action, data = super().do_open_(action)
return action, data