PyJeeves/pyjeeves/models/raw.py
Marcus Lindvall 0af38e286e Stored procedure helpers. Order repo and model. More SQLService updates.
* A generic stored procedure helper added, and support for calling them.
* Order and OrderItem tables added, including helpers and calls to SP for creation and updates.
* Minor updates to other repositories.
2019-08-30 12:09:10 +02:00

386 lines
14 KiB
Python

# -*- coding: utf-8 -*-
"""
pyjeeves.models
~~~~~~~~~~~~~~~~~~~~~~
Jeeves raw data models
"""
from sqlalchemy.schema import ForeignKey, Column
from sqlalchemy.orm import relationship
from sqlalchemy.types import Integer, String
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.sql.expression import and_
from pyjeeves import logging
from .abc import RawBaseModel
from .sp_classes import OrderHead, OrderRow, PlaceOrder
import re
logger = logging.getLogger("PyJeeves." + __name__)
class ProductClass(RawBaseModel):
__tablename__ = 'xp'
__column_map__ = {'ArtProdKlass': 'ProductClassNumber', 'ArtProdklBeskr': 'ProductClassName'}
__to_dict_only__ = ('ArtProdKlass', 'ArtProdklBeskr')
# print_filter = ('Articles', 'articles_collection')
class ArticleClass(RawBaseModel):
__tablename__ = 'xm'
__column_map__ = {'ArtKod': 'ArticleClassNumber', 'ArtTypBeskr': 'ArticleClassName'}
__to_dict_only__ = ('ArtKod', 'ArtTypBeskr')
# print_filter = ('Articles', 'articles_collection')
class CommodityGroup(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(RawBaseModel):
__tablename__ = 'xae'
__column_map__ = {'AltEnhetKod': 'UnitCode', 'AltEnhetBeskr': 'UnitName',
'AltEnhetOmrFaktor': 'DefaultUnitConv'}
__to_dict_only__ = ('AltEnhetBeskr', 'AltEnhetOmrFaktor')
class ArticleUnit(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(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(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(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 DelivLoc(RawBaseModel):
__tablename__ = 'lp'
class CustomerCategory(RawBaseModel):
__tablename__ = 'x1k'
KundKategoriKod = Column(Integer, primary_key=True)
class Customer(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 if self.KundKategori else ""
class PriceList(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(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())
class Order(RawBaseModel):
__tablename__ = 'oh'
__column_map__ = {'OrderNr': 'OrderNumber', 'FtgNr': 'CompanyNumber',
'OrdDatum': 'OrderDate', 'OrdStat': 'OrderStatusCode',
'OrdLevAdr1': 'AddrName', 'OrdLevAdr2': 'AddrCO',
'OrdLevAdr3': 'AddrStreet', 'OrdLevAdrLandsKod': 'AddrCountry',
'KundBestNr': 'CustomerContact', 'KundRef2': 'CustomerReference',
'GodsMarke1': 'ShippingInfo', 'GodsMarke2': 'InternalInfo',
'TA_MailNotified': 'ShippingEmail', 'TA_PhonNotifiedNo': 'ShippingPhone',
'TA_SMSNotifiedNo': 'ShippingSMS', 'LevSattKod': 'ShippingTypeCode'}
__to_dict_only__ = ('OrderNr', 'FtgNr', 'OrdDatum', 'OrdStat', 'CompanyName', 'LevSattKod',
'OrdLevAdr1', 'OrdLevAdr2', 'OrdLevAdr3',
'OrdLevAdrLandsKod', 'KundBestNr', 'KundRef2', 'GodsMarke1',
'GodsMarke2', 'OrderItems', 'AddrPostalCode', 'AddrCity',
'TA_MailNotified', 'TA_PhonNotifiedNo', 'TA_SMSNotifiedNo')
__dict_args__ = {
'adapters': {
**{
'OrdDatum': lambda ord_date, *_: ord_date.strftime("%Y-%m-%d"),
},
**RawBaseModel.__dict_args__['adapters']
}
}
OrderNr = Column(Integer, primary_key=True)
FtgNr = Column(String, ForeignKey('fr.FtgNr'))
Company = relationship('Company', uselist=False)
OrderItems = relationship('OrderItem', uselist=True, back_populates="Order", lazy='joined')
@hybrid_property
def CompanyName(self):
return self.Company.FtgNamn if self.Company else ""
@CompanyName.setter
def CompanyName(self, value):
return
@hybrid_property
def AddrPostalCode(self):
if not self.OrdLevAdr4:
return
s = re.split('(?!\d)\s(?!\d)', self.OrdLevAdr4)
return s[0] if len(s) > 1 else ''
@AddrPostalCode.setter
def AddrPostalCode(self, value):
self.OrdLevAdr4 = value + (self.OrdLevAdr4 if self.OrdLevAdr4 else '')
@hybrid_property
def AddrCity(self):
if not self.OrdLevAdr4:
return
s = re.split('(?!\d)\s(?!\d)', self.OrdLevAdr4, maxsplit=1)
return s[1] if len(s) > 1 else ''
@AddrCity.setter
def AddrCity(self, value):
self.OrdLevAdr4 = (self.OrdLevAdr4 if self.OrdLevAdr4 else '') + ' ' + value
def create(self, webusername=None):
# TODO: Extend with additional functionlity if desired.
self['OrderNr'], invoicing_possible = OrderHead(self['FtgNr'], webusername).callproc()
return self, invoicing_possible
def save(self, invoiced=False):
payment_method = 'invoice'
if not invoiced:
payment_method = 'card'
PlaceOrder(
self['FtgNr'], self['OrderNr'], payment_method, data=self.to_dict()).callproc()
return self
class OrderItem(RawBaseModel):
__tablename__ = 'orp'
__column_map__ = {'OrdRadNr': 'OrderRowNumber', 'vb_pris': 'UnitPrice',
'ArtNr': 'ArticleNumber', 'OrdAntal': 'UnitAmount',
'OrdAntalAltEnh': 'AltUnitAmount', 'AltEnhetKod': 'AltUnit'}
__to_dict_only__ = ('OrdRadNr', 'vb_pris', 'ArtNr', 'ArticleName', 'OrdAntal',
'OrdAntalAltEnh', 'AltEnhetKod')
# Do not serialize order relationship
__dict_args__ = {
'adapters': {
**{
Order: None,
},
**RawBaseModel.__dict_args__['adapters']
}
}
OrderNr = Column(Integer, ForeignKey('oh.OrderNr'), primary_key=True)
OrdRadNr = Column(Integer, primary_key=True)
OrdRadNrStrPos = Column(Integer, primary_key=True)
OrdRestNr = Column(Integer, primary_key=True)
ArtNr = Column(Integer, ForeignKey('ar.ArtNr'))
Order = relationship('Order', uselist=False)
Article = relationship(Article)
@hybrid_property
def ArticleName(self):
return self.Article.ArtBeskr if self.Article else ""
@ArticleName.setter
def ArticleName(self, value):
return
def save(self):
# TODO: Additional information may be returned if desired.
row_no = OrderRow(
company_no=self['FtgNr'], order_no=self['OrderNr'], item_no=self['ArtNr'],
qty=self['OrdAntal'], qty_alt_unit=self['OrdAntalAltEnh'],
alt_unit=self['AltEnhetKod'], pers_sign='marlin').callproc()
self['OrdRadNr'] = row_no
return self