Ersten funktionierende Variante der neuen Verschiebelogik sowie CSVs zum Testen ergänzt

This commit is contained in:
2025-08-08 00:39:00 +02:00
parent b2d78faa8a
commit ec3526b9e4
18 changed files with 817 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python Debugger: Current File",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal"
}
]
}
+5
View File
@@ -0,0 +1,5 @@
ParentCode;Positionsnummer;ChildCode;Menge;Uebergabelaenge
000000369;1;821106001;1;200
000000369;2;822066207;1;0
000000369;3;821106001;1;100
000000369;4;829526517;1;0
1 ParentCode Positionsnummer ChildCode Menge Uebergabelaenge
2 000000369 1 821106001 1 200
3 000000369 2 822066207 1 0
4 000000369 3 821106001 1 100
5 000000369 4 829526517 1 0
+2
View File
@@ -0,0 +1,2 @@
ParentCode;Positionsnummer;ChildCode;Menge;Uebergabelaenge
000000370;1;821106001;1;200
1 ParentCode Positionsnummer ChildCode Menge Uebergabelaenge
2 000000370 1 821106001 1 200
+4
View File
@@ -0,0 +1,4 @@
ParentCode;Positionsnummer;ChildCode;Menge;Uebergabelaenge
000000371;1;821106001;1;150
000000371;2;829416035;1;0
000000371;3;821106001;1;100
1 ParentCode Positionsnummer ChildCode Menge Uebergabelaenge
2 000000371 1 821106001 1 150
3 000000371 2 829416035 1 0
4 000000371 3 821106001 1 100
+5
View File
@@ -0,0 +1,5 @@
ParentCode;Positionsnummer;ChildCode;Menge;Uebergabelaenge
000000372;1;822066207;1;0
000000372;2;821106001;1;150
000000372;3;821106001;1;400
000000372;4;821106001;1;100
1 ParentCode Positionsnummer ChildCode Menge Uebergabelaenge
2 000000372 1 822066207 1 0
3 000000372 2 821106001 1 150
4 000000372 3 821106001 1 400
5 000000372 4 821106001 1 100
+5
View File
@@ -0,0 +1,5 @@
ParentCode;Positionsnummer;ChildCode;Menge;Uebergabelaenge
000000373;1;821106001;1;100
000000373;2;822066207;1;0
000000373;3;821106001;1;200
000000373;4;821106001;1;300
1 ParentCode Positionsnummer ChildCode Menge Uebergabelaenge
2 000000373 1 821106001 1 100
3 000000373 2 822066207 1 0
4 000000373 3 821106001 1 200
5 000000373 4 821106001 1 300
+6
View File
@@ -0,0 +1,6 @@
ParentCode;Positionsnummer;ChildCode;Menge;Uebergabelaenge
000000374;1;821106001;1;200
000000374;2;822066207;1;0
000000374;3;829416035;1;0
000000374;4;829526517;1;0
000000374;5;829416093;1;0
1 ParentCode Positionsnummer ChildCode Menge Uebergabelaenge
2 000000374 1 821106001 1 200
3 000000374 2 822066207 1 0
4 000000374 3 829416035 1 0
5 000000374 4 829526517 1 0
6 000000374 5 829416093 1 0
+5
View File
@@ -0,0 +1,5 @@
ParentCode;Positionsnummer;ChildCode;Menge;Uebergabelaenge
000000375;1;821106001;1;200
000000375;2;822066207;1;0
000000375;3;821106001;1;250
000000375;4;829416035;1;0
1 ParentCode Positionsnummer ChildCode Menge Uebergabelaenge
2 000000375 1 821106001 1 200
3 000000375 2 822066207 1 0
4 000000375 3 821106001 1 250
5 000000375 4 829416035 1 0
+3
View File
@@ -0,0 +1,3 @@
ParentCode;Positionsnummer;ChildCode;Menge;Uebergabelaenge
000000376;1;829416035;1;0
000000376;2;822066207;1;0
1 ParentCode Positionsnummer ChildCode Menge Uebergabelaenge
2 000000376 1 829416035 1 0
3 000000376 2 822066207 1 0
+4
View File
@@ -0,0 +1,4 @@
ParentCode;Positionsnummer;ChildCode;Menge;Uebergabelaenge
000000377;1;829416035;1;0
000000377;2;822066207;1;0
000000377;3;821106001;1;100
1 ParentCode Positionsnummer ChildCode Menge Uebergabelaenge
2 000000377 1 829416035 1 0
3 000000377 2 822066207 1 0
4 000000377 3 821106001 1 100
+2
View File
@@ -0,0 +1,2 @@
ParentCode;Positionsnummer;ChildCode;Menge;Uebergabelaenge
000000378;1;829516004;1;200
1 ParentCode Positionsnummer ChildCode Menge Uebergabelaenge
2 000000378 1 829516004 1 200
+5
View File
@@ -0,0 +1,5 @@
ParentCode;Positionsnummer;ChildCode;Menge;Uebergabelaenge
000000379;1;821106001;1;200
000000379;2;822066207;1;0
000000379;3;821106001;1;100
000000379;4;829526517;1;0
1 ParentCode Positionsnummer ChildCode Menge Uebergabelaenge
2 000000379 1 821106001 1 200
3 000000379 2 822066207 1 0
4 000000379 3 821106001 1 100
5 000000379 4 829526517 1 0
+6
View File
@@ -0,0 +1,6 @@
ParentCode;Positionsnummer;ChildCode;Menge;Uebergabelaenge
000000380;1;821096001;1;300
000000380;2;829416035;1;0
000000380;3;821106001;1;400
000000380;4;821096001;1;250
000000380;5;829516004;1;100
1 ParentCode Positionsnummer ChildCode Menge Uebergabelaenge
2 000000380 1 821096001 1 300
3 000000380 2 829416035 1 0
4 000000380 3 821106001 1 400
5 000000380 4 821096001 1 250
6 000000380 5 829516004 1 100
+10
View File
@@ -0,0 +1,10 @@
ParentCode;Positionsnummer;ChildCode;Menge;Uebergabelaenge
000000381;1;829516004;1;200
000000381;2;829516004;1;400
000000381;3;822066026;1;400
000000381;4;829416035;1;0
000000381;5;822066089;1;250
000000381;6;829526517;1;0
000000381;7;829516004;1;600
000000381;8;822066089;1;350
1 ParentCode Positionsnummer ChildCode Menge Uebergabelaenge
2 000000381 1 829516004 1 200
3 000000381 2 829516004 1 400
4 000000381 3 822066026 1 400
5 000000381 4 829416035 1 0
6 000000381 5 822066089 1 250
7 000000381 6 829526517 1 0
8 000000381 7 829516004 1 600
9 000000381 8 822066089 1 350
+19
View File
@@ -0,0 +1,19 @@
ParentCode;Positionsnummer;ChildCode;Menge;Uebergabelaenge
000000382;1;829416031;1;200
000000382;2;822064013;1;200
000000382;3;822066026;1;400
000000382;4;827076001;1;400
000000382;5;822066089;1;250
000000382;6;827076001;1;200
000000382;7;829416031;1;100
000000382;8;829516001;1;100
000000382;9;827076001;1;150
000000382;10;829526000;1;150
000000382;11;822066089;1;350
000000382;12;829516004;1;500
000000382;13;829516004;1;200
000000382;14;822064013;1;100
000000382;15;829416035;1;0
000000382;16;821096001;1;350
000000382;17;829526517;1;0
1 ParentCode Positionsnummer ChildCode Menge Uebergabelaenge
2 000000382 1 829416031 1 200
3 000000382 2 822064013 1 200
4 000000382 3 822066026 1 400
5 000000382 4 827076001 1 400
6 000000382 5 822066089 1 250
7 000000382 6 827076001 1 200
8 000000382 7 829416031 1 100
9 000000382 8 829516001 1 100
10 000000382 9 827076001 1 150
11 000000382 10 829526000 1 150
12 000000382 11 822066089 1 350
13 000000382 12 829516004 1 500
14 000000382 13 829516004 1 200
15 000000382 14 822064013 1 100
16 000000382 15 829416035 1 0
17 000000382 16 821096001 1 350
18 000000382 17 829526517 1 0
+5
View File
@@ -0,0 +1,5 @@
ParentCode;Positionsnummer;ChildCode;Menge;Uebergabelaenge
000000383;1;821106001;1;200
000000383;2;821106001;4;400
000000383;3;821096001;2;300
000000383;4;829514035;2;0
1 ParentCode Positionsnummer ChildCode Menge Uebergabelaenge
2 000000383 1 821106001 1 200
3 000000383 2 821106001 4 400
4 000000383 3 821096001 2 300
5 000000383 4 829514035 2 0
+207
View File
@@ -0,0 +1,207 @@
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS "Beispiele" (
"ID" INTEGER NOT NULL,
"Parent_ID" TEXT NOT NULL,
"Beispielnr" INTEGER,
PRIMARY KEY("ID" AUTOINCREMENT),
CONSTRAINT "FK_Parent_ID" FOREIGN KEY("Parent_ID") REFERENCES "COMP"("PARENT")
);
CREATE TABLE IF NOT EXISTS "COMP" (
"ID" INTEGER NOT NULL,
"PARENT" TEXT NOT NULL,
"POSNR" INTEGER NOT NULL,
"KOMPTEILENR" TEXT NOT NULL,
"DISPO_AKTIV_JN" TEXT,
"MENGE" REAL NOT NULL,
"ABW_ANZAHL" INTEGER,
"LAENGE" REAL,
"ZUSCHNITT" TEXT,
"RUESTCODE" TEXT,
PRIMARY KEY("ID" AUTOINCREMENT)
);
CREATE TABLE IF NOT EXISTS "PARTS" (
"TEILENR" INTEGER,
"BEZ" TEXT,
"BEZ2" TEXT,
"KZKAUF" TEXT,
"MENGENC" TEXT,
"ABW_ANZAHL_JN" TEXT,
"DISPO_AKTIV_JN" TEXT,
"GRNR" TEXT,
"DISPO_GRUPPE" TEXT,
"DISPOLAUF_GRUPPE" TEXT,
"ÄnderungsIndex" TEXT,
"HERSTELLERNAME" TEXT,
"TEILENR_HERSTELLER" TEXT,
"INFO2" TEXT,
"INFO16" TEXT,
"WERKSTOFF" TEXT,
"OBERFLAECHE" TEXT,
"TRUEMMER" TEXT,
"INFO2_JN" TEXT,
"RUESTCODE" TEXT,
"FERTIGUNGSART" TEXT,
"FOSGRUPPE" TEXT,
"INFO14" TEXT,
"INDEXERFASSER" TEXT,
"STATUS" TEXT
);
CREATE TABLE IF NOT EXISTS "STKL_REGEL" (
"ID" INTEGER NOT NULL,
"COMP_ID" INTEGER,
"REGEL" INTEGER NOT NULL,
PRIMARY KEY("ID" AUTOINCREMENT),
CONSTRAINT "FK_COMP_ID" FOREIGN KEY("COMP_ID") REFERENCES "COMP"("ID")
);
CREATE TABLE IF NOT EXISTS "VAR_TEIL_BEDINGUNG_2" (
"ID" INTEGER NOT NULL,
"COMP_ID" INTEGER NOT NULL,
"BEDINGUNG" INTEGER NOT NULL,
PRIMARY KEY("ID" AUTOINCREMENT),
CONSTRAINT "FK_COMP_ID" FOREIGN KEY("COMP_ID") REFERENCES "COMP"("ID")
);
INSERT INTO "Beispiele" ("ID","Parent_ID","Beispielnr") VALUES (1,'000000369',1),
(2,'000000370',2),
(3,'000000371',3),
(4,'000000372',4),
(5,'000000373',5),
(6,'000000374',6),
(7,'000000375',7),
(8,'000000376',8),
(9,'000000377',9),
(10,'000000378',10),
(11,'000000379',11),
(12,'000000380',12),
(13,'000000381',13),
(14,'000000382',14);
INSERT INTO "COMP" ("ID","PARENT","POSNR","KOMPTEILENR","DISPO_AKTIV_JN","MENGE","ABW_ANZAHL","LAENGE","ZUSCHNITT","RUESTCODE") VALUES (1,'000000380',10,'821096001','J',0.22,1,200.0,'20','HZS'),
(2,'000000380',20,'829416035','J',1.0,NULL,NULL,NULL,'SRC'),
(3,'000000380',30,'200000168','J',1.0,NULL,NULL,NULL,'SRC'),
(4,'000000380',40,'821096001','J',0.11,1,100.0,'10','HZS'),
(5,'000000380',50,'821106001','J',0.23,1,200.0,'30','HZS'),
(6,'000000369',10,'821106001','J',0.105,1,100.0,'5','SRC'),
(7,'000000369',20,'829416035','J',1.0,NULL,NULL,NULL,'SRC'),
(8,'000000369',30,'821106001','J',0.21,1,200.0,'10','SRC'),
(9,'000000369',40,'822066207','J',1.0,NULL,NULL,NULL,'SRC'),
(10,'000000371',10,'821106001','J',0.105,1,100.0,'5','SRC'),
(11,'000000371',20,'829416035','J',1.0,NULL,NULL,NULL,'SRC'),
(12,'000000371',30,'821106001','J',0.21,1,200.0,'10','SRC'),
(13,'000000371',40,'822066207','J',1.0,NULL,NULL,NULL,'SRC'),
(14,'000000372',10,'821106001','J',0.105,1,100.0,'5','SRC'),
(15,'000000372',20,'821106001','J',0.21,1,200.0,'10','SRC'),
(16,'000000372',30,'821106001','J',0.315,1,300.0,'15','SRC'),
(17,'000000372',40,'822066207','J',1.0,NULL,NULL,NULL,'SRC'),
(18,'000000373',10,'821106001','J',0.105,1,100.0,'5','SRC'),
(19,'000000373',20,'821106001','J',0.21,1,200.0,'10','SRC'),
(20,'000000373',30,'822066207','J',1.0,NULL,NULL,NULL,'SRC'),
(21,'000000374',10,'821106001','J',0.105,1,100.0,'5','SRC'),
(22,'000000374',20,'821106001','J',0.21,1,200.0,'10','SRC'),
(23,'000000374',30,'822066207','J',1.0,NULL,NULL,NULL,'SRC'),
(24,'000000374',40,'821106001','J',0.315,1,300.0,'15','SRC'),
(25,'000000375',10,'821106001','J',0.105,1,100.0,'5','SRC'),
(26,'000000375',20,'821106001','J',0.21,1,200.0,'10','SRC'),
(27,'000000375',30,'822066207','J',1.0,NULL,NULL,NULL,'SRC'),
(28,'000000375',40,'821106001','J',0.315,1,300.0,'15','SRC'),
(29,'000000376',10,'821106001','J',0.105,1,100.0,'5','SRC'),
(30,'000000376',20,'829416035','J',1.0,NULL,NULL,NULL,'SRC'),
(31,'000000376',30,'822066207','J',1.0,NULL,NULL,NULL,'SRC'),
(32,'000000377',10,'829416035','J',1.0,NULL,NULL,NULL,'SRC'),
(33,'000000377',20,'822066207','J',1.0,NULL,NULL,NULL,'SRC'),
(34,'000000378',10,'821106001','J',0.105,1,100.0,'5','SRC'),
(35,'000000370',10,'821106001','J',0.105,1,100.0,'5','SRC'),
(36,'000000379',10,'821106001','J',0.1,1,100.0,NULL,'SRC'),
(37,'000000379',20,'829416035','J',1.0,NULL,NULL,NULL,'SRC'),
(38,'000000379',30,'821106001','J',0.2,1,200.0,NULL,'SRC'),
(39,'000000379',40,'822066207','J',1.0,NULL,NULL,NULL,'SRC'),
(40,'000000381',10,'821106001','J',0.107,1,100.0,'7','SRC'),
(41,'000000381',20,'829516004','J',0.207,1,200.0,'7','SRC'),
(42,'000000381',30,'822066026','J',0.302,1,300.0,'2','SRC'),
(43,'000000381',40,'829516004','J',0.307,1,300.0,'7','SRC'),
(44,'000000381',50,'822066089','J',0.153,1,150.0,'3','SRC'),
(45,'000000381',60,'829416035','J',1.0,NULL,NULL,NULL,'SRC'),
(46,'000000381',70,'822066207','J',1.0,NULL,NULL,NULL,'SRC'),
(47,'000000381',80,'822066089','J',0.253,1,250.0,'3','SRC'),
(48,'000000382',10,'822064013','J',0.101,1,100.0,'1','SRC'),
(49,'000000382',20,'829416031','J',0.205,1,200.0,'5','SRC'),
(50,'000000382',30,'822066026','J',0.302,1,300.0,'2','SRC'),
(51,'000000382',40,'829516001','J',0.106,1,100.0,'6','SRC'),
(52,'000000382',50,'822066089','J',0.153,1,150.0,'3','SRC'),
(53,'000000382',60,'827076001','J',0.104,1,100.0,'4','SRC'),
(54,'000000382',70,'827076001','J',0.204,1,200.0,'4','SRC'),
(55,'000000382',80,'827076001','J',0.304,1,300.0,'4','SRC'),
(56,'000000382',90,'829516001','J',0.206,1,200.0,'6','SRC'),
(57,'000000382',100,'822064013','J',0.201,1,200.0,'1','SRC'),
(58,'000000382',110,'822066089','J',0.253,1,250.0,'3','SRC'),
(59,'000000382',120,'829516004','J',0.107,1,100.0,'7','SRC'),
(60,'000000382',130,'829516004','J',0.207,1,200.0,'7','SRC'),
(61,'000000382',140,'829516051','J',0.208,1,200.0,'8','SRC'),
(62,'000000382',150,'829516004','J',0.307,1,300.0,'7','SRC'),
(63,'000000382',160,'821106001','J',0.36,1,350.0,'10','SRC'),
(64,'000000382',170,'829416035','J',1.0,NULL,NULL,NULL,'SRC'),
(65,'000000382',180,'822066207','J',1.0,NULL,NULL,NULL,'SRC');
INSERT INTO "PARTS" ("TEILENR","BEZ","BEZ2","KZKAUF","MENGENC","ABW_ANZAHL_JN","DISPO_AKTIV_JN","GRNR","DISPO_GRUPPE","DISPOLAUF_GRUPPE","ÄnderungsIndex","HERSTELLERNAME","TEILENR_HERSTELLER","INFO2","INFO16","WERKSTOFF","OBERFLAECHE","TRUEMMER","INFO2_JN","RUESTCODE","FERTIGUNGSART","FOSGRUPPE","INFO14","INDEXERFASSER","STATUS") VALUES (200000168,'Außenspur SP-Bogen 30°',NULL,'K','Stck','N','J','6','10','20','C',NULL,NULL,NULL,NULL,'PA 6','Masterbatch (Grundfarbe)','FALSCH','N','SRC','SP','0','Eigenbau','B.MÜLLER','5'),
(821096001,'ALUPROFIL AP60',NULL,'K','M','J','J','3100','10','10','A',NULL,NULL,'980713',NULL,'EN AW-6060-T66 (3.3206-T66)','eloxieren','FALSCH','N','HZS','SN','0','Eigenbau','S.STEUER','5'),
(821106001,'ALUPROFIL AP110',NULL,'K','M','J','J','3100','10','10','A',NULL,NULL,'190723',NULL,'EN AW-6060-T66 (3.3206-T66)','eloxieren','FALSCH','N','HZS','SN','0','Eigenbau','B.MÜLLER','5'),
(822064013,'TEF GLEITLEISTE',NULL,'K','M','J','J','6','20','10','A',NULL,NULL,'981116',NULL,'Murtfeldt "S" 1000','Masterbatch (Grundfarbe)','FALSCH','N','HZS','EX','0','Eigenbau','B.MÜLLER','5'),
(822066026,'TEF GLEITLEISTENFÜHRUNG ALU-PROFIL',NULL,'K','M','J','J','6','20','10','A',NULL,NULL,'980914',NULL,'EN AW-6060-T66 (3.3206-T66)',NULL,'FALSCH','N','HZS','SN','0','Eigenbau','B.MÜLLER','5'),
(822066089,'GLEITLEISTENPROFIL F. BÖGEN NUTBREITE 11,5 MM',NULL,'K','M','J','J','6','20','10','A',NULL,NULL,'170306',NULL,'Murtfeldt "S" 1000','Masterbatch (Grundfarbe)','FALSCH','J','HZS','EX','0','Eigenbau','S.STEUER','5'),
(822066207,'LEISTE FÜR SCHIEBER',NULL,'K','Stck','N','J','6','20','10','A',NULL,NULL,'30225',NULL,'1.0038 (S235JR)',NULL,'FALSCH','N','SRC','BI','0','Eigenbau','B.MÜLLER','5'),
(827076001,'RUTSCHSTANGENPROFIL "S" GRÜN REGENERAT',NULL,'K','M','J','J','6','10','10','A',NULL,NULL,'920715',NULL,'Murtfeldt Original "S" grün','Masterbatch (Grundfarbe)','FALSCH','J','HZS','FR','0','Eigenbau','B.MÜLLER','5'),
(829416031,'STAUSTRECKE SP 73X100 - ELOXIERT',NULL,'K','M','J','J','6','10','20','A',NULL,NULL,'60327',NULL,'EN AW-6063-T66','eloxieren','FALSCH','N','HZS','SN','0','Eigenbau','B.MÜLLER','5'),
(829416035,'ES-EINWEISER RECHTS',NULL,'K','Stck','N','J',NULL,'10','20',NULL,NULL,NULL,'60303',NULL,'POM',NULL,'FALSCH','N','SRC',NULL,NULL,NULL,NULL,'5'),
(829516001,'LAUFPROFIL LP 2.0 - 87X32',NULL,'K','M','J','J','3003','10','20','A',NULL,NULL,'110426',NULL,'EN AW-6060-T66 (3.3206-T66)','eloxieren','FALSCH','N','HZS','SN','0','Eigenbau','B.MÜLLER','5'),
(829516004,'STAUSTRECKE SP 2.0 73X100 - ELOXIERT',NULL,'K','M','J','J','3004','10','20','A',NULL,NULL,'151130',NULL,'EN AW-6063-T66','eloxieren','FALSCH','N','HZS','SN','0','Eigenbau','B.MÜLLER','5'),
(829516051,'GLEITPROFIL - STAHLBANDFÜHRUNG FÜR ILS 2100',NULL,'K','M','J','J','3003','10','20','A',NULL,NULL,'110309',NULL,'Murtfeldt "S" 1000','Masterbatch (Grundfarbe)','FALSCH','J','HZS','EX','0','Eigenbau','B.MÜLLER','5'),
(829526000,'STRUKTUR-PROFIL',NULL,'K','M','J','J','6','10','20','A',NULL,NULL,'120214',NULL,'EN AW-6063-T66','eloxieren','FALSCH','N','HZS','SN','0','Eigenbau','S.STEUER','5'),
(829526517,'WEICHENPLATTE R-G - ILS II',NULL,'K','Stck','N','J','6','10','20','B',NULL,NULL,NULL,NULL,'1.0038 (S235JR)','verzinken','FALSCH','N','SRC','LW','0','Eigenbau','B.MÜLLER','5');
INSERT INTO "VAR_TEIL_BEDINGUNG_2" ("ID","COMP_ID","BEDINGUNG") VALUES (1,6,418),
(2,8,420),
(3,35,419),
(4,10,419),
(5,12,420),
(6,14,418),
(7,15,419),
(8,16,420),
(9,18,417),
(10,19,418),
(11,21,417),
(12,22,418),
(13,24,419),
(14,25,417),
(15,26,418),
(16,28,419),
(17,29,417),
(18,34,419),
(19,37,418),
(20,39,419),
(21,1,417),
(22,2,418),
(23,4,419),
(24,5,420),
(25,40,7),
(26,41,7),
(27,42,2),
(28,43,7),
(29,44,3),
(30,45,11),
(31,46,11),
(32,47,3),
(33,48,1),
(34,49,5),
(35,50,2),
(36,51,6),
(37,52,3),
(38,53,4),
(39,54,4),
(40,55,4),
(41,56,6),
(42,57,1),
(43,58,3),
(44,59,7),
(45,60,7),
(46,61,8),
(47,62,7),
(48,63,10),
(49,64,11),
(50,65,11);
COMMIT;
+509
View File
@@ -0,0 +1,509 @@
import argparse
import csv
import os
import sqlite3
from contextlib import closing
def main(asm_partnum, sql_filedir, csv_filedir):
build_sivas_from_file(sql_filedir)
csv_data = read_csv(csv_filedir, asm_partnum)
csv_to_db(csv_data)
create_new_bom(asm_partnum)
# print_new_bom(asm_partnum)
write_bom_as_csv(asm_partnum)
def csv_to_db(csv_data: list[dict[str, str]]) -> None:
"""Schreibt neue Stücklistendaten aus CSV in temporäre Datenbanktabelle."""
con.execute("""CREATE TABLE RD_COMP (ParentCode TEXT NOT NULL,
Positionsnummer INTEGER NOT NULL,
ChildCode TEXT NOT NULL,
Menge INTEGER NOT NULL,
Uebergabelaenge REAL NOT NULL,
PRIMARY KEY(Positionsnummer))
""")
for line in csv_data:
con.execute(""" INSERT INTO RD_COMP (ParentCode, Positionsnummer, ChildCode, Menge, Uebergabelaenge)
VALUES (:ParentCode, :Positionsnummer, :ChildCode, :Menge, :Uebergabelaenge)""", line)
def dict_factory(cursor, row):
d = {}
for idx, col in enumerate(cursor.description):
d[col[0]] = row[idx]
return d
def read_csv(filedir: str, partnumber: str) -> list[dict[str, str]]:
csv_path = os.path.join(filedir, f"BOM_{partnumber}.csv")
if not csv_path:
raise FileNotFoundError
with open(csv_path, "r", encoding="utf-8", newline="") as csv_file:
reader = csv.DictReader(csv_file, delimiter=";")
csv_data = []
for line in reader:
line["Positionsnummer"] += "0" # Angleichung Positionsnummer an SIVAS-Format (Pos. * 10)
csv_data.append(line)
return csv_data
def build_sivas_from_file(sql_filedir: str, sql_filename="sivas_replika.sql") -> None:
filepath = os.path.join(sql_filedir, sql_filename)
if not filepath:
raise FileNotFoundError
with open(filepath, "r", encoding="utf-8", newline="") as sql_file:
sql = sql_file.read()
con.executescript(sql)
def get_new_bom_data(parent_partnum:str) -> list[dict[str, str]]:
""" Positionsnummern und zugehörige Teilenummern aus neuer Stückliste holen"""
res = con.execute("SELECT * FROM RD_COMP WHERE ParentCode = :parent ORDER BY Positionsnummer", {"parent": parent_partnum}).fetchall()
return res
def get_old_bom_ids(parent_partnum: str) -> list | None:
"""Gibt ID's der Datensätze aus der alten Stückliste zurück"""
res = con.execute("SELECT ID FROM COMP WHERE PARENT = :parent", {"parent": parent_partnum}).fetchall()
return res
def get_positions_to_move(parent_partnum: str) -> set | None:
"""
Gibt alle Positionsnummern zurück, die gemäß alter Logik verschoben werden
"""
res = con.execute("""
SELECT RD.Positionsnummer AS POS
FROM RD_COMP RD
INNER JOIN COMP C
ON RD.ParentCode = C.PARENT
AND RD.ChildCode = C.KOMPTEILENR
AND RD.Uebergabelaenge = COALESCE(C.LAENGE,0)
WHERE RD.ParentCode = :parent
""",
{"parent": parent_partnum}).fetchall()
if res is not None:
res = set([line["POS"] for line in res])
return res
def identical_occurences_in_both_boms(parent_partnum: str, child_id: str) -> bool:
"""
Prüfung Fall 1.1:
"""
res = con.execute("""
SELECT CASE
-- Prüfung 1: Liegen alle Teilenr.+Länge-Kombinationen aus der neuen Stückliste auch in der alten Stückliste vor?
WHEN NOT EXISTS (
SELECT 1
FROM RD_COMP
LEFT JOIN COMP
ON RD_COMP.ParentCode = COMP.PARENT
AND RD_COMP.ChildCode = COMP.KOMPTEILENR
AND RD_COMP.Uebergabelaenge = COALESCE(COMP.LAENGE, 0)
WHERE COMP.KOMPTEILENR IS NULL
AND RD_COMP.ChildCode = :child
AND RD_COMP.ParentCode = :parent
)
-- Prüfung 2: Liegen alle Teilenr.+Länge-Kombinationen aus der alten Stückliste auch in der neuen Stückliste vor?
AND NOT EXISTS (
SELECT 1
FROM COMP
LEFT JOIN RD_COMP
ON COMP.PARENT = RD_COMP.ParentCode
AND COMP.KOMPTEILENR = RD_COMP.ChildCode
AND COALESCE(COMP.LAENGE, 0) = RD_COMP.Uebergabelaenge
WHERE RD_COMP.ChildCode IS NULL
AND COMP.KOMPTEILENR = :child
AND COMP.PARENT = :parent
)
-- Wenn Prüfung 1 und 2 zutreffen, dann liegen alle Teilenr.+Länge-Kombinationen in beiden Stücklisten vor (Fall 1.1 trifft zu)
THEN 'True'
ELSE 'False'
END AS 'Fall 1.1'
""",
{"parent": parent_partnum, "child": child_id}).fetchone()
return True if res["Fall 1.1"] == 'True' else False
def occurences_at_same_pos_in_both_boms(parent_partnum: str, child_id: str) -> bool:
"""
Prüfung Fall 1.2
"""
res = con.execute("""
SELECT CASE
WHEN NOT EXISTS (
-- Prüfung, ob in der neuen Stückliste an denselben Positionen wie in der alten Stückliste Längenvarianten zur angegebenen Teilenr. vorliegen
SELECT 1
FROM COMP
LEFT JOIN RD_COMP
ON COMP.PARENT = RD_COMP.ParentCode
AND COMP.KOMPTEILENR = RD_COMP.ChildCode
AND COMP.POSNR = RD_COMP.Positionsnummer
WHERE RD_COMP.ChildCode IS NULL
AND COMP.PARENT = :parent
AND COMP.KOMPTEILENR = :child
)
AND NOT EXISTS (
-- Prüfung, ob in der alten Stückliste an denselben Positionen wie in der neuen Stückliste Längenvarianten zur angegebenen Teilenr. vorliegen
SELECT 1
FROM RD_COMP LEFT JOIN COMP
ON RD_COMP.ParentCode = COMP.PARENT
AND RD_COMP.ChildCode = COMP.KOMPTEILENR
AND RD_COMP.Positionsnummer = COMP.POSNR
WHERE COMP.KOMPTEILENR IS NULL
AND RD_COMP.ParentCode = :parent
AND RD_COMP.ChildCode = :child
)
-- Wenn beide Fallprüfungen zutreffend liegt die längenabhängige Teilenummer in beiden Stücklisten gleich oft vor und sie liegt in beiden
-- Stücklisten an den gleichen Positionsnummern
THEN 'True'
ELSE 'False'
END AS 'Fall 1.2'
""",
{"parent": parent_partnum, "child": child_id}).fetchone()
return True if res["Fall 1.2"] == 'True' else False
def get_data_for_new_pos(parent_partnum: str, pos: str) -> list[dict[str, str]]:
"""
Fall 1.3 (Neuanlage von Positionen)
"""
res = con.execute("""
SELECT RD.Positionsnummer AS POSNR,
RD.ChildCode AS KOMPTEILENR,
RD.ParentCode AS PARENT,
CASE -- Abw. Anz. = NULL, wenn Pos. nicht längenabhängig, sonst Wert 'Menge' aus CSV
WHEN RD.Uebergabelaenge = 0 THEN NULL
ELSE RD.Menge
END AS ABW_ANZAHL,
CASE -- Mengenwert berechnen, wenn Pos. längenabhängig, sonst Wert 'Menge' aus CSV
WHEN RD.Uebergabelaenge = 0 THEN RD.Menge
ELSE RD.Menge * (RD.Uebergabelaenge + COALESCE(C.ZUSCHNITT,0)) / 1000
END AS MENGE,
CASE -- LAENGE = NULL, wenn Pos. nicht längenabhängig, sonst Wert 'Uebergabelaenge' aus CSV
WHEN RD.Uebergabelaenge = 0 THEN NULL
ELSE RD.Uebergabelaenge
END AS LAENGE,
NULL AS ZUSCHNITT,
P.DISPO_AKTIV_JN,
P.RUESTCODE
FROM RD_COMP RD
LEFT JOIN COMP C
ON RD.ChildCode = C.KOMPTEILENR
AND RD.Uebergabelaenge = COALESCE(C.LAENGE, 0)
AND RD.ParentCode = C.PARENT
-- Da die Positionen neu angelegt werden, werden die Defaultwerte verwendet, die zum Teil aus dem Teilestamm (PARTS) kommen.
LEFT JOIN PARTS P
ON RD.ChildCode = P.TEILENR
WHERE C.KOMPTEILENR IS NULL
AND RD.ParentCode = :parent
AND RD.Positionsnummer = :posnr
""",
{"parent": parent_partnum, "posnr": pos}).fetchone()
return res
def get_update_data_for_diff_pos(parent_partnum: str, pos: str) -> list[dict[str, str]]:
"""
Ermittelt die Daten, die an die neue Position verschoben werden sollen.
(Für Fall 1.1 und Fall 1.3)
"""
res = con.execute("""
SELECT C.ID,
C.DISPO_AKTIV_JN,
C.ZUSCHNITT,
C.RUESTCODE,
RD.Positionsnummer AS POSNR,
B.BEDINGUNG,
R.REGEL,
-- Bestimmt Wert 'Abw. Anzahl': NULL, wenn Pos. nicht längenabhängig, sonst Wert 'Menge' aus CSV
CASE
WHEN RD.Uebergabelaenge = 0 THEN NULL
ELSE RD.Menge
END AS ABW_ANZAHL,
-- Bestimmt Wert für Spalte 'MENGE': Berechnung Dezimalwert, wenn Pos. längenabhängig, sonst Wert 'Menge' aus CSV
CASE
WHEN RD.Uebergabelaenge = 0 THEN RD.Menge
ELSE RD.Menge * (RD.Uebergabelaenge + COALESCE(C.ZUSCHNITT,0)) / 1000
END AS MENGE,
-- Bestimmt Wert 'LAENGE': NULL, wenn Pos. nicht längenabhängig, sonst Wert 'Uebergabelaenge' aus CSV
CASE
WHEN RD.Uebergabelaenge = 0 THEN NULL
ELSE RD.Uebergabelaenge
END AS LAENGE
FROM RD_COMP RD
INNER JOIN COMP C
ON RD.ParentCode = C.PARENT
AND RD.ChildCode = C.KOMPTEILENR
AND RD.Uebergabelaenge = COALESCE(C.LAENGE, 0)
LEFT JOIN VAR_TEIL_BEDINGUNG_2 B
ON C.ID = B.COMP_ID
LEFT JOIN STKL_REGEL R
ON C.ID = R.COMP_ID
WHERE RD.ParentCode = :parent
AND RD.Positionsnummer = :posnr
""",
{"parent": parent_partnum, "posnr": pos}).fetchone()
return res
def get_update_data_for_same_pos(parent_partnum: str, pos: str) -> list[dict[str, str]]:
"""
Ermittelt die Daten, die an dieselbe Position verschoben werden sollen
(Für Fall 1.2)
"""
res = con.execute("""
SELECT C.ID,
C.ZUSCHNITT,
C.DISPO_AKTIV_JN,
C.RUESTCODE,
RD.Positionsnummer AS POSNR,
B.BEDINGUNG,
R.REGEL,
-- Bestimmt Wert für Spalte 'ABW_ANZAHL': NULL, wenn Pos. nicht längenabhängig, sonst Wert 'Menge' aus CSV
CASE
WHEN RD.Uebergabelaenge = 0 THEN NULL
ELSE RD.Menge
END AS ABW_ANZAHL,
-- Bestimmt Wert für Spalte 'MENGE': Berechnung Dezimalwert, wenn Pos. längenabhängig, sonst Wert 'Menge' aus CSV
CASE
WHEN RD.Uebergabelaenge = 0 THEN RD.Menge
ELSE RD.Menge * (RD.Uebergabelaenge + COALESCE(C.ZUSCHNITT, 0)) / 1000
END AS MENGE,
-- Bestimmt Wert für Spalte 'LAENGE': NULL, wenn Pos. nicht längenabhängig, sonst Wert 'Uebergabelaenge' aus CSV
CASE
WHEN RD.Uebergabelaenge = 0 THEN NULL
ELSE RD.Uebergabelaenge
END AS LAENGE
FROM RD_COMP RD
INNER JOIN COMP C
ON RD.ParentCode = C.PARENT
AND RD.ChildCode = C.KOMPTEILENR
AND RD.Positionsnummer = C.POSNR
LEFT JOIN VAR_TEIL_BEDINGUNG_2 B
ON C.ID = B.COMP_ID
LEFT JOIN STKL_REGEL R
ON C.ID = R.COMP_ID
WHERE RD.ParentCode = :parent
AND RD.Positionsnummer = :posnr
""",
{"parent": parent_partnum, "posnr": pos}).fetchone()
return res
def create_new_bom(parent_partnum:str) -> None:
new_bom_data = get_new_bom_data(parent_partnum)
# Ermittlung, welche Teilenummern unter Fall 1.1 , 1.2 oder 1.3 fallen
# Nicht im folgenden Loop, um mehrfache Fallprüfung 1.1 und 1.2 zu vermeiden (wenn längenabhängige Teilenummern mehrfach in Stückliste vorliegen)
partnum_cases = {}
for result_line in new_bom_data:
child_partnum = result_line["ChildCode"]
if child_partnum not in partnum_cases:
if identical_occurences_in_both_boms(parent_partnum, child_partnum):
partnum_cases[child_partnum] = 1.1
elif occurences_at_same_pos_in_both_boms(parent_partnum, child_partnum):
partnum_cases[child_partnum] = 1.2
else:
partnum_cases[child_partnum] = 1.3
# Ermittlung unter welchen Fall der Logik, die jeweilige Positionsnummer der neuen Stückliste fällt
pos_to_update = [] # Sammelt Positionsnummern (inkl. Daten), die upgedated werden sollen
pos_to_insert = [] # Sammelt Positionsnummern (inkl. Daten), die eingefügt werden sollen
pos_to_del = [] # Sammelt Positionsnummern, die gelöscht werden sollen
moved_partnums = [] # Sammelt Positionsnummern, die verschoben werden. Dient später zur Ermittlung der zu löschenden Positionen
for pos_data in new_bom_data:
child_partnum = pos_data["ChildCode"]
child_pos = pos_data["Positionsnummer"]
# Fall 1.1 (Kann nicht mit 1. Fall in Fall 1.3.1 (siehe unten) fusioniert werden, weil sonst z.B. bei Beispiel 3 fälschlicherweise diese Logik greifen würde)
if partnum_cases[child_partnum] == 1.1:
data = get_update_data_for_diff_pos(parent_partnum, child_pos)
pos_to_update.append(data)
moved_partnums.append(data["ID"])
# Fall 1.2
elif partnum_cases[child_partnum] == 1.2:
data = get_update_data_for_same_pos(parent_partnum, child_pos)
pos_to_update.append(data)
moved_partnums.append(data["ID"])
# Fall 1.3
elif partnum_cases[child_partnum] == 1.3:
# Fall 1.3.1 - Verschiebung von Positionen
pos_to_move = get_positions_to_move(parent_partnum)
if child_pos in pos_to_move:
data = get_update_data_for_diff_pos(parent_partnum, child_pos)
pos_to_update.append(data)
moved_partnums.append(data["ID"])
# Fall 1.3.2 - Neuanlage von Positionen
else:
data = get_data_for_new_pos(parent_partnum, child_pos)
pos_to_insert.append(data)
else:
raise NotImplementedError("Fall ist nicht vorgesehen (dürfte laut spezifizierter Logik nicht auftreten). Prüfung erforderlich!")
# Positionen aus der alten Stückliste, die nicht verschoben werden müssen gelöscht werden.
# Positionen, die in der neuen Stückliste neu eingefügt werden wirken sich nicht auf die Positionen der alten Stückliste aus und bleiben
# deshalb bei der Bestimmung der zu löschenden Positionen unberücksichtigt.
old_bom_ids = get_old_bom_ids(parent_partnum)
for idx in old_bom_ids:
if idx["ID"] not in moved_partnums:
pos_to_del.append(idx)
# Datenbankaktionen durchführen, um die neue Stückliste zu schreiben.
if pos_to_del:
delete_pos(pos_to_del)
if pos_to_insert:
insert_pos(pos_to_insert)
if pos_to_update:
update_pos(pos_to_update)
def insert_pos(new_data: dict[str, str]) -> None:
"""
Fügt die Positionen in die Stücklist ein, bei denen keine Verschiebung von der alten Stückliste erfolgt ist.
Diese Positionen werden stets ohne TOS-Regeln oder -Bedingungen angelegt.
"""
con.executemany("""INSERT INTO COMP (PARENT, POSNR, KOMPTEILENR, DISPO_AKTIV_JN, MENGE, ABW_ANZAHL, LAENGE, ZUSCHNITT, RUESTCODE)
VALUES (:PARENT, :POSNR, :KOMPTEILENR, :DISPO_AKTIV_JN, :MENGE, :ABW_ANZAHL, :LAENGE, :ZUSCHNITT, :RUESTCODE)""",
new_data)
def delete_pos(ids: list[dict[str, str]]) -> None:
"""
- Entfernt die Positionen aus der Stückliste (COMP), die nicht verschoben werden und nicht mehr in der alten Stückliste vorliegen.
- Entfernt die TOS-Regeln aus STKL_REGEL und TOS-Bedingungen aus VAR_TEIL_BEDINGUNG_2 zu den oben genannten Positionen.
"""
con.executemany("DELETE FROM COMP WHERE ID = :ID", ids)
con.executemany("DELETE FROM VAR_TEIL_BEDINGUNG_2 WHERE COMP_ID = :ID", ids)
con.executemany("DELETE FROM STKL_REGEL WHERE COMP_ID = :ID", ids)
def update_pos(new_data: list[dict[str, str]]) -> None:
"""
- Verschiebt Positionsdaten von einer Position in der alten Stückliste zu einer Position in der neuen Stückliste
- Aktualisiert TOS-Regeln in STKL_REGEL und
"""
con.executemany("""
UPDATE COMP
SET POSNR = :POSNR,
ABW_ANZAHL = :ABW_ANZAHL,
MENGE = :MENGE,
LAENGE = :LAENGE,
ZUSCHNITT = :ZUSCHNITT,
DISPO_AKTIV_JN = :DISPO_AKTIV_JN,
RUESTCODE = :RUESTCODE
WHERE ID = :ID
""", new_data)
update_bedingungen = [pos_data for pos_data in new_data if pos_data["BEDINGUNG"] is not None]
update_regeln = [pos_data for pos_data in new_data if pos_data["REGEL"] is not None]
if update_bedingungen:
con.executemany("UPDATE VAR_TEIL_BEDINGUNG_2 SET BEDINGUNG = :BEDINGUNG WHERE COMP_ID = :ID", update_bedingungen)
if update_regeln:
con.executemany("UPDATE STKL_REGEL SET REGEL = :REGEL WHERE COMP_ID = :ID", update_regeln)
def print_new_bom(parent_partnum: str) -> None:
res = con.execute("""
SELECT C.POSNR AS Pos,
C.KOMPTEILENR AS 'Art.-Nr.',
C.MENGE As Menge,
C.ABW_ANZAHL AS 'Abw. Anzahl',
C.LAENGE AS Länge,
C.ZUSCHNITT AS Zuschnitt,
B.BEDINGUNG AS Bedingung,
R.REGEL AS Regel,
BSP.Beispielnr AS Beispiel
FROM COMP C
LEFT JOIN VAR_TEIL_BEDINGUNG_2 B
ON C.ID = B.COMP_ID
LEFT JOIN STKL_REGEL R
ON C.ID = R.COMP_ID
LEFT JOIN Beispiele BSP
ON C.PARENT = BSP.Parent_ID
WHERE C.PARENT = :parent
ORDER BY POSNR
""",
{"parent": parent_partnum}).fetchall()
for line in res:
print(line)
def write_bom_as_csv(parent_partnum: str) -> None:
out_path = f".data/BOM_{parent_partnum}_new.csv"
res: list[dict[str, str]]
res = con.execute("""
SELECT C.POSNR AS Pos,
C.KOMPTEILENR AS 'Art.-Nr.',
C.MENGE As Menge,
C.ABW_ANZAHL AS 'Abw. Anzahl',
C.LAENGE AS Länge,
C.ZUSCHNITT AS Zuschnitt,
B.BEDINGUNG AS Bedingung,
R.REGEL AS Regel,
BSP.Beispielnr AS Beispiel
FROM COMP C
LEFT JOIN VAR_TEIL_BEDINGUNG_2 B
ON C.ID = B.COMP_ID
LEFT JOIN STKL_REGEL R
ON C.ID = R.COMP_ID
LEFT JOIN Beispiele BSP
ON C.PARENT = BSP.Parent_ID
WHERE C.PARENT = :parent
ORDER BY POSNR
""",
{"parent": args["partnumber"]}).fetchall()
with open("test.csv", "w", encoding="utf-8", newline="") as csv_file:
writer = csv.DictWriter(csv_file, delimiter=";", fieldnames=res[0].keys())
writer.writeheader()
writer.writerows(res)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-cd", "--csv_dir", help="Verzeichnis, in der CSV-Datei mit den neuen Stücklisteninformationen liegt", default=r".\data")
parser.add_argument("-sd", "--sql_dir", help="Verzeichnis, in der SQL-Datei mit den Informationen zur Teilreplizierung der SIVAS-Datenbank liegt", default=r".\data")
parser.add_argument("-n", "--partnumber", help="Teilenummer der Baugruppe", type=str, default="000000382")
args: dict = vars(parser.parse_args())
# Vorerst wird echte bei jedem Lauf neue db-Datei erstellt, da es mit Datenbank im RAM (:memory:) teilweise zu fehlerhaften Abfragen kam
if "sivas_replika.db" in os.listdir("."):
os.remove("./sivas_replika.db")
with closing(sqlite3.connect("./sivas_replika.db", autocommit=True)) as con:
con.row_factory = dict_factory
main(args["partnumber"], args["sql_dir"], args["csv_dir"])