Skip to content

Commit

Permalink
Merge pull request #163 from sdementen/fix-validation-deleted-objects
Browse files Browse the repository at this point in the history
Fix validation deleted objects
  • Loading branch information
sdementen committed Apr 4, 2021
2 parents 88cc6da + fcba945 commit b67e4dc
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 118 deletions.
22 changes: 9 additions & 13 deletions piecash/core/book.py
@@ -1,8 +1,7 @@
import warnings
from collections import defaultdict
from operator import attrgetter

from sqlalchemy import Column, VARCHAR, ForeignKey
from sqlalchemy import Column, VARCHAR, ForeignKey, inspect
from sqlalchemy.orm import relation, aliased, joinedload
from sqlalchemy.orm.base import instance_state
from sqlalchemy.orm.exc import NoResultFound
Expand Down Expand Up @@ -159,11 +158,6 @@ def default_currency(self, value):

self.root_account.commodity = value

@property
def book(self):
warnings.warn("deprecated", DeprecationWarning)
return self

def validate(self):
Book.validate_book(self.session)

Expand All @@ -174,9 +168,10 @@ def track_dirty(session, flush_context, instances):
"""
for change, l in {"dirty": session.dirty,
"new": session.new,
"deleted": session.deleted}.items():
"deleted": session.deleted
}.items():
for obj in l:
# retrieve the dictionnary of changes for the given obj
# retrieve the dictionary of changes for the given obj
attrs = session._all_changes.setdefault(id(obj), {})
# add the change of state to the list of state changes
attrs.setdefault("STATE_CHANGES", []).append(change)
Expand All @@ -196,18 +191,19 @@ def validate_book(session):

# iterate on all explicitly changes objects to see
# if we need to add other objects for check
# skip deleted objects
for attrs in session._all_changes.values():
obj = attrs["OBJECT"]
for o_to_validate in obj.object_to_validate(attrs["STATE_CHANGES"]):
txs.add(o_to_validate)
if not inspect(o_to_validate).deleted:
txs.add(o_to_validate)

assert None not in txs, "No object should return None to validate. fix the code"

# sort object from local to global (ensure Split checked before Transaction)
# sort object from local to global (ensure Split checked before Transaction) and remove deleted objects
from . import Account, Transaction, Split, Commodity
sort_order = defaultdict(lambda: 20, {Account: 10, Transaction: 5, Split: 3, Commodity: 2})
txs = list(txs)
txs.sort(key=lambda x: sort_order[type(x)])
txs = sorted(txs, key=lambda x: sort_order[type(x)])

# for each object, validate it
for tx in txs:
Expand Down
16 changes: 13 additions & 3 deletions piecash/core/transaction.py
Expand Up @@ -65,7 +65,7 @@ class Split(DeclarativeBaseGuid):
# relation definitions
account = relation('Account', back_populates='splits')
lot = relation('Lot', back_populates='splits')
transaction = relation('Transaction', back_populates='splits')
transaction = relation('Transaction', back_populates='splits', cascade="refresh-expire")

@property
def is_credit(self):
Expand Down Expand Up @@ -144,7 +144,7 @@ def validate(self):

if self.transaction.currency == self.account.commodity:
if self.quantity != self.value:
raise GncValidationError("The split has a quantity diffeerent from value "
raise GncValidationError("The split has a quantity different from value "
"while the transaction currency and the account commodity is the same")
else:
if self.quantity is None:
Expand Down Expand Up @@ -184,6 +184,15 @@ def validate(self):
self.action = "Sell" if self.quantity.is_signed() else "Buy"


# @event.listens_for(Split.transaction, "set")
# def set_item(obj, value, previous, initiator):
# print("hello",obj,value,previous,initiator)
# if obj.transaction is not None:
# previous = None if previous == attributes.NO_VALUE else previous
# print(obj.transaction.splits)
# # obj.transaction.splits.append([value] = obj
# # obj.transaction.splits.pop(previous)

class Transaction(DeclarativeBaseGuid):
"""
A GnuCash Transaction.
Expand Down Expand Up @@ -290,7 +299,8 @@ def validate(self):
if value_imbalance:
# raise exception instead of creating an imbalance entry as probably an error
# (in the gnucash GUI, another decision taken because need of "save unfinished transaction")
raise GncImbalanceError("The transaction {} is not balanced on its value".format(self))
raise GncImbalanceError(
"The transaction {} is not balanced on its value (delta={})".format(self, value_imbalance))

if any(quantity_imbalances.values()) and self.book.use_trading_accounts:
self.normalize_trading_accounts()
Expand Down
4 changes: 2 additions & 2 deletions tests/test_integration.py
Expand Up @@ -108,13 +108,13 @@ def test_slots_strings_access(self, book):
b = book

b["a/b/c/d/e"] = 1
book.book.flush()
book.flush()
assert b["a"]["b"]["c"]["d"]["e"].value == 1

b["a/b/c"] = {"d": {"t": "ok"}}

b["a/b/c/d/f"] = "2"
book.book.flush()
book.flush()
assert len(b["a"]["b"]["c"]["d"].slots) == 2

b["a/b/c/d/f"] = "5"
Expand Down

0 comments on commit b67e4dc

Please sign in to comment.