Started using 'sqlservice'. Added support for many new tables.
This commit is contained in:
parent
9b7d7db996
commit
b77a7069ce
12 changed files with 390 additions and 125 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -66,3 +66,5 @@ docs/_build
|
||||||
|
|
||||||
*.log
|
*.log
|
||||||
*.xls
|
*.xls
|
||||||
|
|
||||||
|
config.yml
|
||||||
|
|
@ -1,6 +1 @@
|
||||||
|
|
||||||
# from logging.config import dictConfig
|
|
||||||
|
|
||||||
# import config
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ try:
|
||||||
with open("config.yml", 'r') as ymlfile:
|
with open("config.yml", 'r') as ymlfile:
|
||||||
file_config = yaml.load(ymlfile, Loader=yaml.FullLoader)
|
file_config = yaml.load(ymlfile, Loader=yaml.FullLoader)
|
||||||
config = {**config, **file_config} # Syntax introduced in Python 3.5
|
config = {**config, **file_config} # Syntax introduced in Python 3.5
|
||||||
except IOError as e:
|
except IOError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
dictConfig(config['logging'])
|
dictConfig(config['logging'])
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
"""
|
"""
|
||||||
Define an Abstract Base Class (ABC) for models
|
Define an Abstract Base Class (ABC) for models
|
||||||
"""
|
"""
|
||||||
from datetime import datetime
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from sqlalchemy import inspect
|
|
||||||
from sqlalchemy.sql.expression import and_
|
from sqlalchemy.sql.expression import and_
|
||||||
from sqlalchemy.orm.collections import InstrumentedList
|
from sqlalchemy.ext.hybrid import hybrid_property
|
||||||
|
|
||||||
|
from sqlservice import ModelBase
|
||||||
|
|
||||||
from pyjeeves import logging
|
from pyjeeves import logging
|
||||||
|
|
||||||
|
|
@ -14,19 +15,25 @@ from . import db
|
||||||
logger = logging.getLogger("PyJeeves." + __name__)
|
logger = logging.getLogger("PyJeeves." + __name__)
|
||||||
|
|
||||||
|
|
||||||
class RawBaseModel():
|
class RawBaseModel(ModelBase):
|
||||||
""" Generalize __init__, __repr__ and to_json
|
""" Generalize __init__, __repr__ and to_json
|
||||||
Based on the models columns , ForetagKod=1"""
|
Based on the models columns , ForetagKod=1"""
|
||||||
|
|
||||||
print_only = () # First filter
|
__to_dict_filter__ = []
|
||||||
print_filter = () # Second filter
|
__to_dict_only__ = ()
|
||||||
to_json_filter = () # Only json filter
|
__column_map__ = {}
|
||||||
column_map = {}
|
|
||||||
|
|
||||||
__table_args__ = {
|
__table_args__ = {
|
||||||
'extend_existing': True
|
'extend_existing': True
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__dict_args__ = {
|
||||||
|
'adapters': {
|
||||||
|
# datetime: lambda value, col, *_: value.strftime('%Y-%m-%d'),
|
||||||
|
Decimal: lambda value, col, *_: "{:.2f}".format(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _base_filters(self, obj, filters=and_()):
|
def _base_filters(self, obj, filters=and_()):
|
||||||
# This method provides base filtering, additional filtering can be done in subclasses
|
# This method provides base filtering, additional filtering can be done in subclasses
|
||||||
|
|
@ -38,63 +45,51 @@ class RawBaseModel():
|
||||||
filters
|
filters
|
||||||
)
|
)
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
""" Define a base way to print models
|
|
||||||
Columns inside `print_filter` are excluded """
|
|
||||||
return '%s(%s)' % (self.__class__.__name__, {
|
|
||||||
column: value
|
|
||||||
for column, value in self._to_dict().items()
|
|
||||||
if column not in self.print_filter
|
|
||||||
})
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _to_json_types(value):
|
|
||||||
if isinstance(value, datetime):
|
|
||||||
return value.strftime('%Y-%m-%d')
|
|
||||||
if isinstance(value, Decimal):
|
|
||||||
return "%.2f" % value
|
|
||||||
try:
|
|
||||||
if isinstance(value, InstrumentedList):
|
|
||||||
return [x.json for x in value]
|
|
||||||
if type(value).__module__ != 'builtins': # Perhaps == builtin?
|
|
||||||
return value.json
|
|
||||||
except AttributeError:
|
|
||||||
logger.debug(str(type(value)) + " was not converted to jsonifyable type")
|
|
||||||
return None
|
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def json(self):
|
|
||||||
""" Define a base way to jsonify models
|
|
||||||
Columns inside `to_json_filter` are excluded
|
|
||||||
Columns inside `to_json_only_filter` are only included """
|
|
||||||
return {
|
|
||||||
column: RawBaseModel._to_json_types(value)
|
|
||||||
# if not isinstance(value, datetime) else value.strftime('%Y-%m-%d')
|
|
||||||
# if type(value).__module__ != self.__module__ # Perhaps == builtin?
|
|
||||||
# else value.json # Convert instances to json if same module
|
|
||||||
for column, value in self._to_dict().items()
|
|
||||||
if column not in self.to_json_filter
|
|
||||||
}
|
|
||||||
|
|
||||||
def _to_dict(self):
|
|
||||||
""" This would more or less be the same as a `to_json`
|
|
||||||
But putting it in a "private" function
|
|
||||||
Allows to_json to be overriden without impacting __repr__
|
|
||||||
Or the other way around
|
|
||||||
And to add filter lists """
|
|
||||||
return {
|
|
||||||
self._map_columns(column.key): getattr(self, column.key)
|
|
||||||
for column in inspect(self.__class__).attrs
|
|
||||||
if not self.print_only or column.key in self.print_only
|
|
||||||
}
|
|
||||||
|
|
||||||
def _map_columns(self, key):
|
def _map_columns(self, key):
|
||||||
if key in self.column_map:
|
if key in self.__column_map__:
|
||||||
return self.column_map[key]
|
return self.__column_map__[key]
|
||||||
return key
|
return key
|
||||||
|
|
||||||
|
def descriptors_to_dict(self):
|
||||||
|
"""Return a ``dict`` that maps data loaded in :attr:`__dict__` to this
|
||||||
|
model's descriptors. The data contained in :attr:`__dict__` represents
|
||||||
|
the model's state that has been loaded from the database. Accessing
|
||||||
|
values in :attr:`__dict__` will prevent SQLAlchemy from issuing
|
||||||
|
database queries for any ORM data that hasn't been loaded from the
|
||||||
|
database already.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
The ``dict`` returned will contain model instances for any
|
||||||
|
relationship data that is loaded. To get a ``dict`` containing all
|
||||||
|
non-ORM objects, use :meth:`to_dict`.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict
|
||||||
|
"""
|
||||||
|
descriptors = self.descriptors()
|
||||||
|
|
||||||
|
return { # Expose hybrid_property extension
|
||||||
|
**{key: getattr(self, key) for key in descriptors.keys()
|
||||||
|
if isinstance(descriptors.get(key), hybrid_property)},
|
||||||
|
# and return all items included in descriptors
|
||||||
|
**{key: value for key, value in self.__dict__.items()
|
||||||
|
if key in descriptors}}
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
rv = super().to_dict()
|
||||||
|
|
||||||
|
if self.__to_dict_only__:
|
||||||
|
return {
|
||||||
|
self._map_columns(key): rv[key]
|
||||||
|
for key in rv
|
||||||
|
if key in self.__to_dict_only__
|
||||||
|
}
|
||||||
|
|
||||||
|
for _filter in self.__to_dict_filter__:
|
||||||
|
rv.pop(_filter)
|
||||||
|
|
||||||
|
return rv
|
||||||
|
|
||||||
def merge(self):
|
def merge(self):
|
||||||
db.raw_session.merge(self)
|
db.raw_session.merge(self)
|
||||||
return self
|
return self
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,10 @@ from sqlalchemy.schema import MetaData, ForeignKey, Column
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
from sqlalchemy.types import Integer, String
|
from sqlalchemy.types import Integer, String
|
||||||
from sqlalchemy.ext.automap import automap_base
|
from sqlalchemy.ext.automap import automap_base
|
||||||
|
from sqlalchemy.ext.hybrid import hybrid_property
|
||||||
from sqlalchemy.sql.expression import and_
|
from sqlalchemy.sql.expression import and_
|
||||||
|
from sqlalchemy.exc import OperationalError
|
||||||
|
|
||||||
# from pyjeeves.session import raw_engine
|
|
||||||
from . import db
|
from . import db
|
||||||
|
|
||||||
from pyjeeves import logging
|
from pyjeeves import logging
|
||||||
|
|
@ -22,64 +23,153 @@ logger = logging.getLogger("PyJeeves." + __name__)
|
||||||
logger.info("Reading Jeeves DB structure")
|
logger.info("Reading Jeeves DB structure")
|
||||||
|
|
||||||
meta = MetaData()
|
meta = MetaData()
|
||||||
meta.reflect(bind=db.raw_session.connection(), only=['ar', 'ars', 'fr', 'kus', 'oh', 'lp', 'vg'])
|
try:
|
||||||
|
meta.reflect(bind=db.raw_session.connection(),
|
||||||
# Table('fr', meta, implicit_returning=False)
|
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)
|
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):
|
class CommodityGroup(Base, RawBaseModel):
|
||||||
__tablename__ = 'vg'
|
__tablename__ = 'vg'
|
||||||
column_map = {'VaruGruppKod': 'CommodityGroupNumber', 'VaruGruppBeskr': 'CommodityGroupName'}
|
__column_map__ = {'VaruGruppKod': 'CommodityGroupNumber',
|
||||||
print_only = ('VaruGruppKod', 'VaruGruppBeskr')
|
'VaruGruppBeskr': 'CommodityGroupName'}
|
||||||
|
__to_dict_only__ = ('VaruGruppKod', 'VaruGruppBeskr', 'ArticleClass')
|
||||||
print_filter = ('Articles', 'articles_collection')
|
print_filter = ('Articles', 'articles_collection')
|
||||||
# to_json_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):
|
class ArticleBalance(Base, RawBaseModel):
|
||||||
__tablename__ = 'ars'
|
__tablename__ = 'ars'
|
||||||
column_map = {'LagSaldo': 'Balance',
|
__column_map__ = {'LagSaldo': 'Balance',
|
||||||
'LagResAnt': 'ReservedBalance',
|
'LagResAnt': 'ReservedBalance',
|
||||||
'LagsaldoAltEnh': 'BalanceAlternative',
|
'LagsaldoAltEnh': 'BalanceAlternative',
|
||||||
'LagResAntAltEnh': 'ReservedAlternativeBalance',
|
'LagResAntAltEnh': 'ReservedAlternativeBalance',
|
||||||
'LagStalle': 'StorageLocationNumber'}
|
'LagStalle': 'StorageLocationNumber'}
|
||||||
print_only = ('LagSaldo',
|
__to_dict_only__ = ('LagSaldo',
|
||||||
'LagResAnt',
|
'LagResAnt',
|
||||||
'LagsaldoAltEnh',
|
'LagsaldoAltEnh',
|
||||||
'LagResAntAltEnh',
|
'LagResAntAltEnh',
|
||||||
'LagStalle')
|
'LagStalle')
|
||||||
# print_filter = ('Articles', 'articles_collection')
|
# print_filter = ('Article', 'articles_collection')
|
||||||
# to_json_filter = ('Articles', 'articles_collection')
|
# to_json_filter = ('Article', 'articles_collection')
|
||||||
|
|
||||||
ArtNr = Column(Integer, ForeignKey('ar.ArtNr'), primary_key=True)
|
ArtNr = Column(Integer, ForeignKey('ar.ArtNr'), primary_key=True)
|
||||||
|
|
||||||
|
|
||||||
class Articles(Base, RawBaseModel):
|
class Article(Base, RawBaseModel):
|
||||||
__tablename__ = 'ar'
|
__tablename__ = 'ar'
|
||||||
|
|
||||||
column_map = {'ArtNr': 'ArticleNumber',
|
__column_map__ = {'ArtNr': 'ArticleNumber',
|
||||||
'ArtBeskr': 'ArticleName',
|
'ArtBeskr': 'ArticleName',
|
||||||
'LagSaldoArtikel': 'Balance',
|
'ArtBeskrSpec': 'ArticleSpec',
|
||||||
'EnhetsKod': 'Unit',
|
'Edit': 'ArticleLongSpec',
|
||||||
'ArtListPris': 'ListPrice'}
|
'LagSaldoArtikel': 'UnitBalance',
|
||||||
print_only = (
|
'EnhetsKod': 'Unit',
|
||||||
|
'ArtListPris': 'UnitListPrice',
|
||||||
|
'Extra1': 'WholeSaleUnit'}
|
||||||
|
__to_dict_only__ = (
|
||||||
'ArtNr',
|
'ArtNr',
|
||||||
'ArtBeskr',
|
'ArtBeskr',
|
||||||
|
'ArtBeskrSpec',
|
||||||
|
'Edit',
|
||||||
'CommodityGroup',
|
'CommodityGroup',
|
||||||
|
'ProductClass',
|
||||||
|
'ArticleClass',
|
||||||
'ArticleBalance',
|
'ArticleBalance',
|
||||||
'EnhetsKod',
|
'EnhetsKod',
|
||||||
'LagSaldoArtikel',
|
'LagSaldoArtikel',
|
||||||
'RowCreatedDt',
|
'RowCreatedDt',
|
||||||
'ArtListPris')
|
'ArtListPris',
|
||||||
|
'PictureFileName',
|
||||||
|
'UnitListPrice',
|
||||||
|
'Extra1',
|
||||||
|
'ListPrice',
|
||||||
|
'Balance')
|
||||||
|
|
||||||
ArtNr = Column(Integer, primary_key=True)
|
ArtNr = Column(Integer, primary_key=True)
|
||||||
|
|
||||||
VaruGruppKod = Column(Integer, ForeignKey('vg.VaruGruppKod'), 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)
|
CommodityGroup = relationship(CommodityGroup, lazy='joined')
|
||||||
|
ProductClass = relationship(ProductClass, lazy='joined')
|
||||||
|
ArticleClass = relationship(ArticleClass, lazy='joined')
|
||||||
ArticleBalance = relationship(ArticleBalance)
|
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
|
@classmethod
|
||||||
def _base_filters(self, obj):
|
def _base_filters(self, obj):
|
||||||
return RawBaseModel._base_filters(
|
return RawBaseModel._base_filters(
|
||||||
|
|
@ -88,17 +178,102 @@ class Articles(Base, RawBaseModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Companies(Base, RawBaseModel):
|
class Company(Base, RawBaseModel):
|
||||||
__tablename__ = 'fr'
|
__tablename__ = 'fr'
|
||||||
column_map = {'FtgNr': 'CompanyNumber', 'FtgNamn': 'CompanyName'}
|
__column_map__ = {'FtgNr': 'CompanyNumber', 'FtgNamn': 'CompanyName'}
|
||||||
print_only = ('CompanyNumber', 'CompanyName')
|
__to_dict_only__ = ('FtgNr', 'FtgNamn', 'Customer')
|
||||||
|
|
||||||
FtgNr = Column(String, primary_key=True)
|
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.prepare()
|
||||||
|
|
||||||
# Base companies for cusomters and suppliers
|
# Base companies for cusomters and suppliers
|
||||||
Customers = Base.classes.kus # Customer information
|
Order = Base.classes.oh # Orders by customers
|
||||||
Orders = Base.classes.oh # Orders by customers
|
|
||||||
DelivLoc = Base.classes.lp # Connections between a delivery company and customer company
|
DelivLoc = Base.classes.lp # Connections between a delivery company and customer company
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,4 @@
|
||||||
from .location import Location
|
from .location import Location
|
||||||
from .article import Article
|
from .article import Article, ArticleCategory
|
||||||
|
from .company import Company
|
||||||
|
from .pricelist import PriceList
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from pyjeeves.models.raw import Articles
|
from pyjeeves.models.raw import Article as ArticleModel, ProductClass, ArticleClass, CommodityGroup
|
||||||
from sqlalchemy.sql.expression import and_
|
from sqlalchemy.sql.expression import and_
|
||||||
|
from sqlalchemy.orm.exc import NoResultFound
|
||||||
|
|
||||||
from pyjeeves import logging
|
from pyjeeves import logging
|
||||||
logger = logging.getLogger("PyJeeves." + __name__)
|
logger = logging.getLogger("PyJeeves." + __name__)
|
||||||
|
|
@ -9,36 +10,51 @@ logger = logging.getLogger("PyJeeves." + __name__)
|
||||||
|
|
||||||
# Relocate Jeeves modules to separate folder and let a "master" module handle imports, and setup.
|
# Relocate Jeeves modules to separate folder and let a "master" module handle imports, and setup.
|
||||||
class Article():
|
class Article():
|
||||||
"""Handles dispatch locations in Jeeves"""
|
"""Handles articles in Jeeves"""
|
||||||
def __init__(self):
|
|
||||||
super(Article, self).__init__()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get(art_no):
|
def get(art_no):
|
||||||
""" Query an article by number """
|
""" Query an article by number """
|
||||||
return Articles.query.filter_by(
|
try:
|
||||||
ArtNr=art_no
|
return ArticleModel.query.filter_by(
|
||||||
).one()
|
ArtNr=art_no
|
||||||
|
).one()
|
||||||
|
except NoResultFound:
|
||||||
|
raise KeyError
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all(filter_=and_(Articles.ItemStatusCode == 0, Articles.ArtKod != 2)):
|
def get_all(filter_=and_(ArticleModel.ItemStatusCode == 0, ArticleModel.ArtKod != 2)):
|
||||||
# .filter_by(ItemStatusCode=0, ArtKod=2)
|
# .filter_by(ItemStatusCode=0, ArtKod=2)
|
||||||
return Articles.query.filter(filter_).all()
|
return ArticleModel.query.filter(filter_).all()
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleCategory():
|
||||||
|
"""Handles article categories, such as classes and groups in Jeeves"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_all():
|
||||||
|
# .filter_by(ItemStatusCode=0, ArtKod=2)
|
||||||
|
prod_classes = ProductClass.query.all()
|
||||||
|
art_classes = ArticleClass.query.all()
|
||||||
|
com_groups = CommodityGroup.query.all()
|
||||||
|
|
||||||
|
return {'ProductClasses': prod_classes,
|
||||||
|
'ArticleClasses': art_classes, 'CommodityGroups': com_groups}
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# print([column.key for column in Companies.__table__.columns])
|
# print([column.key for column in Company.__table__.columns])
|
||||||
|
|
||||||
logger.info("Starting TEST")
|
logger.info("Starting TEST")
|
||||||
# session = RawSession()
|
# session = RawSession()
|
||||||
|
|
||||||
logger.info("Testing gettings an article")
|
logger.info("Testing gettings an article")
|
||||||
# c1 = session.query(Companies).filter_by(FtgNr="179580").first()
|
# c1 = session.query(Company).filter_by(FtgNr="179580").first()
|
||||||
# print(Articles)
|
# print(ArticleModel)
|
||||||
c1 = Articles.query.filter_by(ArtNr="2103").first()
|
c1 = ArticleModel.query.filter_by(ArtNr="2103").first()
|
||||||
print(c1)
|
print(c1)
|
||||||
logger.info(c1.json)
|
logger.info(c1.json)
|
||||||
|
|
||||||
print (
|
print(
|
||||||
len(Article.get_all())
|
len(ArticleModel.get_all())
|
||||||
)
|
)
|
||||||
|
|
|
||||||
55
pyjeeves/repositories/company.py
Normal file
55
pyjeeves/repositories/company.py
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from pyjeeves.models.raw import Company as CompanyModel, Customer as CustomerModel
|
||||||
|
from sqlalchemy.sql.expression import and_
|
||||||
|
|
||||||
|
from pyjeeves import logging
|
||||||
|
logger = logging.getLogger("PyJeeves." + __name__)
|
||||||
|
|
||||||
|
|
||||||
|
# Relocate Jeeves modules to separate folder and let a "master" module handle imports, and setup.
|
||||||
|
class Company():
|
||||||
|
"""Handles companies in Jeeves"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get(ftg_nr):
|
||||||
|
""" Query an article by number """
|
||||||
|
return CompanyModel.query.filter_by(
|
||||||
|
FtgNr=ftg_nr
|
||||||
|
).one()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_all_active_customers():
|
||||||
|
cust = CustomerModel.query.filter(and_(CustomerModel.Makulerad == 0)).all()
|
||||||
|
return [c.CompanyModel for c in cust]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_list(ftg_nr=[]):
|
||||||
|
return CompanyModel.query.filter(
|
||||||
|
CompanyModel.FtgNr.in_(ftg_nr)
|
||||||
|
).all()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# print([column.key for column in CompanyModel.__table__.columns])
|
||||||
|
|
||||||
|
logger.info("Starting TEST")
|
||||||
|
# session = RawSession()
|
||||||
|
|
||||||
|
logger.info("Testing gettings a company")
|
||||||
|
# c1 = session.query(CompanyModel).filter_by(FtgNr="179580").first()
|
||||||
|
# print(CompanyModel)
|
||||||
|
|
||||||
|
# print(CompanyModel.get_list(['406569', '179580', '2440070', '179584']))
|
||||||
|
from pprint import pprint
|
||||||
|
pprint(CompanyModel.get('179584').to_dict())
|
||||||
|
|
||||||
|
# c1 = CompanyModel.query.filter_by(FtgNr="406569").first()
|
||||||
|
# print(c1)
|
||||||
|
# logger.info(c1.json)
|
||||||
|
|
||||||
|
# print(
|
||||||
|
# len(CompanyModel.get_all_active_customers())
|
||||||
|
# )
|
||||||
|
|
||||||
|
# print(CompanyModel.get_all_active_customers()[0].CompanyModel)
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from pyjeeves.models.raw import Companies, DelivLoc
|
from pyjeeves.models.raw import Company, DelivLoc
|
||||||
|
|
||||||
from pyjeeves import logging
|
from pyjeeves import logging
|
||||||
logger = logging.getLogger("PyJeeves." + __name__)
|
logger = logging.getLogger("PyJeeves." + __name__)
|
||||||
|
|
@ -9,6 +9,7 @@ logger = logging.getLogger("PyJeeves." + __name__)
|
||||||
# Relocate Jeeves modules to separate folder and let a "master" module handle imports, and setup.
|
# Relocate Jeeves modules to separate folder and let a "master" module handle imports, and setup.
|
||||||
class Location():
|
class Location():
|
||||||
"""Handles dispatch locations in Jeeves"""
|
"""Handles dispatch locations in Jeeves"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(Location, self).__init__()
|
super(Location, self).__init__()
|
||||||
self.associated_company = '' # Company with new/existing locations
|
self.associated_company = '' # Company with new/existing locations
|
||||||
|
|
@ -30,7 +31,7 @@ class Location():
|
||||||
def create_lev_location(self, ftgnr='', name='', address='',
|
def create_lev_location(self, ftgnr='', name='', address='',
|
||||||
postal_code='', city='', gln='', invoice_ref='', phone=''):
|
postal_code='', city='', gln='', invoice_ref='', phone=''):
|
||||||
|
|
||||||
_loc = Companies(
|
_loc = Company(
|
||||||
FtgNr=str(ftgnr), FtgNamn=name, FtgPostadr5=address,
|
FtgNr=str(ftgnr), FtgNamn=name, FtgPostadr5=address,
|
||||||
FtgLevPostNr=postal_code, FtgPostLevAdr3=city,
|
FtgLevPostNr=postal_code, FtgPostLevAdr3=city,
|
||||||
EAN_Loc_Code=gln, FtgPostAdr1=invoice_ref, ComNr=phone,
|
EAN_Loc_Code=gln, FtgPostAdr1=invoice_ref, ComNr=phone,
|
||||||
|
|
@ -49,21 +50,21 @@ class Location():
|
||||||
for deliv_loc in self._deliv_locs:
|
for deliv_loc in self._deliv_locs:
|
||||||
deliv_loc.merge()
|
deliv_loc.merge()
|
||||||
# self.session.merge(deliv_loc) # Create "connnections" between Customer and Location.
|
# self.session.merge(deliv_loc) # Create "connnections" between Customer and Location.
|
||||||
Companies.commit()
|
Company.commit()
|
||||||
# self.session.commit()
|
# self.session.commit()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# print([column.key for column in Companies.__table__.columns])
|
# print([column.key for column in Company.__table__.columns])
|
||||||
|
|
||||||
logger.info("Starting TEST")
|
logger.info("Starting TEST")
|
||||||
# session = RawSession()
|
# session = RawSession()
|
||||||
|
|
||||||
logger.info("Testing gettings a company")
|
logger.info("Testing gettings a company")
|
||||||
# c1 = session.query(Companies).filter_by(FtgNr="179580").first()
|
# c1 = session.query(Company).filter_by(FtgNr="179580").first()
|
||||||
print(Companies)
|
print(Company)
|
||||||
c1 = Companies.query.filter_by(FtgNr="179580").first()
|
c1 = Company.query.filter_by(FtgNr="179580").first()
|
||||||
logger.info(c1.json)
|
logger.info(c1.json)
|
||||||
# RawSession.remove()
|
# RawSession.remove()
|
||||||
# from sqlalchemy.inspection import inspect
|
# from sqlalchemy.inspection import inspect
|
||||||
# print (inspect(Companies).columns.items())
|
# print (inspect(Company).columns.items())
|
||||||
|
|
|
||||||
22
pyjeeves/repositories/pricelist.py
Normal file
22
pyjeeves/repositories/pricelist.py
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from pyjeeves.models.raw import PriceList as PriceListModel
|
||||||
|
from sqlalchemy.orm.exc import NoResultFound
|
||||||
|
|
||||||
|
from pyjeeves import logging
|
||||||
|
logger = logging.getLogger("PyJeeves." + __name__)
|
||||||
|
|
||||||
|
|
||||||
|
# Relocate Jeeves modules to separate folder and let a "master" module handle imports, and setup.
|
||||||
|
class PriceList():
|
||||||
|
"""Handles price lists in Jeeves"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get(price_list_no):
|
||||||
|
""" Query a price list by number """
|
||||||
|
try:
|
||||||
|
return PriceListModel.query.filter_by(
|
||||||
|
PrisLista=price_list_no
|
||||||
|
).one()
|
||||||
|
except NoResultFound:
|
||||||
|
raise KeyError
|
||||||
|
|
@ -2,6 +2,7 @@ nose==1.3.7
|
||||||
Sphinx==1.8.5
|
Sphinx==1.8.5
|
||||||
pymssql==2.1.4
|
pymssql==2.1.4
|
||||||
SQLAlchemy==1.3.1
|
SQLAlchemy==1.3.1
|
||||||
|
sqlservice==1.1.3
|
||||||
PyMySQL==0.9.3
|
PyMySQL==0.9.3
|
||||||
alembic==1.0.8
|
alembic==1.0.8
|
||||||
PyYAML==5.1
|
PyYAML==5.1
|
||||||
1
setup.py
1
setup.py
|
|
@ -24,6 +24,7 @@ setup(
|
||||||
'sphinx',
|
'sphinx',
|
||||||
'pymssql',
|
'pymssql',
|
||||||
'sqlalchemy',
|
'sqlalchemy',
|
||||||
|
'sqlservice',
|
||||||
'PyMySQL',
|
'PyMySQL',
|
||||||
'alembic',
|
'alembic',
|
||||||
'pyyaml',
|
'pyyaml',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue