cashbook/valuestore.py
2023-12-27 15:49:02 +01:00

207 lines
6.7 KiB
Python

# -*- coding: utf-8 -*-
# This file is part of the cashbook-module from m-ds for Tryton.
# The COPYRIGHT file at the top level of this repository contains the
# full copyright notices and license terms.
from sql.functions import CurrentTimestamp, DateTrunc
from sql.aggregate import Count
from sql.conditionals import Coalesce
from trytond.model import ModelSQL, fields, Unique, Index
from trytond.pool import Pool
from trytond.transaction import Transaction
from trytond.pyson import Eval, PYSON, PYSONEncoder, PYSONDecoder
from trytond.model.modelstorage import EvalEnvironment
from trytond.report import Report
class ValueStore(ModelSQL):
'Value Store'
__name__ = 'cashbook.values'
cashbook = fields.Many2One(
string='Cashbook', required=True, model_name='cashbook.book',
ondelete='CASCADE')
field_name = fields.Char(string='Field Name', required=True)
numvalue = fields.Numeric(
string='Value', digits=(16, Eval('valuedigits', 6)),
depends=['valuedigits'])
valuedigits = fields.Function(fields.Integer(
string='Digits', readonly=True),
'on_change_with_valuedigits')
@classmethod
def __setup__(cls):
super(ValueStore, cls).__setup__()
t = cls.__table__()
cls._sql_constraints.extend([
('uniqu_field',
Unique(t, t.cashbook, t.field_name),
'cashbook.msg_value_exists_in_store'),
])
cls._sql_indexes.update({
Index(
t,
(t.field_name, Index.Equality())),
})
def get_rec_name(self, name):
""" name, balance, state
"""
return '|'.join([
'[' + getattr(self.cashbook, 'rec_name', '-') + ']',
self.field_name or '-',
Report.format_number(
self.numvalue,
None,
digits=self.valuedigits or 2)
if self.numvalue is not None else '-',
str(self.valuedigits) if self.valuedigits is not None else '-'])
@fields.depends('cashbook', 'field_name')
def on_change_with_valuedigits(self, name=None):
""" get digits by field name
"""
Cashbook = Pool().get('cashbook.book')
if self.cashbook and self.field_name:
fieldobj = Cashbook._fields.get(self.field_name, None)
if fieldobj:
digit = getattr(fieldobj, 'digits', (16, 6))[1]
if isinstance(digit, PYSON):
# execute pyson on linked record
digit = PYSONDecoder(
EvalEnvironment(self.cashbook, Cashbook)
).decode(PYSONEncoder().encode(digit))
return digit
return 6
@classmethod
def _maintenance_fields(cls):
""" list of model and fieldnames,
to daily update
"""
return ['balance']
@classmethod
def maintenance_values(cls):
""" update values by cron
"""
pool = Pool()
Cashbook = pool.get('cashbook.book')
tab_val = cls.__table__()
tab_book = Cashbook.__table__()
cursor = Transaction().connection.cursor()
context = Transaction().context
# select records older than 'check_dt'
check_dt = context.get('maintenance_date', None)
if not check_dt:
check_dt = DateTrunc('day', CurrentTimestamp())
# select records to update
query = tab_val.select(
tab_val.id,
where=tab_val.field_name.in_(cls._maintenance_fields()) &
(DateTrunc('day', Coalesce(
tab_val.write_date,
tab_val.create_date,
'1990-01-01 00:00:00')) < check_dt))
cursor.execute(*query)
records = []
for x in cursor.fetchall():
cb = cls(x[0]).cashbook
if cb not in records:
records.append(cb)
# add records with missing fields in value-store
num_fields = len(Cashbook.valuestore_fields())
query = tab_book.join(
tab_val,
condition=tab_book.id == tab_val.cashbook,
type_='LEFT OUTER',
).select(
tab_book.id,
Count(tab_val.id).as_('num'),
group_by=[tab_book.id],
having=Count(tab_val.id) < num_fields)
cursor.execute(*query)
records.extend([Cashbook(x[0]) for x in cursor.fetchall()])
if records:
Cashbook.valuestore_update_records([x for x in records])
@classmethod
def get_book_by_line(cls, records):
""" select books above current record to update
records: cashbook.line
"""
Book = Pool().get('cashbook.book')
to_update = []
if records:
books = Book.search([
('parent', 'parent_of', [x.cashbook.id for x in records])
])
to_update.extend([
x for x in books
if x not in to_update])
return to_update
@classmethod
def get_book_by_books(cls, records):
""" select books above current record to update
records: cashbook.book
"""
Book = Pool().get('cashbook.book')
to_update = []
if records:
books = Book.search([
('parent', 'parent_of', [x.id for x in records])
])
to_update.extend([
x for x in books
if x not in to_update])
return to_update
@classmethod
def update_books(cls, books):
""" get cashbooks to update, queue it
"""
Book = Pool().get('cashbook.book')
if books:
Book.__queue__.valuestore_update_records(books)
@classmethod
def update_values(cls, data):
""" data: {'fieldname': {id1: value, id2: value, ...}, ...}
"""
to_write = []
to_create = []
for name in data.keys():
for record_id in data[name].keys():
# get existing record
records = cls.search([
('cashbook', '=', record_id),
('field_name', '=', name)])
if records:
for record in records:
to_write.extend([
[record],
{'numvalue': data[name][record_id]}])
else:
to_create.append({
'cashbook': record_id,
'field_name': name,
'numvalue': data[name][record_id]})
if to_create:
cls.create(to_create)
if to_write:
cls.write(*to_write)
# end ValueStore