You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
501 lines
24 KiB
Python
501 lines
24 KiB
Python
8 months ago
|
import ast
|
||
|
from collections import defaultdict
|
||
|
from contextlib import contextmanager
|
||
|
from datetime import date, timedelta
|
||
|
from functools import lru_cache
|
||
|
|
||
|
from odoo import api, fields, models, Command, _
|
||
|
from odoo.exceptions import ValidationError, UserError
|
||
|
from odoo.tools import frozendict, formatLang, format_date, float_is_zero, float_compare
|
||
|
from odoo.tools.sql import create_index
|
||
|
from odoo.addons.web.controllers.utils import clean_action
|
||
|
|
||
|
|
||
|
class AccountMoveLine(models.Model):
|
||
|
_inherit = "account.move.line"
|
||
|
# _inherit = "analytic.mixin"
|
||
|
# _description = "Journal Item"
|
||
|
# _order = "date desc, move_name desc, id"
|
||
|
# _check_company_auto = True
|
||
|
# _rec_names_search = ['name', 'move_id', 'product_id']
|
||
|
|
||
|
exclude_from_invoice_tab = fields.Boolean(
|
||
|
help="Technical field used to exclude some lines from the invoice_line_ids tab in the form view.")
|
||
|
|
||
|
recompute_tax_line = fields.Boolean(store=False, readonly=True,
|
||
|
help="Technical field used to know on which lines the taxes must be recomputed.")
|
||
|
analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account',
|
||
|
index=True, compute="_compute_analytic_account", store=True, readonly=False,copy=True)
|
||
|
analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic Tags',
|
||
|
compute="_compute_analytic_account", store=True, readonly=False, copy=True)
|
||
|
|
||
|
@api.model
|
||
|
def _get_default_line_name(self, document, amount, currency, date, partner=None):
|
||
|
''' 新增Helper to construct a default label to set on journal items.
|
||
|
|
||
|
E.g. Vendor Reimbursement $ 1,555.00 - Azure Interior - 05/14/2020.
|
||
|
|
||
|
:param document: A string representing the type of the document.
|
||
|
:param amount: The document's amount.
|
||
|
:param currency: The document's currency.
|
||
|
:param date: The document's date.
|
||
|
:param partner: The optional partner.
|
||
|
:return: A string.
|
||
|
'''
|
||
|
values = ['%s %s' % (document, formatLang(self.env, amount, currency_obj=currency))]
|
||
|
if partner:
|
||
|
values.append(partner.display_name)
|
||
|
values.append(format_date(self.env, fields.Date.to_string(date)))
|
||
|
return ' - '.join(values)
|
||
|
|
||
|
@api.onchange('amount_currency', 'currency_id', 'debit', 'credit', 'tax_ids', 'account_id', 'price_unit')
|
||
|
def _onchange_mark_recompute_taxes(self):
|
||
|
''' Recompute the dynamic onchange based on taxes.
|
||
|
If the edited line is a tax line, don't recompute anything as the user must be able to
|
||
|
set a custom value.
|
||
|
'''
|
||
|
for line in self:
|
||
|
if not line.tax_repartition_line_id:
|
||
|
line.recompute_tax_line = True
|
||
|
|
||
|
@api.onchange('analytic_account_id', 'analytic_tag_ids')
|
||
|
def _onchange_mark_recompute_taxes_analytic(self):
|
||
|
''' Trigger tax recomputation only when some taxes with analytics
|
||
|
'''
|
||
|
for line in self:
|
||
|
if not line.tax_repartition_line_id and any(tax.analytic for tax in line.tax_ids):
|
||
|
line.recompute_tax_line = True
|
||
|
|
||
|
def _get_fields_onchange_balance(self, quantity=None, discount=None, amount_currency=None, move_type=None,
|
||
|
currency=None, taxes=None, price_subtotal=None, force_computation=False):
|
||
|
self.ensure_one()
|
||
|
return self._get_fields_onchange_balance_model(
|
||
|
quantity=quantity or self.quantity,
|
||
|
discount=discount or self.discount,
|
||
|
amount_currency=amount_currency or self.amount_currency,
|
||
|
move_type=move_type or self.move_id.move_type,
|
||
|
currency=currency or self.currency_id or self.move_id.currency_id,
|
||
|
taxes=taxes or self.tax_ids,
|
||
|
price_subtotal=price_subtotal or self.price_subtotal,
|
||
|
force_computation=force_computation,
|
||
|
)
|
||
|
|
||
|
@api.model
|
||
|
def _get_fields_onchange_balance_model(self, quantity, discount, amount_currency, move_type, currency, taxes,
|
||
|
price_subtotal, force_computation=False):
|
||
|
''' This method is used to recompute the values of 'quantity', 'discount', 'price_unit' due to a change made
|
||
|
in some accounting fields such as 'balance'.
|
||
|
|
||
|
This method is a bit complex as we need to handle some special cases.
|
||
|
For example, setting a positive balance with a 100% discount.
|
||
|
|
||
|
:param quantity: The current quantity.
|
||
|
:param discount: The current discount.
|
||
|
:param amount_currency: The new balance in line's currency.
|
||
|
:param move_type: The type of the move.
|
||
|
:param currency: The currency.
|
||
|
:param taxes: The applied taxes.
|
||
|
:param price_subtotal: The price_subtotal.
|
||
|
:return: A dictionary containing 'quantity', 'discount', 'price_unit'.
|
||
|
'''
|
||
|
if move_type in self.move_id.get_outbound_types():
|
||
|
sign = 1
|
||
|
elif move_type in self.move_id.get_inbound_types():
|
||
|
sign = -1
|
||
|
else:
|
||
|
sign = 1
|
||
|
amount_currency *= sign
|
||
|
|
||
|
# Avoid rounding issue when dealing with price included taxes. For example, when the price_unit is 2300.0 and
|
||
|
# a 5.5% price included tax is applied on it, a balance of 2300.0 / 1.055 = 2180.094 ~ 2180.09 is computed.
|
||
|
# However, when triggering the inverse, 2180.09 + (2180.09 * 0.055) = 2180.09 + 119.90 = 2299.99 is computed.
|
||
|
# To avoid that, set the price_subtotal at the balance if the difference between them looks like a rounding
|
||
|
# issue.
|
||
|
if not force_computation and currency.is_zero(amount_currency - price_subtotal):
|
||
|
return {}
|
||
|
|
||
|
taxes = taxes.flatten_taxes_hierarchy()
|
||
|
if taxes and any(tax.price_include for tax in taxes):
|
||
|
# Inverse taxes. E.g:
|
||
|
#
|
||
|
# Price Unit | Taxes | Originator Tax |Price Subtotal | Price Total
|
||
|
# -----------------------------------------------------------------------------------
|
||
|
# 110 | 10% incl, 5% | | 100 | 115
|
||
|
# 10 | | 10% incl | 10 | 10
|
||
|
# 5 | | 5% | 5 | 5
|
||
|
#
|
||
|
# When setting the balance to -200, the expected result is:
|
||
|
#
|
||
|
# Price Unit | Taxes | Originator Tax |Price Subtotal | Price Total
|
||
|
# -----------------------------------------------------------------------------------
|
||
|
# 220 | 10% incl, 5% | | 200 | 230
|
||
|
# 20 | | 10% incl | 20 | 20
|
||
|
# 10 | | 5% | 10 | 10
|
||
|
force_sign = -1 if move_type in ('out_invoice', 'in_refund', 'out_receipt') else 1
|
||
|
taxes_res = taxes._origin.with_context(force_sign=force_sign).compute_all(amount_currency,
|
||
|
currency=currency,
|
||
|
handle_price_include=False)
|
||
|
for tax_res in taxes_res['taxes']:
|
||
|
tax = self.env['account.tax'].browse(tax_res['id'])
|
||
|
if tax.price_include:
|
||
|
amount_currency += tax_res['amount']
|
||
|
|
||
|
discount_factor = 1 - (discount / 100.0)
|
||
|
if amount_currency and discount_factor:
|
||
|
# discount != 100%
|
||
|
vals = {
|
||
|
'quantity': quantity or 1.0,
|
||
|
'price_unit': amount_currency / discount_factor / (quantity or 1.0),
|
||
|
}
|
||
|
elif amount_currency and not discount_factor:
|
||
|
# discount == 100%
|
||
|
vals = {
|
||
|
'quantity': quantity or 1.0,
|
||
|
'discount': 0.0,
|
||
|
'price_unit': amount_currency / (quantity or 1.0),
|
||
|
}
|
||
|
elif not discount_factor:
|
||
|
# balance of line is 0, but discount == 100% so we display the normal unit_price
|
||
|
vals = {}
|
||
|
else:
|
||
|
# balance is 0, so unit price is 0 as well
|
||
|
vals = {'price_unit': 0.0}
|
||
|
return vals
|
||
|
|
||
|
def _get_computed_name(self):
|
||
|
self.ensure_one()
|
||
|
|
||
|
if not self.product_id:
|
||
|
return ''
|
||
|
|
||
|
if self.partner_id.lang:
|
||
|
product = self.product_id.with_context(lang=self.partner_id.lang)
|
||
|
else:
|
||
|
product = self.product_id
|
||
|
|
||
|
values = []
|
||
|
if product.partner_ref:
|
||
|
values.append(product.partner_ref)
|
||
|
if self.journal_id.type == 'sale':
|
||
|
if product.description_sale:
|
||
|
values.append(product.description_sale)
|
||
|
elif self.journal_id.type == 'purchase':
|
||
|
if product.description_purchase:
|
||
|
values.append(product.description_purchase)
|
||
|
return '\n'.join(values)
|
||
|
|
||
|
def _get_computed_account(self):
|
||
|
self.ensure_one()
|
||
|
self = self.with_company(self.move_id.journal_id.company_id)
|
||
|
|
||
|
if not self.product_id:
|
||
|
return
|
||
|
|
||
|
fiscal_position = self.move_id.fiscal_position_id
|
||
|
accounts = self.product_id.product_tmpl_id.get_product_accounts(fiscal_pos=fiscal_position)
|
||
|
if self.move_id.is_sale_document(include_receipts=True):
|
||
|
# Out invoice.
|
||
|
return accounts['income'] or self.account_id
|
||
|
elif self.move_id.is_purchase_document(include_receipts=True):
|
||
|
# In invoice.
|
||
|
return accounts['expense'] or self.account_id
|
||
|
|
||
|
def _get_computed_uom(self):
|
||
|
self.ensure_one()
|
||
|
if self.product_id:
|
||
|
return self.product_id.uom_id
|
||
|
return False
|
||
|
|
||
|
def _set_price_and_tax_after_fpos(self):
|
||
|
self.ensure_one()
|
||
|
# Manage the fiscal position after that and adapt the price_unit.
|
||
|
# E.g. mapping a price-included-tax to a price-excluded-tax must
|
||
|
# remove the tax amount from the price_unit.
|
||
|
# However, mapping a price-included tax to another price-included tax must preserve the balance but
|
||
|
# adapt the price_unit to the new tax.
|
||
|
# E.g. mapping a 10% price-included tax to a 20% price-included tax for a price_unit of 110 should preserve
|
||
|
# 100 as balance but set 120 as price_unit.
|
||
|
if self.tax_ids and self.move_id.fiscal_position_id:
|
||
|
price_subtotal = self._get_price_total_and_subtotal()['price_subtotal']
|
||
|
self.tax_ids = self.move_id.fiscal_position_id.map_tax(
|
||
|
self.tax_ids._origin,
|
||
|
partner=self.move_id.partner_id)
|
||
|
accounting_vals = self._get_fields_onchange_subtotal(
|
||
|
price_subtotal=price_subtotal,
|
||
|
currency=self.move_id.company_currency_id)
|
||
|
amount_currency = accounting_vals['amount_currency']
|
||
|
business_vals = self._get_fields_onchange_balance(amount_currency=amount_currency)
|
||
|
if 'price_unit' in business_vals:
|
||
|
self.price_unit = business_vals['price_unit']
|
||
|
|
||
|
@api.onchange('product_id')
|
||
|
def _onchange_product_id(self):
|
||
|
for line in self:
|
||
|
if not line.product_id or line.display_type in ('line_section', 'line_note'):
|
||
|
continue
|
||
|
|
||
|
line.name = line._get_computed_name()
|
||
|
line.account_id = line._get_computed_account()
|
||
|
line.tax_ids = line._get_computed_taxes()
|
||
|
line.product_uom_id = line._get_computed_uom()
|
||
|
line.price_unit = line._get_computed_price_unit()
|
||
|
|
||
|
# price_unit and taxes may need to be adapted following Fiscal Position
|
||
|
line._set_price_and_tax_after_fpos()
|
||
|
|
||
|
# Convert the unit price to the invoice's currency.
|
||
|
company = line.move_id.company_id
|
||
|
line.price_unit = company.currency_id._convert(line.price_unit, line.move_id.currency_id, company,
|
||
|
line.move_id.date, round=False)
|
||
|
|
||
|
@api.model
|
||
|
def _get_price_total_and_subtotal_model(self, price_unit, quantity, discount, currency, product, partner, taxes,
|
||
|
move_type):
|
||
|
''' This method is used to compute 'price_total' & 'price_subtotal'.
|
||
|
|
||
|
:param price_unit: The current price unit.
|
||
|
:param quantity: The current quantity.
|
||
|
:param discount: The current discount.
|
||
|
:param currency: The line's currency.
|
||
|
:param product: The line's product.
|
||
|
:param partner: The line's partner.
|
||
|
:param taxes: The applied taxes.
|
||
|
:param move_type: The type of the move.
|
||
|
:return: A dictionary containing 'price_subtotal' & 'price_total'.
|
||
|
'''
|
||
|
res = {}
|
||
|
|
||
|
# Compute 'price_subtotal'.
|
||
|
line_discount_price_unit = price_unit * (1 - (discount / 100.0))
|
||
|
subtotal = quantity * line_discount_price_unit
|
||
|
|
||
|
# Compute 'price_total'.
|
||
|
if taxes:
|
||
|
force_sign = -1 if move_type in ('out_invoice', 'in_refund', 'out_receipt') else 1
|
||
|
taxes_res = taxes._origin.with_context(force_sign=force_sign).compute_all(line_discount_price_unit,
|
||
|
quantity=quantity,
|
||
|
currency=currency,
|
||
|
product=product,
|
||
|
partner=partner,
|
||
|
is_refund=move_type in (
|
||
|
'out_refund',
|
||
|
'in_refund'))
|
||
|
res['price_subtotal'] = taxes_res['total_excluded']
|
||
|
res['price_total'] = taxes_res['total_included']
|
||
|
else:
|
||
|
res['price_total'] = res['price_subtotal'] = subtotal
|
||
|
# In case of multi currency, round before it's use for computing debit credit
|
||
|
if currency:
|
||
|
res = {k: currency.round(v) for k, v in res.items()}
|
||
|
return res
|
||
|
|
||
|
def _get_price_total_and_subtotal(self, price_unit=None, quantity=None, discount=None, currency=None,
|
||
|
product=None,
|
||
|
partner=None, taxes=None, move_type=None):
|
||
|
self.ensure_one()
|
||
|
return self._get_price_total_and_subtotal_model(
|
||
|
price_unit=price_unit or self.price_unit,
|
||
|
quantity=quantity or self.quantity,
|
||
|
discount=discount or self.discount,
|
||
|
currency=currency or self.currency_id,
|
||
|
product=product or self.product_id,
|
||
|
partner=partner or self.partner_id,
|
||
|
taxes=taxes or self.tax_ids,
|
||
|
move_type=move_type or self.move_id.move_type,
|
||
|
)
|
||
|
|
||
|
def _get_fields_onchange_subtotal(self, price_subtotal=None, move_type=None, currency=None, company=None,
|
||
|
date=None):
|
||
|
self.ensure_one()
|
||
|
return self._get_fields_onchange_subtotal_model(
|
||
|
price_subtotal=price_subtotal or self.price_subtotal,
|
||
|
move_type=move_type or self.move_id.move_type,
|
||
|
currency=currency or self.currency_id,
|
||
|
company=company or self.move_id.company_id,
|
||
|
date=date or self.move_id.date,
|
||
|
)
|
||
|
|
||
|
@api.model
|
||
|
def _get_fields_onchange_subtotal_model(self, price_subtotal, move_type, currency, company, date):
|
||
|
''' This method is used to recompute the values of 'amount_currency', 'debit', 'credit' due to a change made
|
||
|
in some business fields (affecting the 'price_subtotal' field).
|
||
|
|
||
|
:param price_subtotal: The untaxed amount.
|
||
|
:param move_type: The type of the move.
|
||
|
:param currency: The line's currency.
|
||
|
:param company: The move's company.
|
||
|
:param date: The move's date.
|
||
|
:return: A dictionary containing 'debit', 'credit', 'amount_currency'.
|
||
|
'''
|
||
|
if move_type in self.move_id.get_outbound_types():
|
||
|
sign = 1
|
||
|
elif move_type in self.move_id.get_inbound_types():
|
||
|
sign = -1
|
||
|
else:
|
||
|
sign = 1
|
||
|
|
||
|
amount_currency = price_subtotal * sign
|
||
|
balance = currency._convert(amount_currency, company.currency_id, company,
|
||
|
date or fields.Date.context_today(self))
|
||
|
return {
|
||
|
'amount_currency': amount_currency,
|
||
|
'currency_id': currency.id,
|
||
|
'debit': balance > 0.0 and balance or 0.0,
|
||
|
'credit': balance < 0.0 and -balance or 0.0,
|
||
|
}
|
||
|
|
||
|
def _get_computed_price_unit(self):
|
||
|
self.ensure_one()
|
||
|
|
||
|
if not self.product_id:
|
||
|
return self.price_unit
|
||
|
elif self.move_id.is_sale_document(include_receipts=True):
|
||
|
# Out invoice.
|
||
|
price_unit = self.product_id.lst_price
|
||
|
elif self.move_id.is_purchase_document(include_receipts=True):
|
||
|
# In invoice.
|
||
|
price_unit = self.product_id.standard_price
|
||
|
else:
|
||
|
return self.price_unit
|
||
|
|
||
|
if self.product_uom_id != self.product_id.uom_id:
|
||
|
price_unit = self.product_id.uom_id._compute_price(price_unit, self.product_uom_id)
|
||
|
|
||
|
return price_unit
|
||
|
|
||
|
@api.onchange('product_uom_id')
|
||
|
def _onchange_uom_id(self):
|
||
|
''' Recompute the 'price_unit' depending of the unit of measure. '''
|
||
|
price_unit = self._get_computed_price_unit()
|
||
|
|
||
|
# See '_onchange_product_id' for details.
|
||
|
taxes = self._get_computed_taxes()
|
||
|
if taxes and self.move_id.fiscal_position_id:
|
||
|
price_subtotal = self._get_price_total_and_subtotal(price_unit=price_unit, taxes=taxes)[
|
||
|
'price_subtotal']
|
||
|
accounting_vals = self._get_fields_onchange_subtotal(price_subtotal=price_subtotal,
|
||
|
currency=self.move_id.company_currency_id)
|
||
|
amount_currency = accounting_vals['amount_currency']
|
||
|
price_unit = self._get_fields_onchange_balance(amount_currency=amount_currency,
|
||
|
force_computation=True).get('price_unit', price_unit)
|
||
|
|
||
|
# Convert the unit price to the invoice's currency.
|
||
|
company = self.move_id.company_id
|
||
|
self.price_unit = company.currency_id._convert(price_unit, self.move_id.currency_id, company,
|
||
|
self.move_id.date, round=False)
|
||
|
|
||
|
@api.onchange('account_id')
|
||
|
def _onchange_account_id(self):
|
||
|
''' Recompute 'tax_ids' based on 'account_id'.
|
||
|
/!\ Don't remove existing taxes if there is no explicit taxes set on the account.
|
||
|
'''
|
||
|
if not self.display_type and (self.account_id.tax_ids or not self.tax_ids):
|
||
|
taxes = self._get_computed_taxes()
|
||
|
|
||
|
if taxes and self.move_id.fiscal_position_id:
|
||
|
taxes = self.move_id.fiscal_position_id.map_tax(taxes, partner=self.partner_id)
|
||
|
|
||
|
self.tax_ids = taxes
|
||
|
|
||
|
def _onchange_balance(self):
|
||
|
for line in self:
|
||
|
if line.currency_id == line.move_id.company_id.currency_id:
|
||
|
line.amount_currency = line.balance
|
||
|
else:
|
||
|
continue
|
||
|
if not line.move_id.is_invoice(include_receipts=True):
|
||
|
continue
|
||
|
line.update(line._get_fields_onchange_balance())
|
||
|
|
||
|
@api.onchange('debit')
|
||
|
def _onchange_debit(self):
|
||
|
if self.debit:
|
||
|
self.credit = 0.0
|
||
|
self._onchange_balance()
|
||
|
|
||
|
@api.onchange('credit')
|
||
|
def _onchange_credit(self):
|
||
|
if self.credit:
|
||
|
self.debit = 0.0
|
||
|
self._onchange_balance()
|
||
|
|
||
|
@api.onchange('amount_currency')
|
||
|
def _onchange_amount_currency(self):
|
||
|
for line in self:
|
||
|
company = line.move_id.company_id
|
||
|
balance = line.currency_id._convert(line.amount_currency, company.currency_id, company,
|
||
|
line.move_id.date)
|
||
|
line.debit = balance if balance > 0.0 else 0.0
|
||
|
line.credit = -balance if balance < 0.0 else 0.0
|
||
|
|
||
|
if not line.move_id.is_invoice(include_receipts=True):
|
||
|
continue
|
||
|
|
||
|
line.update(line._get_fields_onchange_balance())
|
||
|
line.update(line._get_price_total_and_subtotal())
|
||
|
|
||
|
@api.onchange('quantity', 'discount', 'price_unit', 'tax_ids')
|
||
|
def _onchange_price_subtotal(self):
|
||
|
for line in self:
|
||
|
if not line.move_id.is_invoice(include_receipts=True):
|
||
|
continue
|
||
|
|
||
|
line.update(line._get_price_total_and_subtotal())
|
||
|
line.update(line._get_fields_onchange_subtotal())
|
||
|
|
||
|
@api.onchange('currency_id')
|
||
|
def _onchange_currency(self):
|
||
|
for line in self:
|
||
|
company = line.move_id.company_id
|
||
|
|
||
|
if line.move_id.is_invoice(include_receipts=True):
|
||
|
line._onchange_price_subtotal()
|
||
|
elif not line.move_id.reversed_entry_id:
|
||
|
balance = line.currency_id._convert(line.amount_currency, company.currency_id, company,
|
||
|
line.move_id.date or fields.Date.context_today(line))
|
||
|
line.debit = balance if balance > 0.0 else 0.0
|
||
|
line.credit = -balance if balance < 0.0 else 0.0
|
||
|
|
||
|
def create_analytic_lines(self):
|
||
|
""" Create analytic items upon validation of an account.move.line having an analytic account or an analytic distribution.
|
||
|
"""
|
||
|
lines_to_create_analytic_entries = self.env['account.move.line']
|
||
|
analytic_line_vals = []
|
||
|
for obj_line in self:
|
||
|
for tag in obj_line.analytic_tag_ids.filtered('active_analytic_distribution'):
|
||
|
for distribution in tag.analytic_distribution_ids:
|
||
|
analytic_line_vals.append(obj_line._prepare_analytic_distribution_line(distribution))
|
||
|
if obj_line.analytic_account_id:
|
||
|
lines_to_create_analytic_entries |= obj_line
|
||
|
|
||
|
# create analytic entries in batch
|
||
|
if lines_to_create_analytic_entries:
|
||
|
analytic_line_vals += lines_to_create_analytic_entries._prepare_analytic_line()
|
||
|
|
||
|
self.env['account.analytic.line'].create(analytic_line_vals)
|
||
|
|
||
|
@api.depends('product_id', 'account_id', 'partner_id', 'date')
|
||
|
def _compute_analytic_account(self):
|
||
|
for record in self:
|
||
|
if not record.exclude_from_invoice_tab or not record.move_id.is_invoice(include_receipts=True):
|
||
|
rec = self.env['account.analytic.default'].account_get(
|
||
|
product_id=record.product_id.id,
|
||
|
partner_id=record.partner_id.commercial_partner_id.id or record.move_id.partner_id.commercial_partner_id.id,
|
||
|
account_id=record.account_id.id,
|
||
|
user_id=record.env.uid,
|
||
|
date=record.date,
|
||
|
company_id=record.move_id.company_id.id
|
||
|
)
|
||
|
if rec:
|
||
|
record.analytic_account_id = rec.analytic_id
|
||
|
record.analytic_tag_ids = rec.analytic_tag_ids
|
||
|
|
||
|
@api.model
|
||
|
def _get_suspense_moves_domain(self):
|
||
|
return [
|
||
|
('move_id.to_check', '=', True),
|
||
|
('full_reconcile_id', '=', False),
|
||
|
('statement_line_id', '!=', False),
|
||
|
]
|
||
|
|