Compare commits
24 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c1eb903f7 | |||
| 229ea6fb5b | |||
| efc505c12e | |||
| 5fe140714e | |||
| f649b5f953 | |||
| 1a7ada9d56 | |||
| 144fdbefb1 | |||
| 5d1b5f90ac | |||
| f9686c5306 | |||
| 2fa1e4aa7a | |||
| 7310954468 | |||
| 1ae563654c | |||
| 7288e88135 | |||
| 0330e7a4bf | |||
| 6339e9d1ce | |||
| fc7c1e13bc | |||
| a23e88ff2f | |||
| cf7ed09049 | |||
| ee902ee733 | |||
| 0af38e286e | |||
| b77a7069ce | |||
| 9b7d7db996 | |||
| 0fae8725e0 | |||
| 28726fee01 |
35 changed files with 11867 additions and 136 deletions
|
|
@ -1,3 +1,6 @@
|
|||
# Jeeves specific scripts
|
||||
jeeves_sp_updates/
|
||||
|
||||
# Git
|
||||
.git
|
||||
.gitignore
|
||||
|
|
|
|||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,3 +1,5 @@
|
|||
*.csv
|
||||
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
|
|
@ -66,3 +68,5 @@ docs/_build
|
|||
|
||||
*.log
|
||||
*.xls
|
||||
|
||||
config.yml
|
||||
|
|
@ -4,6 +4,7 @@ LABEL description="PyJeeves syncronization application" \
|
|||
maintainer="Marcus Lindvall <marcus.lindvall@gmail.com>"
|
||||
|
||||
RUN apk add --no-cache build-base freetds-dev git \
|
||||
&& pip install --no-cache-dir cython \
|
||||
&& pip install --no-cache-dir git+https://github.com/pymssql/pymssql.git \
|
||||
&& apk del --purge build-base freetds-dev git
|
||||
|
||||
|
|
@ -12,7 +13,9 @@ RUN apk add --no-cache freetds
|
|||
WORKDIR /app
|
||||
COPY ./requirements.txt /app/requirements.txt
|
||||
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
RUN apk add --no-cache build-base libffi-dev openssl-dev \
|
||||
&& pip install --no-cache-dir -r requirements.txt \
|
||||
&& apk del --purge build-base
|
||||
|
||||
COPY . /app
|
||||
|
||||
|
|
|
|||
22
README.md
Normal file
22
README.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# PyJeeves Module
|
||||
|
||||
This project is a Jeeves data extraction and integration project.
|
||||
|
||||
## Initial creation of database schema.
|
||||
|
||||
```bash
|
||||
docker run --link db --network marcus_default -v /srv/pyjeeves/config.yml:/app/config.yml gitlab.lndvll.se:5500/lindvallskaffe/pyjeeves python ./pyjeeves/db_raw.py
|
||||
```
|
||||
|
||||
## Connecting to DB with client
|
||||
|
||||
```bash
|
||||
docker run -it --network marcus_default --link db:mysql --rm mysql sh -c 'exec mysql -h"db" -P"3306" -uroot -p"ROOT_PW"'
|
||||
```
|
||||
|
||||
## Forcing updates
|
||||
|
||||
You may force updates of objects by setting RowUpdatedDt to null.
|
||||
For example:
|
||||
´update jvs_customers set RowUpdatedDt = null;´
|
||||
|
||||
22
README.rst
22
README.rst
|
|
@ -1,22 +0,0 @@
|
|||
PyJeeves Module
|
||||
===============
|
||||
|
||||
This project is a Jeeves data extraction and integration project.
|
||||
|
||||
|
||||
## Initial creation of database schema.
|
||||
|
||||
´docker run --link db --network marcus_default -v /srv/pyjeeves/config.yml:/app/config.yml gitlab.lndvll.se:5500/lindvallskaffe/pyjeeves python ./pyjeeves/db.py´
|
||||
|
||||
|
||||
## Connecting to DB with client
|
||||
|
||||
´docker run -it --network marcus_default --link db:mysql --rm mysql sh -c 'exec mysql -h"db" -P"3306" -uroot -p"ROOT_PW"'´
|
||||
|
||||
|
||||
## Forcing updates
|
||||
|
||||
You may force updates of objects by setting RowUpdatedDt to null.
|
||||
For example:
|
||||
´update jvs_customers set RowUpdatedDt = null;´
|
||||
|
||||
50
config.yml
50
config.yml
|
|
@ -1,50 +0,0 @@
|
|||
sync_interval: 60
|
||||
|
||||
mysql:
|
||||
host: localhost
|
||||
port: 3306
|
||||
user: pyjeeves
|
||||
passwd: jeeves
|
||||
db: pyjeeves
|
||||
|
||||
jeeves_db:
|
||||
server: 'BlackSheep01'
|
||||
database: 'LKTest'
|
||||
user: 'jvsdbo'
|
||||
password: 'password'
|
||||
|
||||
logging:
|
||||
version: 1
|
||||
handlers:
|
||||
fileHandler:
|
||||
class: logging.FileHandler
|
||||
formatter: simpleFormatter
|
||||
filename: pyjeeves.log
|
||||
level: INFO
|
||||
consoleHandler:
|
||||
class: logging.StreamHandler
|
||||
level: DEBUG
|
||||
formatter: simpleFormatter
|
||||
stream: ext://sys.stdout
|
||||
loggers:
|
||||
PyJeeves:
|
||||
handlers:
|
||||
- fileHandler
|
||||
- consoleHandler
|
||||
level: DEBUG
|
||||
alembic:
|
||||
handlers:
|
||||
- consoleHandler
|
||||
level: INFO
|
||||
sqlalchemy:
|
||||
handlers:
|
||||
- consoleHandler
|
||||
level: WARN
|
||||
qualname: sqlalchemy.engine
|
||||
formatters:
|
||||
simpleFormatter:
|
||||
format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
|
||||
alembic:
|
||||
script_location: migrations
|
||||
sqlalchemy.url: 'mysql+pymysql://pyjeeves:jeeves@localhost/pyjeeves?charset=utf8mb4'
|
||||
|
|
@ -3,12 +3,14 @@ version: '2'
|
|||
services:
|
||||
db:
|
||||
image: mysql
|
||||
restart: always
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: rootpassword
|
||||
MYSQL_ROOT_PASSWORD: J33v3s33
|
||||
MYSQL_USER: pyjeeves
|
||||
MYSQL_PASSWORD: jeeves
|
||||
MYSQL_DATABASE: pyjeeves
|
||||
ports:
|
||||
- 3306:3306
|
||||
|
||||
pyjeeves:
|
||||
container_name: pyjeeves
|
||||
|
|
|
|||
852
jeeves_sp_updates/Jeeves_Esales_AddOrderRow.spr
Normal file
852
jeeves_sp_updates/Jeeves_Esales_AddOrderRow.spr
Normal file
|
|
@ -0,0 +1,852 @@
|
|||
SET ANSI_NULLS OFF
|
||||
GO
|
||||
SET QUOTED_IDENTIFIER OFF
|
||||
GO
|
||||
SET NOCOUNT ON
|
||||
GO
|
||||
PRINT 'Begin Jeeves_Esales_AddOrderRow'
|
||||
GO
|
||||
PRINT 'Begin Jeeves_Esales_AddOrderRow'
|
||||
PRINT '***************************************************'
|
||||
PRINT '* *'
|
||||
PRINT '* Jeeves_Esales_AddOrderRow *'
|
||||
PRINT '* *'
|
||||
PRINT '***************************************************'
|
||||
GO
|
||||
IF OBJECT_ID( 'Jeeves_Esales_AddOrderRow', 'P' ) IS NOT NULL
|
||||
BEGIN
|
||||
DROP PROCEDURE Jeeves_Esales_AddOrderRow
|
||||
END
|
||||
GO
|
||||
IF (SELECT COUNT(*) FROM xspr WHERE
|
||||
SprTrgName = 'Jeeves_Esales_AddOrderRow' AND
|
||||
SprTrgType = 'P' ) > 0
|
||||
BEGIN
|
||||
UPDATE xspr SET beskrivning=beskrivning WHERE
|
||||
SprTrgName = 'Jeeves_Esales_AddOrderRow' AND
|
||||
SprTrgType = 'P'
|
||||
END ELSE BEGIN
|
||||
INSERT INTO xspr(SprTrgName,SprTrgType,RowCreatedDt)
|
||||
VALUES('Jeeves_Esales_AddOrderRow' , 'P', GETDATE() )
|
||||
END
|
||||
GO
|
||||
----------------------------------------------------
|
||||
|
||||
CREATE procedure Jeeves_Esales_AddOrderRow
|
||||
@c_ForetagKod smallint,
|
||||
@c_OrderNumber int,
|
||||
@c_webUserName Jeeves_StrVarChar64 = null,
|
||||
@c_CompanyNo Jeeves_StrVarChar32 = null,
|
||||
@c_PersSign Jeeves_StrVarChar6,
|
||||
@c_LangID smallint = null,
|
||||
@c_ItemNo Jeeves_StrVarChar32 = null,
|
||||
@c_Qty Jeeves_Qty = 0,
|
||||
@c_QtyAltEnh Jeeves_Qty = 0,
|
||||
@c_RequestedDate Jeeves_StrVarChar10 = null,
|
||||
@c_BatchId Jeeves_StrVarChar16 = null,
|
||||
@c_ArtSerieNr Jeeves_StrVarChar32 = null,
|
||||
@c_OrderType smallint = null,
|
||||
@c_Run_Type char(1) = null,
|
||||
@c_TemplateRowID int = null,
|
||||
@c_Edit Jeeves_StrVarCharMax = null,
|
||||
@c_EditExt Jeeves_StrVarCharMax = null,
|
||||
@c_LagStalle Jeeves_StrVarChar10 = null,
|
||||
@c_AltEnhetKod Jeeves_StrVarChar10 = null,
|
||||
@c_AllocateAvailable smallint = 0,
|
||||
@c_OverrideCreditLimit char(1) = 0,
|
||||
@o_OrderRow int = null output,
|
||||
@o_NextQty Jeeves_Qty = null output,
|
||||
@o_NextDate DateTime = null output,
|
||||
@o_LastQty Jeeves_Qty = null output,
|
||||
@o_LastDate DateTime = null output,
|
||||
@o_AllocatedQty Jeeves_Qty = null output,
|
||||
@o_AllocatedDate DateTime = null output
|
||||
|
||||
as
|
||||
|
||||
set nocount on
|
||||
--set ansi_warnings o f f
|
||||
--set concat_null_yields_null o f f
|
||||
--set ansi_nulls o f f
|
||||
--set ansi_padding o f f
|
||||
|
||||
declare @withPriceCalc char(1)
|
||||
select @withPriceCalc = 'Y'
|
||||
|
||||
declare @oh_OrdStat smallint,
|
||||
@orp_OrdRadSt smallint,
|
||||
@orp_OrdRadNr int,
|
||||
@orp_vbordradsum money,
|
||||
@orp_vb_prisinklmoms Jeeves_Amount,
|
||||
@orp_Vb_Pris Jeeves_Amount,
|
||||
@wr Jeeves_StrVarChar256,
|
||||
@x int,
|
||||
@dbe int,
|
||||
@dbc int,
|
||||
@dbp int,
|
||||
@Enter_TranCount as int,
|
||||
@NewLine as nvarchar(2),
|
||||
@PgmId Jeeves_StrVarChar256,
|
||||
@salj_saljare Jeeves_StrVarChar32,
|
||||
@kus_kundrabatt float,
|
||||
@kus_rabklass smallint,
|
||||
@kus_Kreditsparr char(1),
|
||||
@kus_kundsaldo money,
|
||||
@kus_ordsum money,
|
||||
@kus_kundkredlim money,
|
||||
@kus_momskod smallint,
|
||||
@kus_offnr Jeeves_StrVarChar10,
|
||||
@kus_KundRabattKod0 char(1),
|
||||
@kus_KundRabattKod1 char(1),
|
||||
@kus_KundRabattKod2 char(1),
|
||||
@kus_PrisListaKundSpec int,
|
||||
@kus_KundKategoriKod smallint,
|
||||
@kus_tradecalcmarkup float,
|
||||
@kus_tradefsgmarkup float,
|
||||
@kus_AddArtEjAktiv char(1),
|
||||
@ar_ArtProdKlass Jeeves_StrVarChar4,
|
||||
@ar_OrdvRabKod smallint,
|
||||
@ar_artkundrabkod char(1),
|
||||
@ar_artrabklass smallint,
|
||||
@ar_momskod smallint,
|
||||
@ar_ordTyp smallint,
|
||||
@sy1_PrisListaInklMoms char(1),
|
||||
@sy1_momskod smallint,
|
||||
@oh_vbordsum money,
|
||||
@oh_ordsum money,
|
||||
@oh_lagstalle Jeeves_StrVarChar10,
|
||||
@Today datetime,
|
||||
@Momssats float,
|
||||
@CompanyNo Jeeves_StrVarChar20,
|
||||
@ContactNo int,
|
||||
@prislista int,
|
||||
@Saljare Jeeves_StrVarChar32,
|
||||
@ar_artfsgforp float,
|
||||
@ar_palaggdelforp float,
|
||||
@ar_palaggdelforpbelopp char(1),
|
||||
@ar_lagstalle Jeeves_StrVarChar10,
|
||||
@sy1_lagstalle Jeeves_StrVarChar10,
|
||||
@ValKurs ztCurrencyRate,
|
||||
@valkod Jeeves_StrVarChar4,
|
||||
@Samfaktutskr char(1),
|
||||
@Allowpurchase char(1),
|
||||
@PCode int,
|
||||
@sy1_artbeskr_2_orp char(1),
|
||||
@ArtBeskr Jeeves_StrVarChar64,
|
||||
@ArtBeskr2 Jeeves_StrVarChar256,
|
||||
@LangID smallint,
|
||||
@DateAvailable DateTime,
|
||||
@sy1_rbn char(1),
|
||||
@sy1_PrisListaStaffling int,
|
||||
@sy1_KodAlternativEnhet int,
|
||||
@AltEnhetKod Jeeves_StrVarChar10,
|
||||
@AltEnhetOmrFaktor ZtPurQty2Stock,
|
||||
@InkAvt int,
|
||||
@LevNr JEEVES_CompanyNo,
|
||||
@ArtLevPrior smallint,
|
||||
@ArtHuvudAvt JEEVES_Boolean,
|
||||
@SALES170 JEEVES_Boolean,
|
||||
@SALES190 smallint,
|
||||
@SALES048 smallint,
|
||||
@SALES007 smallint,
|
||||
@KostBar Jeeves_StrVarChar8,
|
||||
@KostStalleKod Jeeves_StrVarChar8,
|
||||
@K4 Jeeves_StrVarChar8,
|
||||
@K5 Jeeves_StrVarChar8,
|
||||
@K6 Jeeves_StrVarChar8,
|
||||
@K7 Jeeves_StrVarChar8,
|
||||
@AutoRegel Jeeves_StrVarChar4,
|
||||
@AnskaffningTillv smallint
|
||||
|
||||
select @LangID = @c_LangID
|
||||
|
||||
exec Jeeves_Esales_GetUserInfo
|
||||
@c_IntrnCoNo = @c_ForetagKod,
|
||||
@c_webUserName = @c_webUserName,
|
||||
@c_CompanyNo = @c_CompanyNo,
|
||||
@o_LangID = @LangID output,
|
||||
@o_CompanyNo = @CompanyNo output,
|
||||
@o_ContactNo = @ContactNo output,
|
||||
@o_Prislista = @Prislista output,
|
||||
@o_Saljare = @Saljare output,
|
||||
@o_Valkod = @Valkod output,
|
||||
@o_ValKurs = @Valkurs output
|
||||
|
||||
|
||||
--Adding from a template
|
||||
if (isnull(@c_TemplateRowID,-1) > -1) begin
|
||||
if exists(select 1 from web_orp where ConnToSQLIDENTITY = @c_TemplateRowID) begin
|
||||
declare cr_items cursor static forward_only local for
|
||||
select artnr, ordantal from web_orp where ConnToSQLIDENTITY = @c_TemplateRowID
|
||||
open cr_items
|
||||
while 1=1 begin
|
||||
fetch next from cr_items into
|
||||
@c_ItemNo,
|
||||
@c_Qty
|
||||
|
||||
if @@Fetch_Status<>0 BREAK
|
||||
if @@Error<>0 BREAK
|
||||
|
||||
if ( isnull(@c_Qty,0) > 0) and (@c_ItemNo is not null) begin
|
||||
exec @x = Jeeves_Esales_AddOrderRow
|
||||
@c_ForetagKod = @c_ForetagKod,
|
||||
@c_OrderNumber = @c_OrderNumber,
|
||||
@c_webUserName = @c_webUserName,
|
||||
@c_PersSign = @c_PersSign,
|
||||
@c_LangID = @c_LangID,
|
||||
@c_ItemNo = @c_ItemNo,
|
||||
@c_Qty = @c_Qty,
|
||||
@c_AllocateAvailable = @c_AllocateAvailable,
|
||||
@c_OverrideCreditLimit = @c_OverrideCreditLimit,
|
||||
@c_RequestedDate = @c_RequestedDate,
|
||||
@c_BatchId = @c_BatchId,
|
||||
@c_ArtSerieNr = @c_ArtSerieNr,
|
||||
@c_OrderType = @c_OrderType
|
||||
if @x<0 Break
|
||||
End
|
||||
End
|
||||
close cr_items
|
||||
deallocate cr_items
|
||||
End
|
||||
if @c_Run_Type = 'R' exec Jeeves_Esales_GetOrp @c_ForetagKod,@c_webUserName,@LangID,@c_OrderNumber
|
||||
Return isnull(@x,0)
|
||||
End
|
||||
|
||||
if @c_qty <= 0 Return -210
|
||||
select @Today = getdate()
|
||||
if @c_RequestedDate is null select @c_RequestedDate = convert(nvarchar(10),getdate(),112)
|
||||
|
||||
---Get customer discount, pricelist, credit info from table kus
|
||||
select @kus_kundrabatt = kundrabatt,
|
||||
@kus_rabklass = rabklass,
|
||||
@kus_Kreditsparr = kreditsparr,
|
||||
@kus_kundsaldo = kus.kundsaldo,
|
||||
@kus_ordsum = kus.ordsum,
|
||||
@kus_kundkredlim = kus.kundkredlim,
|
||||
@kus_momskod = kus.momskod,
|
||||
@kus_offnr = offnr,
|
||||
@Samfaktutskr = samfaktutskr,
|
||||
@kus_KundRabattKod0 = kundrabattkod0,
|
||||
@kus_KundRabattKod1 = kundrabattkod1,
|
||||
@kus_KundRabattKod2 = kundrabattkod2,
|
||||
@kus_PrisListaKundSpec = prislistakundspec,
|
||||
@kus_KundKategoriKod = kundkategorikod,
|
||||
@kus_tradecalcmarkup = tradekalkmarkup,
|
||||
@kus_tradefsgmarkup = tradefsgmarkup,
|
||||
@kus_AddArtEjAktiv = AddArtEjAktiv
|
||||
from kus
|
||||
where
|
||||
ftgnr = @CompanyNo and
|
||||
foretagkod = @c_ForetagKod
|
||||
|
||||
--If customer credit is blocked then abort
|
||||
if @kus_kreditsparr = '1' begin
|
||||
exec Jeeves_GT @wr output,@c_ForetagKod,@LangID,1344,'Kunden <20>r kreditsp<73>rrad'
|
||||
EXECUTE Jeeves_RaisError 21001, @wr
|
||||
return -1344
|
||||
end
|
||||
|
||||
--Get Item discount and descriptions from table ar
|
||||
select @ar_ArtProdKlass = ar.artprodklass,
|
||||
@ar_artrabklass = ar.artrabklass,
|
||||
@ar_artkundrabkod = ar.artkundrabkod,
|
||||
@ar_OrdvRabKod = ar.ordvrabkod,
|
||||
@ar_momskod = ar.momskod,
|
||||
@ar_palaggdelforp = isnull(ar.palaggdelforp,0),
|
||||
@ar_palaggdelforpbelopp = ar.palaggdelforpbelopp,
|
||||
@ar_artfsgforp = artfsgforp,
|
||||
@ar_lagstalle = lagstalle,
|
||||
@ar_ordTyp = ordTyp,
|
||||
@Artbeskr = ArtBeskr,
|
||||
@Artbeskr2 = ArtBeskr2,
|
||||
@AnskaffningTillv = ar.AnskaffningTillv
|
||||
from ar
|
||||
where
|
||||
ar.artnr = @c_ItemNo and
|
||||
ar.foretagkod = @c_ForetagKod
|
||||
|
||||
|
||||
--Assign from sy1
|
||||
select @sy1_PrisListaInklMoms = isnull(prislistainklmoms,0),
|
||||
@sy1_momskod = momskod,
|
||||
@sy1_lagstalle = sy1.lagstalle,
|
||||
@sy1_artbeskr_2_orp = sy1.artbeskr_2_orp,
|
||||
@sy1_rbn = sy1.sy1_rbn,
|
||||
@sy1_PrisListaStaffling = PrisListaStaffling,
|
||||
@sy1_KodAlternativEnhet = KodAlternativEnhet
|
||||
from sy1
|
||||
where
|
||||
sy1.foretagkod = @c_ForetagKod
|
||||
|
||||
|
||||
select @AltEnhetKod = @c_AltEnhetKod
|
||||
|
||||
--HACK: If ArtFsgForp is set, find matching AltEnhetKod
|
||||
if isnull(@ar_artfsgforp,0) > 0 and @AltEnhetKod is NULL begin
|
||||
if @sy1_KodAlternativEnhet = 2 begin
|
||||
select @AltEnhetKod = xae.AltEnhetKod, @AltEnhetOmrFaktor = xae.AltEnhetOmrFaktor
|
||||
from xare join xae
|
||||
on (xare.AltEnhetKod = xae.AltEnhetKod)
|
||||
where
|
||||
xae.AltEnhetOmrFaktor = ROUND(@ar_artfsgforp, 6) and
|
||||
xare.artnr = @c_ItemNo and
|
||||
xare.foretagkod = @c_ForetagKod
|
||||
if @AltEnhetKod is not NULL begin
|
||||
--Abort from futher logic down the line
|
||||
select @sy1_KodAlternativEnhet = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--Get Item alternative unit from table xae
|
||||
if @sy1_KodAlternativEnhet = 1 begin
|
||||
if @AltEnhetKod is not NULL begin
|
||||
select @AltEnhetOmrFaktor = AltEnhetOmrFaktor
|
||||
from xae
|
||||
where
|
||||
xae.AltEnhetKod = @AltEnhetKod and
|
||||
xae.foretagkod = @c_ForetagKod
|
||||
|
||||
end else begin
|
||||
select @AltEnhetKod = AltEnhetKod, @AltEnhetOmrFaktor = AltEnhetOmrFaktor
|
||||
from xae
|
||||
where
|
||||
xae.AltEnhetOrderStd = 1 and
|
||||
xae.foretagkod = @c_ForetagKod
|
||||
end
|
||||
end
|
||||
|
||||
--Get Item alternative unit from table xare
|
||||
if @sy1_KodAlternativEnhet = 2 begin
|
||||
if @AltEnhetKod is not NULL begin
|
||||
select @AltEnhetOmrFaktor = AltEnhetOmrFaktor
|
||||
from xare
|
||||
where
|
||||
xare.AltEnhetKod = @AltEnhetKod and
|
||||
xare.artnr = @c_ItemNo and
|
||||
xare.foretagkod = @c_ForetagKod
|
||||
|
||||
end else begin
|
||||
select @AltEnhetKod = AltEnhetKod, @AltEnhetOmrFaktor = AltEnhetOmrFaktor
|
||||
from xare
|
||||
where
|
||||
xare.AltEnhetOrderStd = 1 and
|
||||
xare.artnr = @c_ItemNo and
|
||||
xare.foretagkod = @c_ForetagKod
|
||||
end
|
||||
|
||||
if @AltEnhetOmrFaktor is NULL begin
|
||||
select @AltEnhetOmrFaktor = AltEnhetOmrFaktor
|
||||
from xae
|
||||
where
|
||||
xae.AltEnhetKod = @AltEnhetKod and
|
||||
xae.foretagkod = @c_ForetagKod
|
||||
end
|
||||
end
|
||||
|
||||
if @AltEnhetKod is not NULL and @AltEnhetOmrFaktor > 0 begin
|
||||
--Set the value that was not provided
|
||||
if isnull(@c_QtyAltEnh,0) = 0 and isnull(@c_Qty,0) != 0 begin
|
||||
select @c_QtyAltEnh = @c_Qty / @AltEnhetOmrFaktor
|
||||
end
|
||||
if isnull(@c_Qty,0) = 0 and isnull(@c_QtyAltEnh,0) != 0 begin
|
||||
select @c_Qty = @c_QtyAltEnh * @AltEnhetOmrFaktor
|
||||
end
|
||||
--If both Qty and QtyAltEnh is provided then verify correct value provided
|
||||
if @c_Qty > 0 and @c_QtyAltEnh > 0 begin
|
||||
if ROUND(@c_Qty, 4) != ROUND(@c_QtyAltEnh * @AltEnhetOmrFaktor, 4) begin
|
||||
EXECUTE Jeeves_RaisError 21001, 'Qty and QtyAltEnh must follow the AltEnhetOmrFaktor when both are provided'
|
||||
return 50100
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
--Fallback if there is no alternative units
|
||||
if isnull(@c_Qty,0) = 0 and isnull(@c_QtyAltEnh,0) != 0 begin
|
||||
select @c_Qty = @c_QtyAltEnh
|
||||
select @c_QtyAltEnh = 0
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
select @Enter_TranCount = @@TranCount,
|
||||
@NewLine=char(13)+char(10),
|
||||
@dbp=@@ProcId
|
||||
|
||||
--Calc pack markup
|
||||
--TODO: Fix this, as somehow ar_artfsgforp was an int in this SP, which is wrong. And now this is broken.
|
||||
if isnull(convert(integer,@ar_artfsgforp),0) > 0 begin
|
||||
if ((select convert(integer,@c_Qty) % convert(integer,@ar_artfsgforp)) = 0)
|
||||
select @ar_palaggdelforp = 0
|
||||
end
|
||||
|
||||
if @c_OrderNumber is not NULL and @oh_OrdStat is NULL
|
||||
select
|
||||
@oh_OrdStat = [oh].[OrdStat],
|
||||
@oh_vbordsum = oh.vbordsum,
|
||||
@oh_ordsum = oh.ordsum,
|
||||
@oh_lagstalle = oh.lagstalle
|
||||
from [oh] where
|
||||
[oh].[OrderNr] = @c_OrderNumber and
|
||||
[oh].[ForetagKod] = @c_ForetagKod
|
||||
|
||||
---Use order type from ar if available
|
||||
select @c_orderType = isnull(@c_orderType, @ar_ordTyp)
|
||||
|
||||
--Always use parameter if provided to the store procedure
|
||||
--Use order location from ar table if present
|
||||
select @c_Lagstalle = isnull(@c_Lagstalle,@ar_Lagstalle)
|
||||
|
||||
--Use order location as default
|
||||
if @c_Lagstalle is null select @c_lagstalle = @oh_Lagstalle
|
||||
|
||||
declare
|
||||
@QtyAvailable as decimal
|
||||
|
||||
exec Jeeves_Ar_Disp_Test
|
||||
@c_IntrnCoNo = @c_ForetagKod,
|
||||
@ArtNr = @c_ItemNo,
|
||||
@OrdAntal = @c_Qty,
|
||||
@OrdDatum = @c_RequestedDate,
|
||||
@LagStalle = @c_LagStalle,
|
||||
@c_Return_Type='Y',
|
||||
@o_DispQty = @QtyAvailable output,
|
||||
@o_FirstQty = @o_NextQty output,
|
||||
@o_FirstDate = @o_NextDate output,
|
||||
@o_LastQty = @o_LastQty output,
|
||||
@o_LastDate = @o_LastDate output
|
||||
|
||||
if @o_NextQty is null begin
|
||||
select @o_NextQty = 0
|
||||
end
|
||||
|
||||
if @o_LastQty is null begin
|
||||
select @o_LastQty = 0
|
||||
end
|
||||
|
||||
-- if the AnskaffningTillv is larger than 0 (the product is produced by orders) then discard Jeeves_Ar_Disp_Test
|
||||
if @AnskaffningTillv>0 begin
|
||||
select @c_AllocateAvailable = 0
|
||||
end
|
||||
|
||||
if @c_AllocateAvailable = 1 begin
|
||||
|
||||
-- Place the order with the amount available on the requested date and
|
||||
-- return information about the rest.
|
||||
|
||||
if @c_RequestedDate = @o_NextDate begin
|
||||
--If the first date and the requested date are the same,
|
||||
--the on hand quantity will appear as first date quantity as well.
|
||||
select @o_NextDate = null
|
||||
select @o_NextQty = 0
|
||||
end
|
||||
|
||||
--Prefer next to last.
|
||||
if @o_NextQty = 0 or @o_NextQty is null begin
|
||||
select @o_NextQty = @o_LastQty
|
||||
select @o_NextDate = @o_LastDate
|
||||
select @o_LastQty = 0
|
||||
select @o_LastDate = null
|
||||
end
|
||||
|
||||
--If there is nothing available on the requested date, use the next
|
||||
if @QtyAvailable = 0 begin
|
||||
select @c_Qty = @o_NextQty
|
||||
select @DateAvailable = @o_NextDate
|
||||
select @o_NextQty = @o_LastQty
|
||||
select @o_NextDate = @o_LastDate
|
||||
select @o_LastQty = 0
|
||||
select @o_LastDate = null
|
||||
end else begin
|
||||
--Only the amount available on the requested date should be allocated on the order row.
|
||||
select @DateAvailable = @c_RequestedDate
|
||||
select @c_Qty = @QtyAvailable
|
||||
end
|
||||
|
||||
if @o_NextQty = 0 begin
|
||||
select @o_NextDate = null
|
||||
end
|
||||
|
||||
if @o_LastQty = 0 begin
|
||||
select @o_LastDate = null
|
||||
end
|
||||
|
||||
end else begin
|
||||
|
||||
--If everything is available immediately, set detaavailable to now
|
||||
if(@QtyAvailable >= @c_Qty) begin
|
||||
select @DateAvailable = @c_RequestedDate
|
||||
end else begin
|
||||
--Everything isn't available, if last quantity is 0, the rest is available on first date.
|
||||
if @o_LastQty = 0 begin
|
||||
select @DateAvailable = @o_NextDate
|
||||
end else begin
|
||||
--leave last date as date available, sice the order can't be delivered before that date.
|
||||
select @DateAvailable = @o_LastDate
|
||||
end
|
||||
end
|
||||
|
||||
select @o_NextQty = 0
|
||||
select @o_NextDate = null
|
||||
select @o_LastQty = 0
|
||||
select @o_LastDate = null
|
||||
|
||||
end
|
||||
|
||||
--Pass the quantity that is placed on the order back to the caller.
|
||||
select @o_AllocatedQty = @c_Qty
|
||||
select @o_AllocatedDate = @DateAvailable
|
||||
|
||||
select @InkAvt = al.InkAvt, @LevNr=al.FtgNr , @ArtLevPrior = al.ArtLevPrior, @ArtHuvudAvt =al.ArtHuvudAvt from al where al.ArtNr=@c_ItemNo and al.ArtHuvudAvt='1' and al.ForetagKod=@c_ForetagKod;
|
||||
--@In_SALES170 JEEVES_Boolean=NULL,
|
||||
--@In_SALES190 smallint=NULL,
|
||||
select @SALES170 = dbo.Jeeves_FN_GetParam(@c_ForetagKod, 'SALES170', 'B',null)
|
||||
select @SALES190 = dbo.Jeeves_FN_GetParam(@c_ForetagKod, 'SALES190', 'N',null)
|
||||
select @SALES048 = dbo.Jeeves_FN_GetParam(@c_ForetagKod, 'SALES048', 'N',null)
|
||||
select @SALES007 = dbo.Jeeves_FN_GetParam(@c_ForetagKod, 'SALES007', 'N',null)
|
||||
|
||||
|
||||
--Get cost codes from sales person or location (the same way as UF_Oh_Get_InternKonton)
|
||||
|
||||
if @SALES048 = 1 begin
|
||||
--Get settings from inventory location
|
||||
|
||||
select
|
||||
@KostStalleKod = KostStalleKod,
|
||||
@KostBar = KostBar,
|
||||
@K4 = K4,
|
||||
@K5 = K5,
|
||||
@K6 = K6,
|
||||
@K7 = K7,
|
||||
@AutoRegel = AutoRegelForskott
|
||||
from xb with (NoLock) where
|
||||
ForetagKod = @c_ForetagKod
|
||||
and LagStalle = @c_LagStalle;
|
||||
|
||||
end else if @SALES048 = 2 begin
|
||||
--Get settings from sales person
|
||||
if @Saljare is not null begin
|
||||
select
|
||||
@KostStalleKod = KostStalleKod,
|
||||
@KostBar = KostBar,
|
||||
@K4 = K4,
|
||||
@K5 = K5,
|
||||
@K6 = K6,
|
||||
@K7 = K7,
|
||||
@AutoRegel = AutoRegelForskott
|
||||
from salj with (NoLock) where
|
||||
ForetagKod = @c_ForetagKod
|
||||
and Saljare=@Saljare;
|
||||
end
|
||||
end else if @SALES048 = 3 begin
|
||||
-- Try sales person first and then and add values
|
||||
-- from location is not provided
|
||||
|
||||
if @Saljare is not null begin
|
||||
select
|
||||
@KostStalleKod = KostStalleKod,
|
||||
@KostBar = KostBar,
|
||||
@K4 = K4,
|
||||
@K5 = K5,
|
||||
@K6 = K6,
|
||||
@K7 = K7,
|
||||
@AutoRegel = AutoRegelForskott
|
||||
from salj with (NoLock) where
|
||||
ForetagKod = @c_ForetagKod
|
||||
and Saljare=@Saljare;
|
||||
end
|
||||
|
||||
select
|
||||
@KostStalleKod = ISNULL(@KostStalleKod, KostStalleKod),
|
||||
@KostBar = ISNULL(@KostBar, KostBar),
|
||||
@K4 = ISNULL(@K4, K4),
|
||||
@K5 = ISNULL(@K5, k5),
|
||||
@K6 = ISNULL(@K6, K6),
|
||||
@K7 = ISNULL(@K7, K7),
|
||||
@AutoRegel = ISNULL(@AutoRegel, AutoRegelForskott)
|
||||
from xb with (NoLock) where
|
||||
ForetagKod = @c_ForetagKod
|
||||
and LagStalle = @c_LagStalle;
|
||||
|
||||
end
|
||||
|
||||
--calculate fiscal year
|
||||
declare @TodayDate datetime;
|
||||
declare @redar integer;
|
||||
declare @period integer;
|
||||
declare @Just_BokfDat datetime;
|
||||
set @TodayDate = convert(varchar(10), @Today, 112);
|
||||
execute CalcPeriodP2P @todayDate,
|
||||
null,
|
||||
@redar output,
|
||||
@period output,
|
||||
@Just_BokfDat output,
|
||||
@c_ForetagKod
|
||||
|
||||
declare @startRow int
|
||||
declare @endRow int
|
||||
declare @maxRow int -- The highest row number in the discount row range.
|
||||
declare @discountQty decimal(17,6)
|
||||
|
||||
if @o_OrderRow is not null begin
|
||||
--All rows that are grouped by being in the same decade should
|
||||
--be updated with the discount, since they probably make up a split order row,
|
||||
--and they should all be taken into account when calculating the discount.
|
||||
--This is only relevant if a row number is provided.
|
||||
|
||||
select @startRow = 10 * (@o_OrderRow / 10) -- yx->y0, e.g. 24->20
|
||||
select @endRow = @startRow + 9
|
||||
--The updated row is excluded from the sum but the new quantity is added directly to discountQty
|
||||
select @discountQty = (isnull(sum(ordantal), 0) + @c_Qty), @maxRow = isnull(max(OrdRadNr), @startRow) from orp
|
||||
where ForetagKod = @c_ForetagKod and OrderNr = @c_OrderNumber and OrdRadNr >= @StartRow and OrdRadNr <= @EndRow
|
||||
and OrdRadNr <> @o_OrderRow and ArtNr =@c_ItemNo
|
||||
|
||||
if @o_OrderRow > @maxRow begin
|
||||
select @maxRow = @o_OrderRow
|
||||
end
|
||||
end else begin
|
||||
select @discountQty = @c_qty
|
||||
select @startRow = -1
|
||||
end
|
||||
|
||||
if @withPriceCalc = 'Y' begin
|
||||
declare @orp_ArtCirkaPris as Jeeves_Amount,
|
||||
@orp_VolymRabatt as float,
|
||||
@orp_KundRabatt as float,
|
||||
@orp_Rabatt1 as float,
|
||||
@orp_Rabatt2 as float,
|
||||
@orp_Rabatt3 as float,
|
||||
@orp_valkod as nvarchar(3),
|
||||
@orp_valkurs as decimal(18,8),
|
||||
@orp_ordDatum as datetime
|
||||
|
||||
execute @x = Jeeves_orp_produkt_pris
|
||||
@c_Foretagkod = @c_ForetagKod,
|
||||
@ArtNr = @c_ItemNo,
|
||||
@OrdAntal = @discountQty,
|
||||
@ValKurs = @ValKurs,
|
||||
@ValKod = @valkod,
|
||||
@PrisLista = @Prislista,
|
||||
@OrdDatum = @Today,
|
||||
@ArtProdKlass = @ar_ArtProdKlass,
|
||||
@offradnr = -32000,
|
||||
@ArtRabKlass = @ar_ArtRabKlass,
|
||||
@RabKlass = @kus_RabKlass,
|
||||
@OrdvRabKod = @ar_OrdvRabKod,
|
||||
@ArtKundRabKod = @ar_ArtKundRabKod,
|
||||
@KundRabattKod0 = @kus_KundRabattKod0,
|
||||
@KundRabattKod1 = @kus_KundRabattKod1,
|
||||
@KundRabattKod2 = @kus_KundRabattKod2,
|
||||
@In_PrisListaStaffling = @sy1_PrisListaStaffling,
|
||||
@In_PrisListaKundSpec = @kus_PrisListaKundSpec,
|
||||
@In_KundKategoriKod = @kus_KundKategoriKod,
|
||||
@KundRabatt = @kus_KundRabatt,
|
||||
@In_InkAvt = @InkAvt,
|
||||
@In_LevNr = @LevNr,
|
||||
@In_ArtLevPrior = @ArtLevPrior,
|
||||
@In_ArtHuvudAvt = @ArtHuvudAvt,
|
||||
@In_tradekalkmarkup = @kus_tradecalcmarkup,
|
||||
@In_tradefsgmarkup = @kus_tradefsgmarkup,
|
||||
@In_OrdBerLevDat = @DateAvailable,
|
||||
@In_Sales170 = @SALES170,
|
||||
@In_Sales190 = @SALES190,
|
||||
@In_RedovisnAr = @redar,
|
||||
@in_FtgNr = @CompanyNo,
|
||||
@Logg = null,
|
||||
@offnr = @kus_offnr,
|
||||
@In_LagStalle = @c_Lagstalle,
|
||||
@O_Svar_Pris_Valuta = @orp_Vb_Pris output,
|
||||
@O_Svar_VolymRabatt = @orp_VolymRabatt output,
|
||||
@O_Svar_KundRabatt = @orp_KundRabatt output,
|
||||
@O_Svar_Rabatt1 = @orp_Rabatt1 output,
|
||||
@O_Svar_Rabatt2 = @orp_Rabatt2 output,
|
||||
@O_Svar_Rabatt3 = @orp_Rabatt3 output,
|
||||
@O_Svar_CirkaPris_Valuta = @orp_ArtCirkaPris output
|
||||
|
||||
if @ar_palaggdelforpbelopp = 1 begin
|
||||
select @orp_vb_pris = @orp_vb_pris + @ar_palaggdelforp
|
||||
end else begin
|
||||
-- palaggdelforp is specified in percentage, so 80 should result in a factor of 1.8.
|
||||
select @orp_vb_pris = @orp_vb_pris * (1 + 0.01 * @ar_palaggdelforp)
|
||||
end
|
||||
end
|
||||
|
||||
---Calc VAT------------------------------------------------------
|
||||
if @kus_momskod = 0 begin
|
||||
select @Momssats = 0
|
||||
end
|
||||
else begin
|
||||
if @ar_momskod is null select @ar_momskod = isnull(@kus_momskod,@sy1_momskod)
|
||||
select @Momssats = x1.momssats
|
||||
from x1
|
||||
where
|
||||
momskod = @ar_momskod
|
||||
end
|
||||
|
||||
if @sy1_PrisListaInklMoms in ('1','3') begin
|
||||
select @orp_vb_prisinklmoms = @orp_Vb_Pris
|
||||
select @orp_vb_pris = @orp_vb_pris/(1.0+(@MomsSats/100.0))
|
||||
end
|
||||
else begin
|
||||
select @orp_vb_prisinklmoms = @orp_vb_pris * (1.0+(@Momssats/100.0))
|
||||
end
|
||||
-----------------------------------------------------------
|
||||
|
||||
---Check if creditlimit is reached
|
||||
if @kus_kundkredlim > 0 and @c_OverrideCreditLimit = 0 begin
|
||||
if (isnull(@oh_ordsum,0) + @kus_kundsaldo + @kus_ordsum + (@Orp_vb_pris*@c_qty)) > @kus_kundkredlim begin
|
||||
exec Jeeves_GT @wr output,@c_ForetagKod,@LangID,3679,'Kundens kreditgr<67>ns <20>r <20>verskriden'
|
||||
EXECUTE Jeeves_RaisError 21001, @wr
|
||||
return -3679
|
||||
end
|
||||
end
|
||||
|
||||
begin tran
|
||||
|
||||
select @oh_vbordsum = isnull(@oh_vbordsum, 0) + (@c_qty * @orp_Vb_Pris * ((100 - @orp_Rabatt1)/100) * ((100 - @orp_Rabatt2)/100) * ((100 - @orp_Rabatt3)/100) * ((100-@orp_kundrabatt)/100))
|
||||
select @oh_ordsum = 0
|
||||
|
||||
select @orp_ordradst = dbo.Jeeves_FN_GetParam (@c_ForetagKod,'WEBAPP018','N','10')
|
||||
if @oh_OrdStat < @orp_ordradst select @orp_ordradst = @oh_OrdStat
|
||||
|
||||
|
||||
if @sy1_artbeskr_2_orp = 1 begin
|
||||
--Add description to orp row if artbeskr_2_orp flag is set.
|
||||
|
||||
-- First try the old way.
|
||||
select @Artbeskr = isnull(ArtBeskr, @Artbeskr), @Artbeskr2 = isnull(ArtBeskr2, @Artbeskr2)
|
||||
from arb where
|
||||
foretagkod = @c_ForetagKod
|
||||
and sprakkod = @LangID
|
||||
and artnr = @c_ItemNo
|
||||
|
||||
--Override with modern translation with old transalation value as default
|
||||
execute @ArtBeskr = Jeeves_FN_GetCustomerDropDownValue
|
||||
@c_ForetagKod,
|
||||
@LangID,
|
||||
'ar',
|
||||
@c_ItemNo,
|
||||
@ArtBeskr
|
||||
|
||||
end
|
||||
else begin
|
||||
select @Artbeskr = null, @Artbeskr2 = null
|
||||
end
|
||||
|
||||
if @c_Qty <= 0
|
||||
begin
|
||||
if @@TranCount > 0 ROLLBACK TRANSACTION
|
||||
EXECUTE Jeeves_RaisError 50100, 'Jeeves_Esales_AddOrderRow'
|
||||
while @@TranCount<@Enter_TranCount BEGIN TRANSACTION
|
||||
return 50100
|
||||
end
|
||||
|
||||
execute @x = Jeeves_Init_Insert_orp
|
||||
@c_ForetagKod = @c_ForetagKod,
|
||||
@c_OrderNr = @c_OrderNumber,
|
||||
@c_FtgNr = @CompanyNo,
|
||||
@c_ArtNr = @c_ItemNo,
|
||||
@c_ArtSerieNr = @c_ArtSerieNr,
|
||||
@c_BatchId = @c_BatchID,
|
||||
@c_Edit = @c_Edit,
|
||||
@c_EditExt = @c_EditExt,
|
||||
@c_OrdBegLevDat = @c_RequestedDate,
|
||||
@c_OrdBerLevDat = @DateAvailable,
|
||||
@c_Lagstalle = @c_lagstalle,
|
||||
@c_OrdAntal = @c_Qty,
|
||||
@c_OrdAntalAltEnh = @c_QtyAltEnh,
|
||||
@c_OrdTyp = @c_OrderType,
|
||||
@c_ordradst = @orp_ordradst,
|
||||
@c_PersSign = @c_PersSign,
|
||||
@c_volymrabatt = @orp_VolymRabatt,
|
||||
@c_KundRabatt = @orp_KundRabatt,
|
||||
@c_Rabatt1 = @orp_Rabatt1,
|
||||
@c_Rabatt2 = @orp_Rabatt2,
|
||||
@c_Rabatt3 = @orp_Rabatt3,
|
||||
@c_Saljare = @Saljare,
|
||||
@c_vb_pris = @orp_Vb_Pris,
|
||||
@c_vb_prisinklmoms = @orp_vb_prisinklmoms,
|
||||
@c_rowcreatedby = @c_perssign,
|
||||
@c_rowcreateddt = @Today,
|
||||
@c_Valkod = @valkod,
|
||||
@c_Valkurs = @valkurs,
|
||||
@c_OrdRadNr = @o_OrderRow output,
|
||||
@c_Artbeskr = @ArtBeskr,
|
||||
@c_OrdArtBeskr = @ArtBeskr2,
|
||||
@c_KostBar = @KostBar,
|
||||
@c_KostStalleKod = @KostStalleKod,
|
||||
@c_K4 = @K4,
|
||||
@c_K5 = @K5,
|
||||
@c_K6 = @K6,
|
||||
@c_K7 = @K7,
|
||||
@c_AutoRegel = @AutoRegel,
|
||||
@c_AltEnhetKod = @AltEnhetKod;
|
||||
|
||||
if (@@Error <> 0) or (@x < 0) begin
|
||||
EXECUTE Jeeves_RaisError 21001, 'Error when creating order row'
|
||||
Rollback tran
|
||||
Return -100
|
||||
end
|
||||
|
||||
--If the row created with a specific row number, the prices of other rows might be affected.
|
||||
if @startRow >= 0 begin
|
||||
declare @rowIndex as int
|
||||
select @rowIndex = @startRow
|
||||
while @rowIndex <= @maxRow begin
|
||||
Update orp SET
|
||||
orp.vb_pris = @orp_Vb_Pris,
|
||||
orp.vb_prisinklmoms = @orp_vb_prisinklmoms,
|
||||
orp.volymrabatt = @orp_VolymRabatt,
|
||||
orp.KundRabatt = @orp_KundRabatt,
|
||||
orp.Rabatt1 = @orp_Rabatt1,
|
||||
orp.Rabatt2 = @orp_Rabatt2,
|
||||
orp.Rabatt3 = @orp_Rabatt3,
|
||||
orp.rowupdatedby= @c_PersSign,
|
||||
orp.rowupdateddt = getdate()
|
||||
Where ordernr = @c_OrderNumber and
|
||||
foretagkod = @c_ForetagKod and
|
||||
ordradnr = @rowIndex and
|
||||
artnr = @c_ItemNo and
|
||||
ordradnr <> @o_OrderRow
|
||||
select @rowIndex = @rowIndex + 1
|
||||
end
|
||||
end
|
||||
|
||||
update oh set
|
||||
oh.ordsum = @oh_ordsum,
|
||||
oh.vbordsum = @oh_vbordsum
|
||||
where
|
||||
oh.ordernr = @c_OrderNumber and
|
||||
oh.foretagkod = @c_ForetagKod
|
||||
|
||||
if @@Error <> 0 begin
|
||||
rollback tran
|
||||
return -100
|
||||
end
|
||||
|
||||
if @SALES007 = 1 AND @kus_AddArtEjAktiv <> '1'
|
||||
BEGIN
|
||||
exec JEEVES_Orp_Create_Tillagg @c_OrderNumber, @c_PersSign, @c_ForetagKod
|
||||
END
|
||||
|
||||
if @@Error <> 0 begin
|
||||
rollback tran
|
||||
return -100
|
||||
end
|
||||
|
||||
commit tran
|
||||
|
||||
--calculate ordervalue discount;
|
||||
execute JEEVES_Oh_Kora @c_OrderNumber,NULL,2,null,@c_ForetagKod;
|
||||
--make sure ordersum is updated;
|
||||
execute JEEVES_Oh_OrderVarde @c_OrderNumber, @ValKurs, @sy1_rbn, @c_PersSign, @c_ForetagKod = @c_ForetagKod, @call_type = 4
|
||||
|
||||
if @c_Run_Type = 'R' exec Jeeves_Esales_GetOrp @c_ForetagKod,@c_webUserName,@LangID,@c_OrderNumber
|
||||
|
||||
return 0
|
||||
GO
|
||||
PRINT 'End Jeeves_Esales_AddOrderRow'
|
||||
GO
|
||||
PRINT 'End Jeeves_Esales_AddOrderRow'
|
||||
GO
|
||||
|
||||
|
||||
340
jeeves_sp_updates/Jeeves_Esales_CreateOrder.spr
Normal file
340
jeeves_sp_updates/Jeeves_Esales_CreateOrder.spr
Normal file
|
|
@ -0,0 +1,340 @@
|
|||
SET ANSI_NULLS OFF
|
||||
GO
|
||||
SET QUOTED_IDENTIFIER OFF
|
||||
GO
|
||||
SET NOCOUNT ON
|
||||
GO
|
||||
PRINT 'Begin Jeeves_Esales_CreateOrder'
|
||||
GO
|
||||
PRINT 'Begin Jeeves_Esales_CreateOrder'
|
||||
PRINT '***************************************************'
|
||||
PRINT '* *'
|
||||
PRINT '* Jeeves_Esales_CreateOrder *'
|
||||
PRINT '* *'
|
||||
PRINT '***************************************************'
|
||||
GO
|
||||
IF OBJECT_ID( 'Jeeves_Esales_CreateOrder', 'P' ) IS NOT NULL
|
||||
BEGIN
|
||||
DROP PROCEDURE Jeeves_Esales_CreateOrder
|
||||
END
|
||||
GO
|
||||
IF (SELECT COUNT(*) FROM xspr WHERE
|
||||
SprTrgName = 'Jeeves_Esales_CreateOrder' AND
|
||||
SprTrgType = 'P' ) > 0
|
||||
BEGIN
|
||||
UPDATE xspr SET beskrivning=beskrivning WHERE
|
||||
SprTrgName = 'Jeeves_Esales_CreateOrder' AND
|
||||
SprTrgType = 'P'
|
||||
END ELSE BEGIN
|
||||
INSERT INTO xspr(SprTrgName,SprTrgType,RowCreatedDt)
|
||||
VALUES('Jeeves_Esales_CreateOrder' , 'P', GETDATE() )
|
||||
END
|
||||
GO
|
||||
----------------------------------------------------
|
||||
|
||||
CREATE procedure Jeeves_Esales_CreateOrder
|
||||
@c_ForetagKod smallint,
|
||||
@c_webUserName Jeeves_StrVarChar64 = null,
|
||||
@c_CompanyNo Jeeves_StrVarChar32 = null,
|
||||
@c_PersSign Jeeves_StrVarChar6,
|
||||
@c_LangID smallint = 0,
|
||||
@c_BatchId Jeeves_StrVarChar16 = null,
|
||||
@c_OrderType smallint = null,
|
||||
@c_Run_Type char(1) = null,
|
||||
@c_TemplateRowID int = null,
|
||||
@c_Edit Jeeves_StrVarCharMax = null,
|
||||
@c_EditExt Jeeves_StrVarCharMax = null,
|
||||
@c_Saljare Jeeves_StrVarChar32 = null,
|
||||
@c_Lagstalle Jeeves_StrVarChar10 = null,
|
||||
@c_OverrideCreditLimit char(1) = 0,
|
||||
@o_OrderNumber int = null output
|
||||
|
||||
as
|
||||
|
||||
set nocount on
|
||||
--set ansi_warnings o f f
|
||||
--set concat_null_yields_null o f f
|
||||
--set ansi_nulls o f f
|
||||
--set ansi_padding o f f
|
||||
|
||||
declare @withPriceCalc char(1)
|
||||
select @withPriceCalc = 'Y'
|
||||
|
||||
declare @oh_OrdStat smallint,
|
||||
@orp_OrdRadSt smallint,
|
||||
@orp_OrdRadNr int,
|
||||
@orp_vbordradsum money,
|
||||
@orp_vb_prisinklmoms Jeeves_Amount,
|
||||
@orp_Vb_Pris Jeeves_Amount,
|
||||
@wr Jeeves_StrVarChar256,
|
||||
@x int,
|
||||
@dbe int,
|
||||
@dbc int,
|
||||
@dbp int,
|
||||
@Enter_TranCount as int,
|
||||
@NewLine as nvarchar(2),
|
||||
@PgmId Jeeves_StrVarChar256,
|
||||
@salj_saljare Jeeves_StrVarChar32,
|
||||
@kus_kundrabatt float,
|
||||
@kus_rabklass smallint,
|
||||
@kus_Kreditsparr char(1),
|
||||
@kus_kundsaldo money,
|
||||
@kus_ordsum money,
|
||||
@kus_kundkredlim money,
|
||||
@kus_momskod smallint,
|
||||
@kus_offnr Jeeves_StrVarChar10,
|
||||
@ar_artkundrabkod char(1),
|
||||
@ar_artrabklass smallint,
|
||||
@ar_momskod smallint,
|
||||
@sy1_PrisListaInklMoms char(1),
|
||||
@sy1_momskod smallint,
|
||||
@oh_vbordsum money,
|
||||
@oh_ordsum money,
|
||||
@Today datetime,
|
||||
@Momssats float,
|
||||
@CompanyNo Jeeves_StrVarChar20,
|
||||
@ContactNo int,
|
||||
@prislista int,
|
||||
@lagstalle Jeeves_StrVarChar10,
|
||||
@Saljare Jeeves_StrVarChar32,
|
||||
@ar_artfsgforp smallint,
|
||||
@ar_palaggdelforp float,
|
||||
@ValKurs ztCurrencyRate,
|
||||
@valkod Jeeves_StrVarChar4,
|
||||
@Samfaktutskr char(1),
|
||||
@Allowpurchase char(1),
|
||||
@PCode int,
|
||||
@SALES048 smallint,
|
||||
@KostBar Jeeves_StrVarChar8,
|
||||
@KostStalleKod Jeeves_StrVarChar8,
|
||||
@K4 Jeeves_StrVarChar8,
|
||||
@K5 Jeeves_StrVarChar8,
|
||||
@K6 Jeeves_StrVarChar8,
|
||||
@K7 Jeeves_StrVarChar8,
|
||||
@AutoRegel Jeeves_StrVarChar4;
|
||||
|
||||
exec Jeeves_Esales_GetUserInfo
|
||||
@c_IntrnCoNo = @c_ForetagKod,
|
||||
@c_webUserName = @c_webUserName,
|
||||
@c_CompanyNo = @c_CompanyNo,
|
||||
@o_LangID = @c_LangID output,
|
||||
@o_CompanyNo = @CompanyNo output,
|
||||
@o_ContactNo = @ContactNo output,
|
||||
@o_Lagstalle = @Lagstalle output,
|
||||
@o_Prislista = @Prislista output,
|
||||
@o_Saljare = @Saljare output,
|
||||
@o_Valkod = @Valkod output,
|
||||
@o_ValKurs = @Valkurs output
|
||||
|
||||
if @c_Saljare is null select @c_Saljare = @Saljare
|
||||
|
||||
select @Today = getdate()
|
||||
|
||||
---Get customer discount, pricelist, credit info from table kus
|
||||
select @kus_kundrabatt = kundrabatt,
|
||||
@kus_rabklass = rabklass,
|
||||
@kus_Kreditsparr = kreditsparr,
|
||||
@kus_kundsaldo = kus.kundsaldo,
|
||||
@kus_ordsum = kus.ordsum,
|
||||
@kus_kundkredlim = kus.kundkredlim,
|
||||
@kus_momskod = kus.momskod,
|
||||
@kus_offnr = offnr,
|
||||
@Samfaktutskr = samfaktutskr
|
||||
from kus
|
||||
where
|
||||
ftgnr = @CompanyNo and
|
||||
foretagkod = @c_ForetagKod
|
||||
|
||||
--If customer credit is blocked then abort
|
||||
if @kus_kreditsparr = '1' begin
|
||||
exec Jeeves_GT @wr output,@c_ForetagKod,@c_LangID,1344,'Kunden är kreditspärrad'
|
||||
EXECUTE Jeeves_RaisError 21001, @wr
|
||||
return -1344
|
||||
end
|
||||
|
||||
--Assign from sy1
|
||||
select @sy1_PrisListaInklMoms = isnull(prislistainklmoms,0),
|
||||
@sy1_momskod = momskod
|
||||
from sy1
|
||||
where
|
||||
sy1.foretagkod = @c_ForetagKod
|
||||
|
||||
select @Enter_TranCount = @@TranCount,
|
||||
@NewLine=char(13)+char(10),
|
||||
@dbp=@@ProcId
|
||||
|
||||
---Calc VAT------------------------------------------------------
|
||||
if @kus_momskod = 0 begin
|
||||
select @Momssats = 0
|
||||
end
|
||||
else begin
|
||||
if @ar_momskod is null select @ar_momskod = isnull(@kus_momskod,@sy1_momskod)
|
||||
select @Momssats = x1.momssats
|
||||
from x1
|
||||
where
|
||||
momskod = @ar_momskod
|
||||
end
|
||||
|
||||
if @sy1_PrisListaInklMoms in ('1','3') begin
|
||||
select @orp_vb_prisinklmoms = @orp_Vb_Pris
|
||||
select @orp_vb_pris = @orp_vb_pris/(1.0+(@MomsSats/100.0))
|
||||
end
|
||||
else begin
|
||||
select @orp_vb_prisinklmoms = @orp_vb_pris * (1.0+(@Momssats/100.0))
|
||||
end
|
||||
--select vbpris2=@orp_vb_pris,prisin=@orp_vb_prisinklmoms, moms=@Momssats, kmoms=@kus_momskod,armoms= @ar_momskod
|
||||
-----------------------------------------------------------
|
||||
|
||||
---Check if creditlimit is reached
|
||||
if @kus_kundkredlim > 0 and @c_OverrideCreditLimit = 0 begin
|
||||
if (isnull(@oh_ordsum,0) + @kus_kundsaldo + @kus_ordsum) > @kus_kundkredlim begin
|
||||
exec Jeeves_GT @wr output,@c_ForetagKod,@c_LangID,3679,'Kundens kreditgräns är överskriden'
|
||||
EXECUTE Jeeves_RaisError 21001, @wr
|
||||
return -3679
|
||||
end
|
||||
end
|
||||
|
||||
--Always use parameter if provided.
|
||||
if @c_Lagstalle is null select @c_Lagstalle = @Lagstalle
|
||||
|
||||
select @SALES048 = dbo.Jeeves_FN_GetParam(@c_ForetagKod, 'SALES048', 'N',null)
|
||||
|
||||
--Get cost codes from sales person or location (the same way as UF_Oh_Get_InternKonton)
|
||||
|
||||
if @SALES048 = 1 begin
|
||||
--Get settings from inventory location
|
||||
|
||||
select
|
||||
@KostStalleKod = KostStalleKod,
|
||||
@KostBar = KostBar,
|
||||
@K4 = K4,
|
||||
@K5 = K5,
|
||||
@K6 = K6,
|
||||
@K7 = K7,
|
||||
@AutoRegel = AutoRegelForskott
|
||||
from xb with (NoLock) where
|
||||
ForetagKod = @c_ForetagKod
|
||||
and LagStalle = @c_LagStalle;
|
||||
|
||||
end else if @SALES048 = 2 begin
|
||||
--Get settings from sales person
|
||||
if @Saljare is not null begin
|
||||
select
|
||||
@KostStalleKod = KostStalleKod,
|
||||
@KostBar = KostBar,
|
||||
@K4 = K4,
|
||||
@K5 = K5,
|
||||
@K6 = K6,
|
||||
@K7 = K7,
|
||||
@AutoRegel = AutoRegelForskott
|
||||
from salj with (NoLock) where
|
||||
ForetagKod = @c_ForetagKod
|
||||
and Saljare=@Saljare;
|
||||
end
|
||||
end else if @SALES048 = 3 begin
|
||||
-- Try sales person first and then and add values
|
||||
-- from location is not provided
|
||||
|
||||
if @Saljare is not null begin
|
||||
select
|
||||
@KostStalleKod = KostStalleKod,
|
||||
@KostBar = KostBar,
|
||||
@K4 = K4,
|
||||
@K5 = K5,
|
||||
@K6 = K6,
|
||||
@K7 = K7,
|
||||
@AutoRegel = AutoRegelForskott
|
||||
from salj with (NoLock) where
|
||||
ForetagKod = @c_ForetagKod
|
||||
and Saljare=@Saljare;
|
||||
end
|
||||
|
||||
select
|
||||
@KostStalleKod = ISNULL(@KostStalleKod, KostStalleKod),
|
||||
@KostBar = ISNULL(@KostBar, KostBar),
|
||||
@K4 = ISNULL(@K4, K4),
|
||||
@K5 = ISNULL(@K5, k5),
|
||||
@K6 = ISNULL(@K6, K6),
|
||||
@K7 = ISNULL(@K7, K7),
|
||||
@AutoRegel = ISNULL(@AutoRegel, AutoRegelForskott)
|
||||
from xb with (NoLock) where
|
||||
ForetagKod = @c_ForetagKod
|
||||
and LagStalle = @c_LagStalle;
|
||||
|
||||
end
|
||||
|
||||
begin tran
|
||||
|
||||
select @oh_vbordsum = 0
|
||||
select @oh_ordsum = 0
|
||||
|
||||
select @oh_OrdStat = Convert(int,dbo.Jeeves_FN_GetParam(@c_ForetagKod,'webapp031','N',null))
|
||||
if @oh_ordstat is null select @oh_OrdStat = Convert(int,dbo.Jeeves_FN_GetParam(@c_ForetagKod,'WEBAPP003','N',null))
|
||||
|
||||
select @c_OrderType = isnull(@c_OrderType, convert(int,dbo.Jeeves_FN_GetParam(@c_ForetagKod,'WEBAPP008','N','1')))
|
||||
|
||||
if @samfaktutskr = '1' begin
|
||||
select @samfaktutskr = samfaktutskr from x6
|
||||
where x6.ordtyp = @c_OrderType and x6.foretagkod = @c_ForetagKod
|
||||
end
|
||||
|
||||
if (@oh_OrdStat is null) or (@oh_OrdStat > 10) select @oh_OrdStat = 10
|
||||
|
||||
execute @x = Jeeves_Init_Insert_oh
|
||||
@c_ForetagKod = @c_ForetagKod,
|
||||
@c_OrderNr = @o_OrderNumber output,
|
||||
@c_OrderNrAlfa = 'Internet',
|
||||
@c_OrdStat = @oh_ordstat,
|
||||
@c_PersSign = @c_PersSign,
|
||||
@c_FtgNr = @CompanyNo,
|
||||
@c_Ordtyp = @c_OrderType,
|
||||
@c_Lagstalle = @c_Lagstalle,
|
||||
@c_Saljare = @c_Saljare,
|
||||
@c_KundRabatt = @kus_kundrabatt,
|
||||
@c_ftgkontaktnr = @ContactNo,
|
||||
@c_vbordsum = @oh_vbordsum,
|
||||
@c_rowcreatedby = @c_perssign,
|
||||
@c_rowcreateddt = @Today,
|
||||
@c_Valkod = @valkod,
|
||||
@c_Valkurs = @Valkurs,
|
||||
@c_Samfaktutskr = @samfaktutskr,
|
||||
@c_KostBar = @KostBar,
|
||||
@c_KostStalleKod = @KostStalleKod,
|
||||
@c_K4 = @K4,
|
||||
@c_K5 = @K5,
|
||||
@c_K6 = @K6,
|
||||
@c_K7 = @K7,
|
||||
@c_AutoRegel = @AutoRegel;
|
||||
|
||||
if (@@Error <> 0) or (@x < 0) begin
|
||||
EXECUTE Jeeves_RaisError 21001, 'Error when creating order header'
|
||||
rollback tran
|
||||
return -100
|
||||
end
|
||||
|
||||
select @orp_ordradst = dbo.Jeeves_FN_GetParam (@c_ForetagKod,'WEBAPP018','N','10')
|
||||
if @oh_OrdStat < @orp_ordradst select @orp_ordradst = @oh_OrdStat
|
||||
|
||||
update oh set
|
||||
oh.ordsum = @oh_ordsum,
|
||||
oh.vbordsum = @oh_vbordsum
|
||||
where
|
||||
oh.ordernr = @o_OrderNumber and
|
||||
oh.foretagkod = @c_ForetagKod
|
||||
|
||||
if @@Error <> 0 begin
|
||||
rollback tran
|
||||
return -100
|
||||
end
|
||||
|
||||
commit tran
|
||||
|
||||
if @c_Run_Type = 'R' exec Jeeves_Esales_GetOrp @c_ForetagKod,@c_webUserName,@c_LangID,@o_OrderNumber
|
||||
|
||||
return
|
||||
GO
|
||||
PRINT 'End Jeeves_Esales_CreateOrder'
|
||||
GO
|
||||
PRINT 'End Jeeves_Esales_CreateOrder'
|
||||
GO
|
||||
|
||||
451
jeeves_sp_updates/Jeeves_Esales_PlaceOrder.spr
Normal file
451
jeeves_sp_updates/Jeeves_Esales_PlaceOrder.spr
Normal file
|
|
@ -0,0 +1,451 @@
|
|||
SET ANSI_NULLS OFF
|
||||
GO
|
||||
SET QUOTED_IDENTIFIER OFF
|
||||
GO
|
||||
SET NOCOUNT ON
|
||||
GO
|
||||
PRINT 'Begin Jeeves_Esales_PlaceOrder'
|
||||
GO
|
||||
PRINT 'Begin Jeeves_Esales_PlaceOrder'
|
||||
PRINT '***************************************************'
|
||||
PRINT '* *'
|
||||
PRINT '* Jeeves_Esales_PlaceOrder *'
|
||||
PRINT '* *'
|
||||
PRINT '***************************************************'
|
||||
GO
|
||||
IF OBJECT_ID( 'Jeeves_Esales_PlaceOrder', 'P' ) IS NOT NULL
|
||||
BEGIN
|
||||
DROP PROCEDURE Jeeves_Esales_PlaceOrder
|
||||
END
|
||||
GO
|
||||
IF (SELECT COUNT(*) FROM xspr WHERE
|
||||
SprTrgName = 'Jeeves_Esales_PlaceOrder' AND
|
||||
SprTrgType = 'P' ) > 0
|
||||
BEGIN
|
||||
UPDATE xspr SET beskrivning=beskrivning WHERE
|
||||
SprTrgName = 'Jeeves_Esales_PlaceOrder' AND
|
||||
SprTrgType = 'P'
|
||||
END ELSE BEGIN
|
||||
INSERT INTO xspr(SprTrgName,SprTrgType,RowCreatedDt)
|
||||
VALUES('Jeeves_Esales_PlaceOrder' , 'P', GETDATE() )
|
||||
END
|
||||
GO
|
||||
----------------------------------------------------
|
||||
|
||||
CREATE Procedure Jeeves_Esales_PlaceOrder
|
||||
@c_ForetagKod smallint,
|
||||
@c_OrderNumber int output,
|
||||
@c_webUserName Jeeves_StrVarChar64 = null,
|
||||
@c_CompanyNo Jeeves_StrVarChar32 = null,
|
||||
@c_LangID smallint = null,
|
||||
@c_LevSattKod smallint = null,
|
||||
@c_kundref2 Jeeves_StrVarChar32 = null,
|
||||
@c_kundbestnr Jeeves_StrVarChar32 = null,
|
||||
@c_editext Jeeves_StrVarCharMax = null,
|
||||
@c_CoName Jeeves_StrVarChar64 = null,
|
||||
@c_Addr1 Jeeves_StrVarChar64 = null,
|
||||
@c_Addr2 Jeeves_StrVarChar64 = null,
|
||||
@c_PostalCode Jeeves_StrVarChar64 = null,
|
||||
@c_City Jeeves_StrVarChar64 = null,
|
||||
@c_ProvinceCode Jeeves_StrVarChar5 = null,
|
||||
@c_CountryCode Jeeves_StrVarChar3 = null,
|
||||
@c_godsmarke1 Jeeves_StrVarChar64 = null,
|
||||
@c_godsmarke2 Jeeves_StrVarChar64 = null,
|
||||
@c_godsmarke3 Jeeves_StrVarChar64 = null,
|
||||
@c_godsmarke4 Jeeves_StrVarChar64 = null,
|
||||
@c_TA_MailNotified Jeeves_StrVarChar64 = null,
|
||||
@c_TA_PhonNotifiedNo Jeeves_StrVarChar64 = null,
|
||||
@c_TA_SMSNotifiedNo Jeeves_StrVarChar64 = null,
|
||||
@c_levforetolv char(1) = '0',
|
||||
@c_withFreightCalc char(1) = 'Y',
|
||||
@c_ordlevplats1 Jeeves_StrVarChar20 = null,
|
||||
@c_vRef Jeeves_StrVarChar64 = null,
|
||||
@c_orderStatus smallint = null,
|
||||
@c_PartDeliveryAllowed smallint = null,
|
||||
@c_text Jeeves_StrVarCharMax = null,
|
||||
@c_PaymentType char(1) = '0' --0=invoice, 1=card
|
||||
|
||||
As
|
||||
|
||||
set nocount on
|
||||
|
||||
declare @dbc int,
|
||||
@ftgnr Jeeves_StrVarChar32,
|
||||
@ftgkontaktnr int,
|
||||
@org_ftgnr Jeeves_StrVarChar32,
|
||||
@kp_ftgperson Jeeves_StrVarChar32,
|
||||
@Momskod smallint,
|
||||
@Levsattkod smallint,
|
||||
@LevvillkKod smallint,
|
||||
@Lagstalle Jeeves_StrVarChar10,
|
||||
@Betkod Jeeves_StrVarChar2,
|
||||
@Kreditorder Jeeves_Boolean,
|
||||
@valkurs ztCurrencyRate,
|
||||
@Ordsum Jeeves_Amount,
|
||||
@PersSign Jeeves_StrVarChar32,
|
||||
@Sy1_rbn char(1),
|
||||
@sy1_FtgKontaktToErRef char(1),
|
||||
@wi smallint,
|
||||
@x7_ordstat smallint,
|
||||
@wr Jeeves_StrVarChar256,
|
||||
@kundref2 Jeeves_StrVarChar32,
|
||||
@TA_MailNotified Jeeves_StrVarChar64,
|
||||
@TA_PhonNotifiedNo Jeeves_StrVarChar64,
|
||||
@TA_SMSNotifiedNo Jeeves_StrVarChar64,
|
||||
/*Output från Jeeves_Calc_Frakt*/
|
||||
@out_Fraktkostnad JEEVES_Amount,
|
||||
@out_ExpeditionKostnad JEEVES_Amount,
|
||||
@Out_Fraktdebkod JEEVES_Boolean,
|
||||
@Out_Ordervarde JEEVES_Amount,
|
||||
@Out_Fraktvikt float,
|
||||
@jvss_OrderReservation char(1),
|
||||
@kpwid integer,
|
||||
@OrdBerLevDat DateTime,
|
||||
@OrdBerednDat DateTime,
|
||||
@SALES007 smallint,
|
||||
@kus_AddArtEjAktiv char(1),
|
||||
@OrdLevAdr4 Jeeves_StrVarChar64
|
||||
|
||||
exec Jeeves_Esales_GetUserInfo @c_IntrnCoNo = @c_ForetagKod,
|
||||
@c_webUserName = @c_webUserName,
|
||||
@c_CompanyNo = @c_CompanyNo,
|
||||
@o_LangID = @c_LangID output,
|
||||
@o_CompanyNo = @ftgnr output,
|
||||
@o_ContactNo = @ftgkontaktnr output,
|
||||
@o_OrgCompanyNo = @org_ftgnr output,
|
||||
@o_kpwid = @kpwid output
|
||||
|
||||
if (@c_ordlevplats1 = @ftgnr) select @c_ordlevplats1 = null
|
||||
|
||||
if (@c_ordlevplats1 = '') select @c_ordlevplats1 = null
|
||||
|
||||
-- concatenate postal code and city for order unique adresses
|
||||
select @OrdLevAdr4 = @c_PostalCode + ' ' + @c_City
|
||||
select @c_City = null
|
||||
|
||||
select @kp_ftgperson = kp.ftgperson from kp
|
||||
where
|
||||
kp.foretagkod = @c_ForetagKod and
|
||||
kp.ftgnr = @ftgnr and
|
||||
kp.ftgkontaktnr = @ftgkontaktnr
|
||||
|
||||
select @Momskod = momskod,
|
||||
@Levsattkod = isnull(@c_LevSattKod, levsattkod),
|
||||
@Kreditorder = kreditorder,
|
||||
@Lagstalle = lagstalle,
|
||||
@valkurs = valkurs,
|
||||
@Ordsum = ordsum,
|
||||
@Levvillkkod = levvillkkod,
|
||||
@Betkod = betkod,
|
||||
@PersSign = perssign,
|
||||
@Kundref2 = kundref2,
|
||||
@c_vRef = isnull(@c_vRef, vref),
|
||||
@TA_MailNotified = TA_MailNotified,
|
||||
@TA_PhonNotifiedNo = TA_PhonNotifiedNo,
|
||||
@TA_SMSNotifiedNo = TA_SMSNotifiedNo
|
||||
from oh where
|
||||
oh.ordernr = @c_OrderNumber and
|
||||
oh.foretagkod = @c_ForetagKod
|
||||
|
||||
if @@Rowcount < 1 begin
|
||||
exec Jeeves_GT @wr output,@c_ForetagKod,@c_LangID,1237,'Kundordern saknas'
|
||||
EXECUTE Jeeves_RaisError 21001, @wr
|
||||
return
|
||||
end
|
||||
|
||||
select @x7_ordstat = dbo.Jeeves_FN_GetParam(@c_ForetagKod,'WEBAPP003','N',null)
|
||||
|
||||
--jeevesparamnumeric from jvss
|
||||
-- where
|
||||
-- jeevesparamname = N'WebOrderStatus' and
|
||||
-- foretagkod = @c_ForetagKod
|
||||
|
||||
--Use passed in value if available
|
||||
select @x7_ordstat = isnull(@c_orderStatus, @x7_ordstat)
|
||||
|
||||
select @jvss_OrderReservation = jvss.jeevesparamboolean from jvss
|
||||
where
|
||||
jvss.jeevesparamname = 'webapp010' and
|
||||
jvss.foretagkod = @c_ForetagKod
|
||||
|
||||
--select @orp_ordradstat = dbo.Jeeves_FN_GetParam (@c_IntrnCoNo,'WEBAPP018','N','10')
|
||||
|
||||
select @Sy1_rbn = sy1_rbn,
|
||||
@sy1_FtgKontaktToErRef=FtgKontaktToErRef
|
||||
from sy1
|
||||
where sy1.foretagkod = @c_ForetagKod
|
||||
|
||||
--if @c_kundref is null and FtgKontaktToErRef is true then insert Contact name as reference
|
||||
if @c_kundref2 is null begin
|
||||
if @sy1_FtgKontaktToErRef = '1' select @c_kundref2 = @kp_ftgperson
|
||||
end
|
||||
|
||||
if @c_vRef is null begin
|
||||
if (@org_ftgnr <> @ftgnr) begin
|
||||
select @c_vRef = ftgnamn from fr
|
||||
where fr.ftgnr = @org_ftgnr and
|
||||
fr.foretagkod = @c_ForetagKod
|
||||
End
|
||||
End
|
||||
|
||||
execute @wi = Jeeves_oh_ordervarde
|
||||
@In_OrderNr = @c_OrderNumber,
|
||||
@In_ValKurs = @Valkurs,
|
||||
@In_sy1_rbn = @Sy1_rbn,
|
||||
@In_PersSign = @Perssign,
|
||||
@Call_Type = 4,
|
||||
@c_Foretagkod = @c_ForetagKod
|
||||
|
||||
--The estimated delivery date of the order will be set to the earliest delivery date of a row.
|
||||
-- Also fetch OrdBerednDat to set base later
|
||||
select @OrdBerLevDat = min(OrdBerLevDat), @OrdBerednDat = min(OrdBerednDat) from orp where foretagkod = @c_ForetagKod AND OrderNr = @c_OrderNumber
|
||||
|
||||
begin tran
|
||||
|
||||
exec @wi = Jeeves_oh_kora @c_OrderNumber,null,2,@c_ForetagKod
|
||||
|
||||
if @Wi < 0 begin
|
||||
Rollback tran
|
||||
return @Wi
|
||||
end
|
||||
|
||||
select @SALES007 = dbo.Jeeves_FN_GetParam(@c_ForetagKod, 'SALES007', 'N',null)
|
||||
select @kus_AddArtEjAktiv = AddArtEjAktiv from kus where foretagkod = @c_ForetagKod and ftgnr = @ftgnr
|
||||
|
||||
if @SALES007 = 1 AND @kus_AddArtEjAktiv <> '1'
|
||||
BEGIN
|
||||
exec @wi = JEEVES_Orp_Create_Tillagg @c_OrderNumber,@Perssign,@c_ForetagKod,0
|
||||
|
||||
if @Wi < 0 begin
|
||||
Rollback tran
|
||||
return @Wi
|
||||
end
|
||||
END
|
||||
|
||||
if @c_withFreightCalc = 'Y' Begin
|
||||
|
||||
declare @Artvikt float,
|
||||
@Volume float,
|
||||
@Fraktvikt float,
|
||||
@Expavgkod Jeeves_Boolean,
|
||||
@Fraktkostnader Jeeves_Amount,
|
||||
@DispatchFeeExists smallint,
|
||||
@valkod Jeeves_StrVarChar4,
|
||||
@Fraktskrymmekod Jeeves_Boolean
|
||||
|
||||
select @Artvikt = artvikt,
|
||||
@Volume = volume,
|
||||
@Fraktkostnader=Emballagekostnad+fraktkostnman+ForsakringsKostnad+ovrfaktkostnad1+ovrfaktkostnad2,
|
||||
@valkod = valkod,
|
||||
@Expavgkod = expavgkd,
|
||||
@Fraktskrymmekod = fraktskrymmekod,
|
||||
@Fraktvikt = fraktvikt
|
||||
from oh where
|
||||
oh.ordernr = @c_OrderNumber and
|
||||
oh.foretagkod = @c_ForetagKod
|
||||
|
||||
--Check that dispatch fee is defined in table fkst
|
||||
--This check has been removed as described in 521932.
|
||||
--select @DispatchFeeExists=faktkostntyp from fkst where
|
||||
-- fkst.faktkostntyp = 4 and
|
||||
-- fkst.foretagkod = @c_ForetagKod
|
||||
|
||||
if @@Rowcount < 1 begin
|
||||
rollback tran
|
||||
exec Jeeves_GT @wr output,@c_ForetagKod,@c_LangID,1333,'Expeditionsavgift kan ej debiteras, definition saknas i tabellen Fakturakostnader (fkst) !'
|
||||
EXECUTE Jeeves_RaisError 21001, @wr
|
||||
return
|
||||
end
|
||||
|
||||
execute JEEVES_Calc_Frakt
|
||||
@In_ForetagKod=@c_ForetagKod,
|
||||
@In_FtgNr = @ftgnr,
|
||||
@In_OrdLevPlats1 = @ftgnr,
|
||||
@In_OrderNr = @c_OrderNumber,
|
||||
@In_KreditOrder = @Kreditorder,
|
||||
--@In_OrdBerLevDat datetime=NULL,
|
||||
@In_LagStalle = @Lagstalle,
|
||||
--@In_OrdLevNr smallint=NULL,
|
||||
--@In_OrdStat smallint=NULL,
|
||||
--@In_OrdFsNr smallint=NULL,
|
||||
@In_FraktVikt = @Artvikt,
|
||||
@In_AntalEURPall = 0,
|
||||
@In_AntalOvrPall = 0,
|
||||
@In_Volume = @Volume,
|
||||
@In_LevForeTolv = @c_Levforetolv,
|
||||
@In_FraktSkrymmeKod = @Fraktskrymmekod,
|
||||
@In_LevSattKod = @Levsattkod,
|
||||
@In_LevVillkKod = @Levvillkkod,
|
||||
@In_ValKurs = @Valkurs,
|
||||
@In_Valkod = @Valkod,
|
||||
@In_ExpAvgKod = @ExpAvgkod,
|
||||
@In_BetKod = @Betkod,
|
||||
@In_MomsKod = @Momskod,
|
||||
@In_OrderVarde = @Ordsum,
|
||||
@Call_Type = 1,
|
||||
@PersSign = @PersSign,
|
||||
@In_OvrFaktKostnader = 0,
|
||||
@Svar_FraktKostnad = @out_Fraktkostnad output,
|
||||
@Svar_ExpeditionKostnad = @out_ExpeditionKostnad output,
|
||||
@Svar_FraktdebKod = @Out_Fraktdebkod output,
|
||||
@Svar_OrderVarde = @Out_Ordervarde output,
|
||||
@Svar_Fraktvikt = @Out_Fraktvikt output
|
||||
|
||||
if @@Error <> 0 begin
|
||||
rollback tran
|
||||
return
|
||||
end
|
||||
End
|
||||
|
||||
if @c_PaymentType <> '1' begin
|
||||
Update oh SET
|
||||
oh.ordernralfa = 'Internet',
|
||||
oh.ordstat = isnull(@x7_ordstat,13),
|
||||
oh.kundref2 = @c_kundref2,
|
||||
oh.kundbestnr = @c_kundbestnr,
|
||||
oh.levforetolv = @c_levforetolv,
|
||||
oh.levsattkod = @Levsattkod,
|
||||
oh.ordberlevdat = @OrdBerLevDat,
|
||||
oh.ohordberlevdatbase = @OrdBerLevDat, -- Fix base LevDat and BerednDat. Used when adding rows in GUI.
|
||||
oh.ohordberedndatbase = @OrdBerednDat,
|
||||
oh.ordlevadr1 = @c_CoName,
|
||||
oh.ordlevadr2 = @c_Addr1,
|
||||
oh.ordlevadr3 = @c_Addr2,
|
||||
oh.ordlevadr4 = @OrdLevAdr4,
|
||||
oh.ordlevadrbstort = @c_City,
|
||||
oh.Ordlevadrlandskod = @c_CountryCode,
|
||||
oh.ordlevadrprovincecode = @c_ProvinceCode,
|
||||
oh.editext = @c_editext,
|
||||
oh.edit = @c_text,
|
||||
oh.bruttovikt = isnull(@artvikt,0),
|
||||
oh.fraktkostnber = isnull(@out_Fraktkostnad,0),
|
||||
oh.godsmarke1 = @c_godsmarke1,
|
||||
oh.godsmarke2 = @c_godsmarke2,
|
||||
oh.godsmarke3 = @c_godsmarke3,
|
||||
oh.godsmarke4 = @c_godsmarke4,
|
||||
oh.ordlevplats1 = @c_ordlevplats1,
|
||||
oh.dellevtillaten = isnull(@c_PartDeliveryAllowed, 0),
|
||||
oh.vref = @c_vRef,
|
||||
oh.ta_mailnotified = isnull(@c_TA_MailNotified, @TA_MailNotified),
|
||||
oh.ta_phonnotifiedno = isnull(@c_TA_PhonNotifiedNo, @TA_PhonNotifiedNo),
|
||||
oh.ta_smsnotifiedno = isnull(@c_TA_SMSNotifiedNo, @TA_SMSNotifiedNo)
|
||||
Where ordernr = @c_OrderNumber and
|
||||
foretagkod = @c_ForetagKod
|
||||
|
||||
if @@Error <> 0 begin
|
||||
rollback tran
|
||||
return
|
||||
end
|
||||
|
||||
update kp set
|
||||
kp.antalkop = isnull(kp.antalkop,0) + 1
|
||||
where
|
||||
kp.foretagkod = @c_ForetagKod and
|
||||
kp.ftgnr = @ftgnr and
|
||||
kp.ftgkontaktnr = @ftgkontaktnr
|
||||
|
||||
if @@Error <> 0 begin
|
||||
rollback tran
|
||||
return
|
||||
end
|
||||
|
||||
end else begin
|
||||
--KORTBETALNING
|
||||
--do not update orderstatus
|
||||
--do not imcrement kp
|
||||
Update oh SET
|
||||
oh.ordernralfa = 'Internet',
|
||||
oh.kundref2 = @c_kundref2,
|
||||
oh.kundbestnr = @c_kundbestnr,
|
||||
oh.levforetolv = @c_levforetolv,
|
||||
oh.levsattkod = @Levsattkod,
|
||||
oh.ordberlevdat = @OrdBerLevDat,
|
||||
oh.ohordberlevdatbase = @OrdBerLevDat, -- Fix base LevDat and BerednDat. Used when adding rows in GUI.
|
||||
oh.ohordberedndatbase = @OrdBerednDat,
|
||||
oh.ordlevadr1 = @c_CoName,
|
||||
oh.ordlevadr2 = @c_Addr1,
|
||||
oh.ordlevadr3 = @c_Addr2,
|
||||
oh.ordlevadr4 = @OrdLevAdr4,
|
||||
oh.ordlevadrbstort = @c_City,
|
||||
oh.Ordlevadrlandskod = @c_CountryCode,
|
||||
oh.ordlevadrprovincecode = @c_ProvinceCode,
|
||||
oh.editext = @c_editext,
|
||||
oh.edit = @c_text,
|
||||
oh.bruttovikt = isnull(@artvikt,0),
|
||||
oh.fraktkostnber = isnull(@out_Fraktkostnad,0),
|
||||
oh.godsmarke1 = @c_godsmarke1,
|
||||
oh.godsmarke2 = @c_godsmarke2,
|
||||
oh.godsmarke3 = @c_godsmarke3,
|
||||
oh.godsmarke4 = @c_godsmarke4,
|
||||
oh.ordlevplats1 = @c_ordlevplats1,
|
||||
oh.dellevtillaten = isnull(@c_PartDeliveryAllowed, 0),
|
||||
oh.vref = @c_vRef,
|
||||
oh.ta_mailnotified = isnull(@c_TA_MailNotified, @TA_MailNotified),
|
||||
oh.ta_phonnotifiedno = isnull(@c_TA_PhonNotifiedNo, @TA_PhonNotifiedNo),
|
||||
oh.ta_smsnotifiedno = isnull(@c_TA_SMSNotifiedNo, @TA_SMSNotifiedNo)
|
||||
Where ordernr = @c_OrderNumber and
|
||||
foretagkod = @c_ForetagKod
|
||||
|
||||
if @@Error <> 0 begin
|
||||
rollback tran
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
--remove any old
|
||||
if exists(select 1 from webl where webl.ConnSQLIDkpw = @kpwid and
|
||||
webl.ordernr = @c_OrderNumber ) begin
|
||||
|
||||
update webl set
|
||||
ordernr = null
|
||||
where
|
||||
webl.ConnSQLIDkpw = @kpwid and
|
||||
webl.ordernr = @c_OrderNumber
|
||||
end
|
||||
|
||||
|
||||
if @@Error <> 0 begin
|
||||
rollback tran
|
||||
return
|
||||
end
|
||||
|
||||
/*
|
||||
insert into webo (Foretagkod,
|
||||
OrderNr,
|
||||
RowCreatedBy,
|
||||
Perssign,
|
||||
RegDat,
|
||||
RowCreatedDt)
|
||||
values (@c_IntrnCoNo,
|
||||
@o_OrderNo,
|
||||
@Perssign,
|
||||
@Perssign,
|
||||
getdate(),
|
||||
getdate())
|
||||
|
||||
if @@Error <> 0 begin
|
||||
rollback tran
|
||||
return
|
||||
end */
|
||||
|
||||
commit tran
|
||||
|
||||
if @jvss_Orderreservation = '1' Begin
|
||||
execute JEEVES_Oh_Bok
|
||||
@OrderNr = @c_OrderNumber,
|
||||
@PersSign = @PersSign,
|
||||
@ForetagKod =@c_ForetagKod
|
||||
|
||||
End
|
||||
|
||||
--select @o_OrderNo = @Ordernr
|
||||
|
||||
return
|
||||
|
||||
|
||||
GO
|
||||
PRINT 'End Jeeves_Esales_PlaceOrder'
|
||||
GO
|
||||
PRINT 'End Jeeves_Esales_PlaceOrder'
|
||||
GO
|
||||
|
||||
3409
jeeves_sp_updates/Jeeves_Init_Insert_oh.spr
Normal file
3409
jeeves_sp_updates/Jeeves_Init_Insert_oh.spr
Normal file
File diff suppressed because it is too large
Load diff
4401
jeeves_sp_updates/Jeeves_Init_Insert_orp.spr
Normal file
4401
jeeves_sp_updates/Jeeves_Init_Insert_orp.spr
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1 @@
|
|||
import logging
|
||||
70
pyjeeves/config.py
Normal file
70
pyjeeves/config.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
from logging.config import dictConfig
|
||||
import os
|
||||
import yaml
|
||||
|
||||
|
||||
config = {
|
||||
'alembic': {
|
||||
'script_location': 'migrations',
|
||||
'sqlalchemy.url': ''},
|
||||
'databases': {
|
||||
'meta': {},
|
||||
'raw': {}},
|
||||
'logging': {
|
||||
'formatters': {
|
||||
'simpleFormatter': {
|
||||
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'}},
|
||||
'handlers': {
|
||||
'consoleHandler': {
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'simpleFormatter',
|
||||
'level': 'DEBUG',
|
||||
'stream': 'ext://sys.stdout'},
|
||||
'fileHandler': {
|
||||
'class': 'logging.FileHandler',
|
||||
'filename': 'pyjeeves.log',
|
||||
'formatter': 'simpleFormatter',
|
||||
'level': 'INFO'}},
|
||||
'loggers': {
|
||||
'PyJeeves': {'handlers': ['fileHandler'],
|
||||
'level': 'DEBUG'},
|
||||
'alembic': {'handlers': ['fileHandler'],
|
||||
'level': 'INFO'},
|
||||
'sqlalchemy': {'handlers': ['fileHandler'],
|
||||
'level': 'WARN',
|
||||
'qualname': 'sqlalchemy.engine'}},
|
||||
'root': {'handlers': ['consoleHandler'], 'level': 'DEBUG'},
|
||||
'version': 1},
|
||||
'sync_interval': 60}
|
||||
|
||||
config['debug'] = os.getenv('ENVIRONEMENT') == 'DEV'
|
||||
|
||||
DB_CONTAINER = os.getenv('APPLICATION_DB_CONTAINER', 'db')
|
||||
config['databases']['raw'] = {
|
||||
'user': os.getenv('JEEVES_USER', 'jvsdbo'),
|
||||
'pw': os.getenv('JEEVES_PW', ''),
|
||||
'host': os.getenv('JEEVES_HOST', ''),
|
||||
'port': os.getenv('JEEVES_PORT', 1433),
|
||||
'db': os.getenv('JEEVES_DB', ''),
|
||||
}
|
||||
config['databases']['meta'] = {
|
||||
'user': os.getenv('META_MYSQL_USER', 'pyjeeves'),
|
||||
'pw': os.getenv('META_MYSQL_PW', ''),
|
||||
'host': os.getenv('META_MYSQL_HOST', DB_CONTAINER),
|
||||
'port': os.getenv('META_MYSQL_PORT', 3306),
|
||||
'db': os.getenv('META_MYSQL_DB', 'pyjeeves'),
|
||||
}
|
||||
|
||||
# DB_URI = 'postgresql://%(user)s:%(pw)s@%(host)s:%(port)s/%(db)s' % POSTGRES
|
||||
config['alembic']['sqlalchemy.url'] = (
|
||||
'mysql+pymysql://%(user)s:%(pw)s@%(host)s:%(port)s/%(db)s?charset=utf8mb4' %
|
||||
config['databases']['meta'])
|
||||
|
||||
try:
|
||||
with open("config.yml", 'r') as ymlfile:
|
||||
file_config = yaml.load(ymlfile, Loader=yaml.FullLoader)
|
||||
config = {**config, **file_config} # Syntax introduced in Python 3.5
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
dictConfig(config['logging'])
|
||||
83
pyjeeves/connector.py
Normal file
83
pyjeeves/connector.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
pyjeeves
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Global objects
|
||||
"""
|
||||
from pyjeeves import logging, config
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker, scoped_session
|
||||
# from sqlalchemy.orm.exc import UnmappedClassError
|
||||
from pymssql import OperationalError
|
||||
|
||||
from sqlservice import Database
|
||||
|
||||
logger = logging.getLogger("PyJeeves." + __name__)
|
||||
|
||||
|
||||
class DBConnector(object):
|
||||
"""This class is used to control the SQLAlchemy integration"""
|
||||
|
||||
def __init__(self, enabled_clients=['raw'], metadata=None):
|
||||
logger.info("Creating engines and sessionmakers")
|
||||
|
||||
self.enabled_clients = enabled_clients
|
||||
self.raw_db, self.raw_session, self.raw_engine = (
|
||||
self.raw_client() if 'raw' in enabled_clients else {})
|
||||
self.meta = (self.meta_session() if 'meta' in enabled_clients else {})
|
||||
|
||||
def callproc(self, procedure="", params=[]):
|
||||
conn = self.raw_engine.raw_connection()
|
||||
|
||||
with conn.cursor() as cursor:
|
||||
try:
|
||||
retval = cursor.callproc(procedure, params)
|
||||
try:
|
||||
cursor.nextset()
|
||||
retval = cursor.fetchall()
|
||||
except OperationalError:
|
||||
logger.debug("Executed statement has no resultset")
|
||||
|
||||
conn.commit()
|
||||
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
return retval
|
||||
|
||||
def execute(self, operation=""):
|
||||
conn = self.raw_engine
|
||||
|
||||
with conn.connection.cursor(as_dict=True) as cursor:
|
||||
try:
|
||||
cursor.execute(operation)
|
||||
results = cursor.fetchall()
|
||||
finally:
|
||||
conn.close()
|
||||
return results
|
||||
|
||||
def raw_client(self):
|
||||
if 'raw' not in self.enabled_clients:
|
||||
logger.error('Raw client is not enabled')
|
||||
|
||||
logger.info("Using DB %s" % config.config['databases']['raw']['db'])
|
||||
|
||||
uri = 'mssql+pymssql://{user}:{pw}@{host}:{port}/{db}?charset=utf8'.format(
|
||||
**config.config['databases']['raw'])
|
||||
|
||||
db = Database(uri, echo=False)
|
||||
|
||||
return db, db.session(), db.connect()
|
||||
|
||||
def set_model_class(self, model_class):
|
||||
self.raw_db.model_class = model_class
|
||||
|
||||
def meta_session(self):
|
||||
|
||||
meta_engine = create_engine(
|
||||
'mysql+pymysql://{user}:{pw}@{host}:{port}/{db}?charset=utf8mb4'.format(
|
||||
**config.config['databases']['meta']))
|
||||
|
||||
return scoped_session(sessionmaker(bind=meta_engine))
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from models.jvsmodels import Base
|
||||
|
||||
|
||||
class MySQLSession(Session):
|
||||
"""docstring for MySQLSession"""
|
||||
def __init__(self, settings):
|
||||
self.engine = create_engine(
|
||||
'mysql+pymysql://{user}:{passwd}@{host}:{port}/{db}?charset=utf8mb4'.format(**settings))
|
||||
super(MySQLSession, self).__init__(bind=self.engine)
|
||||
|
||||
def create_db(self):
|
||||
Base.metadata.create_all(self.engine)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import yaml
|
||||
with open("config.yml", 'r') as ymlfile:
|
||||
cfg = yaml.load(ymlfile)
|
||||
|
||||
session = MySQLSession(cfg['mysql'])
|
||||
session.create_db()
|
||||
12
pyjeeves/db_meta.py
Normal file
12
pyjeeves/db_meta.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from pyjeeves import meta_engine
|
||||
from models.meta import Base
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from pyjeeves import logging
|
||||
logger = logging.getLogger("PyJeeves." + __name__)
|
||||
|
||||
logger.info("Creating meta database")
|
||||
Base.metadata.create_all(meta_engine)
|
||||
|
|
@ -7,14 +7,14 @@
|
|||
"""
|
||||
import pymssql
|
||||
import datetime
|
||||
import logging
|
||||
from pyjeeves import logging, config
|
||||
|
||||
|
||||
class JvsQuery():
|
||||
"""JvsQuery based on http://pymssql.org/en/stable/ """
|
||||
def __init__(self, settings):
|
||||
def __init__(self):
|
||||
super(JvsQuery, self).__init__()
|
||||
self.settings = settings
|
||||
self.settings = config['jeeves_db']
|
||||
self.logger = logging.getLogger("PyJeeves.jvsquery")
|
||||
|
||||
def _execute(self, query="", params=(), iterator=True):
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import pprint
|
||||
import yaml
|
||||
import signal
|
||||
import sys
|
||||
|
||||
import logging
|
||||
import logging.config
|
||||
# import logging
|
||||
# import logging.config
|
||||
|
||||
from alembic.config import Config
|
||||
from alembic import command
|
||||
|
||||
from pyjeeves.connector import DBConnector
|
||||
from pyjeeves import config
|
||||
from process import Process
|
||||
from jvsquery import JvsQuery
|
||||
from db import MySQLSession
|
||||
from utils import TaskThread
|
||||
|
||||
|
||||
|
|
@ -25,8 +25,9 @@ class SyncTread(TaskThread):
|
|||
def __init__(self, config):
|
||||
super(SyncTread, self).__init__()
|
||||
|
||||
jvs_query = JvsQuery(config['jeeves_db'])
|
||||
db_session = MySQLSession(config['mysql'])
|
||||
# Use RawSession instead...
|
||||
jvs_query = JvsQuery()
|
||||
None, db_session = DBConnector.create_scoped_session(['meta'])
|
||||
self.process = Process(jvs_query, db_session)
|
||||
|
||||
self.logger = logging.getLogger("PyJeeves.SyncTread")
|
||||
|
|
@ -38,16 +39,15 @@ class SyncTread(TaskThread):
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
with open("config.yml", 'r') as ymlfile:
|
||||
cfg = yaml.load(ymlfile)
|
||||
|
||||
logging.config.dictConfig(cfg['logging'])
|
||||
from pyjeeves import logging
|
||||
# logging.config.dictConfig(config['logging'])
|
||||
logger = logging.getLogger("PyJeeves")
|
||||
|
||||
logger.info("Running migrations")
|
||||
alembic_cfg = Config()
|
||||
for k in cfg['alembic']:
|
||||
alembic_cfg.set_main_option(k, cfg['alembic'][k])
|
||||
for k in config['alembic']:
|
||||
alembic_cfg.set_main_option(k, config['alembic'][k])
|
||||
command.upgrade(alembic_cfg, "head")
|
||||
|
||||
logger.info("Application started")
|
||||
|
|
@ -59,9 +59,9 @@ if __name__ == '__main__':
|
|||
signal.signal(signal.SIGINT, sigterm_handler)
|
||||
signal.signal(signal.SIGTERM, sigterm_handler)
|
||||
|
||||
sync_thread = SyncTread(cfg)
|
||||
sync_thread = SyncTread()
|
||||
try:
|
||||
sync_thread.setInterval(cfg['sync_interval'])
|
||||
sync_thread.setInterval(config['sync_interval'])
|
||||
sync_thread.start()
|
||||
sync_thread.join()
|
||||
finally:
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
pyjeeves.models
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
consolodated models module
|
||||
models for databases
|
||||
"""
|
||||
from pyjeeves.connector import DBConnector
|
||||
|
||||
from .jvsmodels import * # noqa
|
||||
db = DBConnector()
|
||||
|
|
|
|||
224
pyjeeves/models/abc.py
Normal file
224
pyjeeves/models/abc.py
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
"""
|
||||
Define an Abstract Base Class (ABC) for models
|
||||
"""
|
||||
from decimal import Decimal
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy.sql.expression import and_
|
||||
from sqlalchemy.ext.hybrid import hybrid_property
|
||||
from sqlalchemy.exc import OperationalError
|
||||
|
||||
from sqlalchemy.schema import MetaData, Column
|
||||
from sqlalchemy.types import Integer
|
||||
from sqlalchemy.orm.collections import InstrumentedList
|
||||
from sqlalchemy import event, orm
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from sqlservice import ModelBase, as_declarative
|
||||
|
||||
from pyjeeves import logging
|
||||
|
||||
from . import db
|
||||
from .ext import install_validator_listner
|
||||
|
||||
logger = logging.getLogger("PyJeeves." + __name__)
|
||||
|
||||
logger.info("Reading Jeeves DB structure")
|
||||
|
||||
meta = MetaData()
|
||||
try:
|
||||
# TODO: Split raw.py and reflect tables on separate module loads?
|
||||
meta.reflect(bind=db.raw_session.connection(),
|
||||
only=['ar', 'ars', 'arsh', 'arean', 'xae', 'xare', 'fr', 'kus', 'x1k',
|
||||
'oh', 'orp', 'lp', 'vg', 'xp', 'xm', 'prh', 'prl',
|
||||
'kp', 'kpw', 'cr', 'X4', 'xw', 'X1', 'jfbs', 'lrfb',
|
||||
'JAPP_EWMS_Item_Replenishment_Levels'])
|
||||
except OperationalError as e:
|
||||
logger.error("Failed to read Jeeves DB structure")
|
||||
raise e
|
||||
|
||||
|
||||
|
||||
@event.listens_for(Session, "do_orm_execute")
|
||||
def _add_filtering_criteria(execute_state):
|
||||
"""Intercept all ORM queries. Add a with_loader_criteria option to all
|
||||
of them.
|
||||
|
||||
This option applies to SELECT queries and adds a global WHERE criteria
|
||||
(or as appropriate ON CLAUSE criteria for join targets)
|
||||
to all objects of a certain class or superclass.
|
||||
|
||||
"""
|
||||
|
||||
# the with_loader_criteria automatically applies itself to
|
||||
# relationship loads as well including lazy loads. So if this is
|
||||
# a relationship load, assume the option was set up from the top level
|
||||
# query.
|
||||
|
||||
# TODO: Make configurable if repo made pub
|
||||
company_code = execute_state.execution_options.get("company_code", 1)
|
||||
|
||||
if (
|
||||
not execute_state.is_column_load
|
||||
and not execute_state.is_relationship_load
|
||||
# and not execute_state.execution_options.get("include_private", False)
|
||||
):
|
||||
execute_state.statement = execute_state.statement.options(
|
||||
orm.with_loader_criteria(
|
||||
RawBaseModel,
|
||||
lambda cls: cls.ForetagKod == company_code,
|
||||
include_aliases=True,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@as_declarative(metadata=meta)
|
||||
class RawBaseModel(ModelBase):
|
||||
""" Generalize __init__, __repr__ and to_json
|
||||
Based on the models columns , ForetagKod=1"""
|
||||
|
||||
__to_dict_filter__ = []
|
||||
__to_dict_only__ = ()
|
||||
__column_map__ = {}
|
||||
__reversed_column_map__ = lambda self: {v: k for k, v in self.__column_map__.items()} # noqa
|
||||
|
||||
__table_args__ = {
|
||||
'extend_existing': True
|
||||
}
|
||||
|
||||
__dict_args__ = {
|
||||
'adapters': {
|
||||
datetime: lambda value, col, *_: value.strftime('%Y-%m-%d %H:%M'),
|
||||
Decimal: lambda value, col, *_: float(value) # "{:.2f}".format(value)
|
||||
}
|
||||
}
|
||||
|
||||
ForetagKod = Column(Integer, primary_key=True)
|
||||
|
||||
def __init__(self, data=None, **kargs):
|
||||
if data:
|
||||
data = self._map_keys(data)
|
||||
self.set(**kargs)
|
||||
|
||||
@classmethod
|
||||
def _map_columns(cls, key):
|
||||
if key in cls.__column_map__:
|
||||
return cls.__column_map__[key]
|
||||
return key
|
||||
|
||||
def _map_keys(self, data={}):
|
||||
rv = {}
|
||||
for key, value in self.__reversed_column_map__().items():
|
||||
if key in data:
|
||||
rv[value] = data[key]
|
||||
for key, value in data.items():
|
||||
if hasattr(self, key):
|
||||
if key in self.relationships().keys():
|
||||
rv[key] = self._map_relationship_keys(key, value)
|
||||
else:
|
||||
rv[key] = value
|
||||
return rv
|
||||
|
||||
def _map_relationship_keys(self, field, value):
|
||||
"""Get model relationships fields value. Almost a copy from SQLService ModelBase"""
|
||||
relation_attr = getattr(self.__class__, field)
|
||||
uselist = relation_attr.property.uselist
|
||||
relation_class = relation_attr.property.mapper.class_
|
||||
|
||||
if uselist:
|
||||
if not isinstance(value, (list, tuple)): # pragma: no cover
|
||||
value = [value]
|
||||
|
||||
# Convert each value instance to relationship class.
|
||||
value = [relation_class(val) if not isinstance(val, relation_class)
|
||||
else val
|
||||
for val in value]
|
||||
elif value and isinstance(value, dict):
|
||||
# Convert single value object to relationship class.
|
||||
value = relation_class(value)
|
||||
elif not value and isinstance(value, dict):
|
||||
# If value is {} and we're trying to update a relationship
|
||||
# attribute, then we need to set to None to nullify relationship
|
||||
# value.
|
||||
value = None
|
||||
|
||||
return value
|
||||
|
||||
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 from_dict(self, data={}):
|
||||
for key, value in self.__reversed_column_map__().items():
|
||||
if key in data:
|
||||
self[value] = data[key]
|
||||
for key, value in data.items():
|
||||
if hasattr(self, key):
|
||||
if isinstance(self[key], InstrumentedList):
|
||||
pass
|
||||
else:
|
||||
self[key] = value
|
||||
return self
|
||||
|
||||
def merge(self):
|
||||
db.raw_session.merge(self)
|
||||
return self
|
||||
|
||||
def commit(self):
|
||||
db.raw_session.commit()
|
||||
|
||||
def save(self):
|
||||
db.raw_session.add(self)
|
||||
db.raw_session.commit()
|
||||
return self
|
||||
|
||||
def delete(self):
|
||||
db.raw_session.delete(self)
|
||||
db.raw_session.commit()
|
||||
|
||||
|
||||
# Apply validators for all string attributes in subclasses of RawBaseModel
|
||||
@event.listens_for(RawBaseModel, 'attribute_instrument')
|
||||
def receive_attribute_instrument(cls, key, inst):
|
||||
"listen for the 'attribute_instrument' event"
|
||||
|
||||
install_validator_listner(cls, key, inst)
|
||||
|
||||
|
||||
db.set_model_class(RawBaseModel)
|
||||
56
pyjeeves/models/ext.py
Normal file
56
pyjeeves/models/ext.py
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
from sqlalchemy.orm import ColumnProperty
|
||||
from sqlalchemy.types import String
|
||||
from sqlalchemy import event
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
"""Base class for exceptions in this module."""
|
||||
pass
|
||||
|
||||
|
||||
class ValidationError(Error):
|
||||
pass
|
||||
|
||||
|
||||
class JeevesDBError(Error):
|
||||
"""Exception raised for errors in the input.
|
||||
|
||||
Attributes:
|
||||
expression -- input expression in which the error occurred
|
||||
message -- explanation of the error
|
||||
"""
|
||||
|
||||
def __init__(self, field, message):
|
||||
super().__init__(message)
|
||||
self.field = field
|
||||
self.message = message
|
||||
|
||||
|
||||
def install_validator_listner(class_, key, inst):
|
||||
"""Add validators for any attributes that can be validated."""
|
||||
prop = inst.prop
|
||||
# Only interested in simple columns, not relations
|
||||
if isinstance(prop, ColumnProperty) and len(prop.columns) == 1:
|
||||
col = prop.columns[0]
|
||||
# if we have string column with a length, create a length validator listner
|
||||
if isinstance(col.type, String) and col.type.length:
|
||||
event.listen(
|
||||
getattr(class_, key), 'set', LengthValidator(
|
||||
col.name, col.type.length), retval=True)
|
||||
|
||||
|
||||
class LengthValidator():
|
||||
def __init__(self, col_name, max_length):
|
||||
self.col_name = col_name
|
||||
self.max_length = max_length
|
||||
|
||||
def __call__(self, state, value, oldvalue, initiator):
|
||||
if len(value) > self.max_length:
|
||||
raise ValidationError(
|
||||
"%s.%s: Length %d exceeds allowed %d" % (
|
||||
state.__class__.__name__, state.__class__._map_columns(self.col_name),
|
||||
len(value), self.max_length))
|
||||
return value
|
||||
|
||||
# Add more validators, such as type for ints.
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
pyjeeves.models
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Jeeves data models
|
||||
Jeeves meta data models
|
||||
"""
|
||||
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
587
pyjeeves/models/raw.py
Normal file
587
pyjeeves/models/raw.py
Normal file
|
|
@ -0,0 +1,587 @@
|
|||
# -*- 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',
|
||||
'ArticleClass': 'DefaultArticleClass'}
|
||||
__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, lazy='joined')
|
||||
|
||||
|
||||
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(String, ForeignKey('xae.AltEnhetKod'), primary_key=True)
|
||||
ArticleAlternativeUnit = relationship(ArticleAlternativeUnit, lazy='joined')
|
||||
|
||||
|
||||
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 ArticleEAN(RawBaseModel):
|
||||
__tablename__ = 'arean'
|
||||
__column_map__ = {'ArtNrEAN': 'EAN', 'ArtNr': 'ArticleNumber'}
|
||||
__to_dict_only__ = ('ArtNr', 'ArtNrEAN', 'ArticleUnit')
|
||||
|
||||
ArtNr = Column(String, ForeignKey('ar.ArtNr'), primary_key=True)
|
||||
ArtNrEAN = Column(String, primary_key=True)
|
||||
AltEnhetKod = Column(String, ForeignKey('xare.AltEnhetKod'))
|
||||
|
||||
ArticleUnit = relationship(ArticleUnit, lazy='joined')
|
||||
|
||||
|
||||
class ArticleShelf(RawBaseModel):
|
||||
__tablename__ = 'arsh'
|
||||
__column_map__ = {'LagPlats': 'Shelf',
|
||||
'LagStalle': 'WarehouseID',
|
||||
'JAPP_EWMS_zoneid': 'WMSZoneID',
|
||||
'ArtNr': 'ArticleNumber'}
|
||||
__to_dict_only__ = ('LagPlats', 'LagStalle', 'JAPP_EWMS_zoneid', 'ArtNr')
|
||||
|
||||
LagPlats = Column(String, ForeignKey('ar.ArtNr'), primary_key=True)
|
||||
LagStalle = Column(String, ForeignKey('ar.ArtNr'), primary_key=True)
|
||||
|
||||
|
||||
class VATRate(RawBaseModel):
|
||||
__tablename__ = 'X1'
|
||||
__column_map__ = {'MomsKod': 'VATID', 'MomsSats': 'VATRate'}
|
||||
__to_dict_only__ = ('MomsKod', 'MomsSats')
|
||||
|
||||
|
||||
class Article(RawBaseModel):
|
||||
__tablename__ = 'ar'
|
||||
|
||||
__column_map__ = {'ArtNr': 'ArticleNumber',
|
||||
'ArtBeskr': 'ArticleName',
|
||||
'ArtBeskrSpec': 'ArticleSpec',
|
||||
'Edit': 'ArticleLongSpec',
|
||||
'LagSaldoArtikel': 'UnitBalance',
|
||||
'EnhetsKod': 'Unit',
|
||||
# 'Extra1': 'WholeSaleUnit',
|
||||
'ArtListPris': 'UnitListPrice'}
|
||||
__to_dict_only__ = (
|
||||
'ArtNr',
|
||||
'ArtBeskr',
|
||||
'ArtBeskrSpec',
|
||||
'Edit',
|
||||
'CommodityGroup',
|
||||
'ProductClass',
|
||||
'ArticleClass',
|
||||
'ArticleBalance',
|
||||
'EnhetsKod',
|
||||
'LagSaldoArtikel',
|
||||
'RowCreatedDt',
|
||||
'ArtListPris',
|
||||
'PictureFileName',
|
||||
'UnitListPrice',
|
||||
# 'Extra1',
|
||||
'WholeSaleUnit',
|
||||
'WholeSaleAmount',
|
||||
'ListPrice',
|
||||
'Balance',
|
||||
'VATRate')
|
||||
|
||||
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)
|
||||
|
||||
MomsKod = Column(Integer, ForeignKey('X1.MomsKod'))
|
||||
|
||||
CommodityGroup = relationship(CommodityGroup, lazy='joined')
|
||||
ProductClass = relationship(ProductClass, lazy='joined')
|
||||
ArticleClass = relationship(ArticleClass, lazy='joined')
|
||||
ArticleBalance = relationship(ArticleBalance)
|
||||
|
||||
ArticleUnit = relationship(ArticleUnit, uselist=True)
|
||||
ArticleVATRate = relationship(VATRate, lazy='joined')
|
||||
|
||||
def _get_alt_unit(self):
|
||||
# Find matching alternative unit for amount per piece, or return default for the article.
|
||||
spec_conv = self.ArtFsgForp if self.ArtFsgForp else None
|
||||
|
||||
for unit in self.ArticleUnit:
|
||||
if unit.AltEnhetOrderStd == "1" and not spec_conv:
|
||||
return unit
|
||||
elif (spec_conv and unit.AltEnhetOrder == "1" and (
|
||||
unit.AltEnhetOmrFaktor == spec_conv or
|
||||
unit.ArticleAlternativeUnit.AltEnhetOmrFaktor == spec_conv)):
|
||||
return unit
|
||||
|
||||
def get_unit_conv(self):
|
||||
# Return conversion factor for the article's alternative unit
|
||||
if self.ArtFsgForp:
|
||||
return self.ArtFsgForp
|
||||
|
||||
unit = self._get_alt_unit()
|
||||
if unit:
|
||||
return (unit.AltEnhetOmrFaktor if unit.AltEnhetOmrFaktor
|
||||
else unit.ArticleAlternativeUnit.AltEnhetOmrFaktor)
|
||||
|
||||
return 1
|
||||
|
||||
@hybrid_property
|
||||
def WholeSaleUnit(self):
|
||||
# Description of alternative unit, or Extra1 if no alternative unit is in use.
|
||||
unit = self._get_alt_unit()
|
||||
if unit:
|
||||
return unit.ArticleAlternativeUnit.AltEnhetBeskr
|
||||
else:
|
||||
return self.Extra1
|
||||
|
||||
@hybrid_property
|
||||
def WholeSaleAmount(self):
|
||||
# Amount of units in the alternative unit of the article, or 1 if none exist.
|
||||
return self.get_unit_conv()
|
||||
|
||||
@hybrid_property
|
||||
def VATRate(self):
|
||||
return self.ArticleVATRate.MomsSats
|
||||
|
||||
@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)
|
||||
|
||||
class ContactInformationType(RawBaseModel):
|
||||
__tablename__ = 'X4'
|
||||
__to_dict_only__ = ('ComKod', 'ComBeskr')
|
||||
|
||||
|
||||
class ContactInformation(RawBaseModel):
|
||||
__tablename__ = 'cr'
|
||||
__column_map__ = {'ComNr': 'Information'}
|
||||
__to_dict_only__ = ('InformationType', 'ComNr')
|
||||
|
||||
FtgNr = Column(String, ForeignKey('fr.FtgNr'), primary_key=True)
|
||||
FtgKontaktNr = Column(Integer, ForeignKey('kp.FtgKontaktNr'), primary_key=True)
|
||||
ComKod = Column(String, ForeignKey('X4.ComKod'), primary_key=True)
|
||||
|
||||
ContactInformationType = relationship(ContactInformationType)
|
||||
|
||||
@hybrid_property
|
||||
def InformationType(self):
|
||||
return self.ContactInformationType.ComBeskr if self.ContactInformationType else ""
|
||||
|
||||
|
||||
class ContactPerson(RawBaseModel):
|
||||
__tablename__ = 'kp'
|
||||
__column_map__ = {'FtgKontaktNr': 'ContactNumber', 'FtgPerson': 'ContactName'}
|
||||
__to_dict_only__ = ('FtgKontaktNr', 'FtgPerson', 'ContactInformation')
|
||||
|
||||
FtgNr = Column(String, ForeignKey('fr.FtgNr'), primary_key=True)
|
||||
FtgKontaktNr = Column(Integer, primary_key=True)
|
||||
|
||||
ContactInformation = relationship(
|
||||
"ContactInformation", lazy='joined',
|
||||
foreign_keys='ContactInformation.FtgNr, ContactInformation.FtgKontaktNr',
|
||||
primaryjoin="and_(ContactInformation.FtgNr==ContactPerson.FtgNr,"
|
||||
"ContactInformation.FtgKontaktNr==ContactPerson.FtgKontaktNr)")
|
||||
|
||||
|
||||
class WebUser(RawBaseModel):
|
||||
__tablename__ = 'kpw'
|
||||
__to_dict_only__ = ('WebUserName', 'ValidToUse', 'ContactPersonName', 'ContactPersonNumber')
|
||||
|
||||
FtgNr = Column(String, ForeignKey('fr.FtgNr'), primary_key=True)
|
||||
FtgKontaktNr = Column(Integer, ForeignKey('kp.FtgKontaktNr'), primary_key=True)
|
||||
WebUserName = Column(String, primary_key=True)
|
||||
|
||||
ContactPerson = relationship(
|
||||
ContactPerson, uselist=False, lazy='joined',
|
||||
foreign_keys='ContactPerson.FtgNr, ContactPerson.FtgKontaktNr',
|
||||
primaryjoin="and_(ContactPerson.FtgNr==WebUser.FtgNr,"
|
||||
"ContactPerson.FtgKontaktNr==WebUser.FtgKontaktNr)")
|
||||
|
||||
@hybrid_property
|
||||
def ContactPersonName(self):
|
||||
return self.ContactPerson.FtgPerson if self.ContactPerson else ""
|
||||
|
||||
@hybrid_property
|
||||
def ContactPersonNumber(self):
|
||||
return self.ContactPerson.FtgKontaktNr if self.ContactPerson else ""
|
||||
|
||||
|
||||
class LanguageCodes(RawBaseModel):
|
||||
__tablename__ = 'xw'
|
||||
|
||||
SprakKod = Column(Integer, primary_key=True)
|
||||
|
||||
|
||||
class Company(RawBaseModel):
|
||||
__tablename__ = 'fr'
|
||||
__column_map__ = {'FtgNr': 'CompanyNumber', 'FtgNamn': 'CompanyName',
|
||||
'OrgNr': 'CompanyIdentityNumber', 'EUMomsNr': 'CompanyVATNumber',
|
||||
'FtgPostAdr1': 'AddrCO', 'FtgPostadr5': 'AddrStreet',
|
||||
'FtgLevPostNr': 'AddrPostalCode', 'FtgPostLevAdr3': 'AddrCity',
|
||||
'LandsKod': 'AddrCountry',
|
||||
'FtgPostAdr2': 'PostAddrStreet', 'FtgPostnr': 'PostAddrPostalCode',
|
||||
'FtgPostAdr3': 'PostAddrCity'}
|
||||
__to_dict_only__ = ('FtgNr', 'FtgNamn', 'Customer', 'ContactPersons', 'ContactInformation',
|
||||
'FtgPostadr5', 'FtgPostAdr1', 'FtgPostLevAdr3', 'FtgLevPostNr', 'LandsKod',
|
||||
'FtgPostAdr2', 'FtgPostnr', 'FtgPostAdr3', 'OrgNr', 'EUMomsNr')
|
||||
|
||||
FtgNr = Column(String, primary_key=True)
|
||||
|
||||
Customer = relationship('Customer', uselist=False, back_populates='Company', lazy='joined')
|
||||
ContactPersons = relationship("ContactPerson", lazy='joined')
|
||||
ContactInformation = relationship(
|
||||
"ContactInformation", lazy='joined',
|
||||
foreign_keys='ContactInformation.FtgNr',
|
||||
primaryjoin="and_(ContactInformation.FtgNr==Company.FtgNr,"
|
||||
"ContactInformation.FtgKontaktNr==null())")
|
||||
|
||||
|
||||
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',
|
||||
'GodsMarke1': 'DefaultShippingInfo', 'GodsMarke2': 'InternalInfo',
|
||||
'LevSattKod': 'DefaultShippingTypeCode', 'ValKod': 'CustomerCurrency'}
|
||||
__to_dict_only__ = ('kundkategorikod', 'PriceList', 'PriceListCommon', 'CustomerCategory',
|
||||
'PrisLista', 'PrisListaKundSpec', 'WebUsers', 'GodsMarke1',
|
||||
'GodsMarke2', 'LevSattKod', 'Language', 'ValKod')
|
||||
|
||||
FtgNr = Column(String, ForeignKey('fr.FtgNr'), primary_key=True)
|
||||
SprakKod = Column(Integer, ForeignKey('xw.SprakKod'))
|
||||
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", uselist=False, foreign_keys='CustomerCategory.KundKategoriKod',
|
||||
primaryjoin="Customer.kundkategorikod==CustomerCategory.KundKategoriKod")
|
||||
LanguageCodes = relationship("LanguageCodes")
|
||||
|
||||
WebUsers = relationship(
|
||||
WebUser, uselist=True, lazy='joined',
|
||||
foreign_keys='WebUser.FtgNr', primaryjoin="Customer.FtgNr==WebUser.FtgNr")
|
||||
|
||||
@hybrid_property
|
||||
def CustomerCategory(self):
|
||||
return self.KundKategori.KundKatBeskr if self.KundKategori else ""
|
||||
|
||||
@hybrid_property
|
||||
def Language(self):
|
||||
return self.LanguageCodes.Sprak if self.LanguageCodes 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', 'KodNollPris': 'NullPriceAllowed'}
|
||||
__to_dict_only__ = ('ArtNr', 'vb_pris', 'MarkUpBelopp', 'KodNollPris', '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:
|
||||
if self.KodNollPris == "1":
|
||||
return 0
|
||||
|
||||
MarkUpBelop = self.PriceList.MarkUpBelopp if self.PriceList.MarkUpBelopp else 0
|
||||
return (
|
||||
(self.Article.ArtListPris + MarkUpBelop) *
|
||||
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',
|
||||
'OrdBerLevDat': 'RequestedDeliveryDate'}
|
||||
__to_dict_only__ = ('OrderNr', 'FtgNr', 'OrdDatum', 'OrdStat', 'CompanyName', 'LevSattKod',
|
||||
'OrdLevAdr1', 'OrdLevAdr2', 'OrdLevAdr3', 'OrdBerLevDat',
|
||||
'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"),
|
||||
'OrdBerLevDat': lambda deliv_date, *_: (
|
||||
deliv_date.strftime("%Y-%m-%d")
|
||||
if not isinstance(deliv_date, str) else deliv_date),
|
||||
},
|
||||
**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, pers_sign=self['PersSign']).callproc()
|
||||
return self, invoicing_possible
|
||||
|
||||
def save(self, invoiced=False, webusername=None):
|
||||
payment_method = 'invoice'
|
||||
if not invoiced:
|
||||
payment_method = 'card'
|
||||
PlaceOrder(
|
||||
self['FtgNr'], self['OrderNr'], webusername,
|
||||
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',
|
||||
'OrdBegLevDat': 'RequestedDeliveryDate'}
|
||||
__to_dict_only__ = ('OrdRadNr', 'vb_pris', 'ArtNr', 'ArticleName', 'OrdAntal',
|
||||
'OrdAntalAltEnh', 'AltEnhetKod', 'OrdBegLevDat')
|
||||
|
||||
# Do not serialize order relationship
|
||||
__dict_args__ = {
|
||||
'adapters': {
|
||||
**{
|
||||
Order: None,
|
||||
'OrdBegLevDat': lambda req_date, *_: (
|
||||
req_date.strftime("%Y-%m-%d")
|
||||
if req_date and not isinstance(req_date, str)
|
||||
else req_date),
|
||||
},
|
||||
**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'], requested_date=self['OrdBegLevDat'],
|
||||
pers_sign=self['PersSign']).callproc()
|
||||
self['OrdRadNr'] = row_no
|
||||
return self
|
||||
|
||||
|
||||
class ItemReplenishmentLevels(RawBaseModel):
|
||||
# __table_args__ = {'mssql_autoincrement': False, 'extend_existing': True}
|
||||
# __table_args__ = {'implicit_returning': False, 'extend_existing': True}
|
||||
__tablename__ = 'JAPP_EWMS_Item_Replenishment_Levels'
|
||||
__column_map__ = {'ArtNr': 'ArticleNumber', 'LagPlats': 'Shelf', 'LagStalle': 'WarehouseID'}
|
||||
__to_dict_only__ = ('LagPlats', 'ArtNr')
|
||||
|
||||
# Workaround for:
|
||||
# "Table 'JAPP_EWMS_Item_Replenishment_Levels' does not have the identity property.
|
||||
# Cannot perform SET operation."
|
||||
ForetagKod = Column(Integer, primary_key=True, autoincrement=False)
|
||||
|
||||
|
||||
class SupplierInvoicePayment(RawBaseModel):
|
||||
__tablename__ = 'lrfb'
|
||||
# __column_map__ = {'AltEnhetKod': 'UnitCode', 'AltEnhetBeskr': 'UnitName',
|
||||
# 'AltEnhetOmrFaktor': 'DefaultUnitConv'}
|
||||
# __to_dict_only__ = ('AltEnhetBeskr', 'AltEnhetOmrFaktor')
|
||||
|
||||
|
||||
class SupplierInvoiceJournal(RawBaseModel):
|
||||
__tablename__ = 'jfbs'
|
||||
# __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(String, ForeignKey('xae.AltEnhetKod'), primary_key=True)
|
||||
# ArticleAlternativeUnit = relationship(ArticleAlternativeUnit, lazy='joined')
|
||||
277
pyjeeves/models/sp_classes.py
Normal file
277
pyjeeves/models/sp_classes.py
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import pymssql
|
||||
from collections import OrderedDict
|
||||
from pyjeeves.models import db
|
||||
from pyjeeves import logging
|
||||
from datetime import date
|
||||
# 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, pers_sign='biz'):
|
||||
super(OrderHead, self).__init__('Jeeves_Esales_CreateOrder')
|
||||
|
||||
self['c_CompanyNo'] = company_no
|
||||
|
||||
# Some defaults:
|
||||
self['c_ForetagKod'] = 1 # Hardcoded to LK
|
||||
self['c_PersSign'] = str(pers_sign) # 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.
|
||||
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):
|
||||
try:
|
||||
super(OrderHead, self).callproc()
|
||||
except pymssql.DatabaseError:
|
||||
logger.error("A DatabaseException has been caught. Order could not be created..")
|
||||
|
||||
# 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=None,
|
||||
requested_date=None, 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) if alt_unit else None
|
||||
self['c_PersSign'] = str(pers_sign)
|
||||
|
||||
# Used to set date for delivery (c_OrdBegLevDat) and (c_OrdBerLevDat)
|
||||
self['c_RequestedDate'] = (
|
||||
requested_date.strftime('%Y%m%d')
|
||||
if isinstance(requested_date, date) else requested_date)
|
||||
|
||||
# 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):
|
||||
try:
|
||||
super(OrderRow, self).callproc()
|
||||
except pymssql.DatabaseError:
|
||||
logger.error("A DatabaseException has been caught. Order row not created.")
|
||||
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, web_user_name=None, 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'
|
||||
|
||||
# Unique ID added to 'kpw' when invoicing is allowed.
|
||||
self['c_webUserName'] = web_user_name
|
||||
|
||||
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.
|
||||
|
||||
def callproc(self):
|
||||
try:
|
||||
return super(PlaceOrder, self).callproc()
|
||||
except pymssql.DatabaseError:
|
||||
logger.error("A DatabaseException has been caught. Order %d not updated." %
|
||||
(self['c_OrderNumber']))
|
||||
|
||||
|
||||
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):
|
||||
ret = {'email': None, 'sms': None, 'phone': None}
|
||||
|
||||
try:
|
||||
result = super(NotifyInfo, self).callproc(resultset=True)
|
||||
except pymssql.DatabaseError:
|
||||
logger.error("A DatabaseException has been caught. NotifyInfo not fetched.")
|
||||
return ret
|
||||
|
||||
if isinstance(result, list):
|
||||
for r in result:
|
||||
if r[1][7:].lower() in ret:
|
||||
ret[r[1][7:].lower()] = r[0]
|
||||
|
||||
return ret
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
"""
|
||||
|
||||
|
||||
from models import Articles, Customers, InvoiceRows, OrderRows
|
||||
from models.meta import Articles, Customers, InvoiceRows, OrderRows
|
||||
|
||||
from sqlalchemy import desc
|
||||
from sqlalchemy.inspection import inspect
|
||||
|
|
@ -19,9 +19,10 @@ class Process():
|
|||
"""docstring for Process"""
|
||||
def __init__(self, jvs_query, db_session):
|
||||
super(Process, self).__init__()
|
||||
# Refactor code to use RawSession instead of raw jvs queries
|
||||
self.query = jvs_query
|
||||
self.session = db_session
|
||||
self.logger = logging.getLogger("PyJeeves.process")
|
||||
self.logger = logging.getLogger("PyJeeves." + __name__)
|
||||
|
||||
def _update_model(self, model, kwargs):
|
||||
for k, v in kwargs.items():
|
||||
|
|
@ -47,6 +48,7 @@ class Process():
|
|||
|
||||
if _data:
|
||||
self.logger.info("Syncing %s" % jvs_tbl)
|
||||
nth_item = 0
|
||||
for item in _data:
|
||||
_filter_kwargs = {k: item.get(k) for k in _p_keys}
|
||||
_existing = self.session.query(model).\
|
||||
|
|
@ -57,6 +59,12 @@ class Process():
|
|||
else:
|
||||
_new = model(**item)
|
||||
self.session.add(_new)
|
||||
|
||||
if nth_item % 1000 == 0 and nth_item != 0:
|
||||
self.session.commit()
|
||||
nth_item += 1
|
||||
|
||||
self.session.commit()
|
||||
else:
|
||||
self.logger.info("No sync made for %s" % jvs_tbl)
|
||||
|
||||
|
|
@ -65,4 +73,3 @@ class Process():
|
|||
self._sync_model(Articles, 'Articles')
|
||||
self._sync_model(InvoiceRows, 'InvoiceRows')
|
||||
self._sync_model(OrderRows, 'OrderRows')
|
||||
self.session.commit()
|
||||
|
|
|
|||
6
pyjeeves/repositories/__init__.py
Normal file
6
pyjeeves/repositories/__init__.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
from .location import Location
|
||||
from .article import Article, ArticleCategory
|
||||
from .company import Company
|
||||
from .pricelist import PriceList
|
||||
from .order import Order
|
||||
from .warehouse import Warehouse
|
||||
469
pyjeeves/repositories/article.py
Normal file
469
pyjeeves/repositories/article.py
Normal file
|
|
@ -0,0 +1,469 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from pyjeeves.models.raw import (
|
||||
Article as ArticleModel,
|
||||
ProductClass, ArticleClass, CommodityGroup, ArticleEAN, ArticleUnit)
|
||||
from pyjeeves.models import db
|
||||
from sqlalchemy.sql.expression import and_
|
||||
from sqlalchemy.orm.exc import NoResultFound
|
||||
from gtin import GTIN
|
||||
|
||||
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 Article():
|
||||
"""Handles articles in Jeeves, currently filters out all articles with class = 2"""
|
||||
|
||||
@staticmethod
|
||||
def get(art_no):
|
||||
""" Query an article by number """
|
||||
try:
|
||||
return db.raw_session.query(ArticleModel).filter_by(
|
||||
ArtNr=str(art_no)
|
||||
).one()
|
||||
except NoResultFound:
|
||||
raise KeyError
|
||||
|
||||
@staticmethod
|
||||
def get_all(filter_=and_(
|
||||
ArticleModel.ItemStatusCode == 0,
|
||||
ArticleModel.ArtKod != 2,
|
||||
ArticleModel.VaruGruppKod != 90,
|
||||
ArticleModel.ArtProdKlass != 0)
|
||||
):
|
||||
# .filter_by(ItemStatusCode=0, ArtKod=2)
|
||||
return db.raw_session.query(ArticleModel).filter(filter_).all()
|
||||
|
||||
@staticmethod
|
||||
def get_article_units(filter_=and_()):
|
||||
return db.raw_session.query(ArticleUnit).filter(filter_).all()
|
||||
|
||||
@staticmethod
|
||||
def is_salable(art_no_list=[]):
|
||||
""" Returns true if all articles are salable,
|
||||
else false with error information """
|
||||
articles = db.raw_session.query(ArticleModel).filter(
|
||||
and_(ArticleModel.ArtNr.in_(art_no_list))).all()
|
||||
|
||||
blocked_articles = [article.ArtNr for article in articles
|
||||
if article.ArtKod == 2 or article.ItemStatusCode != 0]
|
||||
unknown_articles = [x for x in art_no_list
|
||||
if x not in set([article.ArtNr for article in articles])]
|
||||
|
||||
if blocked_articles or unknown_articles:
|
||||
errors = {}
|
||||
if blocked_articles:
|
||||
errors['blocked_articles'] = blocked_articles
|
||||
if unknown_articles:
|
||||
errors['unknown_articles'] = unknown_articles
|
||||
return False, errors
|
||||
|
||||
return True, {}
|
||||
|
||||
@staticmethod
|
||||
def get_article_gtins():
|
||||
return db.raw_session.query(ArticleEAN).all()
|
||||
|
||||
@staticmethod
|
||||
def add_article_gtins(gtins=[], dry_run=False):
|
||||
# Expects a list of dicts like this:
|
||||
# [{
|
||||
# 'article_no': article.ArtNr,
|
||||
# 'article_gtin': gtin,
|
||||
# 'unit': unit.AltEnhetKod,
|
||||
# }]
|
||||
for gtin in gtins:
|
||||
n1 = ArticleEAN(
|
||||
ArtNr=gtin['article_no'], AltEnhetKod=gtin.get('unit', None),
|
||||
ArtNrEAN=str(gtin['article_gtin']), ForetagKod=1)
|
||||
if dry_run:
|
||||
logger.info('Creating GTIN for %s, %s, %s' % (n1.ArtNr, n1.AltEnhetKod, n1.ArtNrEAN))
|
||||
continue
|
||||
|
||||
db.raw_session.add(n1)
|
||||
logger.debug('Created/updated Article EAN for %s - %s with GTIN %s' % (
|
||||
gtin['article_no'], gtin.get('unit', 'no unit'), gtin['article_gtin']))
|
||||
|
||||
db.raw_session.commit()
|
||||
logger.info('Succesfully commited %s GTINs to database' % (len(gtins)))
|
||||
|
||||
@staticmethod
|
||||
def clear_article_gtins():
|
||||
gtins = db.raw_session.query(ArticleEAN).all()
|
||||
|
||||
for gtin in gtins:
|
||||
db.raw_session.delete(gtin)
|
||||
|
||||
db.raw_session.commit()
|
||||
logger.info('Deleted %s GTINs' % (len(gtins)))
|
||||
|
||||
|
||||
class ArticleCategory():
|
||||
"""Handles article categories, such as classes and groups in Jeeves"""
|
||||
|
||||
@staticmethod
|
||||
def get_all():
|
||||
# .filter_by(ItemStatusCode=0, ArtKod=2)
|
||||
prod_classes = db.raw_session.query(ProductClass).all()
|
||||
art_classes = db.raw_session.query(ArticleClass).all()
|
||||
com_groups = db.raw_session.query(CommodityGroup).all()
|
||||
|
||||
return {'ProductClasses': prod_classes,
|
||||
'ArticleClasses': art_classes, 'CommodityGroups': com_groups}
|
||||
|
||||
|
||||
# TODO: Should be moved to separate project with Lindvalls specific code
|
||||
def get_gtin_for_article(article_ean, article_unit=None, use_prefix=True):
|
||||
# If we don't want to prefix with 0, then exclude them here.
|
||||
UNIT_MAPPING = {
|
||||
'Påse': '',
|
||||
'st': '',
|
||||
'paket': 0,
|
||||
'200g': 0,
|
||||
'kg': 9,
|
||||
'Kart': 1,
|
||||
'Bricka': 1,
|
||||
'½-pall': 2,
|
||||
'½-pall A': 2,
|
||||
'tray_no_wrap': 8
|
||||
}
|
||||
prefixes = []
|
||||
if article_unit:
|
||||
# Find matching values in unit mapping
|
||||
prefixes = [
|
||||
val for key, val in UNIT_MAPPING.items()
|
||||
if article_unit[0:len(key)].lower() in key.lower()]
|
||||
if len(prefixes) > 1:
|
||||
logger.warning('More than one unit match found in unit mapping')
|
||||
|
||||
# Use the first match
|
||||
raw_gtin = (str(prefixes[0]) + article_ean) if prefixes and use_prefix else article_ean
|
||||
|
||||
# Handle GS1-128 GTIN code
|
||||
if len(raw_gtin) >= 15 and raw_gtin[0:2] == '01':
|
||||
raw_gtin = raw_gtin[2::]
|
||||
|
||||
article_gtin = GTIN(raw=raw_gtin)
|
||||
|
||||
return article_gtin
|
||||
|
||||
|
||||
# TODO: Should be moved to separate project with Lindvalls specific code
|
||||
def create_gtins_for_trading_goods(filename='gtin_trading_goods.csv'):
|
||||
articles = Article.get_all(and_(
|
||||
ArticleModel.ArtProdKlass == 4))
|
||||
|
||||
gtins = []
|
||||
gtin_data = {}
|
||||
|
||||
import csv
|
||||
with open(filename, newline='') as csvfile:
|
||||
gtinreader = csv.reader(csvfile, delimiter=',')
|
||||
headers = gtinreader.__next__()
|
||||
logger.info('Found these columns: %s' % (', '.join(headers)))
|
||||
|
||||
for row in gtinreader:
|
||||
gtin_data[row[0]] = row
|
||||
|
||||
logger.info("Found %s articles and updating with %s rows of data" % (
|
||||
len(articles), len(gtin_data)))
|
||||
|
||||
for article in articles:
|
||||
data = gtin_data.get(article.ArtNr)
|
||||
if data:
|
||||
default_set = False
|
||||
if len(article.ArticleUnit) == 0 and data[3]:
|
||||
logger.warning('Article %s has no ArticleUnits, but requires it' % (article.ArtNr))
|
||||
|
||||
for unit in article.ArticleUnit:
|
||||
if unit.AltEnhetKod[0:3] == 'kart' and not data[3]:
|
||||
logger.warning('Article %s missing kart unit' % (article.ArtNr))
|
||||
|
||||
if data[3] and unit.AltEnhetKod != 'st':
|
||||
gtin = get_gtin_for_article(data[3], unit.AltEnhetKod, False)
|
||||
# Only add GTINs for order units (not PO units)
|
||||
if unit.AltEnhetOrder == '1':
|
||||
gtins.append({
|
||||
'article_no': article.ArtNr,
|
||||
'article_gtin': gtin,
|
||||
'unit': unit.AltEnhetKod
|
||||
})
|
||||
if unit.AltEnhetKod == 'st':
|
||||
gtin = get_gtin_for_article(data[2], unit.AltEnhetKod, False)
|
||||
# Only add GTINs for order units (not PO units)
|
||||
if unit.AltEnhetOrder == '1':
|
||||
gtins.append({
|
||||
'article_no': article.ArtNr,
|
||||
'article_gtin': gtin,
|
||||
'unit': unit.AltEnhetKod
|
||||
})
|
||||
default_set = True
|
||||
|
||||
# Add default gtin if 'st' not used
|
||||
if not default_set:
|
||||
gtin = get_gtin_for_article(data[2], None, False)
|
||||
gtins.append({
|
||||
'article_no': article.ArtNr,
|
||||
'article_gtin': gtin
|
||||
})
|
||||
else:
|
||||
# Warn about active and stock items that didn't get updated.
|
||||
if article.LagTyp == 0 and article.ItemStatusCode == 0:
|
||||
logger.warning('Article %s has no GTIN data in CSV' % (article.ArtNr))
|
||||
|
||||
Article.add_article_gtins(gtins)
|
||||
|
||||
|
||||
# TODO: Should be moved to separate project with Lindvalls specific code
|
||||
def create_gtins(dry_run=True):
|
||||
# GS1 Company Prefixes that we manage locally, prefixing etc.
|
||||
LOCAL_GCPS = [
|
||||
'731083', # Lindvalls Kaffe
|
||||
'7392736', # Sackeus AB
|
||||
'735007318', # Sarria Import AB
|
||||
'732157', # Martin & Servera AB
|
||||
'350096', # Scænsei Thee Kompani AB (Used by REKYL In Omnia Paratus AB)
|
||||
'735003307', # Coffee Please
|
||||
'735003711', # Emmas Skafferi AB (Used by Coffee Please)
|
||||
'735003712', # Prefix no longer subscribed (Used by Coffee Please)
|
||||
'735003302', # Josephine Selander - YogaGo (Used by Coffee Please)
|
||||
]
|
||||
|
||||
articles = Article.get_all(and_(
|
||||
ArticleModel.ItemStatusCode == 0,
|
||||
ArticleModel.VaruGruppKod != 90,
|
||||
ArticleModel.ArtProdKlass != 0))
|
||||
|
||||
articles_with_existing_gtins = [
|
||||
gtin.ArtNr for i, gtin in enumerate(Article.get_article_gtins())]
|
||||
|
||||
gtins = []
|
||||
|
||||
for article in articles:
|
||||
if article.ArtNr in articles_with_existing_gtins:
|
||||
continue
|
||||
|
||||
if not article.ArtStreckKod:
|
||||
logger.warning('No base GTIN for article %s' % (article.ArtNr))
|
||||
continue
|
||||
|
||||
GCP = GTIN(raw=article.ArtStreckKod).gcp
|
||||
|
||||
if 12 < len(article.ArtStreckKod) < 12 and GCP in LOCAL_GCPS:
|
||||
logger.error('Base GTIN is wrong length for article %s' % (article.ArtNr))
|
||||
continue
|
||||
|
||||
# If GTIN is provided by vendor, skip prefixes and gohead if only one or no units exist.
|
||||
if GCP not in LOCAL_GCPS and len(article.ArticleUnit) <= 1:
|
||||
use_prefix = False
|
||||
logger.info('Externally provided GTIN for %s, skipping prefixes' % (article.ArtNr))
|
||||
elif GCP not in LOCAL_GCPS and len(article.ArticleUnit) > 1:
|
||||
logger.warning('Externally provided GTIN for %s, too many units' % (article.ArtNr))
|
||||
continue
|
||||
else:
|
||||
use_prefix = True
|
||||
|
||||
# Create gtin without ArticleUnit, for the base unit.
|
||||
# gtins.append({
|
||||
# 'article_no': article.ArtNr,
|
||||
# 'article_gtin': get_gtin_for_article(article.ArtStreckKod, None, False)
|
||||
# })
|
||||
|
||||
for unit in article.ArticleUnit:
|
||||
|
||||
# Skip paket for 21%, should only match HV with plastic wrapping.
|
||||
if article.ArtNr[0:2] == '21' and unit.AltEnhetKod[0:5].lower() == 'paket':
|
||||
logger.info('Skip paket unit for %s' % (article.ArtNr))
|
||||
continue
|
||||
|
||||
# Special for 20%/30%, should only match HV without plastic wrapping.
|
||||
if article.ArtNr[0:2] in ('20', '30') and unit.AltEnhetKod[0:6].lower() == 'bricka':
|
||||
unit_code = 'tray_no_wrap'
|
||||
else:
|
||||
unit_code = unit.AltEnhetKod
|
||||
|
||||
gtin = get_gtin_for_article(article.ArtStreckKod, unit_code, use_prefix)
|
||||
|
||||
# Only add GTINs for order units (not PO units)
|
||||
if unit.AltEnhetOrder == '1':
|
||||
gtins.append({
|
||||
'article_no': article.ArtNr,
|
||||
'article_gtin': gtin,
|
||||
'unit': unit.AltEnhetKod
|
||||
})
|
||||
|
||||
# Workaround for scanning HV base units without plastic wrapping
|
||||
if str(gtin)[0] == '0':
|
||||
# Create gtin without ArticleUnit, for the base unit.
|
||||
gtins.append({
|
||||
'article_no': article.ArtNr,
|
||||
'article_gtin': get_gtin_for_article(article.ArtStreckKod, None, False)
|
||||
})
|
||||
|
||||
# Add GTIN to articles that don't use article units
|
||||
# Should this still be added to arean/ArticleEAN???
|
||||
# if len(article.ArticleUnit) == 0:
|
||||
# gtin = get_gtin_for_article(article.ArtStreckKod, None, use_prefix)
|
||||
# gtins.append({
|
||||
# 'article_no': article.ArtNr,
|
||||
# 'article_gtin': gtin,
|
||||
# 'unit': None,
|
||||
# })
|
||||
# add_gtin_for_article(
|
||||
# article.ArtNr, article.ArtStreckKod, None, use_prefix)
|
||||
|
||||
Article.add_article_gtins(gtins, dry_run)
|
||||
|
||||
|
||||
# TODO: Should be moved to separate project with Lindvalls specific code
|
||||
def find_articles_without_base_gtin():
|
||||
articles = Article.get_all(and_(
|
||||
ArticleModel.ItemStatusCode == 0,
|
||||
ArticleModel.VaruGruppKod != 90,
|
||||
ArticleModel.ArtProdKlass != 0))
|
||||
|
||||
_list = []
|
||||
|
||||
for article in articles:
|
||||
if not article.ArtStreckKod:
|
||||
_list.append(
|
||||
{'artnr': article.ArtNr,
|
||||
'artbeskr': article.ArtBeskr,
|
||||
'error': 'no_base'})
|
||||
continue
|
||||
else:
|
||||
if 12 < len(article.ArtStreckKod) < 12:
|
||||
_list.append(
|
||||
{'artnr': article.ArtNr,
|
||||
'artbeskr': article.ArtBeskr,
|
||||
'error': 'wrong_length'})
|
||||
continue
|
||||
|
||||
for item in _list:
|
||||
print('{artnr}, "{artbeskr}", {error}'.format(**item))
|
||||
|
||||
|
||||
# TODO: Should be moved to separate project with Lindvalls specific code
|
||||
def set_storage_type():
|
||||
articles = Article.get_all(and_(
|
||||
ArticleModel.LagTyp == 0,
|
||||
ArticleModel.ItemStatusCode == 0,
|
||||
ArticleModel.AnskaffningsSatt == 10))
|
||||
|
||||
for article in articles:
|
||||
article.LagTyp = 4
|
||||
|
||||
db.raw_session.commit()
|
||||
|
||||
logger.info("Updated storage type for %s articles" % (len(articles)))
|
||||
|
||||
|
||||
# TODO: Should be moved to separate project with Lindvalls specific code
|
||||
def set_zone_placement():
|
||||
# Logic for article groups and zones
|
||||
# ArticleClass descides which zone to put it.
|
||||
# set Article.ArticleBalance[0].japp_ewms_rec_zoneid to correct zoneid
|
||||
|
||||
article_class_map = {
|
||||
'Kaffe': 'U',
|
||||
'OoH-Kaffe': 'K',
|
||||
'Private Label': 'S',
|
||||
'Tillbehör och maskiner': 'U',
|
||||
'Komplement': 'U'
|
||||
}
|
||||
|
||||
articles = Article.get_all(and_(
|
||||
ArticleModel.ItemStatusCode == 0,
|
||||
ArticleModel.AnskaffningsSatt == 10))
|
||||
|
||||
zone_placements_update = 0
|
||||
for article in articles:
|
||||
zone_id = article_class_map.get(article.ArticleClass.ArtTypBeskr)
|
||||
if zone_id and article.ArticleBalance:
|
||||
article.ArticleBalance[0].JAPP_EWMS_REC_ZoneID = zone_id
|
||||
zone_placements_update += 1
|
||||
else:
|
||||
logger.info("Excluded %s, wrong article class or no balance " % (article.ArtNr))
|
||||
|
||||
db.raw_session.commit()
|
||||
|
||||
logger.info("Updated placement zone for %s articles" % (zone_placements_update))
|
||||
|
||||
# a = Article.get('2109')
|
||||
# print([ab.to_dict() for ab in a['ArticleBalance']])
|
||||
|
||||
|
||||
def update_decimals_on_alt_units():
|
||||
units = Article.get_article_units(ArticleUnit.AltEnhetKod == 'påse')
|
||||
|
||||
updated_units = 0
|
||||
|
||||
for unit in units:
|
||||
if unit.AltEnhetOmrFaktor is not None:
|
||||
dec_count = 0
|
||||
for digit in unit.AltEnhetOmrFaktor.as_tuple().digits:
|
||||
if digit != 0:
|
||||
dec_count += 1
|
||||
unit.AltEnhetAntDec = dec_count
|
||||
|
||||
updated_units += 1
|
||||
|
||||
db.raw_session.commit()
|
||||
logger.info("Updated decimal count for %s article units" % (updated_units))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# print([column.key for column in Company.__table__.columns])
|
||||
|
||||
# from pprint import pprint
|
||||
# logger.info("Starting TEST")
|
||||
|
||||
# session = RawSession()
|
||||
|
||||
# logger.info("Testing gettings an article")
|
||||
# # c1 = session.query(Company).filter_by(FtgNr="179580").first()
|
||||
# # print(ArticleModel)
|
||||
# c1 = db.raw_session.query(ArticleModel).filter_by(ArtNr="2003").first()
|
||||
# c1 = Article.get("2003")
|
||||
# pprint([unit.to_dict() for unit in c1.ArticleUnit])
|
||||
# pprint(c1.to_dict())
|
||||
# pprint([(au.to_dict(), au.AltEnhetOrder) for au in c1.ArticleUnit])
|
||||
# logger.info(c1.to_dict())
|
||||
|
||||
# print(
|
||||
# len(Article.get_all())
|
||||
# )
|
||||
|
||||
# c1 = db.raw_session.query(ArticleEAN).all()
|
||||
# pprint([c.to_dict() for c in c1])
|
||||
|
||||
# c1 = db.raw_session.query(ArticleEAN).filter_by(ArtNr="1054").first()
|
||||
# pprint(c1.to_dict())
|
||||
# c1.ArtNrEAN = '7310830010548'
|
||||
# pprint(c1.to_dict())
|
||||
# c1.save()
|
||||
# logger.info(c1.to_dict())
|
||||
# create_gtins_for_trading_goods('gtin_trading_goods_test.csv')
|
||||
|
||||
# LIVE FUNCTIONS BELOW
|
||||
|
||||
# find_articles_without_base_gtin()
|
||||
|
||||
# logger.info("Truncating GTINs")
|
||||
# Article.clear_article_gtins()
|
||||
logger.info("Creating new GTINs from base GTIN")
|
||||
create_gtins(dry_run=False)
|
||||
|
||||
# logger.info("Creating new GTINs from trading goods CSV")
|
||||
# create_gtins_for_trading_goods()
|
||||
|
||||
# logger.info("Update articles for batch management")
|
||||
# set_storage_type()
|
||||
|
||||
# logger.info("Set zone information on article balance")
|
||||
# set_zone_placement()
|
||||
|
||||
# logger.info("Updating alt units")
|
||||
# update_decimals_on_alt_units()
|
||||
129
pyjeeves/repositories/company.py
Normal file
129
pyjeeves/repositories/company.py
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from pyjeeves.models.raw import Company as CompanyModel, Customer as CustomerModel
|
||||
from pyjeeves.models import db
|
||||
from sqlalchemy.sql.expression import and_
|
||||
from sqlalchemy.orm.strategy_options import Load
|
||||
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 Company():
|
||||
"""Handles companies in Jeeves"""
|
||||
|
||||
@staticmethod
|
||||
def get(ftg_nr):
|
||||
""" Query an article by number """
|
||||
try:
|
||||
return db.raw_session.query(CompanyModel).filter_by(
|
||||
FtgNr=ftg_nr
|
||||
).one()
|
||||
except NoResultFound:
|
||||
raise KeyError
|
||||
|
||||
@staticmethod
|
||||
def get_all_active_customers():
|
||||
cust = db.raw_session.query(CustomerModel).filter(and_(CustomerModel.Makulerad == 0)).all()
|
||||
return [c.CompanyModel for c in cust]
|
||||
|
||||
@staticmethod
|
||||
def get_customers(category_list=[10], class_list=[], filter_inactive=True, filters=and_()):
|
||||
category_in = CustomerModel.kundkategorikod.in_(category_list) if category_list else and_()
|
||||
class_in = CustomerModel.kundklass.in_(class_list) if class_list else and_()
|
||||
inactive = and_(CustomerModel.Makulerad == 0) if filter_inactive else and_()
|
||||
return db.raw_session.query(CustomerModel).options(
|
||||
Load(CompanyModel).noload('*')).filter(
|
||||
and_(category_in, class_in, inactive, filters)).all()
|
||||
|
||||
@staticmethod
|
||||
def get_customer_numbers(category_list=[10], class_list=[], filter_inactive=True):
|
||||
cust = Company.get_customers(category_list, class_list, filter_inactive)
|
||||
return [c.FtgNr for c in cust]
|
||||
|
||||
@staticmethod
|
||||
def get_list(ftg_nr=[], filter_=and_(CustomerModel.Makulerad == 0), offset=0, limit=100):
|
||||
ftg_filter = and_()
|
||||
if ftg_nr:
|
||||
ftg_filter = CompanyModel.FtgNr.in_(ftg_nr)
|
||||
|
||||
return db.raw_session.query(CompanyModel).join(CustomerModel).filter(
|
||||
and_(ftg_filter, filter_)).order_by(
|
||||
CompanyModel.FtgNr.desc()).offset(offset).limit(limit).all()
|
||||
|
||||
|
||||
# TODO: Should be moved to separate project with Lindvalls specific code
|
||||
def update_customer_delivery_from_csv(filename='zip_codes_svhl.csv'):
|
||||
SVHL_ZONES = {}
|
||||
|
||||
logger.info("Get customers")
|
||||
customers = Company.get_customers(filters=(
|
||||
and_(CustomerModel.LevSattKod == 3, CustomerModel.kundklass == None))) # noqa
|
||||
logger.info("Amount of customers is %d" % len(customers))
|
||||
|
||||
import csv
|
||||
with open(filename, newline='') as csvfile:
|
||||
shelfreader = csv.reader(csvfile, delimiter=',')
|
||||
headers = shelfreader.__next__()
|
||||
logger.info('Found these columns: %s' % (', '.join(headers)))
|
||||
for row in shelfreader:
|
||||
SVHL_ZONES[row[0]] = '%s - %s' % (row[2], row[1])
|
||||
|
||||
logger.info('Length of zones dict is %d' % (len(SVHL_ZONES)))
|
||||
|
||||
customers_to_update = 0
|
||||
|
||||
for customer in customers:
|
||||
|
||||
if customer.Company.FtgLevPostNr:
|
||||
FtgLevPostNr = customer.Company.FtgLevPostNr.strip().replace(" ", "")
|
||||
if FtgLevPostNr and FtgLevPostNr in SVHL_ZONES:
|
||||
logger.info('FtgLevPostNr: %s - %s is within SVHL zone %s' % (
|
||||
customer.FtgNr, customer.Company.FtgNamn, SVHL_ZONES[FtgLevPostNr]))
|
||||
customers_to_update += 1
|
||||
customer.LevSattKod = 11
|
||||
continue
|
||||
# Return? Break?
|
||||
if customer.Company.FtgPostnr:
|
||||
FtgPostnr = customer.Company.FtgPostnr.strip().replace(" ", "")
|
||||
if FtgPostnr and FtgPostnr in SVHL_ZONES:
|
||||
logger.info('FtgPostnr: %s - %s is within SVHL zone %s' % (
|
||||
customer.FtgNr, customer.Company.FtgNamn, SVHL_ZONES[FtgPostnr]))
|
||||
customer.LevSattKod = 11
|
||||
customers_to_update += 1
|
||||
|
||||
logger.info('Amount updated %d' % customers_to_update)
|
||||
|
||||
# db.raw_db.merge(n1)
|
||||
db.raw_session.commit()
|
||||
logger.info('Succesfully commited updated customers to database')
|
||||
|
||||
|
||||
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)
|
||||
|
||||
logger.info("Starting")
|
||||
update_customer_delivery_from_csv()
|
||||
70
pyjeeves/repositories/location.py
Normal file
70
pyjeeves/repositories/location.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from pyjeeves.models.raw import Company, DelivLoc
|
||||
|
||||
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 Location():
|
||||
"""Handles dispatch locations in Jeeves"""
|
||||
|
||||
def __init__(self):
|
||||
super(Location, self).__init__()
|
||||
self.associated_company = '' # Company with new/existing locations
|
||||
self._deliv_locs = [] # List of locations to be connected
|
||||
|
||||
def _connect_deliv_loc(self, ftgnr, description, code):
|
||||
if self.associated_company == '':
|
||||
raise
|
||||
if len(description) > 36:
|
||||
logger.warn("Truncated description %s", (description))
|
||||
description = description[:36]
|
||||
_deliv_loc = DelivLoc(
|
||||
FtgNr=self.associated_company, OrdLevPlats1=ftgnr,
|
||||
OrdLevPlBeskr=description, ForetagKod=1)
|
||||
self._deliv_locs.append(_deliv_loc)
|
||||
# self.session.merge(_deliv_loc)
|
||||
return _deliv_loc
|
||||
|
||||
def create_lev_location(self, ftgnr='', name='', address='',
|
||||
postal_code='', city='', gln='', invoice_ref='', phone=''):
|
||||
|
||||
_loc = Company(
|
||||
FtgNr=str(ftgnr), FtgNamn=name, FtgPostadr5=address,
|
||||
FtgLevPostNr=postal_code, FtgPostLevAdr3=city,
|
||||
EAN_Loc_Code=gln, FtgPostAdr1=invoice_ref, ComNr=phone,
|
||||
ForetagKod=1)
|
||||
|
||||
# logger.debug("Adding company to location session")
|
||||
# with self.session.no_autoflush:
|
||||
# # self.session.merge(_loc) # "merge" updates if existing location exists.
|
||||
_deliv_loc = self._connect_deliv_loc(ftgnr, name, gln)
|
||||
|
||||
return _loc, _deliv_loc
|
||||
|
||||
def save_locations(self):
|
||||
logger.debug("Committing all location changes")
|
||||
# self.session.commit() # Location company needs to be created in order to connect them.
|
||||
for deliv_loc in self._deliv_locs:
|
||||
deliv_loc.merge()
|
||||
# self.session.merge(deliv_loc) # Create "connnections" between Customer and Location.
|
||||
Company.commit()
|
||||
# self.session.commit()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# print([column.key for column in Company.__table__.columns])
|
||||
|
||||
logger.info("Starting TEST")
|
||||
# session = RawSession()
|
||||
|
||||
logger.info("Testing gettings a company")
|
||||
# c1 = session.query(Company).filter_by(FtgNr="179580").first()
|
||||
print(Company)
|
||||
c1 = Company.query.filter_by(FtgNr="179580").first()
|
||||
logger.info(c1.json)
|
||||
# RawSession.remove()
|
||||
# from sqlalchemy.inspection import inspect
|
||||
# print (inspect(Company).columns.items())
|
||||
173
pyjeeves/repositories/order.py
Normal file
173
pyjeeves/repositories/order.py
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from pyjeeves.models.raw import Order as OrderModel, OrderItem as OrderItemModel
|
||||
from pyjeeves.models import db
|
||||
|
||||
from sqlalchemy.sql.expression import and_
|
||||
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 Order():
|
||||
"""Handles orders in Jeeves"""
|
||||
|
||||
@staticmethod
|
||||
def get(order_no):
|
||||
""" Query an order by number """
|
||||
try:
|
||||
return db.raw.query(OrderModel).filter_by(
|
||||
OrderNr=order_no
|
||||
).one()
|
||||
except NoResultFound:
|
||||
raise KeyError
|
||||
|
||||
@staticmethod
|
||||
def get_all_unregistered_order():
|
||||
order = OrderModel.query.filter(and_(OrderModel.OrderStatusCode == 00)).all()
|
||||
return order
|
||||
|
||||
@staticmethod
|
||||
def get_list(order_no=[]):
|
||||
return db.raw.query(OrderModel).filter(
|
||||
OrderModel.OrderNr.in_(order_no)
|
||||
).all()
|
||||
|
||||
@staticmethod
|
||||
def get_all_by_company(ftg_nr=None):
|
||||
if not ftg_nr:
|
||||
raise KeyError
|
||||
return db.raw.query(OrderModel).filter(
|
||||
OrderModel.FtgNr == str(ftg_nr)
|
||||
).all()
|
||||
|
||||
@staticmethod
|
||||
def create(customer_no, head={}, items=[], web_user_name=None, pers_sign='biz'):
|
||||
head['CompanyNumber'] = str(customer_no)
|
||||
head['PersSign'] = str(pers_sign)
|
||||
|
||||
# Create order from head dict to get an order number
|
||||
order, invoice_possible = OrderModel(head).create(web_user_name)
|
||||
if not order['OrderNr']:
|
||||
raise
|
||||
|
||||
# Go through order items, if any, and save them to DB.
|
||||
order['OrderItems'] = Order.create_rows(order, items)
|
||||
|
||||
# Save the information in the order object
|
||||
# Boolean argument deceides if order has contact person, and should be set 'registered'
|
||||
order.save(invoice_possible, web_user_name)
|
||||
|
||||
return order
|
||||
|
||||
@staticmethod
|
||||
def create_rows(order, items=[], pers_sign='biz'):
|
||||
rv = []
|
||||
|
||||
for item in items:
|
||||
if not isinstance(item, OrderItemModel):
|
||||
item = OrderItemModel(item)
|
||||
|
||||
# Set RequestedDeliveryDate from order head if not specified for items.
|
||||
if order['OrdBerLevDat'] and not item['OrdBegLevDat']:
|
||||
item['OrdBegLevDat'] = order['OrdBerLevDat']
|
||||
|
||||
item['OrderNr'] = order['OrderNr']
|
||||
item['FtgNr'] = order['FtgNr']
|
||||
item['PersSign'] = pers_sign
|
||||
rv.append(item.save())
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
logger.info("Starting TEST")
|
||||
logger.info("Testing getting an order")
|
||||
|
||||
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
|
||||
|
||||
# import os
|
||||
# os.environ['TDSDUMP'] = 'stdout'
|
||||
|
||||
# Jeeves_Esales_CreateOrder
|
||||
|
||||
# All "Jeeves_Esales_" procedures may perhaps be used?
|
||||
|
||||
# select *
|
||||
# from LKTest.information_schema.routines
|
||||
# where routine_type = 'PROCEDURE'
|
||||
# AND SPECIFIC_NAME LIKE '%Jeeves_Esales_%';
|
||||
# data = {
|
||||
# 'OrderNr': 500500,
|
||||
# 'OrdDatum': datetime.now(),
|
||||
# 'OrdTyp': 1,
|
||||
# 'FtgNr': customer_no,
|
||||
# 'OrdBerLevDat': datetime(2019, 7, 10),
|
||||
# 'ValKod': 'SEK',
|
||||
# 'OrderItems': [
|
||||
# {'ArtNr': '2005',
|
||||
# 'OrdAntal': 5.4}
|
||||
# ],
|
||||
# 'Saljare': '500',
|
||||
# 'PersSign': 'marlin',
|
||||
# 'MomsKod': 2,
|
||||
# 'BetKod': '10',
|
||||
# 'LevVillkKod': 3,
|
||||
# 'LevSattKod': 2,
|
||||
# 'LagStalle': 0,
|
||||
# 'ForetagKod': 1
|
||||
# }
|
||||
order_head = {
|
||||
'AddrCO': '',
|
||||
'AddrCity': 'Uppsala',
|
||||
'AddrCountry': 'SE',
|
||||
'AddrName': 'Lindvalls Kaffe',
|
||||
'AddrPostalCode': '751 82',
|
||||
'AddrStreet': 'Kungsgatan 60',
|
||||
'CompanyName': 'Lindvalls Kaffe AB (övrigt)',
|
||||
'CompanyNumber': '179580',
|
||||
'CustomerContact': 'Test beställning',
|
||||
'CustomerReference': 'no po number',
|
||||
'InternalInfo': 'Test order',
|
||||
'OrderNumber': 419040,
|
||||
'OrderStatusCode': 13,
|
||||
'ShippingEmail': 'order@lindvallskaffe.se',
|
||||
'ShippingInfo': 'Lev till godsmottagning',
|
||||
'ShippingPhone': '018-480 20 00',
|
||||
'ShippingSMS': '0703 25 25 02',
|
||||
'ShippingTypeCode': 4}
|
||||
order_items = [
|
||||
{'AltEnhetKod': 'Bricka5,4',
|
||||
'ArticleName': 'Lindvalls Mellanrost',
|
||||
'ArticleNumber': '2003',
|
||||
'OrdAntalAltEnh': '1.00',
|
||||
'OrderRowNumber': 10,
|
||||
'UnitAmount': '5.40',
|
||||
'UnitPrice': '92.00'},
|
||||
{'AltEnhetKod': 'Bricka5,4',
|
||||
'ArticleName': 'Lindvalls Mellanrost',
|
||||
'ArticleNumber': '2003',
|
||||
'OrdAntalAltEnh': 1.666666667,
|
||||
'OrderRowNumber': 20,
|
||||
'UnitAmount': '9.00',
|
||||
'UnitPrice': '92.00'},
|
||||
{'AltEnhetKod': 'Bricka5,4',
|
||||
'ArticleName': 'Lindvalls Mellanrost',
|
||||
'ArticleNumber': '2003',
|
||||
'OrdAntalAltEnh': '5.00',
|
||||
'OrderRowNumber': 30,
|
||||
'UnitAmount': '27.00',
|
||||
'UnitPrice': '92.00'}]
|
||||
|
||||
# print(Order.get_list(['406569', '179580', '2440070', '179584']))
|
||||
from pprint import pprint
|
||||
# pprint(Order.get(7000028).to_dict())
|
||||
pprint(Order.create('179584', order_head, order_items).to_dict())
|
||||
# pprint(Order.get('419033').to_dict())
|
||||
|
||||
# c1 = CompanyModel.query.filter_by(FtgNr="406569").first()
|
||||
# print(c1)
|
||||
# logger.info(c1.json)
|
||||
23
pyjeeves/repositories/pricelist.py
Normal file
23
pyjeeves/repositories/pricelist.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from pyjeeves.models.raw import PriceList as PriceListModel
|
||||
from pyjeeves.models import db
|
||||
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 db.raw.query(PriceListModel).filter_by(
|
||||
PrisLista=price_list_no
|
||||
).one()
|
||||
except NoResultFound:
|
||||
raise KeyError
|
||||
142
pyjeeves/repositories/warehouse.py
Normal file
142
pyjeeves/repositories/warehouse.py
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
from pyjeeves.models.raw import ArticleShelf, ItemReplenishmentLevels
|
||||
from pyjeeves.models import db
|
||||
from sqlalchemy.sql.expression import and_
|
||||
from sqlalchemy.orm.exc import NoResultFound
|
||||
|
||||
from pyjeeves.repositories import Article
|
||||
|
||||
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 Warehouse():
|
||||
"""Handles articles in Jeeves, currently filters out all articles with class = 2"""
|
||||
|
||||
@staticmethod
|
||||
def get_shelf(shelf_id):
|
||||
""" Query a shelf by id """
|
||||
try:
|
||||
return db.raw_session.query(ArticleShelf).filter_by(
|
||||
LagPlats=str(shelf_id)
|
||||
).one()
|
||||
except NoResultFound:
|
||||
raise KeyError
|
||||
|
||||
@staticmethod
|
||||
def get_all_shelfs(filter_=and_()):
|
||||
# .filter_by(ItemStatusCode=0, ArtKod=2)
|
||||
return db.raw_session.query(ArticleShelf).filter(filter_).all()
|
||||
|
||||
@staticmethod
|
||||
def delete_empty_shelfs():
|
||||
shelfs = db.raw_session.query(ArticleShelf).filter(and_(
|
||||
ArticleShelf.LagStalle == 0,
|
||||
ArticleShelf.JAPP_EWMS_zoneid.in_(['U', 'K', 'S']),
|
||||
ArticleShelf.LagSaldo == 0)).all()
|
||||
|
||||
for shelf in shelfs:
|
||||
db.raw_session.delete(shelf)
|
||||
|
||||
db.raw_session.commit()
|
||||
logger.info('Deleted %s shelfs' % (len(shelfs)))
|
||||
|
||||
@staticmethod
|
||||
def delete_replenish():
|
||||
replenish = db.raw_session.query(ItemReplenishmentLevels).all()
|
||||
|
||||
for repl in replenish:
|
||||
db.raw_session.delete(repl)
|
||||
|
||||
db.raw_session.commit()
|
||||
logger.info('Deleted old replenishment levels')
|
||||
|
||||
@staticmethod
|
||||
def add_replenish(shelfs=[]):
|
||||
Warehouse.delete_replenish()
|
||||
|
||||
min_level = 54 # Should be how low we'd like the level to get. One layer on pallet?
|
||||
max_level = 270 # How high is the roof?
|
||||
|
||||
# with db.raw_session.no_autoflush:
|
||||
for shelf in shelfs:
|
||||
art = Article.get(shelf['article_no'])
|
||||
|
||||
default_alt_unit = [unit for unit in art.ArticleUnit if unit.AltEnhetOrderStd == '1']
|
||||
if default_alt_unit and len(default_alt_unit) == 1:
|
||||
if default_alt_unit[0].AltEnhetOmrFaktor is not None:
|
||||
unit_multiple = default_alt_unit[0].AltEnhetOmrFaktor
|
||||
else:
|
||||
unit_multiple = default_alt_unit[0].ArticleAlternativeUnit.AltEnhetOmrFaktor
|
||||
else:
|
||||
unit_multiple = 1
|
||||
|
||||
new_level = ItemReplenishmentLevels(
|
||||
LagStalle='0', LagPlats=shelf['shelf'], ArtNr=shelf['article_no'],
|
||||
JAPP_EWMS_minnoofitemsonbin=min_level,
|
||||
MaxNoOfItemsOnBin=max_level,
|
||||
JAPP_EWMS_multipel=unit_multiple,
|
||||
ForetagKod=1)
|
||||
|
||||
db.raw_session.add(new_level)
|
||||
db.raw_session.commit()
|
||||
|
||||
|
||||
# TODO: Should be moved to separate project with Lindvalls specific code
|
||||
def update_shelfs_from_csv(filename='shelf_numbers_20211217.csv'):
|
||||
SHELF_TYPES = {
|
||||
'Hyllplats': 'HP',
|
||||
'Pallplats': 'PP',
|
||||
'Pallrad': 'PR'
|
||||
}
|
||||
SKIP_ZONES = ('U', 'K', 'KB', 'S', 'G')
|
||||
|
||||
import csv
|
||||
with open(filename, newline='') as csvfile:
|
||||
shelfreader = csv.reader(csvfile, delimiter=',')
|
||||
headers = shelfreader.__next__()
|
||||
logger.info('Found these columns: %s' % (', '.join(headers)))
|
||||
for row in shelfreader:
|
||||
if row[3] in SKIP_ZONES:
|
||||
continue
|
||||
maxkg = int(row[4]) if row[4] else 0
|
||||
multiitems = '1' if row[2] == 'Pallrad' else '0'
|
||||
n1 = ArticleShelf(
|
||||
LagPlats=row[1], JAPP_EWMS_zoneid=row[3], LagStalle='0',
|
||||
LagPlatsTyp=SHELF_TYPES.get(row[2]), MaxNoOfItemsOnBin=maxkg,
|
||||
MultiItemsOnBin=multiitems, JAPP_EWMS_AllowMultipleBatchesOnBin=multiitems,
|
||||
ForetagKod=1)
|
||||
|
||||
db.raw_session.merge(n1)
|
||||
db.raw_session.commit()
|
||||
logger.info('Succesfully commited shelfs to database')
|
||||
|
||||
|
||||
# TODO: Should be moved to separate project with Lindvalls specific code
|
||||
def update_replenishment_levels(filename='repl_shelfs.csv'):
|
||||
import csv
|
||||
|
||||
repl_shelfs = []
|
||||
with open(filename, newline='') as csvfile:
|
||||
shelfreader = csv.reader(csvfile, delimiter=',')
|
||||
headers = shelfreader.__next__()
|
||||
logger.info('Found these columns: %s' % (', '.join(headers)))
|
||||
for row in shelfreader:
|
||||
repl_shelfs.append({
|
||||
'shelf': row[0],
|
||||
'article_no': str(row[1])
|
||||
})
|
||||
logger.info('Updating %d replenishment levels' % (len(repl_shelfs)))
|
||||
Warehouse.add_replenish(repl_shelfs)
|
||||
logger.info('Succesfully commited levels to database')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# from pprint import pprint
|
||||
# all_shelfs = Warehouse.get_all_shelfs()
|
||||
# pprint([shelf.to_dict() for shelf in all_shelfs])
|
||||
|
||||
# Warehouse.delete_empty_shelfs()
|
||||
update_shelfs_from_csv()
|
||||
|
||||
# update_replenishment_levels()
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
nose
|
||||
sphinx
|
||||
pymssql
|
||||
sqlalchemy
|
||||
PyMySQL
|
||||
alembic
|
||||
pyyaml
|
||||
nose>=1.3.7
|
||||
Sphinx>=3.2.1
|
||||
pymssql>=2.2.7
|
||||
SQLAlchemy>=1.3.22
|
||||
sqlservice>=2.0.0
|
||||
PyMySQL>=0.10.0
|
||||
alembic>=1.4.2
|
||||
PyYAML>=5.3.1
|
||||
gtin>=0.1.13
|
||||
|
|
|
|||
18
setup.py
18
setup.py
|
|
@ -3,7 +3,7 @@
|
|||
from setuptools import setup, find_packages
|
||||
|
||||
|
||||
with open('README.rst') as f:
|
||||
with open('README.md') as f:
|
||||
readme = f.read()
|
||||
|
||||
with open('LICENSE') as f:
|
||||
|
|
@ -12,12 +12,22 @@ with open('LICENSE') as f:
|
|||
setup(
|
||||
name='pyjeeves',
|
||||
version='0.0.1',
|
||||
description='PyJeeves syncronization module',
|
||||
description='PyJeeves communication module',
|
||||
long_description=readme,
|
||||
author='Marcus Lindvall',
|
||||
author_email='marcus.lindvall@lindvallskaffe.se',
|
||||
url='https://gitlab.lndvll.se/lindvallskaffe/pyjeeves',
|
||||
license=license,
|
||||
packages=find_packages(exclude=('tests', 'docs', 'sample', 'env', 'migrations'))
|
||||
packages=find_packages(exclude=('tests', 'docs', 'sample', 'env', 'migrations')),
|
||||
install_requires=[
|
||||
'nose',
|
||||
'sphinx',
|
||||
'pymssql-py38',
|
||||
'sqlalchemy',
|
||||
'sqlservice',
|
||||
'PyMySQL',
|
||||
'alembic',
|
||||
'pyyaml',
|
||||
'gtin'
|
||||
]
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue