optimize searcher/sort, add search/sort to balance_ref

This commit is contained in:
Frederik Jaeckel 2023-12-29 14:51:39 +01:00
parent 5d8f924960
commit 5ef3a52fdc
6 changed files with 272 additions and 21 deletions

78
book.py
View file

@ -139,7 +139,7 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
states={ states={
'invisible': ~Bool(Eval('company_currency')), 'invisible': ~Bool(Eval('company_currency')),
}, depends=['company_currency_digits', 'company_currency']), }, depends=['company_currency_digits', 'company_currency']),
'get_balance_cashbook') 'get_balance_cashbook', searcher='search_balance')
company_currency = fields.Function(fields.Many2One( company_currency = fields.Function(fields.Many2One(
readonly=True, readonly=True,
string='Company Currency', states={'invisible': True}, string='Company Currency', states={'invisible': True},
@ -346,44 +346,92 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
) )
return (query, tab_line) return (query, tab_line)
@classmethod
def work_order_balance(cls, tables, field_name):
""" get order-query
"""
pool = Pool()
Book2 = pool.get('cashbook.book')
ValueStore = pool.get('cashbook.values')
context = Transaction().context
query_date = context.get('date', None)
table, _ = tables[None]
if query_date is not None:
if field_name == 'balance_ref':
raise UserError(gettext(
'cashbook.msg_nosearch_with_date',
fname=field_name, model=Book2.__name__))
(tab_book, tab2) = Book2.get_balance_of_cashbook_sql()
query = tab_book.select(
getattr(tab_book, field_name),
where=tab_book.cashbook == table.id)
return [query]
else:
tab_val = ValueStore.__table__()
tab_book = Book2.__table__()
query = tab_book.join(
tab_val,
condition=(
tab_book.id == tab_val.cashbook) & (
tab_val.field_name == field_name),
).select(
tab_val.numvalue,
where=tab_book.id == table.id)
return [query]
@staticmethod @staticmethod
def order_balance(tables): def order_balance(tables):
""" order by balance """ order by balance
""" """
Book2 = Pool().get('cashbook.book') Book2 = Pool().get('cashbook.book')
(tab_book, tab2) = Book2.get_balance_of_cashbook_sql() return Book2.work_order_balance(tables, 'balance')
table, _ = tables[None]
query = tab_book.select(
tab_book.balance,
where=tab_book.cashbook == table.id)
return [query]
@staticmethod @staticmethod
def order_balance_all(tables): def order_balance_all(tables):
""" order by balance-all """ order by balance-all
""" """
Book2 = Pool().get('cashbook.book') Book2 = Pool().get('cashbook.book')
(tab_book, tab2) = Book2.get_balance_of_cashbook_sql() return Book2.work_order_balance(tables, 'balance_all')
table, _ = tables[None]
query = tab_book.select( @staticmethod
tab_book.balance_all, def order_balance_ref(tables):
where=tab_book.cashbook == table.id) """ order by balance-all
return [query] """
Book2 = Pool().get('cashbook.book')
return Book2.work_order_balance(tables, 'balance_ref')
@classmethod @classmethod
def search_balance(cls, name, clause): def search_balance(cls, name, clause):
""" search in 'balance' """ search in 'balance'
""" """
(tab_line, tab2) = cls.get_balance_of_cashbook_sql() ValueStore = Pool().get('cashbook.values')
Operator = fields.SQL_OPERATORS[clause[1]] Operator = fields.SQL_OPERATORS[clause[1]]
context = Transaction().context
query_date = context.get('date', None)
if query_date is not None:
if name == 'balance_ref':
raise UserError(gettext(
'cashbook.msg_nosearch_with_date',
fname=name, model=cls.__name__))
(tab_line, tab2) = cls.get_balance_of_cashbook_sql()
query = tab_line.select( query = tab_line.select(
tab_line.cashbook, tab_line.cashbook,
where=Operator( where=Operator(
getattr(tab_line, name), clause[2])) getattr(tab_line, name), clause[2]))
return [('id', 'in', query)] return [('id', 'in', query)]
else:
value_query = ValueStore.search([
('field_name', '=', clause[0]),
('numvalue',) + tuple(clause[1:]),
],
query=True)
return [('value_store', 'in', value_query)]
@classmethod @classmethod
def valuestore_delete_records(cls, records): def valuestore_delete_records(cls, records):

View file

@ -168,7 +168,11 @@ msgstr "Allgemein"
msgctxt "model:ir.message,text:msg_value_exists_in_store" msgctxt "model:ir.message,text:msg_value_exists_in_store"
msgid "The value already exists for the record." msgid "The value already exists for the record."
msgstr "Der Wert existiert für den Datensatz bereits. " msgstr "Der Wert existiert für den Datensatz bereits."
msgctxt "model:ir.message,text:msg_nosearch_with_date"
msgid "Search with 'date' no allowed for field '%(fname)s' on model '%(model)s'."
msgstr "Suche mit 'date' nicht erlaubt für Feld '%(fname)s' auf Modell '%(model)s'."
############# #############

View file

@ -162,6 +162,10 @@ msgctxt "model:ir.message,text:msg_value_exists_in_store"
msgid "The value already exists for the record." msgid "The value already exists for the record."
msgstr "The value already exists for the record." msgstr "The value already exists for the record."
msgctxt "model:ir.message,text:msg_nosearch_with_date"
msgid "Search with 'date' no allowed for field '%(fname)s' on model '%(model)s'."
msgstr "Search with 'date' no allowed for field '%(fname)s' on model '%(model)s'."
msgctxt "model:res.group,name:group_cashbook" msgctxt "model:res.group,name:group_cashbook"
msgid "Cashbook" msgid "Cashbook"
msgstr "Cashbook" msgstr "Cashbook"

View file

@ -122,6 +122,9 @@ full copyright notices and license terms. -->
<record model="ir.message" id="msg_value_exists_in_store"> <record model="ir.message" id="msg_value_exists_in_store">
<field name="text">The value already exists for the record.</field> <field name="text">The value already exists for the record.</field>
</record> </record>
<record model="ir.message" id="msg_nosearch_with_date">
<field name="text">Search with 'date' no allowed for field '%(fname)s' on model '%(model)s'.</field>
</record>
</data> </data>
</tryton> </tryton>

View file

@ -307,6 +307,10 @@ class BookTestCase(object):
Book.search_count([('balance_all', '<', Decimal('5.0'))]), Book.search_count([('balance_all', '<', Decimal('5.0'))]),
0) 0)
self.assertEqual(
Book.search_count([('balance_ref', '<', Decimal('5.0'))]),
0)
@with_transaction() @with_transaction()
def test_book_deny_btype_set_none(self): def test_book_deny_btype_set_none(self):
""" create cashbook, add lines, """ create cashbook, add lines,

View file

@ -539,6 +539,194 @@ class ValuestoreTestCase(object):
values[8].rec_name, values[8].rec_name,
'[Lev 0/Lev 1b | 0.00 € | Open]|balance_ref|0.00|2') '[Lev 0/Lev 1b | 0.00 € | Open]|balance_ref|0.00|2')
@with_transaction()
def test_valstore_search_sort_books(self):
""" create cashbooks add lines, search/sort
with and w/o 'date' in context
"""
pool = Pool()
Book = pool.get('cashbook.book')
Line = pool.get('cashbook.line')
types = self.prep_type()
company = self.prep_company()
(usd, euro) = self.prep_2nd_currency(company)
self.assertEqual(company.currency.rec_name, 'Euro')
with Transaction().set_context({'company': company.id}):
category = self.prep_category(cattype='in')
party = self.prep_party()
books = Book.create([{
'name': 'Cashbook 1',
'btype': types.id,
'company': company.id,
'currency': usd.id,
'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 5, 1),
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': '10 US$',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('10.0'),
'party': party.id,
}])]}, {
'name': 'Cashbook 2',
'btype': types.id,
'company': company.id,
'currency': euro.id,
'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 5, 1),
}, {
'name': 'Cashbook 3',
'btype': types.id,
'company': company.id,
'currency': euro.id,
'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 5, 1),
}])
self.assertEqual(len(books), 3)
self.assertEqual(books[0].rec_name, 'Cashbook 1 | 10.00 usd | Open')
self.assertEqual(books[1].rec_name, 'Cashbook 2 | 0.00 € | Open')
self.assertEqual(books[2].rec_name, 'Cashbook 3 | 0.00 € | Open')
Line.create([{
'cashbook': books[1].id,
'bookingtype': 'in',
'amount': Decimal('5.0'),
'date': date(2022, 5, 6),
'description': '5€ in',
'category': category.id,
}, {
'cashbook': books[2].id,
'bookingtype': 'in',
'amount': Decimal('6.0'),
'date': date(2022, 5, 7),
'description': '6€ in',
'category': category.id,
}])
# no 'date' in context, using stored values
# workers not yet processed
books = Book.search([], order=[('name', 'ASC')])
self.assertEqual(len(books), 3)
self.assertEqual(books[0].rec_name, 'Cashbook 1 | 10.00 usd | Open')
self.assertEqual(books[1].rec_name, 'Cashbook 2 | 0.00 € | Open')
self.assertEqual(books[2].rec_name, 'Cashbook 3 | 0.00 € | Open')
self.assertEqual(
Book.search_count([('balance', '=', Decimal('0.0'))]),
2)
self.assertEqual(
Book.search_count([('balance_all', '=', Decimal('0.0'))]),
2)
self.assertEqual(
Book.search_count([('balance_ref', '=', Decimal('0.0'))]),
2)
# check sorting - using stored values
books = Book.search([], order=[
('balance_all', 'DESC'), ('name', 'ASC')])
self.assertEqual(len(books), 3)
self.assertEqual(books[0].rec_name, 'Cashbook 1 | 10.00 usd | Open')
self.assertEqual(books[1].rec_name, 'Cashbook 2 | 0.00 € | Open')
self.assertEqual(books[2].rec_name, 'Cashbook 3 | 0.00 € | Open')
# search again with 'date' - using computed values
with Transaction().set_context({'date': date(2022, 5, 6)}):
self.assertEqual(
Book.search_count([('balance', '=', Decimal('5.0'))]),
1)
self.assertEqual(
Book.search_count([
('balance', '>=', Decimal('5.0')),
('balance', '<', Decimal('9.0')),
]),
1)
self.assertEqual(
Book.search_count([
('balance_all', '>=', Decimal('5.0'))]),
3)
self.assertRaisesRegex(
UserError,
"Search with 'date' no allowed for field " +
"'balance_ref' on model 'cashbook.book'.",
Book.search_count,
[('balance_ref', '=', Decimal('0.0'))])
self.assertRaisesRegex(
UserError,
"Search with 'date' no allowed for field " +
"'balance_ref' on model 'cashbook.book'.",
Book.search,
[], order=[('balance_ref', 'ASC')])
# check sorting - using computed values
books = Book.search([], order=[
('balance_all', 'DESC'),
('name', 'ASC'),
('balance', 'ASC')])
self.assertEqual(len(books), 3)
self.assertEqual(
books[0].rec_name, 'Cashbook 1 | 10.00 usd | Open')
self.assertEqual(
books[1].rec_name, 'Cashbook 3 | 0.00 € | Open')
self.assertEqual(books[1].balance_all, Decimal('6.0'))
self.assertEqual(
books[2].rec_name, 'Cashbook 2 | 5.00 € | Open')
with Transaction().set_context({'date': date(2022, 5, 7)}):
self.assertEqual(
Book.search_count([('balance', '=', Decimal('5.0'))]),
1)
self.assertEqual(
Book.search_count([
('balance', '>=', Decimal('5.0')),
('balance', '<', Decimal('9.0')),
]),
2)
# run workers
self.prep_valstore_run_worker()
# check stored values - no 'date' in context
books = Book.search([], order=[('name', 'ASC')])
self.assertEqual(len(books), 3)
self.assertEqual(books[0].rec_name, 'Cashbook 1 | 10.00 usd | Open')
self.assertEqual(books[1].rec_name, 'Cashbook 2 | 5.00 € | Open')
self.assertEqual(books[2].rec_name, 'Cashbook 3 | 6.00 € | Open')
# check sorting - using stored values
# run most sorters
books = Book.search([], order=[
('balance_all', 'DESC'),
('name', 'ASC'),
('balance', 'ASC'),
('balance_ref', 'ASC')])
self.assertEqual(len(books), 3)
self.assertEqual(books[0].rec_name, 'Cashbook 1 | 10.00 usd | Open')
self.assertEqual(books[1].rec_name, 'Cashbook 3 | 6.00 € | Open')
self.assertEqual(books[2].rec_name, 'Cashbook 2 | 5.00 € | Open')
self.assertEqual(
Book.search_count([('balance', '=', Decimal('0.0'))]),
0)
self.assertEqual(
Book.search_count([('balance', '=', Decimal('5.0'))]),
1)
self.assertEqual(
Book.search_count([('balance', '=', Decimal('6.0'))]),
1)
self.assertEqual(
Book.search_count([
('balance', '>=', Decimal('5.0')),
('balance', '<', Decimal('9.0')),
]),
2)
self.assertEqual(
Book.search_count([('balance_ref', '=', Decimal('6.0'))]),
1)
@with_transaction() @with_transaction()
def test_valstore_maintain_values(self): def test_valstore_maintain_values(self):
""" create cashbook, check maintenance - """ create cashbook, check maintenance -