PyJeeves/pyjeeves/models/sp_classes.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

250 lines
10 KiB
Python

# -*- coding: utf-8 -*-
import pymssql
from collections import OrderedDict
from pyjeeves.models import db
from pyjeeves import logging
# from datetime import datetime
# from decimal import Decimal
logger = logging.getLogger("PyJeeves." + __name__)
class StoredProcedure(OrderedDict):
__raw_params = {}
# https://www.mssqltips.com/sqlservertip/1669/generate-a-parameter-list-for-all-sql-server-stored-procedures-and-functions/ # noqa
query = """SELECT SCHEMA_NAME(SCHEMA_ID) AS [Schema],
SO.name AS [ObjectName],
SO.Type_Desc AS [ObjectType (UDF/SP)],
P.parameter_id AS [ParameterID],
P.name AS [ParameterName],
TYPE_NAME(P.user_type_id) AS [ParameterDataType],
P.max_length AS [ParameterMaxBytes],
P.is_output AS [IsOutPutParameter]
FROM sys.objects AS SO
INNER JOIN sys.parameters AS P
ON SO.OBJECT_ID = P.OBJECT_ID
WHERE SO.name LIKE '%Jeeves_Esales_%' OR
SO.name LIKE '%JAPP_spr_LogTrade_%' AND
SO.OBJECT_ID IN ( SELECT OBJECT_ID
FROM sys.objects
WHERE TYPE IN ('P','FN'))
ORDER BY [Schema], SO.name, P.parameter_id"""
logger.debug("Getting information about stored procedures from database")
for param in db.execute(query):
if param['ObjectName'] not in __raw_params:
__raw_params[param['ObjectName']] = OrderedDict()
param_name = param['ParameterName'][1:]
__raw_params[param['ObjectName']][param_name] = param
@classmethod
def get_params_for(cls, procedure_name):
rv = OrderedDict()
for key in cls.__raw_params[procedure_name]:
param = cls.__raw_params[procedure_name][key]
if 'int' in param['ParameterDataType'].lower():
param_type = int
elif ('money' in param['ParameterDataType'].lower() or
'decimal' in param['ParameterDataType'].lower() or
'float' in param['ParameterDataType'].lower() or
'qty' in param['ParameterDataType'].lower()):
param_type = float
else:
# TODO: Format datetime and perhaps decimal?
param_type = str
if param['IsOutPutParameter'] == 1:
param_type = pymssql.output(param_type)
else:
param_type = param_type()
rv[key] = param_type
return rv
def __init__(self, procedure_name):
super(StoredProcedure, self).__init__()
self.procedure = procedure_name
self.update(StoredProcedure.get_params_for(self.procedure))
def _set_output(self, data=(), ret_resultset=False):
if ret_resultset:
return data
if len(self) != len(data):
raise
for p, k in enumerate(self):
if isinstance(self[k], pymssql.output):
self[k] = data[p]
return self
# Should the original object be unmodified? Return a new object:
# return [(k, data[p]) for p, k in enumerate(self)]
def callproc(self, resultset=False):
return self._set_output(db.callproc(
self.procedure,
self.values()),
resultset)
def values(self):
return [value if value else None
for value in super(StoredProcedure, self).values()]
def __setitem__(self, key, obj):
if (key in self and type(self[key]) is not type(obj) and
obj is not None and not isinstance(self[key], pymssql.output)):
raise TypeError
super(StoredProcedure, self).__setitem__(key, obj)
class OrderHead(StoredProcedure):
"""Mapping for the Jeeves_Esales_CreateOrder stored procedure parameters
webapp031 and WEBAPP003 determines default order status"""
# TODO: Extend with additional functionlity if desired.
def __init__(self, company_no, web_user_name):
super(OrderHead, self).__init__('Jeeves_Esales_CreateOrder')
self['c_CompanyNo'] = company_no
# Some defaults:
self['c_ForetagKod'] = 1 # Hardcoded to LK
self['c_PersSign'] = 'marlin' # From API profile, or default
# self['c_OrderType'] = None # Default set by WEBAPP008
# self['c_TemplateRowID'] = None # No template used
# self['c_Saljare'] = None # 600 # From API profile, or default
# Unique ID added to 'kpw' when invoicing is allowed.
print(web_user_name)
self['c_webUserName'] = web_user_name
# self['LangID'] = 0 # Default to Swedish
# self['BatchId'] = '' # unused
# self['Run_Type'] = None # Could be 'R', but doesn't work
# self['Edit'] = None # Custom ordertext, currently not used in procedure
# self['EditExt'] = None # Custom ordertext, currently not used in procedure
# self['Lagstalle'] = None # '1' # Used to override customer default
# self['OverrideCreditLimit'] = 0 # Set to a char to override credit limit
# self['OrderNumber'] = pymssql.output(int)
def callproc(self):
super(OrderHead, self).callproc()
# If call succeeded, then order is allowed to be invoiced.
return self['o_OrderNumber'], bool(self['c_webUserName'])
class OrderRow(StoredProcedure):
"""Mapping for the Jeeves_Esales_AddOrderRow stored procedure parameters
AltEnhetKod logic needs to have been added to the procedure"""
def __init__(self, company_no, order_no, item_no,
qty=None, qty_alt_unit=None, alt_unit='', pers_sign='biz'):
super(OrderRow, self).__init__('Jeeves_Esales_AddOrderRow')
self['c_CompanyNo'] = str(company_no)
self['c_OrderNumber'] = int(order_no)
self['c_ItemNo'] = str(item_no)
self['c_Qty'] = float(qty) if qty else None
self['c_QtyAltEnh'] = float(qty_alt_unit) if qty_alt_unit else None
self['c_AltEnhetKod'] = str(alt_unit)
self['c_PersSign'] = str(pers_sign)
# Used to set date for delivery (c_OrdBegLevDat) and (c_OrdBerLevDat)
self['c_RequestedDate'] = None
# Some defaults:
self['c_ForetagKod'] = 1 # Hardcoded to LK
# self['OrderNumber'] = 0 # Required, ordernumber to add row to
# self['webUserName'] = order_head['webUserName']
# self['CompanyNo'] = order_head['CompanyNo']
# self['PersSign'] = order_head['PersSign']
# self['LangID'] = order_head['LangID']
# self['ItemNo'] = '' # Required, item to create row for
# self['c_Qty'] = None # Only one of qty or qtyaltenh may be used
# self['QtyAltEnh'] = None
# self['RequestedDate'] = '' # unused
# self['BatchId'] = order_head['BatchId']
# self['ArtSerieNr'] = '' # unused
# self['c_OrderType'] = None
# self['Run_Type'] = None # Could be 'R', but doesn't work
# self['c_TemplateRowID'] = None # No template used
# self['Edit'] = None # Custom order row text
# self['EditExt'] = None # Custom extended order row text
# self['Lagstalle'] = None # str: use default
# self['AltEnhetKod'] = '' # Override default alternative unit if desired
# self['AllocateAvailable'] = 0 # unused
# self['OverrideCreditLimit'] = 0 # Set to a char to override credit limit
# self['o_OrderRow'] = pymssql.output(int)
# self['o_NextQty'] = pymssql.output(float)
# self['o_NextDate'] = pymssql.output(str)
# self['o_LastQty'] = pymssql.output(float)
# self['o_LastDate'] = pymssql.output(str)
# self['o_AllocatedQty'] = pymssql.output(float)
# self['o_AllocatedDate'] = pymssql.output(str)
def callproc(self):
super(OrderRow, self).callproc()
return self['o_OrderRow']
class PlaceOrder(StoredProcedure):
"""Mapping for the Jeeves_Esales_PlaceOrder stored procedure parameters
webapp031 and WEBAPP003 determines default order status"""
def __init__(self, company_no, order_no, payment_method='card', data={}):
super(PlaceOrder, self).__init__('Jeeves_Esales_PlaceOrder')
self['c_CompanyNo'] = str(company_no)
self['c_OrderNumber'] = int(order_no)
self['c_kundref2'] = data.get('CustomerContact') # Er ref, kontaktperson
self['c_kundbestnr'] = data.get('CustomerReference')
self['c_editext'] = data.get('ExtraText') # Extern text
self['c_CoName'] = data.get('AddrName')
self['c_Addr1'] = data.get('AddrCO') # Lev.adress, c/o
self['c_Addr2'] = data.get('AddrStreet')
self['c_PostalCode'] = data.get('AddrPostalCode')
self['c_City'] = data.get('AddrCity')
self['c_CountryCode'] = data.get('AddrCountry', 'SE') # Ex: SE, FI etc.
self['c_godsmarke1'] = data.get('ShippingInfo')
self['c_godsmarke2'] = data.get('InternalInfo') # Kundspecifikt
notify_info = NotifyInfo(company_no).callproc()
self['c_TA_MailNotified'] = data.get('ShippingEmail', notify_info.get('email'))
self['c_TA_PhonNotifiedNo'] = data.get('ShippingPhone', notify_info.get('phone'))
self['c_TA_SMSNotifiedNo'] = data.get('ShippingSMS', notify_info.get('sms'))
# 1 = card, else invoice. Card requires manual update.
self['c_PaymentType'] = '1' if payment_method is 'card' else '0'
self['c_LevSattKod'] = 2 # 2 = Schenker, 4 = Collect
self['c_orderStatus'] = None # Override orderStatusCode when using invoicing
self['c_ForetagKod'] = 1 # Hardcoded to LK
self['c_orderStatus'] = None
self['c_ProvinceCode'] = None # For US customers etc.
class NotifyInfo(StoredProcedure):
"""Mapping for the JAPP_spr_LogTrade_Get_NotifyInfo stored procedure parameters
webapp031 and WEBAPP003 determines default order status"""
def __init__(self, company_no):
super(NotifyInfo, self).__init__('JAPP_spr_LogTrade_Get_NotifyInfo')
self['c_FtgNr'] = str(company_no)
self['c_ForetagKod'] = 1 # Hardcoded to LK
def callproc(self):
result = super(NotifyInfo, self).callproc(resultset=True)
ret = {'email': None, 'sms': None, 'phone': None}
if isinstance(result, list):
for r in result:
if r[1][7:].lower() in ret:
ret[r[1][7:].lower()] = r[0]
return ret