# plugins/exchange/models.py from datetime import datetime from sqlalchemy import ( Column, Integer, ForeignKey, Enum, DateTime, Numeric, Text, String, JSON, Boolean ) from app.extensions import db # adjust to your project’s extension import path class Listing(db.Model): __tablename__ = 'exchange_listing' id = Column(Integer, primary_key=True) plant_id = Column(Integer, ForeignKey('plants.id'), nullable=False) # ownership actors (exactly one should be non-null) owner_user_id = Column(Integer, ForeignKey('users.id'), nullable=True) owner_vendor_id = Column(Integer, ForeignKey('vendor_vendor.id'), nullable=True) owner_group_id = Column(Integer, ForeignKey('groups_group.id'), nullable=True) # listing types type = Column( Enum( 'sale','trade','auction','loan','donation', 'request','consignment','swap', name='listing_type' ), nullable=False, default='sale' ) # sale/trade core fields bin_price = Column(Numeric, nullable=True) # “Buy It Now” price allow_offers = Column(Boolean, default=True) # allow buyer offers # auction-specific auction_start = Column(Numeric, nullable=True) # e.g. 0.99 auction_reserve = Column(Numeric, nullable=True) # e.g. 50.00 auction_ends_at = Column(DateTime, nullable=True) # trade-specific trade_items = Column(JSON, nullable=True) # list of {plant_id, qty, notes} # loan-specific loan_return_date = Column(DateTime, nullable=True) rental_rate = Column(Numeric, nullable=True) # consignment-specific consignment_split= Column(Numeric, nullable=True) # fraction taken as fee (e.g. 0.10) # swap/batch swap_batch_id = Column(Integer, ForeignKey('exchange_swapbatch.id'), nullable=True) status = Column( Enum('active','inactive', name='listing_status'), default='active' ) created_at = Column(DateTime, default=datetime.utcnow) class SwapBatch(db.Model): __tablename__ = 'exchange_swapbatch' id = Column(Integer, primary_key=True) name = Column(String(128), nullable=False) description = Column(Text, nullable=True) date = Column(DateTime, nullable=False) created_at = Column(DateTime, default=datetime.utcnow) class Offer(db.Model): __tablename__ = 'exchange_offer' id = Column(Integer, primary_key=True) listing_id = Column(Integer, ForeignKey('exchange_listing.id'), nullable=False) # actor making the offer offerer_user_id = Column(Integer, ForeignKey('users.id'), nullable=True) offerer_vendor_id = Column(Integer, ForeignKey('vendor_vendor.id'), nullable=True) offerer_group_id = Column(Integer, ForeignKey('groups_group.id'), nullable=True) amount = Column(Numeric, nullable=False) notes = Column(Text, nullable=True) status = Column( Enum('pending','accepted','rejected', name='offer_status'), default='pending' ) created_at = Column(DateTime, default=datetime.utcnow) responded_at = Column(DateTime, nullable=True) class Ownership(db.Model): __tablename__ = 'exchange_ownership' id = Column(Integer, primary_key=True) listing_id = Column(Integer, ForeignKey('exchange_listing.id'), nullable=False) # snapshot actor owner_user_id = Column(Integer, ForeignKey('users.id'), nullable=True) owner_vendor_id = Column(Integer, ForeignKey('vendor_vendor.id'), nullable=True) owner_group_id = Column(Integer, ForeignKey('groups_group.id'), nullable=True) acquired_on = Column(DateTime, nullable=False) price_paid = Column(Numeric, nullable=False) source_notes = Column(Text, nullable=True) class OwnershipTransfer(db.Model): __tablename__ = 'exchange_ownership_transfer' id = Column(Integer, primary_key=True) listing_id = Column(Integer, ForeignKey('exchange_listing.id'), nullable=False) # from actor from_user_id = Column(Integer, ForeignKey('users.id'), nullable=True) from_vendor_id = Column(Integer, ForeignKey('vendor_vendor.id'), nullable=True) from_group_id = Column(Integer, ForeignKey('groups_group.id'), nullable=True) # to actor to_user_id = Column(Integer, ForeignKey('users.id'), nullable=True) to_vendor_id = Column(Integer, ForeignKey('vendor_vendor.id'), nullable=True) to_group_id = Column(Integer, ForeignKey('groups_group.id'), nullable=True) status = Column( Enum('pending','approved','rejected','returned', name='transfer_status'), default='pending' ) requested_at = Column(DateTime, default=datetime.utcnow) decided_at = Column(DateTime, nullable=True) class PriceSubmission(db.Model): __tablename__ = 'exchange_price_submission' id = Column(Integer, primary_key=True) taxon_id = Column(Integer, ForeignKey('botanica_taxon.id'), nullable=False) listing_id = Column(Integer, ForeignKey('exchange_listing.id'), nullable=True) submitter_user_id = Column(Integer, ForeignKey('users.id'), nullable=False) source_platform = Column(String(64), nullable=False) source_vendor_name = Column(String(128), nullable=True) original_price = Column(Numeric, nullable=True) paid_price = Column(Numeric, nullable=False) shipping_price = Column(Numeric, nullable=True) tax_amount = Column(Numeric, nullable=True) notes = Column(Text, nullable=True) submitted_at = Column(DateTime, default=datetime.utcnow) class UserRating(db.Model): __tablename__ = 'exchange_user_rating' id = Column(Integer, primary_key=True) rater_id = Column(Integer, ForeignKey('users.id'), nullable=False) target_user_id = Column(Integer, ForeignKey('users.id'), nullable=False) score = Column(Integer, nullable=False) notes = Column(Text, nullable=True) rated_at = Column(DateTime, default=datetime.utcnow)