Programming
Programming tutorials for people who want code that actually runs, not code that only looks right. Pick a language, start at the beginning, and don't skip the basics. They are where most problems come from.
If a class mostly holds data, @dataclass writes __init__, a readable printout and equality for you. Use field(default_factory=list) for lists, __post_init__ for checks and frozen=True for values that must not change.
Getters compute a value when you read it, so totals never go stale. Setters check a value when you assign it, so a negative price throws instead of saving. With private fields, nobody can go around them.
Stop checking error messages with includes(). Extend Error, set name, add the fields the handler needs, catch with instanceof, and wrap low-level errors with cause so the real reason isn't lost.
Run the same class with the private keyword and with a hash field, and compare the output, the errors and what outside code can still reach. After this, you can pick one and say why.