Our custom ordering system
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

242 lines
5.7 KiB

import enum
from datetime import datetime, timedelta
from collections import namedtuple
VendorAggregate = namedtuple("VendorAggregate", ["name", "terms"])
@enum.unique
class OrderStatus(enum.Enum):
OPEN = 1
APPROVAL = 2
ORDERED = 3
COMPLETED = 4
HOLD = 5
@enum.unique
class OrderCategory(enum.Enum):
DISPOSABLE = 1
SOLVENT = 2
CHEMICAL = 3
BIOLAB = 4
SYNTHESIS = 5
@enum.unique
class UserRole(enum.Enum):
NEW = 1
USER = 2
PURCHASER = 3
ADMIN = 4
INACTIVE = 5
@property
def principal(self):
"""returns the principal identifier of the role"""
return "role:" + self.name.lower()
class Model:
def __hash__(self):
items = sorted(self.__dict__.items())
no_log = ((k, v) for k, v in items if not k == "log")
content = ((k, v) for k, v in no_log if not k.startswith("_"))
return hash(tuple(content))
def __eq__(self, other):
return hash(self) == hash(other)
class OrderItem(Model):
"""an ordered item"""
# properties
id = None
cas_description = None
catalog_nr = None
vendor = None
category = None
package_size = None
unit_price = None
currency = None
amount = None
account = None
comment = None
# redundant properties, could be determined from orders log
created_on = None
created_by = None
status = None
def __init__(
self,
id,
cas_description,
catalog_nr,
vendor,
category,
package_size,
unit_price,
amount,
currency="",
account="",
comment="",
created_on=None,
created_by=None,
status=None,
):
self.id = id
self.cas_description = cas_description
self.catalog_nr = catalog_nr
self.vendor = vendor
self.category = category
self.package_size = package_size
self.unit_price = unit_price
self.amount = amount
self.currency = currency
self.account = account
self.comment = comment
self.created_on = created_on
self.created_by = created_by
self.status = status
self.log = []
def __repr__(self):
return f"<ordr3.models.OrderItem id={self.id}>"
@property
def total_price(self):
return self.unit_price * self.amount
@property
def in_process(self):
return self.status not in (OrderStatus.OPEN, OrderStatus.HOLD)
def add_to_log(self, log_entry):
"""adds a log item to the status log"""
if len(self.log) == 0:
self.created_by = log_entry.by
self.created_on = log_entry.date
self.status = log_entry.status
self.log.append(log_entry)
class LogEntry(Model):
"""an entry in the order log"""
order_id = None
status = None
by = None
date = None
def __init__(self, order_id, status, by, date=None):
self.order_id = order_id
self.status = status
self.by = by
self.date = date or datetime.utcnow()
def __repr__(self):
return (
f"<ordr3.models.LogEntry({self.order_id}, "
f"{self.status}, {self.by}, {self.date})>"
)
class ProposedConsumable:
"""counting orders to find out if they are consumables"""
def __init__(self, order):
self.order = order
self.times = 0
class Vendor(Model):
"""a model for finding vendor names and their search terms"""
term = None
name = None
def __init__(self, term, name):
self.term = term
self.name = name
def __repr__(self):
return f"<ordr3.models.Vendor term={self.term} name={self.name}>"
class User(Model):
id = None
username = None
first_name = None
last_name = None
email = None
password = None
role = None
def __init__(
self, id, username, first_name, last_name, email, password, role
):
self.id = id
self.username = username
self.first_name = first_name
self.last_name = last_name
self.email = email
self.password = password
self.role = role
@property
def principal(self):
return f"user:{self.username}"
@property
def principals(self):
tmp = [self.principal]
if self.role in {UserRole.PURCHASER, UserRole.ADMIN}:
tmp.append(UserRole.USER.principal)
if self.role == UserRole.ADMIN:
tmp.append(UserRole.PURCHASER.principal)
tmp.append(self.role.principal)
return tmp
@property
def is_active(self):
"""check if it is an active user account"""
return self.role in {UserRole.USER, UserRole.PURCHASER, UserRole.ADMIN}
def __hash__(self):
items = sorted(self.__dict__.items())
content = ((k, v) for k, v in items if not k.startswith("_"))
return hash(tuple(content))
def __repr__(self):
return f"<ordr3.models.User id={self.id}, username={self.username}>"
class PasswordResetToken(Model):
token = None
user_id = None
valid_until = None
def __init__(self, token, user_id, valid_until=None):
self.token = token
self.user_id = user_id
defaul_valid_until = datetime.utcnow() + timedelta(hours=1)
self.valid_until = valid_until or defaul_valid_until
@property
def is_valid(self):
try:
return datetime.utcnow() < self.valid_until
except TypeError:
return False
def __repr__(self):
date_str = self.valid_until.strftime("%Y-%m-%d %H:%M:%S")
return (
f"<ordr3.models.PasswordResetToken token={self.token}, "
f"user_id={self.user_id}, valid_until={date_str}>"
)