Codice sorgente per gestore_spese.domain.entities.spesa

"""Entità di dominio Spesa."""

from __future__ import annotations

from datetime import datetime

from gestore_spese.domain.entities.abstract_spesa import AbstractSpesa


[documenti] class Spesa(AbstractSpesa): """Rappresenta una spesa, implementando il contratto :class:`AbstractSpesa`. Le regole di business sono applicate nei setter: una spesa non può avere una data assente, una descrizione troppo corta o un importo non positivo. """ def __init__(self, data: datetime, descrizione: str, importo: float) -> None: self.data = data self.descrizione = descrizione self.importo = importo @property def data(self) -> datetime: """Ritorna la data della spesa. :return: data della spesa :rtype: datetime """ return self._data @data.setter def data(self, data: datetime) -> None: """Imposta la data della spesa. :param datetime data: nuova data della spesa :raises TypeError: se la data è None o non è di tipo datetime """ if data is None or not isinstance(data, datetime): raise TypeError("La data deve essere di tipo datetime e non può essere None") self._data = data @property def descrizione(self) -> str: """Ritorna la descrizione della spesa. :return: descrizione della spesa :rtype: str """ return self._descrizione @descrizione.setter def descrizione(self, descrizione: str) -> None: """Imposta la descrizione della spesa. :param str descrizione: nuova descrizione della spesa :raises TypeError: se la descrizione non è di tipo str :raises ValueError: se la descrizione ha meno di 3 caratteri non-spazio """ if descrizione is None or not isinstance(descrizione, str): raise TypeError("La descrizione deve essere di tipo str") if len(descrizione.strip()) < 3: raise ValueError("La descrizione deve avere minimo 3 caratteri che non siano spazi") self._descrizione = descrizione @property def importo(self) -> float: """Ritorna l'importo della spesa. :return: importo della spesa :rtype: float """ return self._importo @importo.setter def importo(self, importo: float) -> None: """Imposta l'importo della spesa. :param float importo: nuovo importo della spesa :raises TypeError: se l'importo non è di tipo numerico :raises ValueError: se l'importo è minore o uguale a 0 """ if importo is None or not isinstance(importo, (int, float)): raise TypeError("L'importo deve essere di tipo numerico") if importo <= 0: raise ValueError("L'importo deve essere maggiore di 0") self._importo = float(importo) def __repr__(self) -> str: """Rappresentazione tecnica della spesa.""" return f"Spesa(data={self.data},descrizione='{self.descrizione}',importo={self.importo})" def __str__(self) -> str: """Rappresentazione leggibile della spesa.""" return ( f"Data: {datetime.strftime(self._data, '%d/%m/%Y')}, " f"Descrizione: '{self.descrizione}', Importo: {self.importo}" ) def __eq__(self, other: object) -> bool: """Due spese sono uguali se coincidono data, descrizione e importo. :return: True se uguali, altrimenti False :rtype: bool """ if not isinstance(other, AbstractSpesa): return NotImplemented return (self.data, self.descrizione, self.importo) == ( other.data, other.descrizione, other.importo, ) def __hash__(self) -> int: """Hash coerente con :meth:`__eq__` (sui campi della spesa). :return: hash dell'istanza :rtype: int """ return hash((self.data, self.descrizione, self.importo))