class RoleInvite(SoftDeletionModel): __tablename__ = 'role_invites' __table_args__ = (UniqueConstraint('email', 'role_id', 'event_id', name='email_role_event_uc'), ) id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String, nullable=False) role_name = db.Column(db.String, nullable=False) event_id = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='CASCADE')) event = db.relationship('Event', back_populates='role_invites') role_id = db.Column(db.Integer, db.ForeignKey('roles.id', ondelete='CASCADE')) role = db.relationship("Role") hash = db.Column(db.String) created_at = db.Column(db.DateTime(timezone=True), default=func.now()) status = db.Column(db.String, default="pending") def __init__( self, email=None, role_name=None, event_id=None, role_id=None, created_at=None, status="pending", hash=None, deleted_at=None, ): self.email = email self.role_name = role_name self.event_id = event_id self.role_id = role_id self.created_at = created_at self.status = status self.hash = generate_hash() self.deleted_at = deleted_at def has_expired(self): # Check if invitation link has expired (it expires after 24 hours) return datetime.now(pytz.utc) > self.created_at + timedelta(hours=24) def __repr__(self): return '<RoleInvite %r:%r:%r>' % ( self.email, self.event_id, self.role_id, ) def __str__(self): return self.__repr__()
class UserTokenBlackListTime(db.Model): """user token blacklist time model class""" __tablename__ = 'user_token_blacklist_time' __table_args__ = (db.UniqueConstraint('user_id', name='user_blacklist_time_uc'),) id = db.Column(db.Integer, primary_key=True) created_at = db.Column(db.DateTime(timezone=True), default=sql_func.now(), nullable=False) blacklisted_at = db.Column(db.DateTime(timezone=True), default=sql_func.now(), nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False) user = db.relationship("User", backref="token_blacklist_times", foreign_keys=[user_id]) def __init__(self, user_id=None, created_at=None, blacklisted_at=None): self.user_id = user_id self.created_at = created_at self.blacklisted_at = blacklisted_at def __str__(self): return '<TokenBlackListTime User %s blacklisted at %s>' % (self.user, self.blacklisted_at)
class UserTokenBlackListTime(db.Model): """user token blacklist time model class""" __tablename__ = 'user_token_blacklist_time' __table_args__ = (db.UniqueConstraint('user_id', name='user_blacklist_time_uc'),) id: int = db.Column(db.Integer, primary_key=True) created_at: datetime = db.Column( db.DateTime(timezone=True), default=sql_func.now(), nullable=False ) blacklisted_at: datetime = db.Column( db.DateTime(timezone=True), default=sql_func.now(), nullable=False ) user_id: int = db.Column( db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False ) user = db.relationship( "User", backref="token_blacklist_times", foreign_keys=[user_id] )
class VideoRecording(db.Model): "Video Recording" __tablename__ = 'video_recordings' id = db.Column(db.Integer, primary_key=True) bbb_record_id = db.Column(db.String, nullable=True) participants = db.Column(db.Integer, nullable=False) url = db.Column(db.String, nullable=False) start_time = db.Column(db.DateTime(timezone=False), nullable=False) end_time = db.Column(db.DateTime(timezone=False), nullable=False) video_stream_id = db.Column(db.Integer, db.ForeignKey('video_streams.id', ondelete='CASCADE'), nullable=False) video_stream = db.relationship('VideoStream', backref='video_recordings') def __repr__(self): return f'<VideoRecording {self.video_stream.name!r} {self.url!r}>'
class Group(SoftDeletionModel): __tablename__ = 'groups' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String, nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False) social_links = db.Column(db.JSON) logo_url = db.Column(db.String) banner_url = db.Column(db.String) thumbnail_image_url = db.Column(db.String) is_promoted = db.Column(db.Boolean, default=False, nullable=False) about = db.Column(db.Text) created_at: datetime = db.Column(db.DateTime(timezone=True), default=datetime.utcnow) modified_at: datetime = db.Column(db.DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow) @aggregated('followers', db.Column(db.Integer, default=0, server_default='0', nullable=False)) def follower_count(self): return func.count('1') user = db.relationship('User', backref='groups') roles = db.relationship("UsersGroupsRoles", backref="group") @property def follower(self): if not current_user: return None return UserFollowGroup.query.filter_by(user=current_user, group=self).first() @property def view_page_link(self): frontend_url = get_settings()['frontend_url'] return f"{frontend_url}/g/{self.id}"
class Disease(db.Model): __tablename__ = 'diseases' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(255)) link = db.Column(db.String(255)) added = db.Column(db.DateTime()) def __repr__(self): return "<Disease(name='{}', link='{}', added={})>".format( self.name, self.link, self.added)
class Mail(db.Model): __tablename__ = 'mails' id = db.Column(db.Integer, primary_key=True) recipient = db.Column(db.String) time = db.Column(db.DateTime(timezone=True), default=func.now()) action = db.Column(db.String) subject = db.Column(db.String) message = db.Column(db.String) def __repr__(self): return '<Mail %r to %r>' % (self.id, self.recipient)
class EventOrgaModel(SoftDeletionModel): """Event Orga object table""" __tablename__ = 'events_orga' id = db.Column(db.Integer, primary_key=True, autoincrement=True) name = db.Column(db.String, nullable=False) starts_at = db.Column(db.DateTime(timezone=True), default=func.now()) payment_currency = db.Column(db.String, nullable=False) def __repr__(self): return '<EventOrgaModel %r>' % self.name
class Usuario(db.Model, UserMixin): __tablename__ = 'usuarios' sequencial = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(100), unique=True, nullable=False) email = db.Column(db.String(255), unique=True) password = db.Column(db.String(255), nullable=False) last_login = db.Column(db.DateTime()) last_logout = db.Column(db.DateTime()) papel_id = db.Column(db.Integer, db.ForeignKey('papeis.sequencial'), nullable=False) papel = db.relationship('Papel', backref="usuario", lazy=True) @property def is_authenticated(self): return True @property def is_active(self): return True @property def is_anonymous(self): return False def get_id(self): return str(self.sequencial) def get(self): return self def __init__(self): # Usuario() username = self.username email = self.email password = self.password # self.active = active def __repr__(self): return self.username
class AccessCode(SoftDeletionModel): __tablename__ = "access_codes" id: int = db.Column(db.Integer, primary_key=True) code: str = db.Column(db.String) access_url: str = db.Column(db.String) is_active: bool = db.Column(db.Boolean) tickets_number: int = db.Column( db.Integer) # For event level access this holds the max. uses min_quantity: int = db.Column(db.Integer) max_quantity: int = db.Column( db.Integer ) # For event level access this holds the months for which it is valid valid_from: datetime = db.Column(db.DateTime(timezone=True), nullable=True) valid_till: datetime = db.Column(db.DateTime(timezone=True), nullable=True) ticket_id: int = db.Column(db.Integer, db.ForeignKey('tickets.id', ondelete='CASCADE')) event_id: int = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='CASCADE')) created_at: datetime = db.Column(db.DateTime(timezone=True), default=func.now()) marketer_id: int = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE')) marketer = db.relationship('User', backref='access_codes_') ticket = db.relationship('Ticket', backref='access_code', foreign_keys=[ticket_id]) event = db.relationship('Event', backref='access_codes', foreign_keys=[event_id]) @staticmethod def get_service_name(): return 'access_code' @property def valid_expire_time(self): return self.valid_till or self.event.ends_at
class Task(Base): __tablename__ = 'task' id = db.Column(db.Integer(), primary_key=True) project_id = db.Column(db.Integer()) project_name = db.Column(db.String(200)) project_task_id = db.Column(db.Integer()) project_task_name = db.Column(db.String(200)) user_id = db.Column(db.Integer()) date = db.Column(db.Date()) updated = db.Column(db.DateTime(timezone=True)) hour = db.Column(db.Float(scale=12, precision=2)) minute = db.Column(db.Float(scale=6, precision=0)) timesheet_id = db.Column(db.Integer()) cost_center_id = db.Column(db.Integer()) decimal_hours = hour + minute / 60.00 @staticmethod def get_my_tasks(user_email): query = '''SELECT DISTINCT t.id, t.project_id, p.name AS project_name, t.project_task_id, pt.name AS project_task_name, t.user_id, t.date, t.updated, t.hour, t.minute, t.timesheet_id, t.cost_center_id FROM task t INNER JOIN project_task pt ON t.project_task_id = pt.id INNER JOIN project p ON pt.project_id = p.id LEFT JOIN user u ON t.user_id = u.id WHERE u.email = :email OR p.id IN (SELECT DISTINCT p2.id FROM project p2 LEFT JOIN project_task pt2 ON pt2.project_id = p2.id LEFT JOIN project_task_assign pta ON pta.project_task_id = pt2.id LEFT JOIN booking b ON b.project_id = p2.id LEFT JOIN user u ON (p2.user_id = u.id OR pta.user_id = u.id OR b.user_id = u.id) WHERE u.email = :email AND p2.active="1");''' return Task.query.from_statement( text(query)).params(email=user_email).all() def __repr__(self): return '<Task %r>' % self.id
class User(db.Model, UserMixin): id = db.Column(db.Integer, primary_key=True) fullname = db.Column(db.String(255)) email = db.Column(db.String(255), unique=True) password = db.Column(db.String(255)) active = db.Column(db.Boolean()) confirmed_at = db.Column(db.DateTime()) roles = db.relationship('Role', secondary=roles_users, lazy='dynamic', backref=db.backref('users', lazy='dynamic')) def has_role(self, role_name): return self.roles.filter_by(name=role_name).first()
class RoleInvite(db.Model): __tablename__ = 'role_invites' __table_args__ = (UniqueConstraint( 'email', 'role_id', 'event_id', name='email_role_event_uc', ), ) id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String, nullable=False) role_name = db.Column(db.String, nullable=False) event_id = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='CASCADE')) event = db.relationship('Event', back_populates='role_invites') role_id = db.Column(db.Integer, db.ForeignKey('roles.id', ondelete='CASCADE')) role = db.relationship("Role") hash = db.Column(db.String, default=generate_hash) created_at = db.Column(db.DateTime(timezone=True), default=func.now()) status = db.Column(db.String, default="pending") def has_expired(self): # Check if invitation link has expired (it expires after 24 hours) return datetime.now(pytz.utc) > self.created_at + timedelta(hours=24) def send_invite(self): """ Send mail to invitee """ user = User.query.filter_by(email=self.email).first() event = Event.query.filter_by(id=self.event_id).first() frontend_url = get_settings()['frontend_url'] link = f"{frontend_url}/role-invites?token={self.hash}" if not has_access('is_coorganizer', event_id=event.id): raise ForbiddenError({'source': ''}, "Co-Organizer Access Required") send_email_role_invite(self.email, self.role_name, event.name, link) if user: notify_event_role_invitation(self, user, current_user) def __repr__(self): return '<RoleInvite {!r}:{!r}:{!r}>'.format(self.email, self.event_id, self.role_id)
class User(BaseMixin, UserMixin, db.Model): name = db.Column(db.String(255), unique=True) email = db.Column(db.String(255), unique=True) password = db.Column(db.String(255)) active = db.Column(db.Boolean()) confirmed_at = db.Column(db.DateTime()) roles = db.relationship('Role', secondary=roles_users, backref=db.backref('users', lazy='dynamic')) def __repr__(self): return "<User({id:d}|{name:s}|{email:s})".format( id=self.id, name=self.name, email=self.email, )
class ExportJob(db.Model): """Export Jobs model class""" __tablename__ = 'export_jobs' id = db.Column(db.Integer, primary_key=True) task = db.Column(db.String, nullable=False) starts_at = db.Column(db.DateTime(timezone=True), default=func.now()) user_email = db.Column(db.String) # not linking to User because when user is deleted, this will be lost event_id = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='CASCADE')) event = db.relationship('Event', backref=backref('export_jobs')) def __repr__(self): return '<ExportJob %d for event %d>' % (self.id, self.event.id)
class Process(db.Model): """Request model class""" __tablename__ = 'processes' id = db.Column(db.Integer, primary_key=True) time = db.Column(db.DateTime(timezone=True), default=datetime.now(pytz.utc)) method = db.Column(db.String) path = db.Column(db.String) query = db.Column(db.String) body = db.Column(db.String) headers = db.relationship('Header', secondary=process_headers, backref=db.backref('processes', lazy='dynamic')) duration = db.Column(db.String) def __init__(self, time=None, method=None, path=None, query=None, body=None, headers=None): self.time = time self.method = method self.path = path self.query = query self.body = body if headers is None: self.headers = [] else: self.headers = headers self.duration = self.time_duration() def time_duration(self): then = time.time() time.sleep(random.randint(15, 30)) now = time.time() return now - then def __repr__(self): return '<Request %r>' % self.id def __str__(self): return self.__repr__()
class Notification(SoftDeletionModel): """ Model for storing user notifications. """ __tablename__ = 'notifications' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE')) user = db.relationship('User', backref='notifications', foreign_keys=[user_id]) title = db.Column(db.String) message = db.Column(db.Text) received_at = db.Column(db.DateTime(timezone=True)) is_read = db.Column(db.Boolean) def __repr__(self): return f'<Notif {self.user}:{self.title}>'
class Activity(db.Model): __tablename__ = 'activities' id = db.Column(db.Integer, primary_key=True) actor = db.Column(db.String) # user email + id time = db.Column(db.DateTime(timezone=True)) action = db.Column(db.String) def __init__(self, actor=None, time=None, action=None): self.actor = actor self.time = time if self.time is None: self.time = datetime.now(pytz.utc) self.action = action def __repr__(self): return '<Activity by %s>' % self.actor def __str__(self): return self.__repr__()
class RoleInvite(db.Model): __tablename__ = 'role_invites' id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String, nullable=False) role_name = db.Column(db.String, nullable=False) event_id = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='CASCADE')) event = db.relationship('Event', back_populates='role_invites') role_id = db.Column(db.Integer, db.ForeignKey('roles.id', ondelete='CASCADE')) role = db.relationship("Role") hash = db.Column(db.String) created_at = db.Column(db.DateTime(timezone=True)) status = db.Column(db.String, default="pending") def __init__(self, email=None, role_name=None, event_id=None, role_id=None, created_at=None, status="pending", hash=None): self.email = email self.role_name = role_name self.event_id = event_id self.role_id = role_id self.created_at = created_at self.status = status self.hash = generate_hash() def has_expired(self): # Check if invitation link has expired (it expires after 24 hours) return datetime.now(pytz.utc) > self.created_at + timedelta(hours=24) def __repr__(self): return '<RoleInvite %r:%r:%r>' % (self.email, self.event_id, self.role_id,) def __str__(self): return unicode(self).encode('utf-8') def __unicode__(self): return 'Invite for %r:%r:%r' % (self.email, self.event_id, self.role_id)
class Notification(db.Model): """ Model for storing user notifications. """ __tablename__ = 'notifications' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE')) user = db.relationship('User', backref='notifications', foreign_keys=[user_id]) title = db.Column(db.String) message = db.Column(db.Text) action = db.Column(db.String) received_at = db.Column(db.DateTime(timezone=True)) is_read = db.Column(db.Boolean) def __init__(self, user_id=None, title=None, message=None, action=None, is_read=False): self.user_id = user_id self.title = title self.message = message self.action = action self.received_at = datetime.now(pytz.utc) self.is_read = is_read def __repr__(self): return '<Notif %s:%s>' % (self.user, self.title) def __str__(self): return unicode(self).encode('utf-8') def __unicode__(self): return '%r: %r' % (self.user, self.title)
class ImportJob(db.Model): """Import Jobs model class""" __tablename__ = 'import_jobs' id = db.Column(db.Integer, primary_key=True) task = db.Column(db.String, nullable=False) starts_at = db.Column(db.DateTime(timezone=True)) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE')) user = db.relationship('User', backref='import_jobs') result = db.Column(db.String) result_status = db.Column(db.String) def __init__(self, task=None, user=None, result=None, result_status=None): self.task = task self.user = user self.result = result self.result_status = result_status self.starts_at = datetime.now(pytz.utc) def __repr__(self): return '<ImportJob %d by user %s>' % (self.id, str(self.user)) def __str__(self): return self.__repr__()
class TicketHolder(SoftDeletionModel): __tablename__ = "ticket_holders" id: int = db.Column(db.Integer, primary_key=True) firstname: str = db.Column(db.String, nullable=False) lastname: str = db.Column(db.String, nullable=False) email: str = db.Column(CIText) address: str = db.Column(db.String) city: str = db.Column(db.String) state: str = db.Column(db.String) country: str = db.Column(db.String) job_title: str = db.Column(db.String) phone: str = db.Column(db.String) tax_business_info: str = db.Column(db.String) billing_address: str = db.Column(db.String) home_address: str = db.Column(db.String) shipping_address: str = db.Column(db.String) company: str = db.Column(db.String) work_address: str = db.Column(db.String) work_phone: str = db.Column(db.String) website: str = db.Column(db.String) blog: str = db.Column(db.String) twitter: str = db.Column(db.String) facebook: str = db.Column(db.String) instagram: str = db.Column(db.String) linkedin: str = db.Column(db.String) github: str = db.Column(db.String) gender: str = db.Column(db.String) accept_video_recording: bool = db.Column(db.Boolean) accept_share_details: bool = db.Column(db.Boolean) accept_receive_emails: bool = db.Column(db.Boolean) age_group: str = db.Column(db.String) birth_date: datetime = db.Column(db.DateTime(timezone=True)) pdf_url: str = db.Column(db.String) ticket_id: int = db.Column( db.Integer, db.ForeignKey('tickets.id', ondelete='CASCADE'), nullable=False ) order_id: int = db.Column(db.Integer, db.ForeignKey('orders.id', ondelete='CASCADE')) is_checked_in: bool = db.Column(db.Boolean, default=False) is_checked_out: bool = db.Column(db.Boolean, default=False) device_name_checkin: str = db.Column(db.String) checkin_times: str = db.Column(db.String) checkout_times: str = db.Column(db.String) attendee_notes: str = db.Column(db.String) event_id: int = db.Column( db.Integer, db.ForeignKey('events.id', ondelete='CASCADE'), nullable=False ) created_at: datetime = db.Column(db.DateTime(timezone=True), default=datetime.utcnow) modified_at: datetime = db.Column( db.DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow ) complex_field_values: str = db.Column(db.JSON) user = db.relationship( 'User', foreign_keys=[email], primaryjoin='User.email == TicketHolder.email', viewonly=True, backref='attendees', sync_backref=False, ) order = db.relationship('Order', backref='ticket_holders') ticket = db.relationship('Ticket', backref='ticket_holders') @property def name(self): firstname = self.firstname if self.firstname else '' lastname = self.lastname if self.lastname else '' if firstname and lastname: return f'{firstname} {lastname}' else: return '' @property def qr_code(self): qr = qrcode.QRCode( version=1, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=10, border=0, ) qr.add_data(self.order.identifier + "-" + str(self.id)) qr.make(fit=True) img = qr.make_image() buffer = BytesIO() img.save(buffer, format="JPEG") img_str = str(base64.b64encode(buffer.getvalue()), 'utf-8') return img_str @property def serialize(self): """Return object data in easily serializable format""" return { 'id': self.id, 'firstname': self.firstname, 'lastname': self.lastname, 'email': self.email, 'city': self.city, 'address': self.address, 'state': self.state, 'country': self.country, 'company': self.company, 'taxBusinessInfo': self.tax_business_info, } @property def pdf_url_path(self) -> str: key = UPLOAD_PATHS['pdf']['tickets_all'].format( identifier=self.order.identifier, extra_identifier=self.id ) return ( 'generated/tickets/{}/{}/'.format(key, generate_hash(key)) + self.order.identifier + '.pdf' )
class Order(SoftDeletionModel): __tablename__ = "orders" id = db.Column(db.Integer, primary_key=True) identifier = db.Column(db.String, unique=True, default=get_new_id) amount = db.Column(db.Float, nullable=False, default=0) address = db.Column(db.String) city = db.Column(db.String) state = db.Column(db.String) country = db.Column(db.String) zipcode = db.Column(db.String) company = db.Column(db.String) tax_business_info = db.Column(db.String) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='SET NULL')) event_id = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='SET NULL')) marketer_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='SET NULL')) created_at = db.Column(db.DateTime(timezone=True), default=func.now()) completed_at = db.Column(db.DateTime(timezone=True), nullable=True, default=None) trashed_at = db.Column(db.DateTime(timezone=True), nullable=True, default=None) transaction_id = db.Column(db.String) paid_via = db.Column(db.String) payment_mode = db.Column(db.String) is_billing_enabled = db.Column(db.Boolean, nullable=False, default=False) brand = db.Column(db.String) exp_month = db.Column(db.Integer) exp_year = db.Column(db.Integer) last4 = db.Column(db.String) stripe_token = db.Column(db.String) paypal_token = db.Column(db.String) status = db.Column(db.String, default='initializing') cancel_note = db.Column(db.String, nullable=True) order_notes = db.Column(db.String) tickets_pdf_url = db.Column(db.String) discount_code_id = db.Column( db.Integer, db.ForeignKey('discount_codes.id', ondelete='SET NULL'), nullable=True, default=None, ) discount_code = db.relationship('DiscountCode', backref='orders') event = db.relationship('Event', backref='orders') user = db.relationship('User', backref='orders', foreign_keys=[user_id]) invoices = db.relationship("EventInvoice", backref='invoice_order') marketer = db.relationship('User', backref='marketed_orders', foreign_keys=[marketer_id]) tickets = db.relationship("Ticket", secondary='orders_tickets', backref='order') order_tickets = db.relationship("OrderTicket", backref='order') def __repr__(self): return '<Order %r>' % self.id def get_invoice_number(self): return ('O' + str(int(time.mktime(self.created_at.timetuple()))) + '-' + str(self.id)) @property def invoice_number(self): return self.get_invoice_number() @property def tickets_count(self): return sum(t.quantity for t in self.order_tickets) @property def is_free(self): return self.payment_mode == 'free' def get_revenue(self): if self.amount: return self.amount - min(self.amount * (self.event.fee / 100.0), self.event.maximum_fee) else: return 0.0 # Saves the order and generates and sends appropriate # documents and notifications def populate_and_save(self): from app.api.orders import save_order save_order(self)
class Event(SoftDeletionModel): """Event object table""" __tablename__ = 'events' __versioned__ = {'exclude': ['schedule_published_on', 'created_at']} id = db.Column(db.Integer, primary_key=True) identifier = db.Column(db.String) name = db.Column(db.String, nullable=False) external_event_url = db.Column(db.String) logo_url = db.Column(db.String) starts_at = db.Column(db.DateTime(timezone=True), nullable=False) ends_at = db.Column(db.DateTime(timezone=True), nullable=False) timezone = db.Column(db.String, nullable=False, default="UTC") is_event_online = db.Column(db.Boolean, default=False) latitude = db.Column(db.Float) longitude = db.Column(db.Float) location_name = db.Column(db.String) searchable_location_name = db.Column(db.String) is_featured = db.Column(db.Boolean, default=False, nullable=False) description = db.Column(db.Text) original_image_url = db.Column(db.String) thumbnail_image_url = db.Column(db.String) large_image_url = db.Column(db.String) show_remaining_tickets = db.Column(db.Boolean, default=False, nullable=False) icon_image_url = db.Column(db.String) owner_name = db.Column(db.String) is_map_shown = db.Column(db.Boolean) has_owner_info = db.Column(db.Boolean) owner_description = db.Column(db.String) is_sessions_speakers_enabled = db.Column(db.Boolean, default=False) track = db.relationship('Track', backref="event") microlocation = db.relationship('Microlocation', backref="event") session = db.relationship('Session', backref="event") speaker = db.relationship('Speaker', backref="event") sponsor = db.relationship('Sponsor', backref="event") tickets = db.relationship('Ticket', backref="event_") tags = db.relationship('TicketTag', backref='events') roles = db.relationship("UsersEventsRoles", backref="event") role_invites = db.relationship('RoleInvite', back_populates='event') custom_form = db.relationship('CustomForms', backref="event") faqs = db.relationship('Faq', backref="event") feedbacks = db.relationship('Feedback', backref="event") attendees = db.relationship('TicketHolder', backref="event") privacy = db.Column(db.String, default="public") state = db.Column(db.String, default="Draft") event_type_id = db.Column( db.Integer, db.ForeignKey('event_types.id', ondelete='CASCADE')) event_topic_id = db.Column( db.Integer, db.ForeignKey('event_topics.id', ondelete='CASCADE')) event_sub_topic_id = db.Column( db.Integer, db.ForeignKey('event_sub_topics.id', ondelete='CASCADE')) events_orga_id = db.Column( db.Integer, db.ForeignKey('events_orga.id', ondelete='CASCADE')) ticket_url = db.Column(db.String) db.UniqueConstraint('track.name') code_of_conduct = db.Column(db.String) schedule_published_on = db.Column(db.DateTime(timezone=True)) is_ticketing_enabled = db.Column(db.Boolean, default=False) is_donation_enabled = db.Column(db.Boolean, default=False) is_ticket_form_enabled = db.Column(db.Boolean, default=True, nullable=False) payment_country = db.Column(db.String) payment_currency = db.Column(db.String) paypal_email = db.Column(db.String) is_tax_enabled = db.Column(db.Boolean, default=False) is_billing_info_mandatory = db.Column(db.Boolean, default=False) can_pay_by_paypal = db.Column(db.Boolean, default=False) can_pay_by_stripe = db.Column(db.Boolean, default=False) can_pay_by_cheque = db.Column(db.Boolean, default=False) can_pay_by_bank = db.Column(db.Boolean, default=False) can_pay_onsite = db.Column(db.Boolean, default=False) can_pay_by_omise = db.Column(db.Boolean, default=False) can_pay_by_alipay = db.Column(db.Boolean, default=False) cheque_details = db.Column(db.String) bank_details = db.Column(db.String) onsite_details = db.Column(db.String) created_at = db.Column(db.DateTime(timezone=True)) pentabarf_url = db.Column(db.String) ical_url = db.Column(db.String) xcal_url = db.Column(db.String) is_sponsors_enabled = db.Column(db.Boolean, default=False) refund_policy = db.Column( db.String, default='All sales are final. No refunds shall be issued in any case.') is_stripe_linked = db.Column(db.Boolean, default=False) discount_code_id = db.Column( db.Integer, db.ForeignKey('discount_codes.id', ondelete='CASCADE')) discount_code = db.relationship('DiscountCode', backref='events', foreign_keys=[discount_code_id]) event_type = db.relationship('EventType', backref='event', foreign_keys=[event_type_id]) event_topic = db.relationship('EventTopic', backref='event', foreign_keys=[event_topic_id]) event_sub_topic = db.relationship('EventSubTopic', backref='event', foreign_keys=[event_sub_topic_id]) events_orga = db.relationship('EventOrgaModel', backref='event', foreign_keys=[events_orga_id]) owner = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "owner"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='owner_events', uselist=False) organizers = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "organizer"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='organizer_events') coorganizers = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "coorganizer"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='coorganizer_events') track_organizers = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id,' ' Role.name == "track_organizer"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='track_organizer_events') registrars = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "registrar"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='registrar_events') moderators = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name == "moderator"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='moderator_events') # staff users = db.relationship( 'User', viewonly=True, secondary='join(UsersEventsRoles, Role,' ' and_(Role.id == UsersEventsRoles.role_id, Role.name != "attendee"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='events') def __init__( self, name=None, logo_url=None, starts_at=None, ends_at=None, timezone='UTC', is_event_online=False, latitude=None, longitude=None, location_name=None, description=None, external_event_url=None, original_image_url=None, thumbnail_image_url=None, large_image_url=None, icon_image_url=None, owner_name=None, owner_description=None, state=None, event_type_id=None, privacy=None, event_topic_id=None, event_sub_topic_id=None, events_orga_id=None, ticket_url=None, copyright=None, code_of_conduct=None, schedule_published_on=None, is_sessions_speakers_enabled=False, show_remaining_tickets=False, is_ticket_form_enabled=True, is_donation_enabled=False, is_map_shown=False, has_owner_info=False, searchable_location_name=None, is_ticketing_enabled=None, deleted_at=None, payment_country=None, payment_currency=None, paypal_email=None, speakers_call=None, can_pay_by_paypal=None, can_pay_by_stripe=None, can_pay_by_cheque=None, can_pay_by_omise=None, can_pay_by_alipay=None, identifier=None, can_pay_by_bank=None, is_featured=False, can_pay_onsite=None, cheque_details=None, bank_details=None, pentabarf_url=None, ical_url=None, xcal_url=None, discount_code_id=None, onsite_details=None, is_tax_enabled=None, is_billing_info_mandatory=False, is_sponsors_enabled=None, stripe_authorization=None, tax=None, refund_policy='All sales are final. No refunds shall be issued in any case.', is_stripe_linked=False): self.name = name self.logo_url = logo_url self.starts_at = starts_at self.ends_at = ends_at self.timezone = timezone self.is_event_online = is_event_online self.latitude = latitude self.longitude = longitude self.location_name = location_name self.description = clean_up_string(description) self.external_event_url = external_event_url self.original_image_url = original_image_url self.original_image_url = self.set_default_event_image(event_topic_id) if original_image_url is None \ else original_image_url self.thumbnail_image_url = thumbnail_image_url self.large_image_url = large_image_url self.icon_image_url = icon_image_url self.owner_name = owner_name self.owner_description = clean_up_string(owner_description) self.state = state self.is_map_shown = is_map_shown self.has_owner_info = has_owner_info self.privacy = privacy self.event_type_id = event_type_id self.event_topic_id = event_topic_id self.show_remaining_tickets = show_remaining_tickets self.copyright = copyright self.event_sub_topic_id = event_sub_topic_id self.events_orga_id = events_orga_id self.ticket_url = ticket_url self.code_of_conduct = code_of_conduct self.schedule_published_on = schedule_published_on self.is_sessions_speakers_enabled = is_sessions_speakers_enabled self.searchable_location_name = searchable_location_name self.is_ticketing_enabled = is_ticketing_enabled self.deleted_at = deleted_at self.payment_country = payment_country self.payment_currency = payment_currency self.paypal_email = paypal_email self.speakers_call = speakers_call self.can_pay_by_paypal = can_pay_by_paypal self.can_pay_by_stripe = can_pay_by_stripe self.can_pay_by_cheque = can_pay_by_cheque self.can_pay_by_bank = can_pay_by_bank self.can_pay_onsite = can_pay_onsite self.can_pay_by_omise = can_pay_by_omise self.can_pay_by_alipay = can_pay_by_alipay self.is_donation_enabled = is_donation_enabled self.is_featured = is_featured self.is_ticket_form_enabled = is_ticket_form_enabled self.identifier = get_new_event_identifier() self.cheque_details = cheque_details self.bank_details = bank_details self.pentabarf_url = pentabarf_url self.ical_url = ical_url self.xcal_url = xcal_url self.onsite_details = onsite_details self.discount_code_id = discount_code_id self.created_at = datetime.now(pytz.utc) self.is_tax_enabled = is_tax_enabled self.is_billing_info_mandatory = is_billing_info_mandatory self.is_sponsors_enabled = is_sponsors_enabled self.stripe_authorization = stripe_authorization self.tax = tax self.refund_policy = refund_policy self.is_stripe_linked = is_stripe_linked def __repr__(self): return '<Event %r>' % self.name def __str__(self): return self.__repr__() def __setattr__(self, name, value): if name == 'owner_description' or name == 'description' or name == 'code_of_conduct': super(Event, self).__setattr__(name, clean_html(clean_up_string(value))) else: super(Event, self).__setattr__(name, value) @classmethod def set_default_event_image(self, event_topic_id): if event_topic_id is None: return None else: event_topic = EventTopic.query.filter_by(id=event_topic_id).first() return event_topic.system_image_url @property def fee(self): """ Returns the fee as a percentage from 0 to 100 for this event """ return get_fee(self.payment_country, self.payment_currency) @property def maximum_fee(self): """ Returns the maximum fee for this event """ return get_maximum_fee(self.payment_country, self.payment_currency) def notification_settings(self, user_id): try: return EmailNotification.query.filter_by( user_id=(login.current_user.id if not user_id else int(user_id))). \ filter_by(event_id=self.id).first() except: return None def get_average_rating(self): avg = db.session.query(func.avg( Feedback.rating)).filter_by(event_id=self.id).scalar() if avg is not None: avg = round(avg, 2) return avg def is_payment_enabled(self): return self.can_pay_by_paypal or self.can_pay_by_stripe or self.can_pay_by_omise or self.can_pay_by_alipay \ or self.can_pay_by_cheque or self.can_pay_by_bank or self.can_pay_onsite @property def average_rating(self): return self.get_average_rating() def get_organizer(self): """returns organizer of an event""" for role in self.roles: if role.role.name == ORGANIZER: return role.user return None def get_owner(self): """returns owner of an event""" for role in self.roles: if role.role.name == OWNER: return role.user return None def has_staff_access(self, user_id): """does user have role other than attendee""" for _ in self.roles: if _.user_id == (login.current_user.id if not user_id else int(user_id)): if _.role.name != ATTENDEE: return True return False def get_staff_roles(self): """returns only roles which are staff i.e. not attendee""" return [role for role in self.roles if role.role.name != ATTENDEE] def as_dict(self): return {c.name: getattr(self, c.name) for c in self.__table__.columns} def calc_tickets_sold_count(self): """Calculate total number of tickets sold for the event""" return db.session.query(Order.event_id).filter_by(event_id=self.id, status='completed').join(TicketHolder)\ .count() def calc_total_tickets_count(self): """Calculate total available tickets for all types of tickets""" total_available = db.session.query(func.sum( Ticket.quantity)).filter_by(event_id=self.id).scalar() if total_available is None: total_available = 0 return total_available def calc_revenue(self): """Returns total revenues of all completed orders for the given event""" revenue = db.session.query(func.sum(Order.amount)).filter_by( event_id=self.id, status='completed').scalar() if revenue is None: revenue = 0 return revenue def calc_monthly_revenue(self): """Returns revenue of current month. Invoice sent every 1st of the month for the previous month""" previous_month = datetime.datetime.now().month - 1 monthly_revenue = db.session.query(func.sum(Order.amount)).filter( Order.event_id == self.id, Order.completed_at.month == previous_month, Order.status == 'completed').scalar() if monthly_revenue is None: monthly_revenue = 0 return monthly_revenue @property def tickets_available(self): return self.calc_total_tickets_count() @property def tickets_sold(self): return self.calc_tickets_sold_count() @property def revenue(self): return self.calc_revenue() @property def has_sessions(self): return Session.query.filter_by(event_id=self.id).count() > 0 @property def has_speakers(self): return Speaker.query.filter_by(event_id=self.id).count() > 0
class Ticket(SoftDeletionModel): __tablename__ = 'tickets' __table_args__ = (db.UniqueConstraint('name', 'event_id', 'deleted_at', name='name_event_deleted_at_uc'), ) id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String, nullable=False) description = db.Column(db.String) is_description_visible = db.Column(db.Boolean) type = db.Column(db.String, nullable=False) quantity = db.Column(db.Integer, default=100) position = db.Column(db.Integer, default=1) price = db.Column(db.Float, default=0) min_price = db.Column(db.Float, default=0, nullable=False) max_price = db.Column(db.Float, default=0) is_fee_absorbed = db.Column(db.Boolean, default=False) sales_starts_at = db.Column(db.DateTime(timezone=True), nullable=False) sales_ends_at = db.Column(db.DateTime(timezone=True), nullable=False) is_hidden = db.Column(db.Boolean, default=False) min_order = db.Column(db.Integer, default=1) max_order = db.Column(db.Integer, default=10) is_checkin_restricted = db.Column(db.Boolean) auto_checkin_enabled = db.Column(db.Boolean, default=False) event_id = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='CASCADE')) event = db.relationship('Event', backref='tickets_') tags = db.relationship('TicketTag', secondary=ticket_tags_table, backref='tickets') order_ticket = db.relationship('OrderTicket', backref="ticket", passive_deletes=True) access_codes = db.relationship('AccessCode', secondary=access_codes_tickets, backref='tickets') discount_codes = db.relationship('DiscountCode', secondary=discount_codes_tickets, backref="tickets") def has_order_tickets(self): """Returns True if ticket has already placed orders. Else False. """ from app.api.helpers.db import get_count orders = Order.id.in_( OrderTicket.query.with_entities( OrderTicket.order_id).filter_by(ticket_id=self.id).all()) count = get_count( Order.query.filter(orders).filter(Order.status != 'deleted')) # Count is zero if no completed orders are present return bool(count > 0) def has_completed_order_tickets(self): """Returns True if ticket has already placed orders. Else False. """ order_tickets = OrderTicket.query.filter_by(ticket_id=self.id) count = 0 for order_ticket in order_tickets: order = Order.query.filter_by(id=order_ticket.order_id).first() if order.status == "completed" or order.status == "placed": count += 1 return bool(count > 0) def tags_csv(self): """Return list of Tags in CSV.""" tag_names = [tag.name for tag in self.tags] return ','.join(tag_names) @property def has_current_orders(self): return db.session.query( Order.query.join(TicketHolder).filter( TicketHolder.ticket_id == self.id, or_(Order.status == 'completed', Order.status == 'placed', Order.status == 'pending', Order.status == 'initializing')).exists()).scalar() @property def reserved_count(self): from app.api.attendees import get_sold_and_reserved_tickets_count return get_sold_and_reserved_tickets_count(self.id) @property def is_available(self): return self.reserved_count < self.quantity def raise_if_unavailable(self): if not self.is_available: raise ConflictError({'id': self.id}, f"Ticket {self.id} already sold out") def __repr__(self): return '<Ticket %r>' % self.name
class Order(db.Model): __tablename__ = "orders" class Status: INITIALIZING = 'initializing' PENDING = 'pending' COMPLETED = 'completed' CANCELLED = 'cancelled' EXPIRED = 'expired' id = db.Column(db.Integer, primary_key=True) identifier = db.Column(db.String, unique=True, default=get_new_id) amount = db.Column(db.Float, nullable=False, default=0) address = db.Column(db.String) city = db.Column(db.String) state = db.Column(db.String) country = db.Column(db.String) zipcode = db.Column(db.String) company = db.Column(db.String) tax_business_info = db.Column(db.String) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='SET NULL')) event_id = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='SET NULL')) marketer_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='SET NULL')) created_at = db.Column(db.DateTime(timezone=True), default=func.now()) completed_at = db.Column(db.DateTime(timezone=True), nullable=True, default=None) trashed_at = db.Column(db.DateTime(timezone=True), nullable=True, default=None) transaction_id = db.Column(db.String) paid_via = db.Column(db.String) payment_mode = db.Column(db.String) is_billing_enabled = db.Column(db.Boolean, nullable=False, default=False) brand = db.Column(db.String) exp_month = db.Column(db.Integer) exp_year = db.Column(db.Integer) last4 = db.Column(db.String) stripe_token = db.Column(db.String) stripe_payment_intent_id = db.Column(db.String) paypal_token = db.Column(db.String) status = db.Column(db.String, default='initializing') cancel_note = db.Column(db.String, nullable=True) order_notes = db.Column(db.String) tickets_pdf_url = db.Column(db.String) discount_code_id = db.Column( db.Integer, db.ForeignKey('discount_codes.id', ondelete='SET NULL'), nullable=True, default=None, ) discount_code = db.relationship('DiscountCode', backref='orders') event = db.relationship('Event', backref='orders') user = db.relationship('User', backref='orders', foreign_keys=[user_id]) marketer = db.relationship('User', backref='marketed_orders', foreign_keys=[marketer_id]) tickets = db.relationship("Ticket", secondary='orders_tickets', backref='order') order_tickets = db.relationship("OrderTicket", backref='order') def __repr__(self): return '<Order %r>' % self.id def get_invoice_number(self): return ('O' + str(int(time.mktime(self.created_at.timetuple()))) + '-' + str(self.id)) @property def invoice_number(self): return self.get_invoice_number() @property def tickets_count(self): return sum(t.quantity for t in self.order_tickets) @property def is_free(self): return self.payment_mode == 'free' def get_revenue(self): if self.amount: return self.amount - min(self.amount * (self.event.fee / 100.0), self.event.maximum_fee) return 0.0 # Saves the order and generates and sends appropriate # documents and notifications def populate_and_save(self) -> None: from app.api.orders import save_order save_order(self) def is_attendee(self, user) -> bool: return db.session.query( TicketHolder.query.filter_by(order_id=self.id, user=user).exists()).scalar() @property def ticket_pdf_path(self) -> str: key = UPLOAD_PATHS['pdf']['tickets_all'].format( identifier=self.identifier, extra_identifier=self.identifier) return ('generated/tickets/{}/{}/'.format(key, generate_hash(key)) + self.identifier + '.pdf') @property def invoice_pdf_path(self) -> str: key = UPLOAD_PATHS['pdf']['order'].format(identifier=self.identifier) return ('generated/invoices/{}/{}/'.format(key, generate_hash(key)) + self.identifier + '.pdf') @property def filtered_ticket_holders(self): from app.api.helpers.permission_manager import has_access query_ = TicketHolder.query.filter_by(order_id=self.id, deleted_at=None) if (not has_access( 'is_coorganizer', event_id=self.event_id, ) and current_user.id != self.user_id): query_ = query_.filter(TicketHolder.user == current_user) return query_.all() @property def safe_user(self): from app.api.helpers.permission_manager import has_access if (not has_access( 'is_coorganizer', event_id=self.event_id, ) and current_user.id != self.user_id): return None return self.user @property def site_view_link(self) -> str: frontend_url = get_settings()['frontend_url'] return frontend_url + '/orders/' + self.identifier + '/view'
class Order(db.Model): __tablename__ = "orders" id = db.Column(db.Integer, primary_key=True) identifier = db.Column(db.String, unique=True) amount = db.Column(db.Float) address = db.Column(db.String) city = db.Column(db.String) state = db.Column(db.String) country = db.Column(db.String) zipcode = db.Column(db.String) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='SET NULL')) event_id = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='SET NULL')) marketer_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='SET NULL')) created_at = db.Column(db.DateTime(timezone=True)) completed_at = db.Column(db.DateTime(timezone=True), nullable=True, default=None) trashed_at = db.Column(db.DateTime(timezone=True), nullable=True, default=None) transaction_id = db.Column(db.String) paid_via = db.Column(db.String) payment_mode = db.Column(db.String) brand = db.Column(db.String) exp_month = db.Column(db.Integer) exp_year = db.Column(db.Integer) last4 = db.Column(db.String) stripe_token = db.Column(db.String) paypal_token = db.Column(db.String) status = db.Column(db.String) cancel_note = db.Column(db.String, nullable=True) discount_code_id = db.Column(db.Integer, db.ForeignKey('discount_codes.id', ondelete='SET NULL'), nullable=True, default=None) discount_code = db.relationship('DiscountCode', backref='orders') event = db.relationship('Event', backref='orders') user = db.relationship('User', backref='orders', foreign_keys=[user_id]) marketer = db.relationship('User', backref='marketed_orders', foreign_keys=[marketer_id]) tickets = db.relationship("Ticket", secondary='orders_tickets', backref='order') order_tickets = db.relationship("OrderTicket", backref='order') def __init__(self, quantity=None, amount=None, address=None, city=None, state=None, country=None, zipcode=None, transaction_id=None, paid_via=None, user_id=None, discount_code_id=None, event_id=None, status='pending', payment_mode=None): self.identifier = get_new_order_identifier() self.quantity = quantity self.amount = amount self.city = city self.address = address self.state = state self.country = country self.zipcode = zipcode self.user_id = user_id self.event_id = event_id self.transaction_id = transaction_id self.paid_via = paid_via self.created_at = datetime.datetime.utcnow() self.discount_code_id = discount_code_id self.status = status self.payment_mode = payment_mode def __repr__(self): return '<Order %r>' % self.id def __str__(self): return unicode(self).encode('utf-8') def __unicode__(self): return self.identifier def get_invoice_number(self): return 'O' + str(int(time.mktime( self.created_at.timetuple()))) + '-' + str(self.id) @property def invoice_number(self): return self.get_invoice_number() def get_tickets_count(self): count = 0 for order_ticket in self.order_tickets: count += order_ticket.quantity return count @property def serialize(self): """Return object data in easily serializable format""" return { 'id': self.id, 'identifier': self.identifier, 'quantity': self.quantity, 'amount': self.amount, 'address': self.address, 'state': self.state, 'zipcode': self.zipcode, 'country': self.country, 'transaction_id': self.transaction_id, 'paid_via': self.paid_via, 'payment_mode': self.payment_mode, 'brand': self.brand, 'exp_month': self.exp_month, 'exp_year': self.exp_year, 'last4': self.last4, }
class TicketHolder(SoftDeletionModel): __tablename__ = "ticket_holders" id: int = db.Column(db.Integer, primary_key=True) firstname: str = db.Column(db.String, nullable=False) lastname: str = db.Column(db.String, nullable=False) email: str = db.Column(db.String) address: str = db.Column(db.String) city: str = db.Column(db.String) state: str = db.Column(db.String) country: str = db.Column(db.String) job_title: str = db.Column(db.String) phone: str = db.Column(db.String) tax_business_info: str = db.Column(db.String) billing_address: str = db.Column(db.String) home_address: str = db.Column(db.String) shipping_address: str = db.Column(db.String) company: str = db.Column(db.String) work_address: str = db.Column(db.String) work_phone: str = db.Column(db.String) website: str = db.Column(db.String) blog: str = db.Column(db.String) twitter: str = db.Column(db.String) facebook: str = db.Column(db.String) github: str = db.Column(db.String) gender: str = db.Column(db.String) birth_date: datetime = db.Column(db.DateTime(timezone=True)) pdf_url: str = db.Column(db.String) ticket_id: int = db.Column(db.Integer, db.ForeignKey('tickets.id', ondelete='CASCADE')) order_id: int = db.Column(db.Integer, db.ForeignKey('orders.id', ondelete='CASCADE')) is_checked_in: bool = db.Column(db.Boolean, default=False) is_checked_out: bool = db.Column(db.Boolean, default=False) device_name_checkin: str = db.Column(db.String) checkin_times: str = db.Column(db.String) checkout_times: str = db.Column(db.String) attendee_notes: str = db.Column(db.String) event_id: int = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='CASCADE')) complex_field_values: str = db.Column(db.JSON) user = db.relationship('User', foreign_keys=[email], primaryjoin='User.email == TicketHolder.email', viewonly=True, backref='attendees') order = db.relationship('Order', backref='ticket_holders') ticket = db.relationship('Ticket', backref='ticket_holders') @property def name(self): firstname = self.firstname if self.firstname else '' lastname = self.lastname if self.lastname else '' if firstname and lastname: return u'{} {}'.format(firstname, lastname) else: return '' @property def qr_code(self): qr = qrcode.QRCode( version=1, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=10, border=0, ) qr.add_data(self.order.identifier + "-" + str(self.id)) qr.make(fit=True) img = qr.make_image() buffer = BytesIO() img.save(buffer, format="JPEG") img_str = str(base64.b64encode(buffer.getvalue()), 'utf-8') return img_str @property def serialize(self): """Return object data in easily serializable format""" return {'id': self.id, 'firstname': self.firstname, 'lastname': self.lastname, 'email': self.email, 'city': self.city, 'address': self.address, 'state': self.state, 'country': self.country, 'company': self.company, 'taxBusinessInfo': self.tax_business_info}
class EventInvoice(SoftDeletionModel): DUE_DATE_DAYS = 30 MIN_AMOUNT = 2 # Minimum amount for which the invoice will be generated __tablename__ = 'event_invoices' id = db.Column(db.Integer, primary_key=True) identifier = db.Column(db.String, unique=True, nullable=False) amount = db.Column(db.Float) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='SET NULL')) event_id = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='SET NULL')) created_at = db.Column(db.DateTime(timezone=True), default=func.now()) issued_at = db.Column(db.DateTime(timezone=True), nullable=False) # Payment Fields completed_at = db.Column(db.DateTime(timezone=True), nullable=True, default=None) transaction_id = db.Column(db.String) paid_via = db.Column(db.String) payment_mode = db.Column(db.String) brand = db.Column(db.String) exp_month = db.Column(db.Integer) exp_year = db.Column(db.Integer) last4 = db.Column(db.String) stripe_token = db.Column(db.String) paypal_token = db.Column(db.String) status = db.Column(db.String, default='due') invoice_pdf_url = db.Column(db.String) event = db.relationship('Event', backref='invoices') user = db.relationship('User', backref='event_invoices') def __init__(self, **kwargs): super().__init__(**kwargs) if not self.issued_at: self.issued_at = datetime.now() if not self.identifier: self.identifier = self.get_new_id() def __repr__(self): return '<EventInvoice {!r} {!r} {!r}>'.format( self.id, self.identifier, self.invoice_pdf_url, ) def get_new_id(self) -> str: with db.session.no_autoflush: identifier = self.issued_at.strftime( '%Y%mU-') + '%06d' % (EventInvoice.query.count() + 1) count = EventInvoice.query.filter_by(identifier=identifier).count() if count == 0: return identifier return self.get_new_id() @property def previous_month_date(self): return monthdelta(self.issued_at, -1) @property def due_at(self): return self.issued_at + timedelta(days=EventInvoice.DUE_DATE_DAYS) def populate(self): assert self.event is not None with db.session.no_autoflush: self.user = self.event.owner return self.generate_pdf() def generate_pdf(self, force=False): with db.session.no_autoflush: latest_invoice_date = (EventInvoice.query.filter_by( event=self.event).filter( EventInvoice.issued_at < self.issued_at).with_entities( func.max(EventInvoice.issued_at)).scalar()) admin_info = Setting.query.first() currency = self.event.payment_currency ticket_fee_object = ( TicketFees.query.filter_by( country=self.event.payment_country).first() or TicketFees.query.filter_by(country='global').first()) if not ticket_fee_object: logger.error('Ticket Fee not found for event %s', self.event) return ticket_fee_percentage = ticket_fee_object.service_fee ticket_fee_maximum = ticket_fee_object.maximum_fee gross_revenue = self.event.calc_revenue(start=latest_invoice_date, end=self.issued_at) invoice_amount = gross_revenue * (ticket_fee_percentage / 100) if invoice_amount > ticket_fee_maximum: invoice_amount = ticket_fee_maximum self.amount = round_money(invoice_amount) if not force and self.amount == 0: logger.warning( 'Invoice amount of Event %s is 0, hence skipping generation', self.event, ) return if not force and self.amount < EventInvoice.MIN_AMOUNT: logger.warning( 'Invoice amount of Event %s is %f which is less than %f, hence skipping generation', self.event, self.amount, EventInvoice.MIN_AMOUNT, ) return net_revenue = round_money(gross_revenue - invoice_amount) orders_query = self.event.get_orders_query( start=latest_invoice_date, end=self.issued_at) first_order_date = orders_query.with_entities( func.min(Order.completed_at)).scalar() last_order_date = orders_query.with_entities( func.max(Order.completed_at)).scalar() payment_details = { 'tickets_sold': self.event.tickets_sold, 'gross_revenue': round_money(gross_revenue), 'net_revenue': round_money(net_revenue), 'first_date': first_order_date or self.previous_month_date, 'last_date': last_order_date or self.issued_at, } self.invoice_pdf_url = create_save_pdf( render_template( 'pdf/event_invoice.html', user=self.user, admin_info=admin_info, currency=currency, event=self.event, ticket_fee=ticket_fee_object, payment_details=payment_details, net_revenue=net_revenue, invoice=self, ), UPLOAD_PATHS['pdf']['event_invoice'], dir_path='/static/uploads/pdf/event_invoices/', identifier=self.identifier, extra_identifiers={'event_identifier': self.event.identifier}, new_renderer=True, ) return self.invoice_pdf_url def send_notification(self, follow_up=False): prev_month = self.previous_month_date.astimezone( pytz.timezone(self.event.timezone)).strftime( "%b %Y") # Displayed as Aug 2016 app_name = get_settings()['app_name'] frontend_url = get_settings()['frontend_url'] link = f'{frontend_url}/event-invoice/{self.identifier}/review' currency = self.event.payment_currency amount = f"{currency} {self.amount}" send_email_for_monthly_fee_payment( self.user, self.event.name, prev_month, amount, app_name, link, follow_up=follow_up, ) if isinstance(follow_up, bool): send_notif_monthly_fee_payment( self.user, self.event.name, prev_month, amount, app_name, link, self.event_id, follow_up=follow_up, )
class EventInvoice(SoftDeletionModel): """ Stripe authorization information for an event. """ __tablename__ = 'event_invoices' id = db.Column(db.Integer, primary_key=True) identifier = db.Column(db.String, unique=True) amount = db.Column(db.Float) address = db.Column(db.String) city = db.Column(db.String) state = db.Column(db.String) country = db.Column(db.String) zipcode = db.Column(db.String) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='SET NULL')) event_id = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='SET NULL')) created_at = db.Column(db.DateTime(timezone=True)) completed_at = db.Column(db.DateTime(timezone=True), nullable=True, default=None) transaction_id = db.Column(db.String) paid_via = db.Column(db.String) payment_mode = db.Column(db.String) brand = db.Column(db.String) exp_month = db.Column(db.Integer) exp_year = db.Column(db.Integer) last4 = db.Column(db.String) stripe_token = db.Column(db.String) paypal_token = db.Column(db.String) status = db.Column(db.String) invoice_pdf_url = db.Column(db.String) event = db.relationship('Event', backref='invoices') user = db.relationship('User', backref='invoices') discount_code_id = db.Column(db.Integer, db.ForeignKey('discount_codes.id', ondelete='SET NULL'), nullable=True, default=None) discount_code = db.relationship('DiscountCode', backref='event_invoices') def __init__(self, amount=None, address=None, city=None, state=None, country=None, zipcode=None, transaction_id=None, paid_via=None, user_id=None, discount_code_id=None, event_id=None, invoice_pdf_url=None, payment_mode=None, brand=None, exp_month=None, exp_year=None, last4=None, stripe_token=None, paypal_token=None, deleted_at=None): self.identifier = get_new_identifier() self.amount = amount self.address = address self.state = state self.country = country self.zipcode = zipcode self.city = city self.user_id = user_id self.event_id = event_id self.transaction_id = transaction_id self.paid_via = paid_via self.created_at = datetime.utcnow() self.discount_code_id = discount_code_id self.status = 'pending' self.invoice_pdf_url = invoice_pdf_url self.payment_mode = payment_mode self.brand = brand self.exp_month = exp_month self.exp_year = exp_year self.last4 = last4 self.stripe_token = stripe_token self.paypal_token = paypal_token self.deleted_at = deleted_at def get_invoice_number(self): return 'I' + str(int(time.mktime( self.created_at.timetuple()))) + '-' + str(self.id) def __repr__(self): return '<EventInvoice %r>' % self.invoice_pdf_url def __str__(self): return self.__repr__()