from __future__ import annotations from dataclasses import dataclass, field, replace from decimal import Decimal from domain.framework.entity import Entity from domain.marketplace.classified_ad_id import ClassifiedAdId from domain.marketplace.classified_ad_state import ClassifiedAdState from domain.marketplace.classified_ad_text import ClassifiedAdText from domain.marketplace.classified_ad_title import ClassifiedAdTitle from domain.marketplace.domain_event import ( ClassifiedAdCreated, ClassifiedAdPriceUpdated, ClassifiedAdSentForReview, ClassifiedAdTextUpdated, ClassifiedAdTitleChanged, DomainEvent, ) from domain.marketplace.invalid_entity_state_exception import ( InvalidEntityStateException, ) from domain.marketplace.price import Price from domain.marketplace.user_id import UserId @dataclass(frozen=True) class ClassifiedAd(Entity): id: ClassifiedAdId _ownerId: UserId title: ClassifiedAdTitle = field(default_factory=lambda: ClassifiedAdTitle(title="")) text: ClassifiedAdText = field(default_factory=lambda: ClassifiedAdText(text="")) price: Price = field(default_factory=lambda: Price(amount=Decimal("0.00"))) state: ClassifiedAdState = ClassifiedAdState.Inactive _events: tuple[DomainEvent, ...] = () def __post_init__(self): if not self._events: object.__setattr__(self, '_events', ( ClassifiedAdCreated(id=self.id.value, owner_id=self._ownerId.value), )) def _ensure_valid_state(self): errors = [] if not self.title.title: errors.append("title") if not self.text.text: errors.append("text") if self.price.amount <= 0: errors.append("price") if errors: raise InvalidEntityStateException( f"Cannot publish classified ad with missing or invalid: {', '.join(errors)}" ) def request_to_publish(self): self._ensure_valid_state() updated = replace(self, state=ClassifiedAdState.PendingReview) return updated.raise_event( ClassifiedAdSentForReview(id=self.id.value) ) def set_title(self, title: ClassifiedAdTitle) -> ClassifiedAd: updated = replace(self, title=title) updated._ensure_valid_state() return updated.raise_event( ClassifiedAdTitleChanged(id=self.id.value, title=title.title) ) def update_text(self, text: ClassifiedAdText) -> ClassifiedAd: updated = replace(self, text=text) updated._ensure_valid_state() return updated.raise_event( ClassifiedAdTextUpdated(id=self.id.value, ad_text=text.text) ) def update_price(self, price: Price) -> ClassifiedAd: updated = replace(self, price=price) updated._ensure_valid_state() return updated.raise_event( ClassifiedAdPriceUpdated(id=self.id.value, price=price.amount) )