Compare commits
18 Commits
727f629e76
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 1f82de5735 | |||
| 5c8ca3e366 | |||
| 4ece42825e | |||
| 2668eb89f8 | |||
| eaec4f591a | |||
| 8826b2e132 | |||
| f63f5c8332 | |||
| 4ba0475629 | |||
| d745436431 | |||
| f678b2ac2f | |||
| cf30876e55 | |||
| a7e8ad49a7 | |||
| d27928a591 | |||
| 7fd34f4d12 | |||
| 6844c806c1 | |||
| 54566bdedd | |||
| 92f20c8456 | |||
| 15866d993b |
@@ -1,2 +1,4 @@
|
||||
/.venv
|
||||
/work
|
||||
/lib/__pycache__
|
||||
/data/out
|
||||
|
||||
Vendored
+11
-2
@@ -13,13 +13,22 @@
|
||||
"justMyCode": true
|
||||
},
|
||||
{
|
||||
"name": "ConsoleOutputNewBOM",
|
||||
"name": "OutputInConsole",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true,
|
||||
"args": ["000000382", "csv"]
|
||||
"args": ["-bom", "-av", "-tos", "000000382", "cmd"]
|
||||
},
|
||||
{
|
||||
"name": "OutputAsCSV",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true,
|
||||
"args": ["-bom", "-av", "-tos", "000000382", "csv"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,3 +1,420 @@
|
||||
# SchnittstellenanpassungRDSivas
|
||||
|
||||
Enthält die neue Verschiebelogik für Stücklistenpositionen.
|
||||
Es sollen wegen folgender Problematik Anpassungen an der PDM-ERP-Schnittstelle (SIVAS-Workflow 96) durchgeführt werden:
|
||||
|
||||
Bei Stücklistenänderungen in SIVAS werden aktuell alle manuell durchgeführten Stücklistenänderungen (z.B. hinterlegen von Zuschnittswerten, Änderung des Rüstcodes, Entfernung des Dispoaktiv-Hakens, etc.) und hinterlegten TOS-Regeln / -Bedingungen entfernt.
|
||||
|
||||
Das liegt daran, dass die Stücklistenerstellung aktuell nach folgender, einfacher Logik erfolgt:
|
||||
1. Workflow 96 löscht die gesamte alte Stammstückliste
|
||||
2. Workflow 96 importiert die neue Stücklisten-CSV aus RuleDesigner und baut die Stückliste danach auf.
|
||||
|
||||
Wird nun vergessen, die manuellen Stücklistenänderungen / TOS-Regeln- und Bedingungen händisch in der SIVAS-Stammstückliste wiederherzustellen, stimmen unter Umständen Kalkulationen, Komissionierungen, usw. nicht mehr, was je nach Teilenummer und Auftrag einen großen finanziellen Schaden nach sich ziehen kann.
|
||||
|
||||
**Ziel der Schnittstellenanpassung**
|
||||
|
||||
Manuelle Stücklistenänderungen sowie TOS-Regeln und -Bedingungen sollen soweit möglich in die neue Stückliste übernommen werden.
|
||||
|
||||
Die Hauptaufgabe besteht folglich darin, eine Logik zu definieren, die feststellt, von welcher zu welcher Positionsnummer die Stücklistenpositionen infolge einer Stücklistenänderung verschoben wurden.
|
||||
|
||||
<ins>Die Feststellung dieser eindeutigen Zuordnung wird durch folgende Punkte erschwert</ins>
|
||||
|
||||
1. Die Stücklisten-CSV, die RuleDesigner erzeugt, beinhaltet weder Informationen zu den Spaltenüberschreibungen, noch Angaben über hinterlegte TOS-Regeln und/oder TOS-Bedingungen. Sie beinhaltet lediglich:
|
||||
- Teilenummer der Baugruppe in der das Teil eingebaut ist (bzw. Teilenummer des bearbeiteten Halbzeuges)
|
||||
- Positionsnummern
|
||||
- Teilenummern von allen Stücklistenpositionen
|
||||
- Mengen der Stücklistenpositionen (entspricht bei längenabhängigen Teilen der Spalte "Abweichende Menge" in SIVAS und bei nicht längenabhängigen Teilen der Spalte "Menge")
|
||||
- Länge der Stücklistenpositionen (bei nicht-längenabhängigen Teilen = 0)
|
||||
|
||||
2. Bei nicht-längenabhängigen Teilen kann die Änderung der Positionsnummer allein anhand der Teilenummer identifiziert werden (da diese Teile immer genau 1x in der Stückliste auftreten (zwar evtl. mit unterschiedlicher Menge, aber immer nur mit einer Positionsnummer)). Bei längenabhängigen Teilen ist das allerdings nicht so einfach, weil eine längenabhängige Teilenummer mit mehreren Längen in derselben Stückliste vertreten sein kann (z.B. mit 1x mit Länge 100 und 1x mit Länge 200). Warum dies zu Problemen führt wird an folgendem Beispiel deutlich:
|
||||
|
||||
<table>
|
||||
<tr><th>Alte Stückliste</th><th>Neue Stückliste</th></tr>
|
||||
<tr><td>
|
||||
|
||||
| Pos. | Teilenr. |Abw. Menge | Menge | Länge | Zuschnitt | TOS-Regel |
|
||||
|------|-----------|-----------|-------|-------|-----------|-----------|
|
||||
| 1 | 100000000 | 1 | 0,505 | 500 | 5 | 418 |
|
||||
| 2 | 200000000 | - | 1 | - | - | - |
|
||||
| 3 | 300000000 | - | 1 | - | - | - |
|
||||
| 4 | 100000000 | 1 | 0,715 | 700 | 15 | 420 |
|
||||
|
||||
</td><td>
|
||||
|
||||
| Pos. | Teilenr. |Abw. Menge | Menge | Länge | Zuschnitt | TOS-Regel |
|
||||
|------|-----------|-----------|-------|-------|-----------|-----------|
|
||||
| 1 | 100000000 | 2 | 0,610 | 300 | ? | ? |
|
||||
| 2 | 200000000 | - | 1 | - | - | - |
|
||||
| 3 | 100000000 | 2 | 0,830 | 400 | ? | ? |
|
||||
| 4 | 400000000 | - | 1 | - | - | - |
|
||||
|
||||
</tr></table>
|
||||
|
||||
Hier stellt sich die z.B. die Frage, ob Pos. 1 aus der alten Stückliste zu Pos. 3 in der neuen Stückliste wurde, oder ob es sich immer noch um Pos. 1 handelt? Gleiches gilt für Pos. 3 in der alten Stückliste - hat sich dessen Länge von 700 auf 300 geändert, sodass es sich in der neuen Stückliste um Pos. 1 handelt, oder aber beträgt die Länge nun 400, sodass die neue Positionsnummer = 3 ist? Oder ist es ganz anders und Pos. 1 und 3 aus der alten Stückliste wurden beim konstruieren gelöscht und durch komplett neue Teile mit den Längen 300 und 400 ersetzt, die gar nichts mehr mit den beiden Teilen der alten Stückliste zu tun haben?
|
||||
|
||||
Es zeigt sich also, dass bei längenabhängigen Teilen oftmals eine eindeutige Zuordnung zwischen den Positionsnummern der alten und neuen Stückliste schwierig, bis unmöglich ist.
|
||||
|
||||
Die spezifizierte Logik siehe ([Logik](#verschiebelogik)) soll eine eindeutige Zuordnung in möglichst vielen Fällen herstellen. Nichtsdestotrotz verbleiben relativ viele Fällen, in denen dies nicht möglich ist. In solchen Fällen bleibt nichts anderes übrig, als die mehrdeutigen Positionen in der neuen Stüdckliste neu anzulegen, etwaige Änderungen der Spaltenwerte oder TOS-Regeln /-Bedingungen also zu entfernen. Damit diese Löschung jedoch nicht einfach so erfolgt, wird in diesen Fällen eine SIVAS-Workflowmeldung an die Verantwortlichen der Arbeitsvorbereitung, bzw. an die Verantwortlichen für das TOS geschickt, in der aufgelistet ist, welche Positionen in der alten Stückliste geänderte Spaltenwerte oder TOS-Regeln / -Bedingungen aufgewiesen haben. Folglich können sich die entsprechenden Personen dann nochmal manuell die Stammstückliste anschauen und evtl. gelöschte Bedingungen oder geänderte Spaltenwerte nachpflegen!
|
||||
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- Windows -> Korrekte Funktionsweise auf Linux/Mac voraussichtlich insbesondere wegen den Batch-Dateien im [bin-Verzeichnis](/bin) nicht gegeben!
|
||||
- Es wird Python benötigt - Empfehlung: Version ≥ 3.12
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
git clone http://gitea.schoenenberger.de/s.steuer/SchnittstellenanpassungRDSivas.git
|
||||
cd SchnittstellenanpassungRDSivas\bin
|
||||
install.bat
|
||||
```
|
||||
|
||||
- install.bat erstellt die erforderlichen Verzeichnisse und generiert ein virtual environment.
|
||||
- Es werden keine externen Packages installiert.
|
||||
|
||||
## Verwendung
|
||||
|
||||
### Batch-File
|
||||
|
||||
1. Ins [bin-Verzeichnis](./bin) wechseln
|
||||
2. ```ProcessBOM.bat ``` mit einem der Dateinamen aus dem [data/in-Verzeichnis](/data/in) und dem Ausgabetyp "cmd" oder "csv" ausführen. Siehe hierzu [Verwendungsbeispiele](#verwendungsbeispiele)
|
||||
|
||||
### Verwendungsbeispiele
|
||||
|
||||
- Ausgabe der fertigen BOM von 000000369 in Konsole:
|
||||
```cmd
|
||||
ProcessBOM.bat -bom 000000369 cmd
|
||||
```
|
||||
|
||||
- Ausgabe der fertigen BOM + AV-Workflowmeldung von 000000382 in Konsole:
|
||||
```cmd
|
||||
ProcessBOM.bat -bom -av 000000382 cmd
|
||||
```
|
||||
|
||||
- Ausgabe der fertigen BOM + AV-Workflowmeldung + TOS-Workflowmeldung von 000000382 in Konsole:
|
||||
```cmd
|
||||
ProcessBOM.bat -bom -av -tos 000000382 cmd
|
||||
```
|
||||
|
||||
- Ausgabe der fertigen BOM von 000000369 als CSV ins [data/out-Verzeichnis](/data/out) schreiben:
|
||||
```cmd
|
||||
ProcessBOM.bat -bom 000000369 csv
|
||||
```
|
||||
|
||||
- Ausgabe der fertigen BOM + der Nummern, die in den AV- und TOS-Workflowmeldungen enthalten sind von 000000382 als separate CSV-Dateien ins [data/out-Verzeichnis](/data/out):
|
||||
```cmd
|
||||
ProcessBOM.bat -bom -av -tos 000000382 csv
|
||||
```
|
||||
|
||||
### Command-Line
|
||||
|
||||
1. Virtual-environment aktivieren:
|
||||
```bash
|
||||
.venv\Scripts\activate
|
||||
```
|
||||
|
||||
2. Umgebungsvariablen laden:
|
||||
```bash
|
||||
bin\setenv.bat
|
||||
```
|
||||
|
||||
3. Ins [lib-Verzeichnis](./lib) wechseln:
|
||||
```bash
|
||||
cd lib
|
||||
```
|
||||
|
||||
4. verschiebelogik.py ausführen (für alle verfügbaren Schalter siehe -h, bzw. siehe [Verwendungsbeispiele](#verwendungsbeispiele))
|
||||
|
||||
```bash
|
||||
python process_bom.py [Dateiname aus data/in] {cmd|csv} ...
|
||||
```
|
||||
|
||||
## Verschiebelogik
|
||||
|
||||
### Fall 1.1:
|
||||
|
||||
**Ziel:**
|
||||
|
||||
Längenabhängige Teilenummern identifizieren, deren Teilenr-Länge-Kombinationen identisch in der alten und neuen Stückliste vorliegen.
|
||||
|
||||
<ins>Zu erfüllende Kriterien, damit eine Teilenummer unter Fall 1.1 fällt:</ins>
|
||||
- sie muss in beiden Stücklisten mit der gleichen Anzahl an Positionsnummern vertreten sein
|
||||
- alle Kombinationen aus Teilenr. + Länge (bei längenabhängigen Teilenummern) müssen in beiden Stücklisten vorliegen
|
||||
<br></br>
|
||||
|
||||
**Folge, für Positionsnummern, die Fall 1.1 zugeordnet werden können:**
|
||||
|
||||
Alle hinterlegten TOS-Regeln und -Bedingungen sowie alle manuell durchgeführten Spaltenanpassungen (z.B. Ergänzung eines Zuschnittswertes) werden an die Positionsnummer in der neuen Stückliste verschoben, an der dieselbe Teilenr-Länge-Kombination auftritt.
|
||||
|
||||
**Beispiele (siehe jeweils [Beispiele.pdf](doc/Beispiele.pdf))**
|
||||
- Teilenummer 821106001 in Beispiel 1
|
||||
- Teilenummer 822064013 in Beispiel 14
|
||||
|
||||
**Hinweise zu Fall 1.1:**
|
||||
|
||||
1. Die Verschiebung erfolgt auch dann, wenn die Teilenr-Länge-Kombination in der neuen Stückliste eine andere:
|
||||
- Positionsnummer
|
||||
- (abweichende) Menge
|
||||
im Vergleich zur alten Stückliste
|
||||
|
||||
aufweist
|
||||
|
||||
|
||||
<ins>Beispiel</ins>
|
||||
|
||||
<table>
|
||||
<tr><th>Alte Stückliste</th><th>Neue Stückliste</th></tr>
|
||||
<tr><td>
|
||||
|
||||
| Pos. | Teilenr. |Abw. Menge | Menge | Länge | Zuschnitt | TOS-Regel |
|
||||
|------|-----------|-----------|-------|-------|-----------|-----------|
|
||||
| 1 | 100000000 | 1 | 0,505 | 500 | 5 | 418 |
|
||||
| 2 | 200000000 | - | 1 | - | - | - |
|
||||
| 3 | 300000000 | - | 1 | - | - | - |
|
||||
| 4 | 100000000 | 1 | 0,715 | 700 | 15 | 420 |
|
||||
|
||||
</td><td>
|
||||
|
||||
| Pos. | Teilenr. |Abw. Menge | Menge | Länge | Zuschnitt | TOS-Regel |
|
||||
|------|-----------|-----------|-------|-------|-----------|-----------|
|
||||
| 1 | 100000000 | 2 | 1,100 | 500 | 5 | 418 |
|
||||
| 2 | 200000000 | - | 1 | - | - | - |
|
||||
| 3 | 100000000 | 2 | 1,430 | 700 | 15 | 420 |
|
||||
| 4 | 400000000 | - | 1 | - | - | - |
|
||||
|
||||
</tr></table>
|
||||
|
||||
<ins>Erklärung:</ins>
|
||||
|
||||
Fall 1.1 trifft hier auf die Teilenummer 100000000 zu, weil 100000000 in beiden Stücklisten:
|
||||
- mit 2 Positionsnummern vertreten ist
|
||||
- die Längen 500 & 700 aufweist (gleiche Teilenr-Länge-Kombinationen)
|
||||
|
||||
Folglich werden die manuellen Spaltenanpassungen (hier die Zuschnittswerte) sowie die TOS-Regeln und -Bedingungen (in diesem Fall sind nur Regeln definiert) an die neuen Positionsnummern in der neuen Stückliste verschoben.
|
||||
|
||||
Konkret wird bei folgenden Teilenr-Länge-Kombinationen:
|
||||
- Teilenr: 100000000 + Länge: 500:
|
||||
- der Zuschnittswert 5 und die TOS-Bedingung 418 von Pos. 1 in der alten zu Pos. 1 in der neuen Stückliste verschoben. Die Positionsnummern bleiben hier gleich. Beachte, dass sich die abweichende Menge der Position um 1 erhöht hat. Wie eingangs erwähnt spielt das aber keine Rolle, die Verschiebung erfolgt trotzdem!
|
||||
- Teilenr: 100000000 + Länge: 700:
|
||||
- der Zuschnittswert 15 und die TOS-Bedingung 420 von Pos. 4 in der alten zu Pos. 3 in der neuen Stückliste verschoben. Auch die abweichende Menge erhöht sich um 1. Für Fall 1.1 spielt jedoch weder die Positionsänderung, noch die Mengenanpassung eine Rolle. Ausschlaggebend ist allein, dass alle Teilenr-Längen-Kombinationen in beiden Stücklisten vorliegen! Dementsprechend erfolgt auch hier die Verschiebung des Zuschnittswertes und der TOS-Regel.
|
||||
|
||||
1. Fall 1.1 muss für jede längenabh. Teilenr. in der neuen Stückliste separat geprüft werden werden und falls zutreffend auf alle Positionsnummern dieser Teilenummer angewendet werden!
|
||||
2. Implementierung: siehe [Programm](/lib/process_bom.py) -> Klasse *BOMProcessing* -> Methode *_get_case_11_positions()*
|
||||
|
||||
### Fall 1.2:
|
||||
|
||||
**Ziel:**
|
||||
|
||||
Längenabhängige Teilenummern identifizieren, die in beiden Stücklisten an denselben Positionen befinden.
|
||||
|
||||
<ins>Folgende Kriterien muss die längenabhängige Teilenummer erfüllen:</ins>
|
||||
- sie muss in beiden Stücklisten gleich oft vorliegen (gleiche Anzahl an Positionsnummern)
|
||||
- ihre Positionsnummern müssen in beiden Stücklisten identisch sein
|
||||
|
||||
**Folge, wenn Fall 1.2 zutrifft:**
|
||||
|
||||
Bei allen Positionsnummern der längenabh. Teilenr. werden TOS-Regeln und -Bedingungen, die hinterlegt wurden, bzw. alle
|
||||
Spaltenanpassungen, die manuell in der SIVAS-Stammstückliste durchgeführt wurden (z.B. Ergänzung eines Zuschnittswertes)
|
||||
an dieselbe Position wie in der alten Stückliste verschoben.
|
||||
|
||||
**Beispiele (siehe jeweils [Beispiele.pdf](doc/Beispiele.pdf))**
|
||||
- Teilenummer 821106001 in Beispiel 2
|
||||
- Teilenummer 821106001 in Beispiel 3
|
||||
- Teilenummer 821096001 in Beispiel 12
|
||||
- Teilenummern 822066026 + 822066089 in Beispiel 13
|
||||
- Teilenummern 822066026 + 822066089 in Beispiel 14
|
||||
|
||||
**Hinweise zu Fall 1.2:**
|
||||
|
||||
1. Fall 1.2 muss für jede längenabh. Teilenr. in der neuen Stückliste separat geprüft werden werden!
|
||||
|
||||
<ins>Beispiel:</ins>
|
||||
|
||||
<table>
|
||||
<tr><th>Alte Stückliste</th><th>Neue Stückliste</th></tr>
|
||||
<tr><td>
|
||||
|
||||
| Pos. | Teilenr. | Länge | Zuschnitt | TOS-Regel |
|
||||
|------|-----------|-------|-----------|-----------|
|
||||
| 1 | 100000000 | 500 | 5 | 418 |
|
||||
| 2 | 400000000 | 600 | | |
|
||||
| 3 | 100000000 | 700 | 15 | 420 |
|
||||
| 4 | 300000000 | 800 | 20 | 421 |
|
||||
| 5 | 500000000 | 900 | | |
|
||||
|
||||
</td><td>
|
||||
|
||||
| Pos. | Teilenr. | Länge | Zuschnitt | TOS-Regel |
|
||||
|------|-----------|-------|-----------|-----------|
|
||||
| 1 | 100000000 | 100 | 5 | 418 |
|
||||
| 2 | 200000000 | 200 | 10 | 419 |
|
||||
| 3 | 100000000 | 300 | 15 | 420 |
|
||||
| 4 | 300000000 | 400 | 20 | 421 |
|
||||
| 5 | 400000000 | 500 | 25 | 422 |
|
||||
|
||||
</tr></table>
|
||||
|
||||
<ins>Die Prüfung von Fall 1.2 erfolgt für folgende Teilenummern:</ins>
|
||||
- 100000000
|
||||
- 400000000
|
||||
- 300000000
|
||||
- 500000000
|
||||
|
||||
|
||||
<ins>Die Prüfung von Fall 1.2 erfolgt **NICHT** für folgende Teilenummern:</ins>
|
||||
- 200000000 (da sie nicht in der neuen Stückliste vorliegt)
|
||||
|
||||
<ins>Fall 1.2 trifft auf folgenden Teilenummern zu:</ins>
|
||||
- 100000000, weil diese Teilenr. in beiden Stücklisten 2x vorkommt und in beiden Stücklisten dieselben Positionsnummern aufweist (1 & 3)
|
||||
|
||||
Die Zuschnittswerte 5 und 15 sowie die TOS-Regeln 418 und 420 werden daher an dieselben Positionen wie in der alten Stücklisten übernommen. Konkret also von Pos. 1 zu Pos. 1 und von Pos. 3 zu Pos. 3.
|
||||
- 300000000, weil diese Teilenr. in beiden Stücklisten 1x vorkommt und in beiden Stücklisten an Pos. 4 liegt
|
||||
|
||||
Der Zuschnittswert 5 und 15 sowie die TOS-Regeln 418 und 420 wird daher an dieselbe Position wie in der alten Stückliste, d.h. von Pos. 4 zu Pos. 4, verschoben
|
||||
|
||||
<ins>Fall 1.2 trifft NICHT auf folgenden Teilenummern zu:</ins>
|
||||
- 400000000, da sie zwar in beiden Stücklisten genau 1x vorkommt, aber nicht an derselben Positionsnummer
|
||||
|
||||
Es erfolgt daher keine Verschiebung des Zuschnittes 25 und der TOS-Bedingung 422 von Pos. 5 zu Pos. 2
|
||||
|
||||
- 500000000, da sie neu in der neuen Stückliste ist
|
||||
|
||||
Neue Positionen werden immer ohne Spaltenänderungen oder TOS-Regeln /-Bedingungen angelegt!
|
||||
|
||||
|
||||
2. Die Längen an denselben Positionsnummern dürfen unterschiedlich sein!
|
||||
|
||||
<ins>Beispiel:</ins>
|
||||
<table>
|
||||
<tr><th>Alte Stückliste</th><th>Neue Stückliste</th></tr>
|
||||
<tr><td>
|
||||
|
||||
| Pos. | Teilenr. | Länge | Zuschnitt | TOS-Regel |
|
||||
|------|-----------|-------|-----------|-----------|
|
||||
| 1 | 821106001 | 100 | 5 | 418 |
|
||||
| 2 | 821106001 | 200 | 10 | 419 |
|
||||
|
||||
</td><td>
|
||||
|
||||
| Pos. | Teilenr. | Länge | Zuschnitt | TOS-Regel |
|
||||
|------|-----------|-------|-----------|-----------|
|
||||
| 1 | 821106001 | 400 | 5 | 418 |
|
||||
| 2 | 821106001 | 500 | 10 | 419 |
|
||||
|
||||
</tr></table>
|
||||
|
||||
<ins>Erklärung:</ins>
|
||||
|
||||
Die Zuschnittswerte 5 & 10 sowie die TOS-Regeln 418 & 419 werden an dieselbe Position wie in der alten Stückliste verschoben, obwohl sich die Längen zwischen der alten und neuen Stückliste komplett unterscheiden. Denn wie im Ziel beschrieben sind allein folgende Punkte ausschlaggebend, dass die Positionsnummern der Teilenummern unter Fall 1.2 fallen:
|
||||
- dass die Anzahl an Positionsnummern einer Teilenummer in beiden Stücklisten gleich ist. Das ist hier gegeben, weil 821106001 in beiden Stücklisten mit 2 Positionsnummern vertreten ist
|
||||
- dass ihre Positionsnummern in beiden Stücklisten identisch sind. Auch das ist der Fall, denn sie besitzt in beiden Stücklisten die Positionsnummern 1 und 2.
|
||||
|
||||
3. Wenn Fall 1.1 und Fall 1.2 gleichzeitig auf eine längenabh. Teilenr. zutreffen gilt Fall 1.1.
|
||||
|
||||
|
||||
<ins>Beispiel:</ins>
|
||||
<table>
|
||||
<tr><th>Alte Stückliste</th><th>Neue Stückliste</th></tr>
|
||||
<tr><td>
|
||||
|
||||
| Pos. | Teilenr. | Länge | Zuschnitt | TOS-Regel |
|
||||
|------|-----------|-------|-----------|-----------|
|
||||
| 1 | 821106001 | 100 | 5 | 418 |
|
||||
| 2 | 821106001 | 200 | 10 | 419 |
|
||||
|
||||
</td><td>
|
||||
|
||||
| Pos. | Teilenr. | Länge | Zuschnitt | TOS-Regel |
|
||||
|------|-----------|-------|-----------|-----------|
|
||||
| 1 | 821106001 | 200 | ? | ? |
|
||||
| 2 | 821106001 | 100 | ? | ? |
|
||||
|
||||
</tr></table>
|
||||
|
||||
<ins>Erklärung:</ins>
|
||||
|
||||
Hier trifft sowohl Fall 1.1, als auch Fall 1.2 zu:
|
||||
|
||||
- Fall 1.1, weil in beiden Stücklisten:
|
||||
- Kombination aus Teilenr. 821106001 + Länge 100 vorliegt
|
||||
- Kombination aus Teilenr. 821106001 + Länge 200 vorliegt
|
||||
|
||||
- Fall 1.2, weil:
|
||||
- 821106001 in beiden Stücklisten 2x vorliegt
|
||||
- 821106001 in beiden Stücklisten an derselben Position vorliegt (Pos. 1 & 2)
|
||||
|
||||
Die Frage ist nun, an welche Positionsnummern sollen die Zuschnittswerte 5 & 10 sowie die TOS-Regeln 418 & 419 verschoben werden:
|
||||
(siehe Fragezeichen in neuen Stückliste)
|
||||
|
||||
Die eingangs beschriebene Regel liegt nun fest, dass in diesem Fall immer Fall 1.1 gilt, weil Fall 1.1 eine höhere Priorität als Fall 1.2 besitzt. Daraus folgt, dass die neue Stückliste wie folgt ausschaut (Verschiebung des Zuschnittswertes / der TOS-Regel von Pos. 1 in der alten Stückliste zu Pos. 2 in der Neuen, bzw. von Pos. 2 in der alten zu Pos. 1 in der neuen Stückliste):
|
||||
|
||||
| Pos. | Teilenr. | Länge | Zuschnitt | TOS-Regel |
|
||||
|------|-----------|-------|-----------|-----------|
|
||||
| 1 | 821106001 | 200 | 10 | 419 |
|
||||
| 2 | 821106001 | 100 | 5 | 418 |
|
||||
|
||||
4. Implementierung: siehe [Programm](/lib/process_bom.py) -> Klasse *BOMProcessing* -> Methode *_get_case_12_positions()*
|
||||
|
||||
### Fall 1.3:
|
||||
|
||||
**Ziel:**
|
||||
|
||||
Positionsnummern identifizieren, die entweder:
|
||||
- als neue Stücklistenposition eingefügt werden müssen (nachfolgend als Fall 1.31 bezeichnet)
|
||||
- in die neue Stückliste verschoben werden sollen, aber nicht unter [Fall 1.1](#fall-11) oder [Fall 1.2](#fall-12) fallen (nachfolgend als Fall 1.32 bezeichnet)
|
||||
|
||||
<ins>Folgende Kriterien muss die Positionsnummer erfüllen, damit sie unter Fall 1.31 fällt:</ins>
|
||||
- sie darf nur in der neuen Stückliste vorliegen
|
||||
|
||||
<ins>Folgende Kriterien muss die längenabhängige Teilenummer erfüllen, damit sie unter Fall 1.32 fällt:</ins>
|
||||
- sie muss in beiden Stücklisten dieselbe Teilenr-Längen-Kombination aufweisen
|
||||
|
||||
**Folge, wenn Fall 1.31 zutrifft:**
|
||||
|
||||
- Die Position wird in der neuen Stückliste mit den Werten aus der RuleDesigner-Stücklisten-CSV neu angelegt (es erfolgt keine Verschiebung von Stücklisteninformationen aus der alten Stückliste). Die Spaltenwerte, die nicht in genannter Stücklisten-CSV enthalten sind, werden mit den Defaultwerten befüllt (so wie wenn die Position manuell in der SIVAS-Stückliste eingefügt wird). Defaultwerte sind abhängig von der Spalte entweder entweder *leer* sind, oder verfügen über die Werte der Teilenummer im Teilestamm (Tabelle PARTS)
|
||||
- Die Position wird ohne TOS-Regeln oder -Bedingungen angelegt. Sie müssen wenn dann händisch von einem SIVAS-User in der Stammstückliste ergänzt werden. Im Zuge einer nachfolgenden Stücklistenanpassung erfolgt dann evtl. eine Verschiebung der Regeln und Bedingungen, sofern die zu diesem Zeitpunkt Fall 1.1, 1.2 oder 1.32 auf diese Positionsnummer zutrifft.
|
||||
|
||||
**Beispiele zu Fall 1.31 (siehe jeweils [Beispiele.pdf](doc/Beispiele.pdf))**
|
||||
- Positionsnummer 4 der neuen Stückliste in Beispiel 1
|
||||
- Positionsnummer 2 & 3 der neuen Stückliste in Beispiel 4
|
||||
- Positionsnummer 4 der neuen Stückliste in Beispiel 5
|
||||
- Positionsnummer 3, 4, 5 der neuen Stückliste in Beispiel 6
|
||||
- Positionsnummer 3, 4 der neuen Stückliste in Beispiel 7
|
||||
- Positionsnummer 3 der neuen Stückliste in Beispiel 9
|
||||
- Positionsnummer 1 der neuen Stückliste in Beispiel 10
|
||||
- Positionsnummer 4 der neuen Stückliste in Beispiel 11
|
||||
- Positionsnummer 3, 5 der neuen Stückliste in Beispiel 12
|
||||
- Positionsnummer 2, 6, 7 der neuen Stückliste in Beispiel 13
|
||||
- Positionsnummer 4, 7, 9, 10, 12, 16, 17 der neuen Stückliste in Beispiel 14
|
||||
|
||||
**Hinweise zu Fall 1.31:**
|
||||
|
||||
1. Implementierung: siehe [Programm](/lib/process_bom.py) -> Klasse *BOMProcessing* -> Methode *_get_case_131_positions()*
|
||||
|
||||
**Folge, wenn Fall 1.32 zutrifft:**
|
||||
|
||||
Alle hinterlegten TOS-Regeln und -Bedingungen sowie alle manuell durchgeführten Spaltenanpassungen (z.B. Ergänzung eines Zuschnittswertes) werden an die Positionsnummer in der neuen Stückliste verschoben, an der dieselbe Teilenr-Länge-Kombination auftritt.
|
||||
|
||||
**Beispiele zu Fall 1.32 (siehe jeweils [Beispiele.pdf](doc/Beispiele.pdf))**
|
||||
- Positionsnummer 2 der neuen Stückliste in Beispiel 1
|
||||
- Positionsnummer 2 der neuen Stückliste in Beispiel 3
|
||||
- Positionsnummer 1, 4 der neuen Stückliste in Beispiel 4
|
||||
- Positionsnummer 1, 2, 3 der neuen Stückliste in Beispiel 5
|
||||
- Positionsnummer 1, 2 der neuen Stückliste in Beispiel 6
|
||||
- Positionsnummer 1, 2 der neuen Stückliste in Beispiel 7
|
||||
- Positionsnummer 1, 2 der neuen Stückliste in Beispiel 8
|
||||
- Positionsnummer 1, 2 der neuen Stückliste in Beispiel 9
|
||||
- Positionsnummer 2 der neuen Stückliste in Beispiel 12
|
||||
- Positionsnummer 1, 4 der neuen Stückliste in Beispiel 13
|
||||
- Positionsnummer 1, 6, 8, 13, 15 der neuen Stückliste in Beispiel 14
|
||||
|
||||
**Hinweise zu Fall 1.32:**
|
||||
|
||||
1. Positionsnummern bei denen bereits Fall 1.1 greift, oder die unter Fall 1.2 fallen und deren Länge dabei in beiden Stücklisten gleich bleibt würden grundlegend ebenfalls unter Fall 1.32 fallen und somit identifiziert werden. Es gilt jedoch das Vorrangsprinzip, d.h. die Zuordnung zu Fall 1.1 oder Fall 1.2 erfolgt (sofern möglich) immer vor der Zuordnung zu Fall 1.32!
|
||||
|
||||
2. Die Verschiebung erfolgt auch dann, wenn die Teilenr-Länge-Kombination in der neuen Stückliste eine andere:
|
||||
- Positionsnummer
|
||||
- (abweichende) Menge
|
||||
im Vergleich zur alten Stückliste aufweist.
|
||||
|
||||
3. Implementierung: siehe [Programm](/lib/process_bom.py) -> Klasse *BOMProcessing* -> Methode *_get_case_132_positions()*
|
||||
@@ -0,0 +1,5 @@
|
||||
@echo off
|
||||
CALL setenv.bat
|
||||
CALL ..\.venv\Scripts\activate.bat
|
||||
python %PROJECT_LIB%\process_bom.py %*
|
||||
CALL ..\.venv\Scripts\deactivate.bat
|
||||
@@ -0,0 +1,19 @@
|
||||
@echo off
|
||||
call setenv.bat
|
||||
if not exist %PROJECT%\.venv (
|
||||
ECHO Initialisiere Python virtual environment...
|
||||
python -m venv %PROJECT%\.venv --upgrade-deps
|
||||
ECHO Erfolgreich.
|
||||
|
||||
call %PROJECT%\.venv\Scripts\activate.bat
|
||||
|
||||
if exist %PROJECT_LIB%\requirements.txt (
|
||||
ECHO Installiere erforderliche Python Packages...
|
||||
pip install -r %PROJECT_LIB%/requirements.txt -q
|
||||
ECHO Erfolgreich
|
||||
)
|
||||
call %PROJECT%\.venv\Scripts\deactivate.bat
|
||||
|
||||
) else (
|
||||
ECHO Virtual environment liegt bereits vor, siehe %PROJECT%\.venv!
|
||||
)
|
||||
+18
-16
@@ -1,21 +1,23 @@
|
||||
@echo off
|
||||
REM ~dp0 steht für das Verzeichnis, in der diese Datei liegt
|
||||
pushd %~dp0\..
|
||||
PUSHD %~dp0\..
|
||||
|
||||
set PROJECT=%cd%
|
||||
set PROJECT_BIN=%PROJECT%\bin
|
||||
set PROJECT_CFG=%PROJECT%\cfg
|
||||
set PROJECT_DOC=%PROJECT%\doc
|
||||
set PROJECT_LIB=%PROJECT%\lib
|
||||
set PROJECT_DATA=%PROJECT%\data
|
||||
set PROJECT_WORK=%PROJECT%\work
|
||||
set PROJECT_TEST=%PROJECT%\testdata
|
||||
SET PROJECT=%cd%
|
||||
SET PROJECT_BIN=%PROJECT%\bin
|
||||
SET PROJECT_CFG=%PROJECT%\cfg
|
||||
SET PROJECT_DATA=%PROJECT%\data
|
||||
SET PROJECT_DATA_IN=%PROJECT%\data\in
|
||||
SET PROJECT_DATA_OUT=%PROJECT%\data\out
|
||||
SET PROJECT_DOC=%PROJECT%\doc
|
||||
SET PROJECT_LIB=%PROJECT%\lib
|
||||
SET PROJECT_LOG=%PROJECT%\log
|
||||
SET PROJECT_WORK=%PROJECT%\work
|
||||
|
||||
if not exist %PROJECT%\work mkdir %PROJECT%\work
|
||||
if not exist %PROJECT%\data mkdir %PROJECT%\data
|
||||
IF NOT EXIST %PROJECT%\data mkdir %PROJECT%\data
|
||||
IF NOT EXIST %PROJECT%\data\out mkdir %PROJECT%\data\out
|
||||
IF NOT EXIST %PROJECT%\log mkdir %PROJECT%\log
|
||||
IF NOT EXIST %PROJECT%\work mkdir %PROJECT%\work
|
||||
|
||||
set DB_PATH=%PROJECT_WORK%\sivas_replika.db
|
||||
set PATH=%PROJECT_BIN%;%PATH%
|
||||
SET PATH=%PROJECT_BIN%;%PATH%
|
||||
SET DB_PATH=%PROJECT_WORK%\sivas_replika.db
|
||||
|
||||
popd
|
||||
goto :eof
|
||||
POPD
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
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,
|
||||
"ROWID" INTEGER NOT NULL,
|
||||
"UEBTEILENR" TEXT NOT NULL,
|
||||
"POSNR" INTEGER NOT NULL,
|
||||
"KOMPTEILENR" TEXT NOT NULL,
|
||||
"DISPO_AKTIV_JN" TEXT,
|
||||
@@ -17,7 +10,7 @@ CREATE TABLE IF NOT EXISTS "COMP" (
|
||||
"LAENGE" REAL,
|
||||
"ZUSCHNITT" TEXT,
|
||||
"RUESTCODE" TEXT,
|
||||
PRIMARY KEY("ID" AUTOINCREMENT)
|
||||
PRIMARY KEY("ROWID" AUTOINCREMENT)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "PARTS" (
|
||||
"TEILENR" INTEGER,
|
||||
@@ -47,96 +40,82 @@ CREATE TABLE IF NOT EXISTS "PARTS" (
|
||||
"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")
|
||||
"ROWID" INTEGER NOT NULL,
|
||||
"UEBTEILENR" TEXT,
|
||||
"POSNR" INTEGER,
|
||||
"LFD_NR" INTEGER NOT NULL,
|
||||
PRIMARY KEY("ROWID" AUTOINCREMENT)
|
||||
);
|
||||
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")
|
||||
"ROWID" INTEGER NOT NULL,
|
||||
"UEBTEILENR" TEXT NOT NULL,
|
||||
"POSNR" INTEGER NOT NULL,
|
||||
"UEB_LEISTEN_NR" INTEGER NOT NULL,
|
||||
PRIMARY KEY("ROWID" AUTOINCREMENT)
|
||||
);
|
||||
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'),
|
||||
INSERT INTO "COMP" ("ROWID","UEBTEILENR","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'),
|
||||
(6,'000000369',10,'821106001','J',0.105,1,100.0,'5','HZS'),
|
||||
(7,'000000369',20,'829416035','J',1.0,NULL,NULL,NULL,'SRC'),
|
||||
(8,'000000369',30,'821106001','J',0.21,1,200.0,'10','SRC'),
|
||||
(8,'000000369',30,'821106001','J',0.21,1,200.0,'10','HZS'),
|
||||
(9,'000000369',40,'822066207','J',1.0,NULL,NULL,NULL,'SRC'),
|
||||
(10,'000000371',10,'821106001','J',0.105,1,100.0,'5','SRC'),
|
||||
(10,'000000371',10,'821106001','J',0.105,1,100.0,'5','HZS'),
|
||||
(11,'000000371',20,'829416035','J',1.0,NULL,NULL,NULL,'SRC'),
|
||||
(12,'000000371',30,'821106001','J',0.21,1,200.0,'10','SRC'),
|
||||
(12,'000000371',30,'821106001','J',0.21,1,200.0,'10','HZS'),
|
||||
(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'),
|
||||
(14,'000000372',10,'821106001','J',0.105,1,100.0,'5','HZS'),
|
||||
(15,'000000372',20,'821106001','J',0.21,1,200.0,'10','HZS'),
|
||||
(16,'000000372',30,'821106001','J',0.315,1,300.0,'15','HZS'),
|
||||
(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'),
|
||||
(18,'000000373',10,'821106001','J',0.105,1,100.0,'5','HZS'),
|
||||
(19,'000000373',20,'821106001','J',0.21,1,200.0,'10','HZS'),
|
||||
(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'),
|
||||
(21,'000000374',10,'821106001','J',0.105,1,100.0,'5','HZS'),
|
||||
(22,'000000374',20,'821106001','J',0.21,1,200.0,'10','HZS'),
|
||||
(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'),
|
||||
(24,'000000374',40,'821106001','J',0.315,1,300.0,'15','HZS'),
|
||||
(25,'000000375',10,'821106001','J',0.105,1,100.0,'5','HZS'),
|
||||
(26,'000000375',20,'821106001','J',0.21,1,200.0,'10','HZS'),
|
||||
(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'),
|
||||
(28,'000000375',40,'821106001','J',0.315,1,300.0,'15','HZS'),
|
||||
(29,'000000376',10,'821106001','J',0.105,1,100.0,'5','HZS'),
|
||||
(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'),
|
||||
(34,'000000378',10,'821106001','J',0.105,1,100.0,'5','HZS'),
|
||||
(35,'000000370',10,'821106001','J',0.105,1,100.0,'5','HZS'),
|
||||
(36,'000000379',10,'821106001','J',0.1,1,100.0,NULL,'HZS'),
|
||||
(37,'000000379',20,'829416035','J',1.0,NULL,NULL,NULL,'SRC'),
|
||||
(38,'000000379',30,'821106001','J',0.2,1,200.0,NULL,'SRC'),
|
||||
(38,'000000379',30,'821106001','J',0.2,1,200.0,NULL,'HZS'),
|
||||
(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'),
|
||||
(40,'000000381',10,'829516004','J',0.107,1,100.0,'7','HZS'),
|
||||
(41,'000000381',20,'829516004','J',0.207,1,200.0,'7','HZS'),
|
||||
(42,'000000381',30,'822066026','J',0.302,1,300.0,'2','HZS'),
|
||||
(43,'000000381',40,'829516004','J',0.307,1,300.0,'7','HZS'),
|
||||
(44,'000000381',50,'822066089','J',0.153,1,150.0,'3','HZS'),
|
||||
(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'),
|
||||
(47,'000000381',80,'822066089','J',0.253,1,250.0,'3','HZS'),
|
||||
(48,'000000382',10,'822064013','J',0.101,1,100.0,'1','HZS'),
|
||||
(49,'000000382',20,'829416031','J',0.205,1,200.0,'5','HZS'),
|
||||
(50,'000000382',30,'822066026','J',0.302,1,300.0,'2','HZS'),
|
||||
(51,'000000382',40,'829516001','J',0.106,1,100.0,'6','HZS'),
|
||||
(52,'000000382',50,'822066089','J',0.153,1,150.0,'3','HZS'),
|
||||
(53,'000000382',60,'827076001','J',0.104,1,100.0,'4','HZS'),
|
||||
(54,'000000382',70,'827076001','J',0.204,1,200.0,'4','HZS'),
|
||||
(55,'000000382',80,'827076001','J',0.304,1,300.0,'4','HZS'),
|
||||
(56,'000000382',90,'829516001','J',0.206,1,200.0,'6','HZS'),
|
||||
(57,'000000382',100,'822064013','J',0.201,1,200.0,'1','HZS'),
|
||||
(58,'000000382',110,'822066089','J',0.253,1,250.0,'3','HZS'),
|
||||
(59,'000000382',120,'829516004','J',0.107,1,100.0,'7','HZS'),
|
||||
(60,'000000382',130,'829516004','J',0.207,1,200.0,'7','HZS'),
|
||||
(61,'000000382',140,'829516051','J',0.208,1,200.0,'8','HZS'),
|
||||
(62,'000000382',150,'829516004','J',0.307,1,300.0,'7','HZS'),
|
||||
(63,'000000382',160,'821106001','J',0.36,1,350.0,'10','HZS'),
|
||||
(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'),
|
||||
@@ -154,54 +133,55 @@ INSERT INTO "PARTS" ("TEILENR","BEZ","BEZ2","KZKAUF","MENGENC","ABW_ANZAHL_JN","
|
||||
(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);
|
||||
INSERT INTO "STKL_REGEL" ("ROWID","UEBTEILENR","POSNR","LFD_NR") VALUES (1,'000000369',20,1);
|
||||
INSERT INTO "VAR_TEIL_BEDINGUNG_2" ("ROWID","UEBTEILENR","POSNR","UEB_LEISTEN_NR") VALUES (1,'000000369',10,418),
|
||||
(2,'000000369',30,420),
|
||||
(3,'000000370',10,419),
|
||||
(4,'000000371',10,419),
|
||||
(5,'000000371',30,420),
|
||||
(6,'000000372',10,418),
|
||||
(7,'000000372',20,419),
|
||||
(8,'000000372',30,420),
|
||||
(9,'000000373',10,417),
|
||||
(10,'000000373',20,418),
|
||||
(11,'000000374',10,417),
|
||||
(12,'000000374',20,418),
|
||||
(13,'000000374',40,419),
|
||||
(14,'000000375',10,417),
|
||||
(15,'000000375',20,418),
|
||||
(16,'000000375',40,419),
|
||||
(17,'000000376',10,417),
|
||||
(18,'000000378',10,419),
|
||||
(19,'000000379',20,418),
|
||||
(20,'000000379',40,419),
|
||||
(21,'000000380',10,417),
|
||||
(22,'000000380',20,418),
|
||||
(23,'000000380',40,419),
|
||||
(24,'000000380',50,420),
|
||||
(25,'000000381',10,7),
|
||||
(26,'000000381',20,7),
|
||||
(27,'000000381',30,2),
|
||||
(28,'000000381',40,7),
|
||||
(29,'000000381',50,3),
|
||||
(30,'000000381',60,11),
|
||||
(31,'000000381',70,11),
|
||||
(32,'000000381',80,3),
|
||||
(33,'000000382',10,1),
|
||||
(34,'000000382',20,5),
|
||||
(35,'000000382',30,2),
|
||||
(36,'000000382',40,6),
|
||||
(37,'000000382',50,3),
|
||||
(38,'000000382',60,4),
|
||||
(39,'000000382',70,4),
|
||||
(40,'000000382',80,4),
|
||||
(41,'000000382',90,6),
|
||||
(42,'000000382',100,1),
|
||||
(43,'000000382',110,3),
|
||||
(44,'000000382',120,7),
|
||||
(45,'000000382',130,7),
|
||||
(46,'000000382',140,8),
|
||||
(47,'000000382',150,7),
|
||||
(48,'000000382',160,10),
|
||||
(49,'000000382',170,11),
|
||||
(50,'000000382',180,11);
|
||||
COMMIT;
|
||||
File diff suppressed because it is too large
Load Diff
+1008
File diff suppressed because it is too large
Load Diff
@@ -1,695 +0,0 @@
|
||||
import argparse
|
||||
import csv
|
||||
import os
|
||||
import sqlite3
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Programm bearbeitet Stücklisten-CSV aus RuleDesigner, sodass Spaltenüberschreibungen + TOS-Bedingungen und -Regeln (falls vorhanden) mit verschoben werden',
|
||||
prog='verschiebelogik',
|
||||
formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=50))
|
||||
parser.register("action", "store_after_check", ArgparseDirectoryCheck)
|
||||
parser.add_argument("parent_code", action="store", type=str, help="Teilenummer der Baugruppe")
|
||||
parser.add_argument("-cid", "--in_csv_dir", action="store_after_check", default=os.environ.get("PROJECT_DATA"), help="Eingabeverzeichnis der Stücklisten-CSV aus RuleDesigner")
|
||||
subparsers = parser.add_subparsers(title="output type", help="Ausgabe der fertigen Stückliste in Konsole oder als CSV", dest="output_type")
|
||||
sp_cmd = subparsers.add_parser("console", help="Ausgabe der fertig bearbeiteten Stückliste in der Konsole")
|
||||
sp_csv = subparsers.add_parser("csv", help="Ausgabe der fertig bearbeiteten Stückliste als CSV-Datei")
|
||||
sp_csv.add_argument("-v", "--verbose", action="store_true", help="Alle Spalten ausgeben")
|
||||
sp_csv.register("action", "store_after_check", ArgparseDirectoryCheck)
|
||||
sp_cmd.add_argument("-v", "--verbose", action="store_true", help="Alle Spalten ausgeben")
|
||||
sp_csv.add_argument("-cod", "--out_csv_dir", action="store_after_check", default=os.environ.get("PROJECT_WORK"), help="Ausgabeverzeichnis fertigen Stücklisten-CSV")
|
||||
args: dict = vars(parser.parse_args())
|
||||
|
||||
if os.environ.get("DB_PATH") is not None:
|
||||
# nur 1 Datenbankinstanz über gesamte Laufzeit (ähnlich Singleton)
|
||||
db = Database(os.environ.get("DB_PATH"))
|
||||
else:
|
||||
raise ValueError("Bitte bin/setenv.bat vor diesem Programm für erforderliche Umgebungsvariablen ausführen!")
|
||||
|
||||
try:
|
||||
old_bom = COMP(db)
|
||||
new_bom = RD_COMP(db, args["parent_code"])
|
||||
csv_handler = CSVHandler()
|
||||
sql_handler = SQLFileHandler()
|
||||
bom_processing = BOMProcessing(db, args["parent_code"])
|
||||
|
||||
# SIVAS "nachbauen"
|
||||
sql_source_filename = "sivas_replika.sql"
|
||||
sivas_source_data = sql_handler.read_from_file(filename=sql_source_filename)
|
||||
old_bom.rebuild_sivas(sivas_source_data)
|
||||
|
||||
# CSV-Daten in Datenbank schreiben
|
||||
csv_data = csv_handler.read_as_dict(filename=f"BOM_{args["parent_code"]}")
|
||||
new_bom.create()
|
||||
new_bom.insert_csv_data(csv_data)
|
||||
|
||||
# Neue Stückliste erstellen / schreiben
|
||||
bom_processing.write_new_bom()
|
||||
|
||||
if args["output_type"] == "console":
|
||||
if args["verbose"]:
|
||||
output = repr(new_bom)
|
||||
print(output)
|
||||
else:
|
||||
print(new_bom)
|
||||
elif args["output_type"] == "csv":
|
||||
if args["verbose"]:
|
||||
bom_data = new_bom.get_verbose_data()
|
||||
else:
|
||||
bom_data = new_bom.get_compact_data()
|
||||
|
||||
csv_handler.write_from_dict(data=bom_data,
|
||||
filename=f"BOM_{args["parent_code"]}_final",
|
||||
filedir=args["out_csv_dir"])
|
||||
else:
|
||||
NotImplementedError
|
||||
|
||||
finally:
|
||||
db.close()
|
||||
db.remove() # Damit bei jedem Lauf eine neue erstellt wird -> mit :memory: traten Bugs auf
|
||||
|
||||
|
||||
class ArgparseDirectoryCheck(argparse.Action):
|
||||
def __call__(self, parser, namespace, values: list[str], option_string = None):
|
||||
if not os.path.isdir(values[0]):
|
||||
raise NotADirectoryError
|
||||
setattr(namespace, self.dest, values[0])
|
||||
|
||||
class FileHandler:
|
||||
def check_filedir(self, filedir: str) -> str:
|
||||
if filedir is None:
|
||||
raise ValueError("Bitte bin/setenv.bat ausführen, oder Verzeichnis angegeben, in dem die Eingabedatei liegt!")
|
||||
if not os.path.isdir(filedir):
|
||||
raise NotADirectoryError
|
||||
return filedir
|
||||
|
||||
def check_filepath(self, filepath: str) -> str:
|
||||
if not os.path.isfile(filepath):
|
||||
raise FileNotFoundError("Angegebener Dateiname existiert nicht!")
|
||||
return filepath
|
||||
|
||||
class CSVHandler(FileHandler):
|
||||
|
||||
def __init__(self, encoding="utf-8", delimiter: str = ";"):
|
||||
self.delimiter = delimiter
|
||||
self.encoding = encoding
|
||||
|
||||
def read_as_dict(self, filename: str, filedir: str = os.environ.get("PROJECT_DATA"), ) -> list[dict[str]]:
|
||||
|
||||
filename = f"{filename.removesuffix(".csv")}.csv"
|
||||
filedir = self.check_filedir(filedir)
|
||||
|
||||
filepath = os.path.join(filedir, filename)
|
||||
if not filepath:
|
||||
raise FileNotFoundError("Zur angegebenen Teilenummer existiert keine Stücklisten-CSV!")
|
||||
|
||||
with open(filepath, "r", encoding=self.encoding, newline="") as csv_file:
|
||||
reader = csv.DictReader(csv_file, delimiter=self.delimiter)
|
||||
|
||||
required_col_names = ["ParentCode", "Positionsnummer", "ChildCode", "Menge", "Uebergabelaenge"]
|
||||
if reader.fieldnames != required_col_names:
|
||||
raise ValueError(f"Spaltennamen falsch. Korrekt: {required_col_names}")
|
||||
|
||||
csv_data = []
|
||||
for line in reader:
|
||||
line["Positionsnummer"] += "0" # Angleichung Positionsnummer an SIVAS-Format (Pos. * 10)
|
||||
csv_data.append(line)
|
||||
return csv_data
|
||||
|
||||
def write_from_dict(self, filename: str, data: list[dict[str]], filedir: str = os.environ.get("PROJECT_WORK")) -> None:
|
||||
|
||||
filedir = self.check_filedir(filedir)
|
||||
filepath = os.path.join(filedir, f"{filename.removesuffix(".csv")}.csv")
|
||||
|
||||
with open(filepath, "w", encoding=self.encoding, newline="") as csv_file:
|
||||
writer = csv.DictWriter(csv_file,
|
||||
fieldnames=data[0].keys(),
|
||||
delimiter=self.delimiter)
|
||||
writer.writeheader()
|
||||
writer.writerows(data)
|
||||
|
||||
self.check_filepath(filepath)
|
||||
|
||||
class SQLFileHandler(FileHandler):
|
||||
|
||||
def __init__(self, encoding: str = "utf-8"):
|
||||
self.encoding = encoding
|
||||
|
||||
def read_from_file(self, filename: str, filedir: str = os.environ.get("PROJECT_DATA")) -> str:
|
||||
|
||||
filedir = self.check_filedir(filedir)
|
||||
|
||||
filepath = os.path.join(filedir, filename)
|
||||
filepath = self.check_filepath(filepath)
|
||||
|
||||
with open(filepath, "r", encoding="utf-8", newline="") as sql_file:
|
||||
sql = sql_file.read()
|
||||
return sql
|
||||
|
||||
class Database:
|
||||
def __init__(self, db_path: str):
|
||||
if not os.path.isdir(os.path.dirname(db_path)):
|
||||
raise NotADirectoryError
|
||||
|
||||
self.db_path = db_path
|
||||
self.con = sqlite3.connect(self.db_path, autocommit=True)
|
||||
self.con.row_factory = self.dict_factory
|
||||
|
||||
@staticmethod
|
||||
def dict_factory(cursor, row):
|
||||
d = {}
|
||||
for idx, col in enumerate(cursor.description):
|
||||
d[col[0]] = row[idx]
|
||||
return d
|
||||
|
||||
def close(self):
|
||||
self.con.close()
|
||||
|
||||
def remove(self):
|
||||
if os.path.isfile(self.db_path):
|
||||
os.remove(self.db_path)
|
||||
|
||||
|
||||
class RD_COMP:
|
||||
|
||||
"""
|
||||
Temporäre Tabelle, in die die Daten der Stücklisten-CSV aus RuleDesigner geschrieben werden
|
||||
Tabelle wird für die Verarbeitung der Stückliste benötigt. Sobald die finale Stückliste steht und in die Tabelle 'COMP'
|
||||
wird sie nicht mehr benötigt.
|
||||
"""
|
||||
|
||||
def __init__(self, db: Database, parent_code: str):
|
||||
self.db = db
|
||||
self.parent_code = parent_code
|
||||
|
||||
def __repr__(self):
|
||||
res = self.get_verbose_data()
|
||||
output = self._sql_as_ascii_table(res)
|
||||
return output
|
||||
|
||||
def __str__(self):
|
||||
res = self.get_compact_data()
|
||||
output = self._sql_as_ascii_table(res)
|
||||
return output
|
||||
|
||||
|
||||
def create(self) -> None:
|
||||
"""Schreibt neue Stücklistendaten aus CSV in temporäre Datenbanktabelle 'RD_COMP'."""
|
||||
|
||||
self.db.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))""")
|
||||
|
||||
def drop(self):
|
||||
pass
|
||||
|
||||
def insert_csv_data(self, csv_data: list[dict[str]]):
|
||||
self.db.con.executemany(""" INSERT INTO RD_COMP (ParentCode, Positionsnummer, ChildCode, Menge, Uebergabelaenge)
|
||||
VALUES (:ParentCode, :Positionsnummer, :ChildCode, :Menge, :Uebergabelaenge)""", csv_data)
|
||||
|
||||
def get_all_bom_data(self) -> list[dict[str]]:
|
||||
res = self.db.con.execute("SELECT * FROM RD_COMP WHERE ParentCode = :parent ORDER BY Positionsnummer",
|
||||
{"parent": self.parent_code}).fetchall()
|
||||
return res
|
||||
|
||||
def get_verbose_data(self) -> list[dict[str]]:
|
||||
res = self.db.con.execute("""
|
||||
SELECT C.*,
|
||||
R.REGEL,
|
||||
B.BEDINGUNG
|
||||
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
|
||||
WHERE C.PARENT = :parent_code
|
||||
ORDER BY C.POSNR
|
||||
""", {"parent_code": self.parent_code}).fetchall()
|
||||
return res
|
||||
|
||||
def get_compact_data(self) -> list[dict[str]]:
|
||||
res = self.db.con.execute("""
|
||||
SELECT C.POSNR,
|
||||
C.KOMPTEILENR,
|
||||
C.ABW_ANZAHL,
|
||||
C.MENGE,
|
||||
C.LAENGE,
|
||||
C.ZUSCHNITT,
|
||||
R.REGEL,
|
||||
B.Bedingung
|
||||
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
|
||||
WHERE C.PARENT = :parent_code
|
||||
ORDER BY C.POSNR
|
||||
""", {"parent_code": self.parent_code}).fetchall()
|
||||
return res
|
||||
|
||||
def _sql_as_ascii_table(self, sql_data: list[dict[str]]):
|
||||
"""SQL-Rohdaten als Tabelle ausgeben"""
|
||||
headers = list(sql_data[0].keys())
|
||||
rows = [[str(row.get(h, "") if row.get(h, "") is not None else "") for h in headers] for row in sql_data]
|
||||
col_widths = [max(len(str(h)), max(len(str(row[i])) for row in rows)) for i, h in enumerate(headers)]
|
||||
header_line = " | ".join(f"{h:<{col_widths[i]}}" for i, h in enumerate(headers))
|
||||
separator = "-+-".join("-" * col_widths[i] for i in range(len(headers)))
|
||||
row_lines = [" | ".join(f"{row[i]:<{col_widths[i]}}" for i in range(len(headers))) for row in rows]
|
||||
return "\n".join([header_line, separator] + row_lines)
|
||||
|
||||
def insert(self, new_data: dict[str, str]) -> None:
|
||||
self.db.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(self, ids: list[dict[str]]) -> None:
|
||||
self.db.con.executemany("DELETE FROM COMP WHERE ID = :ID", ids)
|
||||
|
||||
def update(self, pos_data: list[dict[str]]) -> None:
|
||||
self.db.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
|
||||
""", pos_data)
|
||||
|
||||
|
||||
class COMP:
|
||||
|
||||
"""Beinhaltet Methoden, zur Interaktion mit Tabelle COMP (gleichnamig wie in SIVAS)"""
|
||||
|
||||
def __init__(self, db: Database):
|
||||
self.db = db
|
||||
|
||||
def rebuild_sivas(self, sql: str) -> None:
|
||||
self.db.con.executescript(sql)
|
||||
|
||||
def get_child_ids(self, parent_code: str) -> list[dict[str]]:
|
||||
res = self.db.con.execute("SELECT ID FROM COMP WHERE PARENT = :parent",
|
||||
{"parent": parent_code}).fetchall()
|
||||
return res
|
||||
|
||||
|
||||
class VAR_TEIL_BEDINGUNGEN_2:
|
||||
|
||||
"""
|
||||
Beinhaltet Methoden, zur Interaktion mit Tabelle VAR_TEIL_BEDINGUNG_2 (gleichnamig wie in SIVAS),
|
||||
in der TOS-Bedingungen gespeichert sind
|
||||
"""
|
||||
|
||||
def __init__(self, db: Database) -> None:
|
||||
self.db = db
|
||||
|
||||
def update(self, data: list[dict[str]]) -> None:
|
||||
self.db.con.executemany("UPDATE VAR_TEIL_BEDINGUNG_2 SET BEDINGUNG = :BEDINGUNG WHERE COMP_ID = :ID", data)
|
||||
|
||||
def delete(self, data: list[dict[str]]) -> None:
|
||||
self.db.con.executemany("DELETE FROM VAR_TEIL_BEDINGUNG_2 WHERE COMP_ID = :ID", data)
|
||||
|
||||
|
||||
class STKL_REGEL:
|
||||
|
||||
"""
|
||||
Beinhaltet Methoden, zur Interaktion mit Tabelle STKL_REGEL (gleichnamig wie in SIVAS),
|
||||
in der TOS-Regeln gespeichert sind
|
||||
"""
|
||||
|
||||
def __init__(self, db: Database) -> None:
|
||||
self.db = db
|
||||
|
||||
def update(self, data: list[dict[str]]) -> None:
|
||||
self.db.con.executemany("UPDATE STKL_REGEL SET REGEL = :REGEL WHERE COMP_ID = :ID", data)
|
||||
|
||||
def delete(self, data: list[dict[str]]) -> None:
|
||||
self.db.con.executemany("DELETE FROM STKL_REGEL WHERE COMP_ID = :ID", data)
|
||||
|
||||
class BOMProcessing:
|
||||
|
||||
"""
|
||||
Die Methoden dieser Klasse verarbeiten die Daten aus der RuleDesigner-Stücklisten CSV und initiiert das Schreiben der verarbeiteten
|
||||
Daten in COMP.
|
||||
"""
|
||||
|
||||
def __init__(self, db: Database, parent_code: str):
|
||||
self.db = db
|
||||
self.parent_code = parent_code
|
||||
self.new_bom = RD_COMP(db, parent_code)
|
||||
self.old_bom = COMP(db)
|
||||
self.tos_bedingungen = VAR_TEIL_BEDINGUNGEN_2(db)
|
||||
self.tos_regeln = STKL_REGEL(db)
|
||||
|
||||
def extract_col_data(self, query_results: list[dict[str]], *col_names: tuple) -> list[dict[str]]:
|
||||
extracted_data = []
|
||||
for result in query_results:
|
||||
data = {col_name: result[col_name] for col_name in col_names if col_name in result}
|
||||
extracted_data.append(data)
|
||||
return extracted_data
|
||||
|
||||
def _get_positions_to_move_old_logic(self) -> set | None:
|
||||
"""
|
||||
Gibt alle Positionsnummern zurück, die gemäß alter Logik verschoben werden
|
||||
"""
|
||||
res = self.db.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": self.parent_code}).fetchall()
|
||||
|
||||
if res is not None:
|
||||
res = set([line["POS"] for line in res])
|
||||
return res
|
||||
|
||||
def _check_identical_code_len_combo(self, child_code: str) -> bool:
|
||||
"""
|
||||
Prüfung Fall 1.1:
|
||||
"""
|
||||
res = self.db.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": self.parent_code, "child": child_code}).fetchone()
|
||||
|
||||
return True if res["Fall 1.1"] == 'True' else False
|
||||
|
||||
def _check_same_pos(self, child_code: str) -> bool:
|
||||
"""
|
||||
Prüfung Fall 1.2
|
||||
"""
|
||||
res = self.db.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": self.parent_code, "child": child_code}).fetchone()
|
||||
return True if res["Fall 1.2"] == 'True' else False
|
||||
|
||||
def _get_update_data_for_diff_pos(self, pos_nr: str) -> list[dict[str]]:
|
||||
"""
|
||||
Ermittelt die Daten, die an die neue Position verschoben werden sollen.
|
||||
(Für Fall 1.1 und Fall 1.3)
|
||||
"""
|
||||
res = self.db.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": self.parent_code, "posnr": pos_nr}).fetchone()
|
||||
return res
|
||||
|
||||
def _get_update_data_for_same_pos(self, pos_nr: str) -> list[dict[str]]:
|
||||
"""
|
||||
Ermittelt die Daten, die an dieselbe Position verschoben werden sollen
|
||||
(Für Fall 1.2)
|
||||
"""
|
||||
res = self.db.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": self.parent_code, "posnr": pos_nr}).fetchone()
|
||||
return res
|
||||
|
||||
def _get_ids_to_del(self, ignored_ids: list = []) -> list[dict[str]]:
|
||||
"""
|
||||
Die zu löschenden ID's ergeben sich, indem man die ID's der Positionen aus der alten Stückliste betrachtet
|
||||
und die ID's aus der neuen Stückliste abzieht, die verschoben werden. Positionen, die in der neuen Stückliste
|
||||
neu dazukommen sind, wirken sich nicht auf die zu löschenden ID's aus!
|
||||
"""
|
||||
old_bom_ids = self.old_bom.get_child_ids(self.parent_code)
|
||||
ids_to_delete = [idx for idx in old_bom_ids if idx["ID"] not in ignored_ids]
|
||||
return ids_to_delete
|
||||
|
||||
def _get_data_for_insert(self, posnr: str) -> list[dict[str]]:
|
||||
"""
|
||||
Fall 1.3 (Neuanlage von Positionen)
|
||||
"""
|
||||
res = self.db.con.execute("""
|
||||
SELECT RD.Positionsnummer AS POSNR,
|
||||
RD.ChildCode AS KOMPTEILENR,
|
||||
RD.ParentCode AS PARENT,
|
||||
NULL AS ZUSCHNITT,
|
||||
P.DISPO_AKTIV_JN,
|
||||
P.RUESTCODE,
|
||||
|
||||
-- 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
|
||||
LEFT JOIN COMP C
|
||||
ON RD.ChildCode = C.KOMPTEILENR
|
||||
AND RD.Uebergabelaenge = COALESCE(C.LAENGE, 0)
|
||||
AND RD.ParentCode = C.PARENT
|
||||
|
||||
-- Da es sich um neue Positionen handelt, werden Defaultwerte verwendet, die z.T. 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": self.parent_code, "posnr": posnr}).fetchone()
|
||||
return res
|
||||
|
||||
def write_new_bom(self) -> None:
|
||||
"""
|
||||
Diese Methode binhaltet die Hauptlogik:
|
||||
Hier wird festgelegt, welche Positionsnummern eingefügt, gelöscht oder verschoben werden müssen
|
||||
Am Ende werden die daraus erforderlichen Datenbankaktionen (insert / update / delete) initiiert,
|
||||
um die neue, fertig verarbeite Stückliste in COMP zu überführen.
|
||||
"""
|
||||
update_data_comp = []
|
||||
insert_data_comp = []
|
||||
update_data_regel = []
|
||||
update_data_bedingung = []
|
||||
ids_to_delete = []
|
||||
ids_not_to_delete = []
|
||||
childcodes_case_11 = set() # vermeidet erneute Prüfung einer Teilenummer durch _check_identical_code_len_combo
|
||||
childcodes_case_12 = set() # vermeidet erneute Prüfung einer Teilenummer durch _check_same_pos()
|
||||
|
||||
new_bom_data = self.new_bom.get_all_bom_data()
|
||||
|
||||
for pos_data in new_bom_data:
|
||||
|
||||
child_code: str = pos_data["ChildCode"]
|
||||
posnr: int = pos_data["Positionsnummer"]
|
||||
|
||||
# Prüfung Fall 1.1
|
||||
if child_code in childcodes_case_11 or self._check_identical_code_len_combo(child_code) is True:
|
||||
logic_case = 1.1
|
||||
childcodes_case_11.add(child_code)
|
||||
data = self._get_update_data_for_diff_pos(posnr)
|
||||
|
||||
# Prüfung Fall 1.2
|
||||
elif child_code in childcodes_case_12 or self._check_same_pos(child_code) is True:
|
||||
logic_case = 1.2
|
||||
childcodes_case_12.add(child_code)
|
||||
data = self._get_update_data_for_same_pos(posnr)
|
||||
|
||||
# Prüfungen Fall 1.3
|
||||
else:
|
||||
|
||||
# Prüfung Fall 1.31 - Verschiebung bestehender Positionen
|
||||
posnrs_to_move = self._get_positions_to_move_old_logic()
|
||||
if posnr in posnrs_to_move:
|
||||
logic_case = 1.31
|
||||
data = self._get_update_data_for_diff_pos(posnr)
|
||||
|
||||
# Prüfung Fall 1.32 - Einfügen neuer Positionen
|
||||
else:
|
||||
logic_case = 1.32
|
||||
data = self._get_data_for_insert(posnr)
|
||||
|
||||
# Teilenummern der Stückliste, die verschoben werden (Fal 1.1 / 1.2 / 1.3.1):
|
||||
# - werden nie gelöscht (konkret: deren ID's in COMP), weil IMMER eine Verschiebung der Positionsnummer erfolgt
|
||||
if logic_case in (1.1, 1.2, 1.31):
|
||||
ids_not_to_delete.append(data["ID"])
|
||||
update_data_comp.append(data)
|
||||
|
||||
if logic_case == 1.32:
|
||||
insert_data_comp.append(data)
|
||||
|
||||
|
||||
# Zu löschende Positionen ermitteln
|
||||
ids_to_delete = self._get_ids_to_del(ignored_ids=ids_not_to_delete)
|
||||
|
||||
# Positionen löschen (falls erforderlich)
|
||||
if ids_to_delete:
|
||||
self.new_bom.delete(ids_to_delete)
|
||||
self.tos_bedingungen.delete(ids_to_delete)
|
||||
self.tos_regeln.delete(ids_to_delete)
|
||||
|
||||
# Positionen einfügen (falls erforderlich). Neue Positionen haben nie TOS-Regeln oder -Bedingungen
|
||||
if insert_data_comp:
|
||||
self.new_bom.insert(insert_data_comp)
|
||||
|
||||
# Positionen sowie deren TOS-Regeln und -Bedingungen updaten (falls erforderlich)
|
||||
if update_data_comp:
|
||||
update_data_regel = self.extract_col_data(update_data_comp, "ID", "REGEL")
|
||||
update_data_bedingung = self.extract_col_data(update_data_comp, "ID", "BEDINGUNG")
|
||||
|
||||
self.new_bom.update(update_data_comp)
|
||||
self.tos_regeln.update(update_data_regel)
|
||||
self.tos_bedingungen.update(update_data_bedingung)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Binary file not shown.
@@ -0,0 +1,308 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><sqlb_project><db path="test.db" readonly="0" foreign_keys="1" case_sensitive_like="0" temp_store="0" wal_autocheckpoint="1000" synchronous="2"/><attached/><window><main_tabs open="structure browser pragmas query" current="3"/></window><tab_structure><column_width id="0" width="300"/><column_width id="1" width="0"/><column_width id="2" width="100"/><column_width id="3" width="3270"/><column_width id="4" width="0"/><expanded_item id="0" parent="1"/><expanded_item id="1" parent="1"/><expanded_item id="2" parent="1"/><expanded_item id="3" parent="1"/></tab_structure><tab_browse><table title="PARTS" custom_title="0" dock_id="1" table="4,5:mainPARTS"/><dock_state state="000000ff00000000fd00000001000000020000058b00000395fc0100000001fc000000000000058b0000012100fffffffa000000000100000002fb000000160064006f0063006b00420072006f00770073006500310100000000ffffffff0000012100fffffffb000000160064006f0063006b00420072006f00770073006500340000000000ffffffff00000000000000000000058b0000000000000004000000040000000800000008fc00000000"/><default_encoding codec=""/><browse_table_settings><table schema="main" name="COMP" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort><column index="1" mode="0"/></sort><column_widths><column index="1" value="50"/><column index="2" value="79"/><column index="3" value="48"/><column index="4" value="90"/><column index="5" value="107"/><column index="6" value="48"/><column index="7" value="87"/><column index="8" value="52"/><column index="9" value="73"/><column index="10" value="76"/><column index="11" value="72"/><column index="12" value="63"/><column index="13" value="72"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="PARTS" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort><column index="6" mode="1"/></sort><column_widths><column index="1" value="78"/><column index="2" value="300"/><column index="3" value="39"/><column index="4" value="53"/><column index="5" value="64"/><column index="6" value="108"/><column index="7" value="107"/><column index="8" value="41"/><column index="9" value="97"/><column index="10" value="126"/><column index="12" value="112"/><column index="13" value="135"/><column index="14" value="54"/><column index="15" value="51"/><column index="16" value="218"/><column index="17" value="195"/><column index="18" value="73"/><column index="19" value="65"/><column index="20" value="76"/><column index="21" value="113"/><column index="22" value="76"/><column index="23" value="70"/><column index="24" value="102"/><column index="25" value="52"/></column_widths><filter_values><column index="4" value="M"/><column index="0" value="822066089"/></filter_values><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="RD_COMP" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort><column index="1" mode="0"/></sort><column_widths><column index="1" value="35"/><column index="2" value="78"/><column index="3" value="105"/><column index="4" value="78"/><column index="5" value="45"/><column index="6" value="105"/><column index="7" value="122"/></column_widths><filter_values><column index="6" value="5"/></filter_values><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="STKL_REGEL" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="35"/><column index="2" value="82"/><column index="3" value="134"/><column index="4" value="44"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="VAR_TEIL_BEDINGUNG_2" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort><column index="3" mode="0"/></sort><column_widths><column index="1" value="48"/><column index="2" value="140"/><column index="3" value="137"/><column index="4" value="102"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table></browse_table_settings></tab_browse><tab_sql><sql name="1_create_tmp_not_identical">DROP TABLE IF EXISTS TMP_CODES_CASE_12_OR_13;
|
||||
CREATE TABLE TMP_CODES_CASE_12_OR_13 (KOMPTEILENR TEXT NOT NULL, PRIMARY KEY (KOMPTEILENR));
|
||||
|
||||
WITH combined_bom AS (
|
||||
SELECT C.KOMPTEILENR, COALESCE(C.LAENGE, 0) AS LAENGE
|
||||
FROM COMP C
|
||||
WHERE C.UEBTEILENR = '000000372'
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT ChildCode, Uebergabelaenge
|
||||
FROM RD_COMP
|
||||
WHERE ParentCode = '000000372'
|
||||
)
|
||||
|
||||
INSERT INTO TMP_CODES_CASE_12_OR_13
|
||||
SELECT DISTINCT KOMPTEILENR
|
||||
FROM (
|
||||
SELECT KOMPTEILENR, LAENGE, COUNT(*) AS ANZAHL
|
||||
FROM combined_bom
|
||||
GROUP BY KOMPTEILENR, LAENGE
|
||||
)
|
||||
WHERE ANZAHL = 1;
|
||||
|
||||
SELECT * FROM TMP_CODES_CASE_12_OR_13</sql><sql name="2_create_tmp_not_same_pos">DROP TABLE IF EXISTS TMP_CODES_CASE_13;
|
||||
CREATE TABLE TMP_CODES_CASE_13 (KOMPTEILENR TEXT NOT NULL, PRIMARY KEY(KOMPTEILENR));
|
||||
|
||||
WITH combined_bom AS (
|
||||
SELECT KOMPTEILENR, POSNR
|
||||
FROM COMP
|
||||
WHERE UEBTEILENR = '000000372'
|
||||
AND KOMPTEILENR IN (SELECT * FROM TMP_CODES_CASE_12_OR_13)
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT ChildCode, Positionsnummer
|
||||
FROM RD_COMP
|
||||
WHERE ParentCode = '000000372'
|
||||
AND ChildCode IN (SELECT * FROM TMP_CODES_CASE_12_OR_13)
|
||||
)
|
||||
|
||||
INSERT INTO TMP_CODES_CASE_13 (KOMPTEILENR)
|
||||
SELECT DISTINCT KOMPTEILENR
|
||||
FROM (
|
||||
SELECT KOMPTEILENR, COUNT(*) AS ANZAHL
|
||||
FROM combined_bom
|
||||
GROUP BY KOMPTEILENR, POSNR
|
||||
)
|
||||
WHERE ANZAHL = 1;
|
||||
|
||||
SELECT * FROM TMP_CODES_CASE_13</sql><sql name="3_get_case_11_pos">DROP TABLE IF EXISTS TMP_POSNRS_CASE_11;
|
||||
CREATE TABLE TMP_POSNRS_CASE_11 (UEBTEILENR TEXT NOT NULL,
|
||||
POS_ALT INTEGER NOT NULL,
|
||||
POS_NEU INTEGER NOT NULL,
|
||||
PRIMARY KEY(POS_NEU));
|
||||
|
||||
INSERT INTO TMP_POSNRS_CASE_11 (UEBTEILENR, POS_ALT, POS_NEU)
|
||||
|
||||
SELECT C.UEBTEILENR, C.POSNR AS POS_ALT, RD.Positionsnummer AS POS_NEU
|
||||
FROM RD_COMP RD
|
||||
|
||||
LEFT JOIN TMP_CODES_CASE_12_OR_13 TMP
|
||||
ON RD.ChildCode = TMP.KOMPTEILENR
|
||||
|
||||
LEFT JOIN COMP C
|
||||
ON RD.ParentCode = C.UEBTEILENR
|
||||
AND RD.ChildCode = C.KOMPTEILENR
|
||||
AND RD.Uebergabelaenge = COALESCE(C.LAENGE, 0)
|
||||
|
||||
WHERE RD.ParentCode = '000000372'
|
||||
AND TMP.KOMPTEILENR IS NULL;
|
||||
|
||||
SELECT * FROM TMP_POSNRS_CASE_11</sql><sql name="4_get_case_12_pos">DROP TABLE IF EXISTS TMP_POSNRS_CASE_12;
|
||||
CREATE TABLE TMP_POSNRS_CASE_12 (UEBTEILENR TEXT NOT NULL,
|
||||
POS_ALT INTEGER NOT NULL,
|
||||
POS_NEU INTEGER NOT NULL,
|
||||
PRIMARY KEY(POS_NEU));
|
||||
|
||||
INSERT INTO TMP_POSNRS_CASE_12 (UEBTEILENR, POS_ALT, POS_NEU)
|
||||
|
||||
SELECT C.UEBTEILENR, C.POSNR AS POS_ALT, RD.Positionsnummer AS POS_NEU
|
||||
FROM RD_COMP RD
|
||||
|
||||
LEFT JOIN TMP_CODES_CASE_13 TMP
|
||||
ON RD.ChildCode = TMP.KOMPTEILENR
|
||||
|
||||
LEFT JOIN COMP C
|
||||
ON RD.ParentCode = C.UEBTEILENR
|
||||
AND RD.ChildCode = C.KOMPTEILENR
|
||||
AND RD.Positionsnummer = C.POSNR
|
||||
|
||||
WHERE RD.ParentCode = '000000372'
|
||||
AND TMP.KOMPTEILENR IS NULL
|
||||
AND RD.Positionsnummer NOT IN (SELECT POSNR FROM TMP_POSNRS_CASE_11);
|
||||
|
||||
SELECT * FROM TMP_POSNRS_CASE_12;</sql><sql name="5_get_case_131_pos">DROP TABLE IF EXISTS TMP_POSNRS_CASE_131;
|
||||
CREATE TABLE TMP_POSNRS_CASE_131 (POSNR INTEGER NOT NULL, PRIMARY KEY(POSNR));
|
||||
|
||||
INSERT INTO TMP_POSNRS_CASE_131
|
||||
|
||||
SELECT RD.Positionsnummer AS POSNR
|
||||
FROM RD_COMP RD
|
||||
|
||||
LEFT JOIN COMP C
|
||||
ON RD.ParentCode = C.UEBTEILENR
|
||||
AND RD.ChildCode = C.KOMPTEILENR
|
||||
AND RD.Uebergabelaenge = COALESCE(C.LAENGE, 0)
|
||||
|
||||
WHERE RD.ParentCode = '000000372'
|
||||
AND RD.ChildCode IN (SELECT * FROM TMP_CODES_CASE_13)
|
||||
AND C.KOMPTEILENR IS NULL;
|
||||
|
||||
SELECT * FROM TMP_POSNRS_CASE_131;</sql><sql name="6_get_case_132_pos">DROP TABLE IF EXISTS TMP_POSNRS_CASE_132;
|
||||
CREATE TABLE TMP_POSNRS_CASE_132 (UEBTEILENR TEXT NOT NULL,
|
||||
POS_ALT INTEGER NOT NULL,
|
||||
POS_NEU INTEGER NOT NULL,
|
||||
PRIMARY KEY(POS_NEU));
|
||||
|
||||
INSERT INTO TMP_POSNRS_CASE_132 (UEBTEILENR, POS_ALT, POS_NEU)
|
||||
|
||||
SELECT C.UEBTEILENR, C.POSNR AS POS_ALT, RD.Positionsnummer AS POS_NEU
|
||||
FROM RD_COMP RD
|
||||
|
||||
INNER JOIN COMP C
|
||||
ON RD.ParentCode = C.UEBTEILENR
|
||||
AND RD.ChildCode = C.KOMPTEILENR
|
||||
AND RD.Uebergabelaenge = COALESCE(C.LAENGE, 0)
|
||||
|
||||
WHERE RD.ParentCode = '000000372'
|
||||
AND RD.ChildCode IN (SELECT * FROM TMP_CODES_CASE_13);
|
||||
|
||||
SELECT * FROM TMP_POSNRS_CASE_132;</sql><sql name="7_get_ids_to_delete">WITH COMBINED_UPDATE_DATA AS (
|
||||
SELECT * FROM TMP_POSNRS_CASE_11
|
||||
UNION
|
||||
SELECT * FROM TMP_POSNRS_CASE_12
|
||||
UNION
|
||||
SELECT * FROM TMP_POSNRS_CASE_132
|
||||
)
|
||||
--INSERT INTO TMP_IDS_TO_DELETE
|
||||
|
||||
SELECT C.ROWID AS COMP_ROWID, B.ROWID AS BED_ROWID, R.ROWID AS REG_ROWID
|
||||
FROM COMP C
|
||||
|
||||
LEFT JOIN COMBINED_UPDATE_DATA CUD
|
||||
ON C.UEBTEILENR = CUD.UEBTEILENR
|
||||
AND C.POSNR = CUD.POS_ALT
|
||||
|
||||
LEFT JOIN VAR_TEIL_BEDINGUNG_2 B
|
||||
ON C.UEBTEILENR = B.UEBTEILENR
|
||||
AND C.POSNR = B.POSNR
|
||||
|
||||
LEFT JOIN STKL_REGEL R
|
||||
ON C.UEBTEILENR = R.UEBTEILENR
|
||||
AND C.POSNR = R.POSNR
|
||||
|
||||
WHERE C.UEBTEILENR = '000000372'
|
||||
AND CUD.UEBTEILENR IS NULL</sql><sql name="get_update_data">DROP TABLE IF EXISTS TMP_UPDATE_DATA;
|
||||
CREATE TABLE TMP_UPDATE_DATA (ROWID INTEGER NOT NULL,
|
||||
POS_ALT INTEGER NOT NULL,
|
||||
POS_NEU INTEGER NOT NULL,
|
||||
ABW_ANZAHL INTEGER,
|
||||
MENGE REAL,
|
||||
LAENGE REAL,
|
||||
ZUSCHNITT REAL,
|
||||
DISPO_AKTIV_JN TEXT,
|
||||
RUESTCODE TEXT,
|
||||
BED_ROWID INTEGER,
|
||||
REG_ROWID INTEGER,
|
||||
UEB_LEISTEN_NR INTEGER,
|
||||
LFD_NR INTEGER);
|
||||
|
||||
WITH ALL_POSNRS_TO_UPDATE AS (
|
||||
SELECT * FROM TMP_POSNRS_CASE_11
|
||||
UNION
|
||||
SELECT * FROM TMP_POSNRS_CASE_12
|
||||
UNION
|
||||
SELECT * FROM TMP_POSNRS_CASE_132
|
||||
)
|
||||
INSERT INTO TMP_UPDATE_DATA (ROWID, POS_ALT, POS_NEU, ABW_ANZAHL, MENGE, LAENGE, ZUSCHNITT, DISPO_AKTIV_JN, RUESTCODE, BED_ROWID, REG_ROWID, UEB_LEISTEN_NR, LFD_NR)
|
||||
|
||||
SELECT C.ROWID,
|
||||
P.POS_ALT,
|
||||
RD.Positionsnummer AS POS_NEU,
|
||||
|
||||
-- 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,
|
||||
|
||||
C.ZUSCHNITT,
|
||||
C.DISPO_AKTIV_JN,
|
||||
C.RUESTCODE,
|
||||
B.ROWID,
|
||||
R.ROWID,
|
||||
B.UEB_LEISTEN_NR,
|
||||
R.LFD_NR
|
||||
|
||||
FROM ALL_POSNRS_TO_UPDATE P
|
||||
INNER JOIN COMP C
|
||||
ON P.UEBTEILENR = C.UEBTEILENR
|
||||
AND P.POS_ALT = C.POSNR
|
||||
|
||||
INNER JOIN RD_COMP RD
|
||||
ON P.UEBTEILENR = RD.ParentCode
|
||||
AND P.POS_NEU = RD.Positionsnummer
|
||||
|
||||
LEFT JOIN VAR_TEIL_BEDINGUNG_2 B
|
||||
ON P.UEBTEILENR = B.UEBTEILENR
|
||||
AND P.POS_ALT = B.POSNR
|
||||
|
||||
LEFT JOIN STKL_REGEL R
|
||||
ON P.UEBTEILENR = R.UEBTEILENR
|
||||
AND P.POS_ALT = R.POSNR;
|
||||
|
||||
SELECT * FROM TMP_UPDATE_DATA</sql><sql name="TOS-Workflowmeldung">SELECT C.POSNR, C.KOMPTEILENR
|
||||
|
||||
|
||||
|
||||
FROM COMP C
|
||||
|
||||
LEFT JOIN VAR_TEIL_BEDINGUNG_2 B
|
||||
|
||||
ON C.UEBTEILENR = B.UEBTEILENR
|
||||
|
||||
AND C.POSNR = B.POSNR
|
||||
|
||||
|
||||
|
||||
LEFT JOIN STKL_REGEL R
|
||||
|
||||
ON C.UEBTEILENR = R.UEBTEILENR
|
||||
|
||||
AND C.POSNR = R.POSNR
|
||||
|
||||
|
||||
|
||||
WHERE C.UEBTEILENR = '000000382'
|
||||
|
||||
AND (
|
||||
|
||||
B.UEB_LEISTEN_NR IS NOT NULL
|
||||
|
||||
OR R.LFD_NR IS NOT NULL
|
||||
|
||||
)
|
||||
|
||||
|
||||
|
||||
ORDER BY C.POSNR</sql><sql name="AV-Workflowmeldungen*">--längenabhängige Teilenummern herausfinden, die in beiden Stücklisten vorliegen
|
||||
|
||||
WITH laengenabh_codes_in_both_boms AS (
|
||||
|
||||
SELECT DISTINCT ChildCode
|
||||
|
||||
FROM RD_COMP
|
||||
|
||||
WHERE ParentCode = '000000382'
|
||||
|
||||
AND ChildCode in (
|
||||
|
||||
SELECT DISTINCT KOMPTEILENR
|
||||
|
||||
FROM COMP
|
||||
|
||||
WHERE UEBTEILENR = '000000382'
|
||||
|
||||
AND COALESCE(LAENGE, 0) != 0
|
||||
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
|
||||
|
||||
SELECT C.POSNR, C.KOMPTEILENR
|
||||
|
||||
FROM COMP C
|
||||
|
||||
|
||||
|
||||
LEFT JOIN RD_COMP RD
|
||||
|
||||
ON C.UEBTEILENR = RD.ParentCode
|
||||
|
||||
AND C.KOMPTEILENR = RD.ChildCode
|
||||
|
||||
AND COALESCE(C.LAENGE, 0) = RD.Uebergabelaenge
|
||||
|
||||
|
||||
|
||||
INNER JOIN PARTS P
|
||||
|
||||
ON C.KOMPTEILENR = P.TEILENR
|
||||
|
||||
|
||||
Reference in New Issue
Block a user