# -*- coding: utf-8 -*- """ pyjeeves.models ~~~~~~~~~~~~~~~~~~~~~~ Jeeves raw data models """ # from sqlalchemy import Column, String from sqlalchemy.schema import MetaData, ForeignKey, Column from sqlalchemy.orm import relationship from sqlalchemy.types import Integer, String from sqlalchemy.ext.automap import automap_base from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.sql.expression import and_ from sqlalchemy.exc import OperationalError from . import db from pyjeeves import logging from .abc import RawBaseModel logger = logging.getLogger("PyJeeves." + __name__) logger.info("Reading Jeeves DB structure") meta = MetaData() try: meta.reflect(bind=db.raw_session.connection(), only=['ar', 'ars', 'xae', 'xare', 'fr', 'kus', 'x1k', 'oh', 'lp', 'vg', 'xp', 'xm', 'prh', 'prl']) except OperationalError as e: logger.error("Failed to read Jeeves DB structure") raise e Base = automap_base(cls=db.Model, name='Model', metadata=meta) class ProductClass(Base, RawBaseModel): __tablename__ = 'xp' __column_map__ = {'ArtProdKlass': 'ProductClassNumber', 'ArtProdklBeskr': 'ProductClassName'} __to_dict_only__ = ('ArtProdKlass', 'ArtProdklBeskr') # print_filter = ('Articles', 'articles_collection') class ArticleClass(Base, RawBaseModel): __tablename__ = 'xm' __column_map__ = {'ArtKod': 'ArticleClassNumber', 'ArtTypBeskr': 'ArticleClassName'} __to_dict_only__ = ('ArtKod', 'ArtTypBeskr') # print_filter = ('Articles', 'articles_collection') class CommodityGroup(Base, RawBaseModel): __tablename__ = 'vg' __column_map__ = {'VaruGruppKod': 'CommodityGroupNumber', 'VaruGruppBeskr': 'CommodityGroupName'} __to_dict_only__ = ('VaruGruppKod', 'VaruGruppBeskr', 'ArticleClass') print_filter = ('Articles', 'articles_collection') # to_json_filter = ('Articles', 'articles_collection') ArtKod = Column(Integer, ForeignKey('xm.ArtKod'), primary_key=True) ArticleClass = relationship(ArticleClass) class ArticleAlternativeUnit(Base, RawBaseModel): __tablename__ = 'xae' __column_map__ = {'AltEnhetKod': 'UnitCode', 'AltEnhetBeskr': 'UnitName', 'AltEnhetOmrFaktor': 'DefaultUnitConv'} __to_dict_only__ = ('AltEnhetBeskr', 'AltEnhetOmrFaktor') class ArticleUnit(Base, RawBaseModel): __tablename__ = 'xare' __column_map__ = {'ArtNr': 'ArticleNumber', 'AltEnhetKod': 'UnitCode', 'AltEnhetOmrFaktor': 'UnitConv', 'AltEnhetOrderStd': 'DefaultSalesUnit'} __to_dict_only__ = ('AltEnhetKod', 'AltEnhetOmrFaktor', 'AltEnhetOrderStd', 'ArticleAlternativeUnit') ArtNr = Column(String, ForeignKey('ar.ArtNr'), primary_key=True) AltEnhetKod = Column(Integer, ForeignKey('xae.AltEnhetKod'), primary_key=True) ArticleAlternativeUnit = relationship(ArticleAlternativeUnit) class ArticleBalance(Base, RawBaseModel): __tablename__ = 'ars' __column_map__ = {'LagSaldo': 'Balance', 'LagResAnt': 'ReservedBalance', 'LagsaldoAltEnh': 'BalanceAlternative', 'LagResAntAltEnh': 'ReservedAlternativeBalance', 'LagStalle': 'StorageLocationNumber'} __to_dict_only__ = ('LagSaldo', 'LagResAnt', 'LagsaldoAltEnh', 'LagResAntAltEnh', 'LagStalle') # print_filter = ('Article', 'articles_collection') # to_json_filter = ('Article', 'articles_collection') ArtNr = Column(Integer, ForeignKey('ar.ArtNr'), primary_key=True) class Article(Base, RawBaseModel): __tablename__ = 'ar' __column_map__ = {'ArtNr': 'ArticleNumber', 'ArtBeskr': 'ArticleName', 'ArtBeskrSpec': 'ArticleSpec', 'Edit': 'ArticleLongSpec', 'LagSaldoArtikel': 'UnitBalance', 'EnhetsKod': 'Unit', 'ArtListPris': 'UnitListPrice', 'Extra1': 'WholeSaleUnit'} __to_dict_only__ = ( 'ArtNr', 'ArtBeskr', 'ArtBeskrSpec', 'Edit', 'CommodityGroup', 'ProductClass', 'ArticleClass', 'ArticleBalance', 'EnhetsKod', 'LagSaldoArtikel', 'RowCreatedDt', 'ArtListPris', 'PictureFileName', 'UnitListPrice', 'Extra1', 'ListPrice', 'Balance') ArtNr = Column(Integer, primary_key=True) VaruGruppKod = Column(Integer, ForeignKey('vg.VaruGruppKod'), primary_key=True) ArtProdKlass = Column(Integer, ForeignKey('xp.ArtProdKlass'), primary_key=True) ArtKod = Column(Integer, ForeignKey('xm.ArtKod'), primary_key=True) CommodityGroup = relationship(CommodityGroup, lazy='joined') ProductClass = relationship(ProductClass, lazy='joined') ArticleClass = relationship(ArticleClass, lazy='joined') ArticleBalance = relationship(ArticleBalance) ArticleUnit = relationship(ArticleUnit) def get_unit_conv(self): if self.ArtFsgForp: return self.ArtFsgForp for unit in self.ArticleUnit: if unit.AltEnhetOrderStd == "1": if unit.AltEnhetOmrFaktor: return unit.AltEnhetOmrFaktor else: return unit.ArticleAlternativeUnit.AltEnhetOmrFaktor return 1 @hybrid_property def ListPrice(self): try: return self.ArtListPris * self.get_unit_conv() except TypeError: logger.debug("NoneType error, %s" % self.ArtNr) @hybrid_property def Balance(self): try: return self.LagSaldoArtikel / self.get_unit_conv() except TypeError: logger.debug("NoneType error, %s" % self.ArtNr) @classmethod def _base_filters(self, obj): return RawBaseModel._base_filters( obj, and_(obj.LagTyp == 0) ) class Company(Base, RawBaseModel): __tablename__ = 'fr' __column_map__ = {'FtgNr': 'CompanyNumber', 'FtgNamn': 'CompanyName'} __to_dict_only__ = ('FtgNr', 'FtgNamn', 'Customer') FtgNr = Column(String, primary_key=True) Customer = relationship('Customer', uselist=False, back_populates='Company', lazy='joined') class CustomerCategory(Base, RawBaseModel): __tablename__ = 'x1k' KundKategoriKod = Column(Integer, primary_key=True) class Customer(Base, RawBaseModel): __tablename__ = 'kus' __column_map__ = {'FtgNr': 'CompanyNumber', 'kundkategorikod': 'CustomerCategoryCode', 'PrisListaKundSpec': 'PriceListPrimary', 'PrisLista': 'PriceListSecondary'} __to_dict_only__ = ('kundkategorikod', 'PriceList', 'PriceListCommon', 'CustomerCategory', 'PrisLista', 'PrisListaKundSpec') FtgNr = Column(String, ForeignKey('fr.FtgNr'), primary_key=True) KundKategoriKod = Column(Integer, ForeignKey('x1k.KundKategoriKod')) PrisLista = Column(Integer, ForeignKey('prh.PrisLista')) PrisListaKundSpec = Column(Integer, ForeignKey('prh.PrisLista')) Company = relationship("Company", back_populates="Customer") PriceList = relationship("PriceList", uselist=False, lazy='joined', foreign_keys='PriceList.FtgNr') PriceListCommon = relationship("PriceList", uselist=False, foreign_keys='PriceList.PrisLista', primaryjoin="Customer.PrisLista==PriceList.PrisLista") KundKategori = relationship("CustomerCategory") @hybrid_property def CustomerCategory(self): return self.KundKategori.KundKatBeskr class PriceList(Base, RawBaseModel): __tablename__ = 'prh' __column_map__ = {'PrisListaBeskr': 'Description', 'PrisLista': 'PriceListNumber', 'MarkUpBelopp': 'PriceFactor'} __to_dict_only__ = ('PrisListaBeskr', 'PrisLista', 'PriceListItems', 'MarkUpBelopp') PrisLista = Column(Integer, primary_key=True) FtgNr = Column(String, ForeignKey('kus.FtgNr')) Customer = relationship('Customer', uselist=False, foreign_keys='Customer.PrisListaKundSpec') PriceListItems = relationship('PriceListItem', back_populates="PriceList", lazy='joined') class PriceListItem(Base, RawBaseModel): __tablename__ = 'prl' __column_map__ = {'ArtNr': 'ArticleNumber', 'vb_pris': 'UnitPrice', 'MarkUpBelopp': 'UnitPriceFactor', 'NollFaktor': 'NullPriceAllowed'} __to_dict_only__ = ('ArtNr', 'vb_pris', 'MarkUpBelopp', 'NollFaktor', 'Price') __to_dict_filter__ = ['PriceList'] # Do not serialize price list relationship __dict_args__ = { 'adapters': { **{ PriceList: None }, **RawBaseModel.__dict_args__['adapters'] } } PrisLista = Column(Integer, ForeignKey('prh.PrisLista'), primary_key=True) ArtNr = Column(Integer, ForeignKey('ar.ArtNr'), primary_key=True) PriceList = relationship('PriceList', uselist=False) Article = relationship(Article) # TODO: Could likely be optimized by getting all articles in one query and mangled in repo @hybrid_property def Price(self): if not self.vb_pris and not self.MarkUpBelopp: return ( (self.Article.ArtListPris + self.PriceList.MarkUpBelopp) * self.Article.get_unit_conv()) if self.vb_pris: return self.vb_pris * self.Article.get_unit_conv() else: return ( (self.Article.ArtListPris + self.MarkUpBelopp) * self.Article.get_unit_conv()) Base.prepare() # Base companies for cusomters and suppliers Order = Base.classes.oh # Orders by customers DelivLoc = Base.classes.lp # Connections between a delivery company and customer company