"""Domain events: one per workflow transition, named for what happened. An aggregate records them in
`.events`; nothing publishes them yet — a brick that needs another to react calls its seam.
"""
from dataclasses import dataclass
@dataclass(frozen=True)
class ProductAdded:
"""add_product: product none → draft. The code is fixed from now on."""
product_id: str
by: str = ""
@dataclass(frozen=True)
class DuplicateChecked:
"""ai_check_duplicate: product draft → draft. A likely duplicate is shown; nothing is merged."""
product_id: str
by: str = ""
@dataclass(frozen=True)
class DetailsSuggested:
"""ai_suggest_details: product draft → draft. A suggestion the catalogue manager accepts or changes."""
product_id: str
by: str = ""
@dataclass(frozen=True)
class UnitsSet:
"""set_units: product draft → draft. Purchase, sales and issue units chosen."""
product_id: str
by: str = ""
@dataclass(frozen=True)
class UnitAdded:
"""add_unit: product active → active. The new unit can be bought, sold or issued from now."""
product_id: str
unit_code: str = ""
by: str = ""
@dataclass(frozen=True)
class ProductCompleted:
"""complete_product: product draft → draft."""
product_id: str
by: str = ""
@dataclass(frozen=True)
class ProductActivated:
"""activate_product: product draft → active. Offered to sales."""
product_id: str
by: str = ""
@dataclass(frozen=True)
class ProductRenamed:
"""rename_product: product active → active. Documents already raised keep their copy."""
product_id: str
by: str = ""
@dataclass(frozen=True)
class ProductArchived:
"""archive_product: product active → archived. No new lines; existing documents unchanged."""
product_id: str
by: str = ""
@dataclass(frozen=True)
class ProductReactivated:
"""reactivate_product: product archived → active."""
product_id: str
reason: str = ""
by: str = ""
@dataclass(frozen=True)
class PriceListOpened:
"""open_price_list: price_list none → draft. Based on another list, or standalone."""
price_list_id: str
by: str = ""
@dataclass(frozen=True)
class PricesSet:
"""set_prices: price_list draft → draft. Dated rows; nothing visible to sales yet."""
price_list_id: str
by: str = ""
@dataclass(frozen=True)
class PricesChecked:
"""ai_price_check: price_list draft → draft. Outliers are listed for the pricing manager."""
price_list_id: str
by: str = ""
@dataclass(frozen=True)
class PriceListPublished:
"""publish_price_list: price_list draft → published, or a pending change of a published list goes live."""
price_list_id: str
by: str = ""
@dataclass(frozen=True)
class PricesChanged:
"""change_prices: price_list published → published. New rows with a later valid_from; old rows never edited."""
price_list_id: str
by: str = ""
@dataclass(frozen=True)
class CustomerAssigned:
"""assign_customer: price_list published → published. Their prices in this currency resolve from this list."""
price_list_id: str
bp_id: int = 0
by: str = ""
@dataclass(frozen=True)
class PriceListRetired:
"""retire_price_list: price_list published → retired. Assigned customers fall back to the default list."""
price_list_id: str
by: str = ""
@dataclass(frozen=True)
class ProductTypeChanged:
"""change_product_type: product draft → draft. Kind, stocked default and activation requirements follow the new type."""
product_id: str
product_type_code: str = ""
by: str = ""