Closed ChrisCScott closed 2 years ago
On reflection, it's not clear that there's a good reason for using Decimal or Money at all in the base Ledger
-derived classes. These could simply manipulate integer or floating-point values and leave it to client code to convert these to suitable Money types for display purposes. This would remove a considerable amount of complexity from the code - and it would probably help resolve issue #76 too.
Closed by #78.
Various classes (among them
Account
andPerson
) cast their inputs to eitherDecimal
orMoney
. None of the functionality of these base classes relies on the use of these types; it's assumed that the client code will want to use them (e.g. for localized display of currencies).Rather than make this assumption, we should simply use whatever types are passed by client code. We can replicate the current functionality simply by passing
Decimal
andMoney
objects from client code.This does raise two points of complexity:
Money
to override comparison operators specifically when the operand is a non-Money
0-valuedNumber
. It's a bit of a hack, and not one we'd want to push onto client code.Money(0)
or use it as a default value, without necessarily having first received an instance of a monetary value from client code. This makes it tricky to instantiate new monetary values with the appropriate type.We can address these by storing a
MONEY0
object - a zero-valued object of the appropriate type for monetary values. Consider whether this object should be global, class-specific, or instance-specific. I'm currently leaning toward instance-specific, perhaps by defining a property that returns a zero-valued object of the same type as some reference property. For example:You can imagine extending this. We could define a str-valued class or instance attribute that names the attribute to use for type-matching (e.g.
self._moneyref="balance"
) and invokegetattr
to acquire that attribute dynamically.We could move this logic into a class and define some suitable methods (e.g.
_setmoneytype
could implement the two repeated lines above settingself._money*
attributes) to make it reusable, although if it provides an__init__
method then we may need to consider whether init order and multiple inheritance make this a headache - do we need to callsuper().__init__
fromLedger
? Do we need to assignself.balance = balance
before callingsuper().__init__
? (Would that break someLedger
code, given thatbalance
is arecorded_property
that requires some setup?) Another option is that it could lack an__init__
method and we could simply call_setmoneytype
at the end of the inheriting object's__init__
method (or after setting the reference attribute and before calling any methods that need_MONEY0
to be defined). A further option would be to assign to the underlying_money*
attributes in the class's__init__
using some default type (e.g.float
) so that a sensible_MONEY0
object is available at all stages of__init__
, though this may make bugs harder to track down.