Compare commits
497
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03cb74370b | ||
|
|
d05524222b | ||
|
|
c02b1b57c7 | ||
|
|
efff024196 | ||
|
|
021671a45f | ||
|
|
744cf55a7e | ||
|
|
f2c12a9f03 | ||
|
|
5cf4b0f9ea | ||
|
|
370bc9fc5f | ||
|
|
a1d43b9c5a | ||
|
|
90fc66277e | ||
|
|
cd5d353158 | ||
|
|
590146be9e | ||
|
|
8e2c688641 | ||
|
|
f705a851c2 | ||
|
|
d33ff9779c | ||
|
|
876cd5a8fa | ||
|
|
5a64c85fb6 | ||
|
|
31153e90b4 | ||
|
|
949bea723d | ||
|
|
0365daa57b | ||
|
|
3996e02dd9 | ||
|
|
bd488211bd | ||
|
|
ca34d4a4f3 | ||
|
|
a93a8469c4 | ||
|
|
cec481741b | ||
|
|
39bacd2927 | ||
|
|
39972c1418 | ||
|
|
45cfc459aa | ||
|
|
21356f00d5 | ||
|
|
4529fe5472 | ||
|
|
08aaff1e2d | ||
|
|
0884a39f3d | ||
|
|
41b0f56b5b | ||
|
|
b034b8c7f8 | ||
|
|
a4cae4f321 | ||
|
|
ab67f9d7b5 | ||
|
|
a22688c87e | ||
|
|
cfc52a9cf3 | ||
|
|
33e09bbe89 | ||
|
|
a339f8a517 | ||
|
|
abeb91b59f | ||
|
|
350a9e6742 | ||
|
|
c373a98953 | ||
|
|
3863999f2a | ||
|
|
54410628b4 | ||
|
|
2c3ce32282 | ||
|
|
dd03cdd77f | ||
|
|
bc8d4f74c1 | ||
|
|
6f869bdb1a | ||
|
|
6adc0769c7 | ||
|
|
2f9930a240 | ||
|
|
3036a7773c | ||
|
|
e668c2d882 | ||
|
|
bb4768c491 | ||
|
|
9140e4f3fc | ||
|
|
6b205c7a86 | ||
|
|
0aa4bb5055 | ||
|
|
ee3b1f47a7 | ||
|
|
40da2ecaf3 | ||
|
|
5d0ba0ef9a | ||
|
|
786a4306dd | ||
|
|
677ffc7368 | ||
|
|
93eb08c610 | ||
|
|
6365e4b24c | ||
|
|
e138fbeca8 | ||
|
|
d29a6a1835 | ||
|
|
dc91660488 | ||
|
|
e9548d9f38 | ||
|
|
d5944bf717 | ||
|
|
2355442ac0 | ||
|
|
789555078d | ||
|
|
34f190f469 | ||
|
|
8646c4ffdd | ||
|
|
419d3b78c6 | ||
|
|
e8df7d193e | ||
|
|
aa08307500 | ||
|
|
c41524c1fd | ||
|
|
8dec0be375 | ||
|
|
9471abce94 | ||
|
|
2aeed762fd | ||
|
|
512b63b88b | ||
|
|
92d7eac3a1 | ||
|
|
548b8a8ef0 | ||
|
|
fb8aabe34c | ||
|
|
0c1f143b12 | ||
|
|
24ac2efc53 | ||
|
|
e7a139b025 | ||
|
|
cfe19b7a74 | ||
|
|
f725a6b72f | ||
|
|
a097159897 | ||
|
|
da33620ea9 | ||
|
|
0b44394b11 | ||
|
|
2949870acc | ||
|
|
35c289240d | ||
|
|
3b165bf292 | ||
|
|
f0c43a17d8 | ||
|
|
2776429639 | ||
|
|
5c9f8c2a2e | ||
|
|
3e6533829c | ||
|
|
054505d853 | ||
|
|
c793550af5 | ||
|
|
b18fccff9d | ||
|
|
398eb448cb | ||
|
|
6cdede138e | ||
|
|
3046aad94d | ||
|
|
ec2ce6ec62 | ||
|
|
3c61e7eaf3 | ||
|
|
f942600dd8 | ||
|
|
efacbf64cb | ||
|
|
e38445f184 | ||
|
|
00803b2e73 | ||
|
|
a52f65919f | ||
|
|
3d4d88e986 | ||
|
|
5221cea53b | ||
|
|
7636ee41af | ||
|
|
830fd2464c | ||
|
|
d922fd2b9c | ||
|
|
d22078b26e | ||
|
|
195af95995 | ||
|
|
d95d0a8930 | ||
|
|
6fca40963d | ||
|
|
ace251c1dd | ||
|
|
db2e92c058 | ||
|
|
28b62f5071 | ||
|
|
168c128b1d | ||
|
|
90af012e56 | ||
|
|
d6c36b4171 | ||
|
|
9575aad10c | ||
|
|
7b98cccbe9 | ||
|
|
fe2268c897 | ||
|
|
f2c1988d75 | ||
|
|
5b9f35eddc | ||
|
|
c8ed5b2cc7 | ||
|
|
da517b37b6 | ||
|
|
0992c05539 | ||
|
|
ed8a8a2f8b | ||
|
|
5516ef45f6 | ||
|
|
6beb965c85 | ||
|
|
479a88e131 | ||
|
|
e4a1a992be | ||
|
|
97a5d7f9f1 | ||
|
|
776b9d0912 | ||
|
|
df35ed3032 | ||
|
|
59b08ed098 | ||
|
|
936004bc2d | ||
|
|
365658d727 | ||
|
|
d603e57c62 | ||
|
|
58ec94eaeb | ||
|
|
2f4184f60f | ||
|
|
b0b608f12e | ||
|
|
97be776b10 | ||
|
|
ddb876bc1c | ||
|
|
6fee5c048a | ||
|
|
80f22b7cd0 | ||
|
|
e1123577f4 | ||
|
|
5f33f82b8b | ||
|
|
5285ded46f | ||
|
|
69df93b773 | ||
|
|
1e3389e21a | ||
|
|
192b1fdcf4 | ||
|
|
6008a1b2d9 | ||
|
|
62fc734f82 | ||
|
|
7fb5466eb4 | ||
|
|
aafac9d43b | ||
|
|
585f87e1f9 | ||
|
|
eb1d05aa10 | ||
|
|
db1de2653c | ||
|
|
8bdc33b4f0 | ||
|
|
1c1766bbd4 | ||
|
|
96205c14db | ||
|
|
7ec4562064 | ||
|
|
1eb1e9f02f | ||
|
|
0b35b3494b | ||
|
|
e021fc7570 | ||
|
|
2a13a62291 | ||
|
|
60c1459902 | ||
|
|
97f8baba06 | ||
|
|
3ffa80e2bc | ||
|
|
ab1a21dffc | ||
|
|
bccd8c6fc3 | ||
|
|
2ea298854d | ||
|
|
b7e4f29813 | ||
|
|
7780646601 | ||
|
|
0f58f07757 | ||
|
|
ef263e064a | ||
|
|
7127b4d050 | ||
|
|
42744fa30f | ||
|
|
be3f6e6288 | ||
|
|
9291adfdff | ||
|
|
c9426eff3b | ||
|
|
cd350c212e | ||
|
|
1f1ac1a0cc | ||
|
|
2dd5b7ad64 | ||
|
|
62ac8038dc | ||
|
|
5f94f0487b | ||
|
|
64880355e8 | ||
|
|
1dc8489239 | ||
|
|
5831294d33 | ||
|
|
073bbdc0f8 | ||
|
|
ef75383885 | ||
|
|
bfb25a4051 | ||
|
|
f55febd545 | ||
|
|
d27b78cec1 | ||
|
|
1bb145e3be | ||
|
|
59b9a42009 | ||
|
|
a576fa2d23 | ||
|
|
14fe9bd44f | ||
|
|
cce59b8ed6 | ||
|
|
8904e1c7fe | ||
|
|
a0f763af63 | ||
|
|
667c9d4677 | ||
|
|
1622f2be6a | ||
|
|
28914d6586 | ||
|
|
cfc3957fec | ||
|
|
aee2eb848d | ||
|
|
42f17db542 | ||
|
|
6315fbd0f7 | ||
|
|
79e5a3c12d | ||
|
|
e9c1dd8b9d | ||
|
|
b1ff6989ec | ||
|
|
1d97a19982 | ||
|
|
c3519124b7 | ||
|
|
05c769bab2 | ||
|
|
c1539a47f9 | ||
|
|
f54f66cd32 | ||
|
|
c8a33cdf43 | ||
|
|
b4c03160ca | ||
|
|
72cd36a573 | ||
|
|
4d3a4b7c04 | ||
|
|
b9dfeb2d3e | ||
|
|
e42ef2d3c9 | ||
|
|
bb69a28ec2 | ||
|
|
e8c2261b7e | ||
|
|
7665385cbf | ||
|
|
346dee23e4 | ||
|
|
109f36e7f5 | ||
|
|
0e9c5e6e1a | ||
|
|
145a08e511 | ||
|
|
2b8046b416 | ||
|
|
e27b5da3c8 | ||
|
|
613e0b9a5c | ||
|
|
0564e3c424 | ||
|
|
32c73ab7b1 | ||
|
|
420efb25ad | ||
|
|
150998e120 | ||
|
|
31f414b4d8 | ||
|
|
9c6860ecc6 | ||
|
|
6b32b6baa4 | ||
|
|
3527be9c29 | ||
|
|
0239aee89a | ||
|
|
f21f4a0385 | ||
|
|
fa2b5e0d7f | ||
|
|
1ac7720765 | ||
|
|
4182c30b72 | ||
|
|
e4aad8a456 | ||
|
|
d78638e6e5 | ||
|
|
1726523eaf | ||
|
|
1aa564960d | ||
|
|
cd2765bab5 | ||
|
|
390ee81675 | ||
|
|
6dbbfd87f8 | ||
|
|
118f86b2a0 | ||
|
|
27d77624da | ||
|
|
20a67e9b96 | ||
|
|
233748a6c5 | ||
|
|
67491a7f68 | ||
|
|
c7129d5791 | ||
|
|
4abee677f6 | ||
|
|
21600dc2d6 | ||
|
|
f0b84c9943 | ||
|
|
59418adc02 | ||
|
|
de52907eec | ||
|
|
bfb1a04ea0 | ||
|
|
b206c3990b | ||
|
|
54a37c5bf7 | ||
|
|
fa98ceac29 | ||
|
|
1bab230f10 | ||
|
|
5b55d0984a | ||
|
|
2b3a8f1c69 | ||
|
|
696800aafd | ||
|
|
e7997f84eb | ||
|
|
8d3a7b99b4 | ||
|
|
61a8afdde7 | ||
|
|
1f3e79d57c | ||
|
|
f4691ec67e | ||
|
|
32bef9a4e9 | ||
|
|
30704f936a | ||
|
|
9167c84b4c | ||
|
|
f5f5d43ab5 | ||
|
|
9b2ad3845d | ||
|
|
e2513d6245 | ||
|
|
126fce9687 | ||
|
|
c1364a458b | ||
|
|
ecc4741f56 | ||
|
|
f33e55f201 | ||
|
|
ce1b9ece09 | ||
|
|
9d28d2f102 | ||
|
|
aaa7e9f4c3 | ||
|
|
26681e5c62 | ||
|
|
33c3dc6022 | ||
|
|
a6b0051f1b | ||
|
|
d0d1edb3ae | ||
|
|
6e96f6fb44 | ||
|
|
c6b28e7f4e | ||
|
|
3ce4d81a4c | ||
|
|
d5c572c25b | ||
|
|
bcded403be | ||
|
|
aeb25f3b7e | ||
|
|
421851cc4c | ||
|
|
66eb392151 | ||
|
|
beaccd93e0 | ||
|
|
111cda95f3 | ||
|
|
84ed41f325 | ||
|
|
e7cf172b9f | ||
|
|
e52cb98163 | ||
|
|
d85e81e8b9 | ||
|
|
1e5fe23159 | ||
|
|
2f78ddb8ad | ||
|
|
f19762d686 | ||
|
|
eba2f8e503 | ||
|
|
0a0bef9e82 | ||
|
|
29f415a91f | ||
|
|
a619add4e3 | ||
|
|
34a8d42fc3 | ||
|
|
cfd346d5df | ||
|
|
3d694ed7eb | ||
|
|
5021f414d1 | ||
|
|
51dbf20118 | ||
|
|
a50bd205cd | ||
|
|
da7b4dfb48 | ||
|
|
eee7be0c1e | ||
|
|
d55a201681 | ||
|
|
ba0eab9e96 | ||
|
|
b63804df88 | ||
|
|
647a25a78f | ||
|
|
6b0dfb3426 | ||
|
|
c8bb051ae6 | ||
|
|
b326510b04 | ||
|
|
293574187f | ||
|
|
b74f84e8ad | ||
|
|
19a272db96 | ||
|
|
67843c2f2e | ||
|
|
a01e966d6d | ||
|
|
9140b97a71 | ||
|
|
405f03ee7a | ||
|
|
b88ad24c3d | ||
|
|
bb630da5ed | ||
|
|
39d944351c | ||
|
|
2b272ef789 | ||
|
|
3e54a58b9b | ||
|
|
60176d19f4 | ||
|
|
9c716477aa | ||
|
|
be2fffad23 | ||
|
|
34230e5520 | ||
|
|
1949bf9b97 | ||
|
|
7ab60b1048 | ||
|
|
0e6c31b8ce | ||
|
|
a2cfb693e0 | ||
|
|
10df3ebb35 | ||
|
|
ab91c138d5 | ||
|
|
cc1d1e5746 | ||
|
|
8c75d0070d | ||
|
|
4df04d0504 | ||
|
|
38bc594ca4 | ||
|
|
fb9c9d91ee | ||
|
|
ee69346eb4 | ||
|
|
e6c47019d5 | ||
|
|
b894bbf5e6 | ||
|
|
0f8fed605c | ||
|
|
479b177e90 | ||
|
|
08210b8e52 | ||
|
|
b5dfe17b73 | ||
|
|
49b5ccf308 | ||
|
|
434c4d0a11 | ||
|
|
dc7ed0728a | ||
|
|
b1dbb15ad7 | ||
|
|
e76170b6dd | ||
|
|
8225d4e92d | ||
|
|
a5addf8d33 | ||
|
|
0542a669bd | ||
|
|
49b7a3fe34 | ||
|
|
ead2c092da | ||
|
|
6e818c3480 | ||
|
|
a02490ea36 | ||
|
|
9eda12981a | ||
|
|
4939a1a9ad | ||
|
|
989f599b54 | ||
|
|
67c642bc2f | ||
|
|
cba0f747cd | ||
|
|
c3008d7f7a | ||
|
|
7304fb50c6 | ||
|
|
ec1bba7c46 | ||
|
|
b66d453881 | ||
|
|
b7b80296e9 | ||
|
|
2f582f0548 | ||
|
|
fad28c2442 | ||
|
|
7f1ef4059d | ||
|
|
139ec3c1db | ||
|
|
798beae597 | ||
|
|
3edf5a80aa | ||
|
|
fc93b28af0 | ||
|
|
cd82b70139 | ||
|
|
fba9edc931 | ||
|
|
8e43edcbaf | ||
|
|
8984b2edd5 | ||
|
|
32ec2c1deb | ||
|
|
25029dff5d | ||
|
|
875b181aff | ||
|
|
0f3d9b87fa | ||
|
|
d7929dd855 | ||
|
|
8a495346d6 | ||
|
|
fef4997132 | ||
|
|
b4eaf695c7 | ||
|
|
372490ed5a | ||
|
|
e37b6234e8 | ||
|
|
71b42786f7 | ||
|
|
009eabd09c | ||
|
|
49a4393980 | ||
|
|
c0e20a15fe | ||
|
|
061317ed4c | ||
|
|
a5dce776de | ||
|
|
90ae78262a | ||
|
|
0809f07d46 | ||
|
|
199ef08e35 | ||
|
|
4930a2ba42 | ||
|
|
0ceabe465b | ||
|
|
bd7b70e465 | ||
|
|
55a24b99f0 | ||
|
|
122df1a3f5 | ||
|
|
391dd63ebf | ||
|
|
9d44197246 | ||
|
|
582fb9b52d | ||
|
|
eed7ba4d7b | ||
|
|
5642193610 | ||
|
|
1a1e120cf6 | ||
|
|
cf66b85993 | ||
|
|
8677e9a8a5 | ||
|
|
906f591979 | ||
|
|
97cc0ac422 | ||
|
|
bdc7100f74 | ||
|
|
29a51ed66d | ||
|
|
c9a258a350 | ||
|
|
096dd96db0 | ||
|
|
ddd5971c06 | ||
|
|
b7638f5e9e | ||
|
|
b197152f12 | ||
|
|
297becf7a0 | ||
|
|
b6f991b291 | ||
|
|
5469c834a6 | ||
|
|
a3ea02a136 | ||
|
|
195246af89 | ||
|
|
2953ed0b2d | ||
|
|
ccb2fa75ac | ||
|
|
a33e6a0dd4 | ||
|
|
435e3a79e8 | ||
|
|
21054f0c45 | ||
|
|
605b6ca64c | ||
|
|
bc9616e7de | ||
|
|
491169b9d7 | ||
|
|
0e0ce254a9 | ||
|
|
7b25fbf28b | ||
|
|
2a5100e76d | ||
|
|
e98695ea06 | ||
|
|
1c2421fb7e | ||
|
|
76aeb101bd | ||
|
|
60b655a195 | ||
|
|
369d660e24 | ||
|
|
47703dc205 | ||
|
|
25bbf3d44f | ||
|
|
803b10f981 | ||
|
|
b992e4a5ea | ||
|
|
9c5597eb23 | ||
|
|
2ec61d5086 | ||
|
|
8bae958e24 | ||
|
|
e7f0c09b36 | ||
|
|
ae2ed306b9 | ||
|
|
2b122a7cc4 | ||
|
|
787e76c21c | ||
|
|
7b1f2e7468 | ||
|
|
3f94c51bc8 | ||
|
|
64d51c01b2 | ||
|
|
955dbd2f05 | ||
|
|
318a326fdb | ||
|
|
86223dc8a6 | ||
|
|
4bb62830a4 | ||
|
|
4ce731bfcb | ||
|
|
f225dac5fe | ||
|
|
5e1f2a8711 | ||
|
|
8746397d83 | ||
|
|
e8c27cb51b | ||
|
|
d2a80ea8fd | ||
|
|
c85c72863c | ||
|
|
77afc0fe7b | ||
|
|
7a53908b80 | ||
|
|
ca4495169f | ||
|
|
d1344bbfb4 |
Executable
+19
@@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
## Description: Run Claude Code
|
||||||
|
## Usage: claude [args]
|
||||||
|
## Example: ddev claude
|
||||||
|
## Example: ddev claude --help
|
||||||
|
## Example: ddev claude doctor
|
||||||
|
|
||||||
|
# Ensure ~/.local/bin is in PATH
|
||||||
|
export PATH="$HOME/.local/bin:$PATH"
|
||||||
|
|
||||||
|
# Check if claude is installed
|
||||||
|
if [ ! -f "$HOME/.local/bin/claude" ]; then
|
||||||
|
echo "Claude Code is not installed. Installing now..."
|
||||||
|
curl -fsSL https://claude.ai/install.sh | bash
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run claude with all arguments
|
||||||
|
exec "$HOME/.local/bin/claude" "$@"
|
||||||
+31
-44
@@ -1,24 +1,21 @@
|
|||||||
name: myep
|
|
||||||
type: php
|
type: php
|
||||||
docroot: public
|
docroot: public
|
||||||
php_version: "8.4"
|
php_version: "8.3"
|
||||||
webserver_type: apache-fpm
|
webserver_type: apache-fpm
|
||||||
xdebug_enabled: false
|
xdebug_enabled: false
|
||||||
additional_hostnames:
|
additional_hostnames:
|
||||||
- mysnz
|
- mysnz
|
||||||
- mysbw
|
- mysbw
|
||||||
- myser
|
- myser
|
||||||
additional_fqdns: [ ]
|
additional_fqdns: []
|
||||||
database:
|
database:
|
||||||
type: mariadb
|
type: mysql
|
||||||
version: "11.8"
|
version: "8.4"
|
||||||
webimage_extra_packages: [ ripgrep, bubblewrap, socat ]
|
|
||||||
use_dns_when_possible: true
|
use_dns_when_possible: true
|
||||||
timezone: Europe/Berlin
|
|
||||||
composer_version: "2"
|
composer_version: "2"
|
||||||
web_environment: [ ]
|
web_environment: []
|
||||||
nodejs_version: "22"
|
|
||||||
corepack_enable: false
|
corepack_enable: false
|
||||||
|
nodejs_version: "22"
|
||||||
|
|
||||||
# Key features of DDEV's config.yaml:
|
# Key features of DDEV's config.yaml:
|
||||||
|
|
||||||
@@ -27,16 +24,16 @@ corepack_enable: false
|
|||||||
# If the name is omitted, the project will take the name of the enclosing directory,
|
# If the name is omitted, the project will take the name of the enclosing directory,
|
||||||
# which is useful if you want to have a copy of the project side by side with this one.
|
# which is useful if you want to have a copy of the project side by side with this one.
|
||||||
|
|
||||||
# type: <projecttype> # asterios, backdrop, cakephp, codeigniter, craftcms, drupal, drupal6, drupal7, drupal8, drupal9, drupal10, drupal11, drupal12, generic, joomla, laravel, magento, magento2, maho, modx, php, shopware6, silverstripe, symfony, typo3, wordpress, wp-bedrock
|
# type: <projecttype> # backdrop, cakephp, craftcms, drupal, drupal6, drupal7, drupal8, drupal9, drupal10, drupal11, generic, laravel, magento, magento2, php, shopware6, silverstripe, symfony, typo3, wordpress
|
||||||
# See https://docs.ddev.com/en/stable/users/quickstart/ for more
|
# See https://docs.ddev.com/en/stable/users/quickstart/ for more
|
||||||
# information on the different project types
|
# information on the different project types
|
||||||
|
|
||||||
# docroot: <relative_path> # Relative path to the directory containing index.php.
|
# docroot: <relative_path> # Relative path to the directory containing index.php.
|
||||||
|
|
||||||
# php_version: "8.4" # PHP version to use, "5.6" through "8.5"
|
# php_version: "8.3" # PHP version to use, "5.6" through "8.5"
|
||||||
|
|
||||||
# You can explicitly specify the webimage but this
|
# You can explicitly specify the webimage but this
|
||||||
# is not recommended, as the images are often closely tied to DDEV's behavior,
|
# is not recommended, as the images are often closely tied to DDEV's' behavior,
|
||||||
# so this can break upgrades.
|
# so this can break upgrades.
|
||||||
|
|
||||||
# webimage: <docker_image>
|
# webimage: <docker_image>
|
||||||
@@ -46,18 +43,10 @@ corepack_enable: false
|
|||||||
# database:
|
# database:
|
||||||
# type: <dbtype> # mysql, mariadb, postgres
|
# type: <dbtype> # mysql, mariadb, postgres
|
||||||
# version: <version> # database version, like "10.11" or "8.0"
|
# version: <version> # database version, like "10.11" or "8.0"
|
||||||
# MariaDB versions can be 5.5-10.8, 10.11, 11.4, 11.8, 12.3
|
# MariaDB versions can be 5.5-10.8, 10.11, 11.4, 11.8
|
||||||
# MySQL versions can be 5.5-8.0, 8.4, 9.7
|
# MySQL versions can be 5.5-8.0, 8.4
|
||||||
# PostgreSQL versions can be 9-18
|
# PostgreSQL versions can be 9-18
|
||||||
|
|
||||||
# You can explicitly specify the dbimage but this
|
|
||||||
# is not recommended, as the images are often closely tied to DDEV's behavior,
|
|
||||||
# so this can break upgrades.
|
|
||||||
|
|
||||||
# dbimage: <docker_image>
|
|
||||||
# It’s unusual to change this option, and we don’t recommend it without Docker experience and a good reason.
|
|
||||||
# Typically, this means additions to the existing db image using a .ddev/db-build/Dockerfile.*
|
|
||||||
|
|
||||||
# router_http_port: <port> # Port to be used for http (defaults to global configuration, usually 80)
|
# router_http_port: <port> # Port to be used for http (defaults to global configuration, usually 80)
|
||||||
# router_https_port: <port> # Port for https (defaults to global configuration, usually 443)
|
# router_https_port: <port> # Port for https (defaults to global configuration, usually 443)
|
||||||
|
|
||||||
@@ -77,7 +66,8 @@ corepack_enable: false
|
|||||||
# bind_all_ports is used (normally with router disabled)
|
# bind_all_ports is used (normally with router disabled)
|
||||||
|
|
||||||
# xhprof_mode: [prepend|xhgui|global]
|
# xhprof_mode: [prepend|xhgui|global]
|
||||||
# Default is "xhgui"
|
# Set to "xhgui" to enable XHGui features
|
||||||
|
# "xhgui" will become default in a future major release
|
||||||
|
|
||||||
# webserver_type: nginx-fpm, apache-fpm, generic
|
# webserver_type: nginx-fpm, apache-fpm, generic
|
||||||
|
|
||||||
@@ -95,24 +85,22 @@ corepack_enable: false
|
|||||||
# commands are executed.
|
# commands are executed.
|
||||||
|
|
||||||
# composer_version: "2"
|
# composer_version: "2"
|
||||||
# You can set it to "" or "2" (default) for Composer v2
|
# You can set it to "" or "2" (default) for Composer v2 or "1" for Composer v1
|
||||||
# to use the latest major version available at the time your container is built.
|
# to use the latest major version available at the time your container is built.
|
||||||
# It is also possible to use any other Composer version channel. This includes:
|
# It is also possible to use each other Composer version channel. This includes:
|
||||||
# - 2.2 (latest Composer LTS version)
|
# - 2.2 (latest Composer LTS version)
|
||||||
# - stable
|
# - stable
|
||||||
# - preview
|
# - preview
|
||||||
# - snapshot
|
# - snapshot
|
||||||
# Alternatively, an explicit Composer version may be specified, for example "2.2.18".
|
# Alternatively, an explicit Composer version may be specified, for example "2.2.18".
|
||||||
# To reinstall Composer after the image was built, run "ddev utility rebuild".
|
# To reinstall Composer after the image was built, run "ddev debug rebuild".
|
||||||
|
|
||||||
# nodejs_root: <relative_path>
|
# nodejs_version: "22"
|
||||||
# Relative path to the directory containing the Node.js version file from the
|
|
||||||
# project root. Only used with "nodejs_version: auto" or "nodejs_version: engine".
|
|
||||||
|
|
||||||
# nodejs_version: "24"
|
|
||||||
# change from the default system Node.js version to any other version.
|
# change from the default system Node.js version to any other version.
|
||||||
# See https://docs.ddev.com/en/stable/users/configuration/config/#nodejs_version for more information
|
# See https://docs.ddev.com/en/stable/users/configuration/config/#nodejs_version for more information
|
||||||
# and https://www.npmjs.com/package/n#specifying-nodejs-versions for the full documentation.
|
# and https://www.npmjs.com/package/n#specifying-nodejs-versions for the full documentation,
|
||||||
|
# Note that using of 'ddev nvm' is discouraged because "nodejs_version" is much easier to use,
|
||||||
|
# can specify any version, and is more robust than using 'nvm'.
|
||||||
|
|
||||||
# corepack_enable: false
|
# corepack_enable: false
|
||||||
# Change to 'true' to 'corepack enable' and gain access to latest versions of yarn/pnpm
|
# Change to 'true' to 'corepack enable' and gain access to latest versions of yarn/pnpm
|
||||||
@@ -172,7 +160,9 @@ corepack_enable: false
|
|||||||
# - "global": uses the value from the global config.
|
# - "global": uses the value from the global config.
|
||||||
# - "none": disables performance optimization for this project.
|
# - "none": disables performance optimization for this project.
|
||||||
# - "mutagen": enables Mutagen for this project.
|
# - "mutagen": enables Mutagen for this project.
|
||||||
|
# - "nfs": enables NFS for this project.
|
||||||
#
|
#
|
||||||
|
# See https://docs.ddev.com/en/stable/users/install/performance/#nfs
|
||||||
# See https://docs.ddev.com/en/stable/users/install/performance/#mutagen
|
# See https://docs.ddev.com/en/stable/users/install/performance/#mutagen
|
||||||
|
|
||||||
# fail_on_hook_fail: False
|
# fail_on_hook_fail: False
|
||||||
@@ -202,10 +192,10 @@ corepack_enable: false
|
|||||||
# The mailpit port is not normally bound on the host at all, instead being routed
|
# The mailpit port is not normally bound on the host at all, instead being routed
|
||||||
# through ddev-router, but it can be bound directly to localhost if specified here.
|
# through ddev-router, but it can be bound directly to localhost if specified here.
|
||||||
|
|
||||||
# webimage_extra_packages: ['php${DDEV_PHP_VERSION}-tidy', 'php${DDEV_PHP_VERSION}-yac']
|
# webimage_extra_packages: [php7.4-tidy, php-bcmath]
|
||||||
# Extra Debian packages that are needed in the webimage can be added here
|
# Extra Debian packages that are needed in the webimage can be added here
|
||||||
|
|
||||||
# dbimage_extra_packages: [netcat, telnet, sudo]
|
# dbimage_extra_packages: [telnet,netcat]
|
||||||
# Extra Debian packages that are needed in the dbimage can be added here
|
# Extra Debian packages that are needed in the dbimage can be added here
|
||||||
|
|
||||||
# use_dns_when_possible: true
|
# use_dns_when_possible: true
|
||||||
@@ -217,15 +207,12 @@ corepack_enable: false
|
|||||||
# project_tld: ddev.site
|
# project_tld: ddev.site
|
||||||
# The top-level domain used for project URLs
|
# The top-level domain used for project URLs
|
||||||
# The default "ddev.site" allows DNS lookup via a wildcard
|
# The default "ddev.site" allows DNS lookup via a wildcard
|
||||||
|
# If you prefer you can change this to "ddev.local" to preserve
|
||||||
|
# pre-v1.9 behavior.
|
||||||
|
|
||||||
# share_default_provider: ngrok
|
# ngrok_args: --basic-auth username:pass1234
|
||||||
# The default share provider to use for "ddev share"
|
# Provide extra flags to the "ngrok http" command, see
|
||||||
# Defaults to global configuration, usually "ngrok"
|
# https://ngrok.com/docs/agent/config/v3/#agent-configuration or run "ngrok http -h"
|
||||||
# Can be "ngrok" or "cloudflared" or the name of a custom provider from .ddev/share-providers/
|
|
||||||
|
|
||||||
# share_provider_args: --basic-auth username:pass1234
|
|
||||||
# Provide extra flags to the share provider script
|
|
||||||
# See https://docs.ddev.com/en/stable/users/configuration/config/#share_provider_args
|
|
||||||
|
|
||||||
# disable_settings_management: false
|
# disable_settings_management: false
|
||||||
# If true, DDEV will not create CMS-specific settings files like
|
# If true, DDEV will not create CMS-specific settings files like
|
||||||
@@ -287,7 +274,7 @@ corepack_enable: false
|
|||||||
# override_config: false
|
# override_config: false
|
||||||
# By default, config.*.yaml files are *merged* into the configuration
|
# By default, config.*.yaml files are *merged* into the configuration
|
||||||
# But this means that some things can't be overridden
|
# But this means that some things can't be overridden
|
||||||
# For example, if you have 'use_dns_when_possible: true' you can't override it with a merge
|
# For example, if you have 'use_dns_when_possible: true'' you can't override it with a merge
|
||||||
# and you can't erase existing hooks or all environment variables.
|
# and you can't erase existing hooks or all environment variables.
|
||||||
# However, with "override_config: true" in a particular config.*.yaml file,
|
# However, with "override_config: true" in a particular config.*.yaml file,
|
||||||
# 'use_dns_when_possible: false' can override the existing values, and
|
# 'use_dns_when_possible: false' can override the existing values, and
|
||||||
@@ -297,7 +284,7 @@ corepack_enable: false
|
|||||||
# web_environment: []
|
# web_environment: []
|
||||||
# or
|
# or
|
||||||
# additional_hostnames: []
|
# additional_hostnames: []
|
||||||
# can have their intended effect. 'override_config' affects only behavior of the
|
# can have their intended affect. 'override_config' affects only behavior of the
|
||||||
# config.*.yaml file it exists in.
|
# config.*.yaml file it exists in.
|
||||||
|
|
||||||
# Many DDEV commands can be extended to run tasks before or after the
|
# Many DDEV commands can be extended to run tasks before or after the
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ APP_SECRET=642ead390b8a38f0a017ec84d5362129
|
|||||||
#
|
#
|
||||||
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
|
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
|
||||||
# DATABASE_URL="mysql://app:[email protected]:3306/app?serverVersion=8.0.32&charset=utf8mb4"
|
# DATABASE_URL="mysql://app:[email protected]:3306/app?serverVersion=8.0.32&charset=utf8mb4"
|
||||||
DATABASE_URL="mysql://db:db@db:3306/db?serverVersion=mariadb-11.8.9&charset=utf8mb4"
|
DATABASE_URL="mysql://db:db@db:3306/db?serverVersion=8.4.0&charset=utf8mb4"
|
||||||
# DATABASE_URL="postgresql://app:[email protected]:5432/app?serverVersion=16&charset=utf8"
|
# DATABASE_URL="postgresql://app:[email protected]:5432/app?serverVersion=16&charset=utf8"
|
||||||
###< doctrine/doctrine-bundle ###
|
###< doctrine/doctrine-bundle ###
|
||||||
|
|
||||||
@@ -40,34 +40,6 @@ MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0
|
|||||||
MAILER_DSN=null://null
|
MAILER_DSN=null://null
|
||||||
###< symfony/mailer ###
|
###< symfony/mailer ###
|
||||||
|
|
||||||
MAILJET_API_KEY=
|
|
||||||
MAILJET_API_SECRET=
|
|
||||||
MAILJET_API_BASE_URL=https://api.mailjet.com/v3/REST
|
|
||||||
MAILJET_DEFAULT_LIST_ID=
|
|
||||||
# Development default for HTTP Basic Auth password; override in production secrets.
|
|
||||||
MAILJET_WEBHOOK_BASIC_PASSWORD=secret
|
|
||||||
APP_NEWSLETTER_CONFIRMATION_TTL_HOURS=1
|
|
||||||
|
|
||||||
# Dedicated signing secret for accommodation booking customer access links (kept separate
|
|
||||||
# from APP_SECRET so it can be rotated independently). Unlike lazily-used API keys, UriSigner
|
|
||||||
# validates this is non-empty at construction time, so a real dev value is required here
|
|
||||||
# (override in .env.local/production secrets as usual).
|
|
||||||
ACCOMMODATION_OFFER_LINK_SECRET=abf8ec28cc2162d9121d35d6f8cbf7c29728cdacce0303e22e1bac854501728a
|
|
||||||
|
|
||||||
ACCOMMODATION_TERMS_AT=
|
|
||||||
ACCOMMODATION_TERMS_CH=
|
|
||||||
ACCOMMODATION_TERMS_IT=
|
|
||||||
|
|
||||||
BPN_CONNECT_BASE_URL=https://bpn-connect.ep-reisen.app
|
|
||||||
BPN_CONNECT_API_KEY=
|
|
||||||
|
|
||||||
# ClickUp task created when an accommodation booking is confirmed. Token/ids are secrets and
|
|
||||||
# stay empty here — set them in .env.local. Without them the handler skips with a warning.
|
|
||||||
CLICKUP_API_TOKEN=
|
|
||||||
CLICKUP_LIST_ID=
|
|
||||||
CLICKUP_TEMPLATE_ID=
|
|
||||||
CLICKUP_API_BASE_URL=https://api.clickup.com/api/v2
|
|
||||||
|
|
||||||
APP_BPN_USER=
|
APP_BPN_USER=
|
||||||
APP_BPN_PASSWORD=
|
APP_BPN_PASSWORD=
|
||||||
APP_BPN_IP=
|
APP_BPN_IP=
|
||||||
@@ -83,40 +55,26 @@ APP_TERMS_AND_CONDITIONS_URL="https://www.ep-reisen.de/skireisen/infos/allgemein
|
|||||||
APP_PRIVACY_POLICY_URL="https://www.ep-reisen.de/unternehmen/impressum-skireiseveranstalter/datenschutz-winterreisen/"
|
APP_PRIVACY_POLICY_URL="https://www.ep-reisen.de/unternehmen/impressum-skireiseveranstalter/datenschutz-winterreisen/"
|
||||||
APP_CMS_API_BASE_URL="https://www.ep-reisen.de/"
|
APP_CMS_API_BASE_URL="https://www.ep-reisen.de/"
|
||||||
APP_WEBSITE_BASE_URL="https://www.ep-reisen.de/"
|
APP_WEBSITE_BASE_URL="https://www.ep-reisen.de/"
|
||||||
APP_BASE_URL="https://my.ep-reisen.de"
|
|
||||||
APP_CUSTOMER_SERVICE_EMAIL="[email protected]"
|
APP_CUSTOMER_SERVICE_EMAIL="[email protected]"
|
||||||
APP_INSURANCE_REVOCATION_URL="https://www.hmrv.de/daten-aendern/vertrag-beenden/widerruf"
|
|
||||||
|
|
||||||
# Travel Data Service Configuration
|
# Travel Data Service Configuration
|
||||||
APP_TRAVEL_PREFER_REMOTE=false
|
APP_TRAVEL_PREFER_REMOTE=false
|
||||||
APP_TRAVEL_ENABLE_FALLBACK=true
|
APP_TRAVEL_ENABLE_FALLBACK=true
|
||||||
APP_TRAVEL_SNAPSHOT_RETENTION_BUFFER_DAYS=14
|
|
||||||
|
|
||||||
# Emails
|
|
||||||
APP_DEFAULT_EMAIL_FROM=[email protected]
|
|
||||||
APP_DEFAULT_EMAIL_TO=[email protected]
|
|
||||||
ACCOMMODATION_INQUIRY_EMAIL=[email protected]
|
|
||||||
|
|
||||||
# Global common defaults
|
|
||||||
APP_SEASON_WINTER_FROM=2026-10-01
|
|
||||||
APP_SEASON_SUMMER_FROM=2026-05-01
|
|
||||||
APP_RUNNING_COSTS_FACTOR_EUR=2.75
|
|
||||||
APP_RUNNING_COSTS_FACTOR_CHF=3.2
|
|
||||||
APP_UNDERSUBSCRIPTION_SURCHARGE_30_EUR=5.0
|
|
||||||
APP_UNDERSUBSCRIPTION_SURCHARGE_30_CHF=5.0
|
|
||||||
APP_UNDERSUBSCRIPTION_SURCHARGE_40_EUR=2.5
|
|
||||||
APP_UNDERSUBSCRIPTION_SURCHARGE_40_CHF=2.5
|
|
||||||
|
|
||||||
# Booking Configuration
|
# Booking Configuration
|
||||||
# Status for new bookings: 'F' (Fixed/Final), 'O' (Option - requires agency confirmation)
|
# Status for new bookings: 'F' (Fixed/Final), 'O' (Option - requires agency confirmation)
|
||||||
# Use 'O' during beta phase, switch to 'F' for production
|
# Use 'O' during beta phase, switch to 'F' for production
|
||||||
DEFAULT_BOOKING_STATUS=F
|
DEFAULT_BOOKING_STATUS=F
|
||||||
|
|
||||||
SFTP_XML_SOURCE_HOST=
|
API_KEYS=
|
||||||
SFTP_XML_SOURCE_PORT=
|
|
||||||
SFTP_XML_SOURCE_USER=
|
XML_EXPORT_PATH="%kernel.project_dir%/var/xmlexport"
|
||||||
SFTP_XML_SOURCE_PASSWORD=
|
JSON_EXPORT_PATH="%kernel.project_dir%/var/jsonexport"
|
||||||
SFTP_XML_SOURCE_ROOT=
|
|
||||||
|
SFTP_XML_EXPORT_HOST=
|
||||||
|
SFTP_XML_EXPORT_PORT=
|
||||||
|
SFTP_XML_EXPORT_USER=
|
||||||
|
SFTP_XML_EXPORT_PASSWORD=
|
||||||
|
|
||||||
###> league/oauth2-server-bundle ###
|
###> league/oauth2-server-bundle ###
|
||||||
OAUTH_PRIVATE_KEY=%kernel.project_dir%/config/secret/private.key
|
OAUTH_PRIVATE_KEY=%kernel.project_dir%/config/secret/private.key
|
||||||
@@ -129,8 +87,3 @@ OAUTH_ENCRYPTION_KEY=580084fd179e67399467f59ee96658ac
|
|||||||
# postgresql+advisory://db_user:db_password@localhost/db_name
|
# postgresql+advisory://db_user:db_password@localhost/db_name
|
||||||
LOCK_DSN=flock
|
LOCK_DSN=flock
|
||||||
###< symfony/lock ###
|
###< symfony/lock ###
|
||||||
|
|
||||||
MAINTENANCE_MODE_ENABLED=false
|
|
||||||
MAINTENANCE_MODE_IP_WHITELIST=188.245.62.21
|
|
||||||
MAINTENANCE_MODE_ROUTE_WHITELIST=
|
|
||||||
MAINTENANCE_MODE_PATH_WHITELIST=/api,/token
|
|
||||||
|
|||||||
@@ -4,4 +4,3 @@ APP_SECRET='$ecretf0rt3st'
|
|||||||
SYMFONY_DEPRECATIONS_HELPER=999999
|
SYMFONY_DEPRECATIONS_HELPER=999999
|
||||||
PANTHER_APP_ENV=panther
|
PANTHER_APP_ENV=panther
|
||||||
PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots
|
PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots
|
||||||
MAILJET_WEBHOOK_BASIC_PASSWORD=secret
|
|
||||||
|
|||||||
+5
-9
@@ -8,9 +8,13 @@
|
|||||||
/vendor/
|
/vendor/
|
||||||
###< symfony/framework-bundle ###
|
###< symfony/framework-bundle ###
|
||||||
|
|
||||||
|
###> phpunit/phpunit ###
|
||||||
|
/phpunit.xml
|
||||||
|
.phpunit.result.cache
|
||||||
|
###< phpunit/phpunit ###
|
||||||
|
|
||||||
###> symfony/phpunit-bridge ###
|
###> symfony/phpunit-bridge ###
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
.phpunit.cache
|
|
||||||
/phpunit.xml
|
/phpunit.xml
|
||||||
###< symfony/phpunit-bridge ###
|
###< symfony/phpunit-bridge ###
|
||||||
|
|
||||||
@@ -29,11 +33,3 @@ yarn-error.log
|
|||||||
###< friendsofphp/php-cs-fixer ###
|
###< friendsofphp/php-cs-fixer ###
|
||||||
|
|
||||||
/ray.php
|
/ray.php
|
||||||
|
|
||||||
###> phpstan/phpstan ###
|
|
||||||
phpstan.neon
|
|
||||||
###< phpstan/phpstan ###
|
|
||||||
|
|
||||||
###> config reference ###
|
|
||||||
/config/reference.php
|
|
||||||
###< config reference ###
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ Authorization: Bearer {{$auth.token("oauth2_api")}}
|
|||||||
|
|
||||||
### API travel
|
### API travel
|
||||||
# @no-cookie-jar
|
# @no-cookie-jar
|
||||||
GET {{base_url}}/api/travels/DPWMP140326
|
GET {{base_url}}/api/travels/SBW131225
|
||||||
Accept: application/json
|
Accept: application/json
|
||||||
Authorization: Bearer {{$auth.token("oauth2_api")}}
|
Authorization: Bearer {{$auth.token("oauth2_api")}}
|
||||||
|
|
||||||
@@ -100,40 +100,6 @@ Authorization: Bearer {{$auth.token("oauth2_api")}}
|
|||||||
"phone": "{{$random.phoneNumber.cellPhone}}"
|
"phone": "{{$random.phoneNumber.cellPhone}}"
|
||||||
}
|
}
|
||||||
|
|
||||||
### API newsletter lists map
|
|
||||||
# @no-cookie-jar
|
|
||||||
GET {{base_url}}/api/newsletters
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
Authorization: Bearer {{$auth.token("oauth2_newsletter")}}
|
|
||||||
|
|
||||||
### API newsletter subscriptions
|
|
||||||
# @no-cookie-jar
|
|
||||||
POST {{base_url}}/api/newsletter-subscriptions
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
Authorization: Bearer {{$auth.token("oauth2_newsletter")}}
|
|
||||||
|
|
||||||
{
|
|
||||||
"email": "{{$random.email}}",
|
|
||||||
"firstName": "{{$random.address.firstName}}",
|
|
||||||
"lastName": "{{$random.address.lastName}}",
|
|
||||||
"listIds": [10321569,10321382,10321383]
|
|
||||||
}
|
|
||||||
|
|
||||||
### API contingent calendar
|
|
||||||
# @no-cookie-jar
|
|
||||||
GET {{base_url}}/api/contingents/calendar?hotelCode=DGS&dateFrom=2026-06-01&dateTo=2026-06-30
|
|
||||||
Accept: application/json
|
|
||||||
Authorization: Bearer {{$auth.token("oauth2_api")}}
|
|
||||||
|
|
||||||
### API contintent prices
|
|
||||||
# @no-cookie-jar
|
|
||||||
GET {{base_url}}/api/contingents/prices?hotelCode=DGS&year=2026
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
Authorization: Bearer {{$auth.token("oauth2_api")}}
|
|
||||||
|
|
||||||
### API pickups planning webhook
|
### API pickups planning webhook
|
||||||
# @no-cookie-jar
|
# @no-cookie-jar
|
||||||
POST {{base_url}}/api/pickups-planning
|
POST {{base_url}}/api/pickups-planning
|
||||||
@@ -149,10 +115,3 @@ GET {{base_url}}/api/pickups-planning/DPWWTS191125
|
|||||||
Accept: application/json
|
Accept: application/json
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Authorization: Bearer {{$auth.token("oauth2_api")}}
|
Authorization: Bearer {{$auth.token("oauth2_api")}}
|
||||||
|
|
||||||
### API accommodation booking
|
|
||||||
# @no-cookie-jar
|
|
||||||
GET {{base_url}}/api/accommodation-bookings/e4591076-eb65-41e8-a115-09d18d15604a
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
Authorization: Bearer {{$auth.token("oauth2_api")}}
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
import htmx from 'htmx.org'
|
|
||||||
htmx.config.includeIndicatorStyles = false
|
|
||||||
htmx.config.historyCacheSize = 0
|
|
||||||
htmx.config.allowScriptTags = false
|
|
||||||
|
|
||||||
import './bootstrap.js'
|
|
||||||
|
|
||||||
import './styles/admin.css'
|
|
||||||
@@ -13,12 +13,3 @@ htmx.config.allowScriptTags = false
|
|||||||
htmx.config.withCredentials = true
|
htmx.config.withCredentials = true
|
||||||
htmx.config.selfRequestsOnly = false
|
htmx.config.selfRequestsOnly = false
|
||||||
htmx.config.timeout = 50000 // 50 seconds - slightly higher than backend timeout (45s)
|
htmx.config.timeout = 50000 // 50 seconds - slightly higher than backend timeout (45s)
|
||||||
|
|
||||||
document.body.addEventListener('htmx:beforeSwap', (event) => {
|
|
||||||
// Symfony renders invalid forms with 422; let HTMX swap those responses so inline
|
|
||||||
// validation errors are actually shown to the user.
|
|
||||||
if (event.detail?.xhr?.status === 422) {
|
|
||||||
event.detail.shouldSwap = true
|
|
||||||
event.detail.isError = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
import { Controller } from '@hotwired/stimulus'
|
|
||||||
import { useFetch } from '../mixins/use_fetch'
|
|
||||||
import Autocomplete from '@trevoreyre/autocomplete-js'
|
|
||||||
|
|
||||||
export default class extends Controller {
|
|
||||||
|
|
||||||
static targets = [ 'input', 'field', 'resetButton', 'searchIcon' ]
|
|
||||||
|
|
||||||
static values = { url: String, choices: Array, selected: Boolean }
|
|
||||||
|
|
||||||
initialize() {
|
|
||||||
useFetch(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
connect() {
|
|
||||||
const searchHandler = input => {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
// Check input minimum length
|
|
||||||
if (input.length < 2) {
|
|
||||||
return resolve([])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search choices if provided
|
|
||||||
if (this.choicesValue.length > 0) {
|
|
||||||
return resolve(this.choicesValue.filter(choice => {
|
|
||||||
return -1 !== choice.text.toLowerCase().search(input.toLowerCase())
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use API otherwise
|
|
||||||
let data = { search: input }
|
|
||||||
fetch(this.urlValue, { method: 'POST', body: JSON.stringify(data), credentials: 'include' })
|
|
||||||
.then(this.checkStatus)
|
|
||||||
.then(this.parseJSON)
|
|
||||||
.then(data => {
|
|
||||||
resolve(data)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const submitHandler = result => {
|
|
||||||
this.fieldTarget.value = result.value
|
|
||||||
this.selectedValue = true
|
|
||||||
this.element.dispatchEvent(new CustomEvent('select', {
|
|
||||||
detail: { result }
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
this.autocomplete = new Autocomplete(this.element, {
|
|
||||||
search: searchHandler,
|
|
||||||
getResultValue: result => result.text,
|
|
||||||
submitOnEnter: true,
|
|
||||||
onSubmit: submitHandler,
|
|
||||||
debounceTime: 250,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (this.fieldTarget.value) {
|
|
||||||
this.selectedValue = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// prevent form submission when selecting entry in autocomplete list with enter key
|
|
||||||
this.element.addEventListener('keypress', e => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
reset() {
|
|
||||||
this.inputTarget.value = null
|
|
||||||
this.fieldTarget.value = null
|
|
||||||
this.selectedValue = false
|
|
||||||
this.element.dispatchEvent(new CustomEvent('reset'))
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedValueChanged() {
|
|
||||||
this.resetButtonTarget.classList.toggle('hidden', false === this.selectedValue)
|
|
||||||
this.searchIconTarget.classList.toggle('hidden', this.selectedValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect() {
|
|
||||||
this.autocomplete.destroy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import { Controller } from "@hotwired/stimulus"
|
|
||||||
|
|
||||||
export default class extends Controller {
|
|
||||||
|
|
||||||
static values = {
|
|
||||||
content: String,
|
|
||||||
label: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
connect() {
|
|
||||||
this.originalLabel = this.element.innerText
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect() {
|
|
||||||
clearTimeout(this.resetTimer)
|
|
||||||
}
|
|
||||||
|
|
||||||
copy() {
|
|
||||||
navigator.clipboard.writeText(this.contentValue).then(() => {
|
|
||||||
this.dispatch('copied', {
|
|
||||||
detail: {
|
|
||||||
content: this.contentValue,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}).catch((error) => {
|
|
||||||
console.error('Failed to copy to clipboard:', error)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (this.hasLabelValue) {
|
|
||||||
this.element.innerText = this.labelValue
|
|
||||||
clearTimeout(this.resetTimer)
|
|
||||||
this.resetTimer = setTimeout(() => {
|
|
||||||
this.element.innerText = this.originalLabel
|
|
||||||
}, 3000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
import { Controller } from '@hotwired/stimulus'
|
|
||||||
import { Calendar } from 'vanilla-calendar-pro'
|
|
||||||
|
|
||||||
/* stimulusFetch: 'lazy' */
|
|
||||||
export default class extends Controller {
|
|
||||||
|
|
||||||
static targets = ['field', 'hiddenField']
|
|
||||||
static values = {
|
|
||||||
minDate: String,
|
|
||||||
maxDate: String,
|
|
||||||
disableWeekends: Boolean,
|
|
||||||
disabledDates: Array,
|
|
||||||
}
|
|
||||||
|
|
||||||
connect() {
|
|
||||||
const options = {
|
|
||||||
inputMode: true,
|
|
||||||
locale: 'de-DE',
|
|
||||||
selectedTheme: 'light',
|
|
||||||
selectedDates: this.hiddenFieldTarget.value ? [this.hiddenFieldTarget.value] : [],
|
|
||||||
onClickDate: (self) => {
|
|
||||||
const iso = self.context.selectedDates[0] ?? null
|
|
||||||
this.hiddenFieldTarget.value = iso ?? ''
|
|
||||||
if (iso) {
|
|
||||||
const [year, month, day] = iso.split('-')
|
|
||||||
this.fieldTarget.value = `${day}.${month}.${year}`
|
|
||||||
} else {
|
|
||||||
this.fieldTarget.value = ''
|
|
||||||
}
|
|
||||||
this.element.dispatchEvent(new CustomEvent('datepicker:picked', {
|
|
||||||
detail: { date: this.fieldTarget.value }
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
onHide: () => {
|
|
||||||
this.element.dispatchEvent(new CustomEvent('datepicker:closed'))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.minDateValue) options.dateMin = this.minDateValue
|
|
||||||
if (this.maxDateValue) options.dateMax = this.maxDateValue
|
|
||||||
if (this.disableWeekendsValue) options.disableWeekdays = [0, 6]
|
|
||||||
if (this.disabledDatesValue.length) options.disableDates = this.disabledDatesValue
|
|
||||||
|
|
||||||
this.picker = new Calendar(this.fieldTarget, options)
|
|
||||||
this.picker.init()
|
|
||||||
|
|
||||||
this.handleManualInput = () => {
|
|
||||||
const value = this.fieldTarget.value.trim()
|
|
||||||
if (!value) {
|
|
||||||
this.hiddenFieldTarget.value = ''
|
|
||||||
this.picker.set({ selectedDates: [] }, { dates: true })
|
|
||||||
} else {
|
|
||||||
const match = value.match(/^(\d{1,2})\.(\d{1,2})\.(\d{4})$/)
|
|
||||||
if (match) {
|
|
||||||
const [, day, month, year] = match
|
|
||||||
const iso = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`
|
|
||||||
this.hiddenFieldTarget.value = iso
|
|
||||||
this.picker.set({ selectedDates: [iso] }, { dates: true })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.element.dispatchEvent(new CustomEvent('datepicker:closed'))
|
|
||||||
}
|
|
||||||
this.fieldTarget.addEventListener('change', this.handleManualInput)
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect() {
|
|
||||||
this.fieldTarget.removeEventListener('change', this.handleManualInput)
|
|
||||||
this.picker.destroy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { Controller } from '@hotwired/stimulus'
|
|
||||||
import GLightbox from 'glightbox'
|
|
||||||
|
|
||||||
/* stimulusFetch: 'lazy' */
|
|
||||||
export default class extends Controller {
|
|
||||||
|
|
||||||
static targets = ['item']
|
|
||||||
|
|
||||||
connect() {
|
|
||||||
this.lightbox = GLightbox({
|
|
||||||
elements: this.itemTargets.map((el) => ({
|
|
||||||
href: el.dataset.galleryHref,
|
|
||||||
type: 'image',
|
|
||||||
alt: el.dataset.galleryAlt ?? '',
|
|
||||||
})),
|
|
||||||
})
|
|
||||||
|
|
||||||
this.itemTargets.forEach((el, index) => {
|
|
||||||
el.addEventListener('click', (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
this.lightbox.openAt(index)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect() {
|
|
||||||
this.lightbox.destroy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { Controller } from '@hotwired/stimulus'
|
|
||||||
|
|
||||||
export default class extends Controller {
|
|
||||||
static outlets = [ 'mobilenav' ]
|
|
||||||
static classes = [ 'closed' ]
|
|
||||||
|
|
||||||
trigger({ params: { mode } }) {
|
|
||||||
this.mobilenavOutlet.toggle(mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
toggle(mode) {
|
|
||||||
// Add animation classes only now to avoid fouc
|
|
||||||
this.element.classList.add('transition-transform', 'duration-300')
|
|
||||||
this.element.classList.toggle(this.closedClass, 'close' === mode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import { Controller } from '@hotwired/stimulus'
|
|
||||||
import { useClickOutside } from 'stimulus-use'
|
|
||||||
|
|
||||||
export default class extends Controller {
|
|
||||||
|
|
||||||
static targets = [ 'label', 'dropdown', 'choice' ]
|
|
||||||
static values = { placeholder: String }
|
|
||||||
|
|
||||||
connect() {
|
|
||||||
this.update()
|
|
||||||
useClickOutside(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
toggle() {
|
|
||||||
this.dropdownTarget.classList.toggle('hidden')
|
|
||||||
}
|
|
||||||
|
|
||||||
update() {
|
|
||||||
let labels = []
|
|
||||||
|
|
||||||
this.choiceTargets.forEach((choice) => {
|
|
||||||
if (choice.checked) {
|
|
||||||
labels.push(choice.dataset.label)
|
|
||||||
}
|
|
||||||
this.labelTarget.innerText = labels.join(', ')
|
|
||||||
})
|
|
||||||
|
|
||||||
if (0 === labels.length) {
|
|
||||||
this.labelTarget.innerText = this.placeholderValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clickOutside(e) {
|
|
||||||
this.dropdownTarget.classList.toggle('hidden', true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { Controller } from '@hotwired/stimulus'
|
|
||||||
|
|
||||||
export default class extends Controller {
|
|
||||||
|
|
||||||
static targets = [ 'field' ]
|
|
||||||
|
|
||||||
toggle() {
|
|
||||||
let type = this.fieldTarget.getAttribute('type') === 'password' ? 'text' : 'password'
|
|
||||||
this.fieldTarget.setAttribute('type', type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
import { Controller } from '@hotwired/stimulus'
|
|
||||||
|
|
||||||
export default class extends Controller {
|
|
||||||
|
|
||||||
static targets = ['mount', 'error', 'message', 'resetButton', 'day', 'dateFrom', 'dateTo', 'submit']
|
|
||||||
|
|
||||||
static values = {
|
|
||||||
hotelCode: String,
|
|
||||||
selectedFrom: String,
|
|
||||||
selectedTo: String,
|
|
||||||
calendarRefreshUrl: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
connect() {
|
|
||||||
this.startDate = null
|
|
||||||
this.endDate = null
|
|
||||||
this.sessionRestored = false
|
|
||||||
|
|
||||||
// Re-apply highlights after every HTMX month swap (navigation or initial load).
|
|
||||||
this.mountTarget.addEventListener('htmx:afterSwap', () => requestAnimationFrame(() => this.initAfterLoad()))
|
|
||||||
this.mountTarget.addEventListener('htmx:responseError', () => this.showError())
|
|
||||||
}
|
|
||||||
|
|
||||||
initAfterLoad() {
|
|
||||||
// Restore from session values exactly once on the first swap.
|
|
||||||
if (!this.sessionRestored) {
|
|
||||||
this.sessionRestored = true
|
|
||||||
const from = this.selectedFromValue
|
|
||||||
const to = this.selectedToValue
|
|
||||||
if (from && to) {
|
|
||||||
this.startDate = from
|
|
||||||
this.endDate = to
|
|
||||||
if (this.hasResetButtonTarget) this.resetButtonTarget.classList.remove('hidden')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.updateDisplay()
|
|
||||||
}
|
|
||||||
|
|
||||||
selectDate(event) {
|
|
||||||
const btn = event.currentTarget
|
|
||||||
btn.blur()
|
|
||||||
const date = btn.dataset.date
|
|
||||||
const status = btn.dataset.status
|
|
||||||
|
|
||||||
if (!status || status === 'blocked' || status === 'past') return
|
|
||||||
|
|
||||||
if (this.startDate && this.endDate) {
|
|
||||||
this.resetSelection()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.startDate) {
|
|
||||||
if (status === 'checkout-only') {
|
|
||||||
this.showMessage('Bitte wähle einen Anreisetag.')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.startDate = date
|
|
||||||
this.endDate = null
|
|
||||||
this.updateDisplay()
|
|
||||||
this.showMessage('Bitte wähle das Abreisedatum.')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (date === this.startDate) {
|
|
||||||
this.resetSelection()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const [from, to] = date < this.startDate ? [date, this.startDate] : [this.startDate, date]
|
|
||||||
|
|
||||||
if (this.hasBlockedInRange(from, to)) {
|
|
||||||
this.resetSelection()
|
|
||||||
this.showMessage('Dieser Zeitraum enthält nicht verfügbare Daten.')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const startBtn = this.dayTargets.find(b => b.dataset.date === from)
|
|
||||||
const minNights = parseInt(startBtn?.dataset.minNights) || 0
|
|
||||||
const nights = (new Date(to) - new Date(from)) / 86400000
|
|
||||||
|
|
||||||
if (minNights > 0 && nights < minNights) {
|
|
||||||
this.showMessage(`Weniger als ${minNights} Nächte nur auf Anfrage.`)
|
|
||||||
} else {
|
|
||||||
this.hideMessage()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.startDate = from
|
|
||||||
this.endDate = to
|
|
||||||
this.updateDisplay()
|
|
||||||
this.activateSubmit(from, to)
|
|
||||||
this.saveAndRefreshSummary(from, to)
|
|
||||||
this.dispatch('range-select', { detail: { dateFrom: from, dateTo: to } })
|
|
||||||
}
|
|
||||||
|
|
||||||
hasBlockedInRange(from, to) {
|
|
||||||
for (const btn of this.dayTargets) {
|
|
||||||
const d = btn.dataset.date
|
|
||||||
const s = btn.dataset.status
|
|
||||||
if (d >= from && d < to && (s === 'blocked' || s === 'past' || s === 'checkout-only')) return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDisplay() {
|
|
||||||
for (const btn of this.dayTargets) {
|
|
||||||
const d = btn.dataset.date
|
|
||||||
btn.classList.remove('pc-day--pending-start', 'pc-day--selected-start', 'pc-day--selected-end', 'pc-day--in-range')
|
|
||||||
if (!this.startDate) continue
|
|
||||||
if (d === this.startDate) {
|
|
||||||
btn.classList.add(this.endDate ? 'pc-day--selected-start' : 'pc-day--pending-start')
|
|
||||||
} else if (this.endDate) {
|
|
||||||
if (d === this.endDate) btn.classList.add('pc-day--selected-end')
|
|
||||||
else if (d > this.startDate && d < this.endDate) btn.classList.add('pc-day--in-range')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
activateSubmit(dateFrom, dateTo) {
|
|
||||||
if (this.hasDateFromTarget) this.dateFromTarget.value = dateFrom
|
|
||||||
if (this.hasDateToTarget) this.dateToTarget.value = dateTo
|
|
||||||
if (this.hasSubmitTarget) {
|
|
||||||
this.submitTarget.disabled = false
|
|
||||||
this.submitTarget.classList.remove('opacity-50', 'cursor-not-allowed', 'pointer-events-none')
|
|
||||||
}
|
|
||||||
if (this.hasResetButtonTarget) this.resetButtonTarget.classList.remove('hidden')
|
|
||||||
}
|
|
||||||
|
|
||||||
resetSelection() {
|
|
||||||
this.startDate = null
|
|
||||||
this.endDate = null
|
|
||||||
this.updateDisplay()
|
|
||||||
this.hideMessage()
|
|
||||||
if (this.hasDateFromTarget) this.dateFromTarget.value = ''
|
|
||||||
if (this.hasDateToTarget) this.dateToTarget.value = ''
|
|
||||||
if (this.hasSubmitTarget) {
|
|
||||||
this.submitTarget.disabled = true
|
|
||||||
this.submitTarget.classList.add('opacity-50', 'cursor-not-allowed', 'pointer-events-none')
|
|
||||||
}
|
|
||||||
if (this.hasResetButtonTarget) this.resetButtonTarget.classList.add('hidden')
|
|
||||||
this.saveAndRefreshSummary()
|
|
||||||
}
|
|
||||||
|
|
||||||
reset() {
|
|
||||||
this.resetSelection()
|
|
||||||
}
|
|
||||||
|
|
||||||
saveAndRefreshSummary(dateFrom = '', dateTo = '') {
|
|
||||||
if (!this.hasCalendarRefreshUrlValue) return
|
|
||||||
htmx.ajax('POST', this.calendarRefreshUrlValue, {
|
|
||||||
values: { date_from: dateFrom, date_to: dateTo },
|
|
||||||
swap: 'none',
|
|
||||||
target: this.element,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
showMessage(text) {
|
|
||||||
if (this.hasMessageTarget) this.messageTarget.textContent = text
|
|
||||||
}
|
|
||||||
|
|
||||||
hideMessage() {
|
|
||||||
if (this.hasMessageTarget) this.messageTarget.textContent = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
showError() {
|
|
||||||
if (this.hasErrorTarget) this.errorTarget.classList.remove('hidden')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import { Controller } from '@hotwired/stimulus'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unchecks conflicting additional services as soon as one is selected, so the user sees the
|
|
||||||
* result immediately instead of waiting for the debounced htmx refresh. The server resolves
|
|
||||||
* the same conflicts again and stays the source of truth.
|
|
||||||
*/
|
|
||||||
export default class extends Controller {
|
|
||||||
|
|
||||||
static targets = [ 'input' ]
|
|
||||||
|
|
||||||
select(event) {
|
|
||||||
const input = event.target
|
|
||||||
|
|
||||||
if (!input.checked) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const group = input.dataset.exclusionGroup
|
|
||||||
|
|
||||||
if (!group) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const exclusive = '1' === input.dataset.exclusive
|
|
||||||
|
|
||||||
this.inputTargets
|
|
||||||
.filter(other => other !== input)
|
|
||||||
.filter(other => other.dataset.exclusionGroup === group)
|
|
||||||
.filter(other => exclusive || '1' === other.dataset.exclusive)
|
|
||||||
.forEach(other => { other.checked = false })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { Controller } from '@hotwired/stimulus'
|
|
||||||
|
|
||||||
export default class extends Controller {
|
|
||||||
|
|
||||||
static classes = ['closed', 'iconClosed', 'iconExpanded']
|
|
||||||
static targets = ['submenu', 'button', 'icon']
|
|
||||||
static values = { expanded: Boolean }
|
|
||||||
|
|
||||||
toggleSubmenu() {
|
|
||||||
this.expandedValue = !this.expandedValue
|
|
||||||
}
|
|
||||||
|
|
||||||
expandedValueChanged(expanded) {
|
|
||||||
this.submenuTarget.classList.toggle(this.closedClass, false === expanded)
|
|
||||||
if (false === this.hasButtonTarget) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.buttonTarget.setAttribute('aria-expanded', expanded)
|
|
||||||
if (false === this.hasIconTarget) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (true === expanded) {
|
|
||||||
this.iconTarget.classList.add(...this.iconExpandedClasses)
|
|
||||||
this.iconTarget.classList.remove(...this.iconClosedClasses)
|
|
||||||
} else {
|
|
||||||
this.iconTarget.classList.add(...this.iconClosedClasses)
|
|
||||||
this.iconTarget.classList.remove(...this.iconExpandedClasses)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
import { Controller } from '@hotwired/stimulus'
|
|
||||||
import Sortable from 'sortablejs'
|
|
||||||
|
|
||||||
export default class extends Controller {
|
|
||||||
|
|
||||||
static targets = [ 'list', 'item', 'handle', 'positionField', 'positionLabel', 'status' ]
|
|
||||||
|
|
||||||
static values = {
|
|
||||||
url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
connect() {
|
|
||||||
this.abortController = null
|
|
||||||
|
|
||||||
// The sortable container may be nested (e.g. a tbody) when the controller element also
|
|
||||||
// has to wrap elements that must not be dragged, like the status message.
|
|
||||||
new Sortable(this.hasListTarget ? this.listTarget : this.element, {
|
|
||||||
draggable: '[data-sortable-target="item"]',
|
|
||||||
handle: '[data-sortable-target="handle"]',
|
|
||||||
sort: true,
|
|
||||||
fallbackOnBody: true,
|
|
||||||
onEnd: e => {
|
|
||||||
this.updateOrdering()
|
|
||||||
this.persist()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect() {
|
|
||||||
this.abortController?.abort()
|
|
||||||
}
|
|
||||||
|
|
||||||
updateOrdering() {
|
|
||||||
this.positionFieldTargets.forEach((field, index) => {
|
|
||||||
field.value = index + 1
|
|
||||||
})
|
|
||||||
this.positionLabelTargets.forEach((label, index) => {
|
|
||||||
label.innerText = index + 1
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends the new order to the server. Only active when a url value is present, so the
|
|
||||||
* controller stays usable for plain form ordering.
|
|
||||||
*/
|
|
||||||
persist() {
|
|
||||||
if (!this.hasUrlValue || this.urlValue === '') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// A drag while the previous request is still running makes that request obsolete.
|
|
||||||
this.abortController?.abort()
|
|
||||||
this.abortController = new AbortController()
|
|
||||||
|
|
||||||
const ids = this.itemTargets
|
|
||||||
.map(item => Number(item.dataset.sortableId))
|
|
||||||
.filter(id => Number.isInteger(id) && id > 0)
|
|
||||||
|
|
||||||
this.setStatus('speichert …')
|
|
||||||
|
|
||||||
fetch(this.urlValue, {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: 'include',
|
|
||||||
signal: this.abortController.signal,
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ ids }),
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Unexpected response ${response.status}`)
|
|
||||||
}
|
|
||||||
this.setStatus('Reihenfolge gespeichert')
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
if (error.name === 'AbortError') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
console.error('Failed to persist ordering', error)
|
|
||||||
this.setStatus('Speichern fehlgeschlagen')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
setStatus(message) {
|
|
||||||
this.statusTargets.forEach(status => {
|
|
||||||
status.innerText = message
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ import {Controller} from '@hotwired/stimulus'
|
|||||||
|
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
static classes = ['closed', 'open', 'iconOpen']
|
static classes = ['closed', 'open', 'iconOpen']
|
||||||
static targets = ['content', 'icon', 'container', 'trigger']
|
static targets = ['content', 'icon', 'container']
|
||||||
static values = {
|
static values = {
|
||||||
open: {
|
open: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -43,10 +43,6 @@ export default class extends Controller {
|
|||||||
this.iconTarget.classList.toggle(iconClass, true === open)
|
this.iconTarget.classList.toggle(iconClass, true === open)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.hasTriggerTarget) {
|
|
||||||
this.triggerTarget.setAttribute('aria-expanded', String(open))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toggle class on container element (or controller element) if specified
|
// Toggle class on container element (or controller element) if specified
|
||||||
if (this.hasOpenClass) {
|
if (this.hasOpenClass) {
|
||||||
const container = this.hasContainerTarget ? this.containerTarget : this.element
|
const container = this.hasContainerTarget ? this.containerTarget : this.element
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Controller } from '@hotwired/stimulus'
|
import { Controller } from '@hotwired/stimulus'
|
||||||
import tippy, { hideAll } from 'tippy.js'
|
import tippy from 'tippy.js'
|
||||||
|
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
static targets = ['trigger', 'template']
|
static targets = ['trigger', 'template']
|
||||||
@@ -27,7 +27,6 @@ export default class extends Controller {
|
|||||||
allowHTML: true,
|
allowHTML: true,
|
||||||
interactive: true,
|
interactive: true,
|
||||||
appendTo: document.body,
|
appendTo: document.body,
|
||||||
onShow(instance) { hideAll({ exclude: instance }) },
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.hasTriggerTarget) {
|
if (this.hasTriggerTarget) {
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 839 KiB |
@@ -1,95 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<symbol id="icon-table" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M3.375 19.5h17.25m-17.25 0a1.125 1.125 0 0 1-1.125-1.125M3.375 19.5h7.5c.621 0 1.125-.504 1.125-1.125m-9.75 0V5.625m0 12.75v-1.5c0-.621.504-1.125 1.125-1.125m18.375 2.625V5.625m0 12.75c0 .621-.504 1.125-1.125 1.125m1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125m0 3.75h-7.5A1.125 1.125 0 0 1 12 18.375m9.75-12.75c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125m19.5 0v1.5c0 .621-.504 1.125-1.125 1.125M2.25 5.625v1.5c0 .621.504 1.125 1.125 1.125m0 0h17.25m-17.25 0h7.5c.621 0 1.125.504 1.125 1.125M3.375 8.25c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125m17.25-3.75h-7.5c-.621 0-1.125.504-1.125 1.125m8.625-1.125c.621 0 1.125.504 1.125 1.125v1.5c0 .621-.504 1.125-1.125 1.125m-17.25 0h7.5m-7.5 0c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125M12 10.875v-1.5m0 1.5c0 .621-.504 1.125-1.125 1.125M12 10.875c0 .621.504 1.125 1.125 1.125m-2.25 0c.621 0 1.125.504 1.125 1.125M13.125 12h7.5m-7.5 0c-.621 0-1.125.504-1.125 1.125M20.625 12c.621 0 1.125.504 1.125 1.125v1.5c0 .621-.504 1.125-1.125 1.125m-17.25 0h7.5M12 14.625v-1.5m0 1.5c0 .621-.504 1.125-1.125 1.125M12 14.625c0 .621.504 1.125 1.125 1.125m-2.25 0c.621 0 1.125.504 1.125 1.125m0 1.5v-1.5m0 0c0-.621.504-1.125 1.125-1.125m0 0h7.5" /></symbol>
|
|
||||||
<symbol id="icon-dots" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 12.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 18.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Z" /></symbol>
|
|
||||||
<symbol id="icon-feedback" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.129.166 2.27.293 3.423.379.35.026.67.21.865.501L12 21l2.755-4.133a1.14 1.14 0 01.865-.501 48.172 48.172 0 003.423-.379c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z" /></symbol>
|
|
||||||
<symbol id="icon-login" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15m3 0l3-3m0 0l-3-3m3 3H9" /></symbol>
|
|
||||||
<symbol id="icon-logout" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15M12 9l-3 3m0 0l3 3m-3-3h12.75" /></symbol>
|
|
||||||
<symbol id="icon-alert" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z" /></symbol>
|
|
||||||
<symbol id="icon-close" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></symbol>
|
|
||||||
<symbol id="icon-back" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M9 15L3 9m0 0l6-6M3 9h12a6 6 0 010 12h-3" /></symbol>
|
|
||||||
<symbol id="icon-list" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" d="M8.25 6.75h12M8.25 12h12m-12 5.25h12M3.75 6.75h.007v.008H3.75V6.75zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zM3.75 12h.007v.008H3.75V12zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm-.375 5.25h.007v.008H3.75v-.008zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" /></symbol>
|
|
||||||
<symbol id="icon-menu" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" /></symbol>
|
|
||||||
<symbol id="icon-edit" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" /></symbol>
|
|
||||||
<symbol id="icon-plus" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" /></symbol>
|
|
||||||
<symbol id="icon-minus" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 12h-15" /></symbol>
|
|
||||||
<symbol id="icon-search" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" /></symbol>
|
|
||||||
<symbol id="icon-user" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z" /></symbol>
|
|
||||||
<symbol id="icon-users" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z" /></symbol>
|
|
||||||
<symbol id="icon-delete" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" /></symbol>
|
|
||||||
<symbol id="icon-check" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5" /></symbol>
|
|
||||||
<symbol id="icon-upload" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" stroke="currentColor"><path d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"/></symbol>
|
|
||||||
<symbol id="icon-download" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" /></symbol>
|
|
||||||
<symbol id="icon-sort" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" /></symbol>
|
|
||||||
<symbol id="icon-arrow-left" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 12h-15m0 0l6.75 6.75M4.5 12l6.75-6.75" /></symbol>
|
|
||||||
<symbol id="icon-chevron-down" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" /></symbol>
|
|
||||||
<symbol id="icon-arrow-right" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12h15m0 0l-6.75-6.75M19.5 12l-6.75 6.75" /></symbol>
|
|
||||||
<symbol id="icon-document" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" /></symbol>
|
|
||||||
<symbol id="icon-filter" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 3c2.755 0 5.455.232 8.083.678.533.09.917.556.917 1.096v1.044a2.25 2.25 0 01-.659 1.591l-5.432 5.432a2.25 2.25 0 00-.659 1.591v2.927a2.25 2.25 0 01-1.244 2.013L9.75 21v-6.568a2.25 2.25 0 00-.659-1.591L3.659 7.409A2.25 2.25 0 013 5.818V4.774c0-.54.384-1.006.917-1.096A48.32 48.32 0 0112 3z" /></symbol>
|
|
||||||
<symbol id="icon-refresh" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" /></symbol>
|
|
||||||
<symbol id="icon-settings" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M10.343 3.94c.09-.542.56-.94 1.11-.94h1.093c.55 0 1.02.398 1.11.94l.149.894c.07.424.384.764.78.93.398.164.855.142 1.205-.108l.737-.527a1.125 1.125 0 011.45.12l.773.774c.39.389.44 1.002.12 1.45l-.527.737c-.25.35-.272.806-.107 1.204.165.397.505.71.93.78l.893.15c.543.09.94.56.94 1.109v1.094c0 .55-.397 1.02-.94 1.11l-.893.149c-.425.07-.765.383-.93.78-.165.398-.143.854.107 1.204l.527.738c.32.447.269 1.06-.12 1.45l-.774.773a1.125 1.125 0 01-1.449.12l-.738-.527c-.35-.25-.806-.272-1.203-.107-.397.165-.71.505-.781.929l-.149.894c-.09.542-.56.94-1.11.94h-1.094c-.55 0-1.019-.398-1.11-.94l-.148-.894c-.071-.424-.384-.764-.781-.93-.398-.164-.854-.142-1.204.108l-.738.527c-.447.32-1.06.269-1.45-.12l-.773-.774a1.125 1.125 0 01-.12-1.45l.527-.737c.25-.35.273-.806.108-1.204-.165-.397-.505-.71-.93-.78l-.894-.15c-.542-.09-.94-.56-.94-1.109v-1.094c0-.55.398-1.02.94-1.11l.894-.149c.424-.07.765-.383.93-.78.165-.398.143-.854-.107-1.204l-.527-.738a1.125 1.125 0 01.12-1.45l.773-.773a1.125 1.125 0 011.45-.12l.737.527c.35.25.807.272 1.204.107.397-.165.71-.505.78-.929l.15-.894z" /><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /></symbol>
|
|
||||||
<symbol id="icon-at" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" d="M16.5 12a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0zm0 0c0 1.657 1.007 3 2.25 3S21 13.657 21 12a9 9 0 10-2.636 6.364M16.5 12V8.25" /></symbol>
|
|
||||||
<symbol id="icon-mail" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75" /></symbol>
|
|
||||||
<symbol id="icon-house" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" d="M8.25 21v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21m0 0h4.5V3.545M12.75 21h7.5V10.75M2.25 21h1.5m18 0h-18M2.25 9l4.5-1.636M18.75 3l-1.5.545m0 6.205l3 1m1.5.5l-1.5-.5M6.75 7.364V3h-3v18m3-13.636l10.5-3.819" /></symbol>
|
|
||||||
<symbol id="icon-phone" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 6.75c0 8.284 6.716 15 15 15h2.25a2.25 2.25 0 002.25-2.25v-1.372c0-.516-.351-.966-.852-1.091l-4.423-1.106c-.44-.11-.902.055-1.173.417l-.97 1.293c-.282.376-.769.542-1.21.38a12.035 12.035 0 01-7.143-7.143c-.162-.441.004-.928.38-1.21l1.293-.97c.363-.271.527-.734.417-1.173L6.963 3.102a1.125 1.125 0 00-1.091-.852H4.5A2.25 2.25 0 002.25 4.5v2.25z" /></symbol>
|
|
||||||
<symbol id="icon-mobile" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M10.5 1.5H8.25A2.25 2.25 0 0 0 6 3.75v16.5a2.25 2.25 0 0 0 2.25 2.25h7.5A2.25 2.25 0 0 0 18 20.25V3.75a2.25 2.25 0 0 0-2.25-2.25H13.5m-3 0V3h3V1.5m-3 0h3m-3 18.75h3" /></symbol>
|
|
||||||
<symbol id="icon-calendar" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 012.25-2.25h13.5A2.25 2.25 0 0121 7.5v11.25m-18 0A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75m-18 0v-7.5A2.25 2.25 0 015.25 9h13.5A2.25 2.25 0 0121 11.25v7.5m-9-6h.008v.008H12v-.008zM12 15h.008v.008H12V15zm0 2.25h.008v.008H12v-.008zM9.75 15h.008v.008H9.75V15zm0 2.25h.008v.008H9.75v-.008zM7.5 15h.008v.008H7.5V15zm0 2.25h.008v.008H7.5v-.008zm6.75-4.5h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V15zm0 2.25h.008v.008h-.008v-.008zm2.25-4.5h.008v.008H16.5v-.008zm0 2.25h.008v.008H16.5V15z" /></symbol>
|
|
||||||
<symbol id="icon-hand" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" d="M10.05 4.575a1.575 1.575 0 10-3.15 0v3m3.15-3v-1.5a1.575 1.575 0 013.15 0v1.5m-3.15 0l.075 5.925m3.075.75V4.575m0 0a1.575 1.575 0 013.15 0V15M6.9 7.575a1.575 1.575 0 10-3.15 0v8.175a6.75 6.75 0 006.75 6.75h2.018a5.25 5.25 0 003.712-1.538l1.732-1.732a5.25 5.25 0 001.538-3.712l.003-2.024a.668.668 0 01.198-.471 1.575 1.575 0 10-2.228-2.228 3.818 3.818 0 00-1.12 2.687M6.9 7.575V12m6.27 4.318A4.49 4.49 0 0116.35 15m.002 0h-.002" /></symbol>
|
|
||||||
<symbol id="icon-copy" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6" /></symbol>
|
|
||||||
<symbol id="icon-photo" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" /></symbol>
|
|
||||||
<symbol id="icon-print" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path d="M6.72 13.829c-.24.03-.48.062-.72.096m.72-.096a42.415 42.415 0 0110.56 0m-10.56 0L6.34 18m10.94-4.171c.24.03.48.062.72.096m-.72-.096L17.66 18m0 0l.229 2.523a1.125 1.125 0 01-1.12 1.227H7.231c-.662 0-1.18-.568-1.12-1.227L6.34 18m11.318 0h1.091A2.25 2.25 0 0021 15.75V9.456c0-1.081-.768-2.015-1.837-2.175a48.055 48.055 0 00-1.913-.247M6.34 18H5.25A2.25 2.25 0 013 15.75V9.456c0-1.081.768-2.015 1.837-2.175a48.041 48.041 0 011.913-.247m10.5 0a48.536 48.536 0 00-10.5 0m10.5 0V3.375c0-.621-.504-1.125-1.125-1.125h-8.25c-.621 0-1.125.504-1.125 1.125v3.659M18 10.5h.008v.008H18V10.5zm-3 0h.008v.008H15V10.5z" stroke-linecap="round" stroke-linejoin="round"/></symbol>
|
|
||||||
<symbol id="icon-locked" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z" /></symbol>
|
|
||||||
<symbol id="icon-unlocked" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M13.5 10.5V6.75a4.5 4.5 0 119 0v3.75M3.75 21.75h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H3.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z" /></symbol>
|
|
||||||
<symbol id="icon-external-link" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M10 6V8H5V19H16V14H18V20C18 20.5523 17.5523 21 17 21H4C3.44772 21 3 20.5523 3 20V7C3 6.44772 3.44772 6 4 6H10ZM21 3V11H19L18.9999 6.413L11.2071 14.2071L9.79289 12.7929L17.5849 5H13V3H21Z"/></symbol>
|
|
||||||
<symbol id="icon-cookie" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M21.1634 10.9711C21.5132 10.8204 21.9579 11.0073 21.9811 11.3875C22.1494 14.1467 21.1794 16.9625 19.071 19.0709C15.1657 22.976 8.83405 22.9761 4.92886 19.0709C1.02367 15.1657 1.02377 8.83405 4.92885 4.9288C7.03704 2.82061 9.8525 1.85015 12.6115 2.01806C12.9916 2.04119 13.1787 2.48598 13.028 2.83576C12.4774 4.11367 12.7239 5.65275 13.7677 6.69657C14.3307 7.25963 15.038 7.58971 15.7706 7.68848C16.0483 7.72594 16.2738 7.95144 16.3113 8.2292C16.4101 8.96178 16.7402 9.66905 17.3032 10.2321C18.347 11.2759 19.8857 11.5218 21.1634 10.9711ZM19.7845 13.8397C19.8558 13.5362 19.6123 13.2603 19.3017 13.2337C18.0569 13.1271 16.8417 12.599 15.889 11.6463C15.3166 11.0739 14.8979 10.4061 14.6309 9.69455C14.5744 9.54422 14.4556 9.42534 14.3052 9.36892C13.5936 9.10186 12.9259 8.68322 12.3535 8.11078C11.4005 7.15778 10.8715 5.94208 10.7648 4.69683C10.7381 4.38618 10.4621 4.14264 10.1586 4.2141C8.76039 4.54327 7.43341 5.25267 6.34306 6.34301C3.21903 9.46721 3.21893 14.5326 6.34307 17.6567C9.46721 20.7809 14.5326 20.7808 17.6568 17.6567C18.7474 16.566 19.4557 15.2384 19.7845 13.8397ZM9.8786 14.1212C10.4644 14.707 10.4644 15.6567 9.8786 16.2425C9.29281 16.8283 8.34307 16.8283 7.75728 16.2425C7.17149 15.6567 7.17149 14.707 7.75728 14.1212C8.34306 13.5354 9.29281 13.5354 9.8786 14.1212ZM10.2321 8.11078C10.8179 8.69657 10.8179 9.64631 10.2321 10.2321C9.64636 10.8179 8.69661 10.8179 8.11083 10.2321C7.52504 9.64631 7.52504 8.69656 8.11083 8.11078C8.69661 7.52499 9.64636 7.52499 10.2321 8.11078ZM15.889 13.7676C16.4748 14.3534 16.4748 15.3032 15.889 15.889C15.3032 16.4747 14.3535 16.4747 13.7677 15.889C13.1819 15.3032 13.1819 14.3534 13.7677 13.7676C14.3535 13.1818 15.3032 13.1818 15.889 13.7676Z"/></symbol>
|
|
||||||
<symbol id="icon-mask" fill="currentColor" stroke="none" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M288 64C39.52 64 0 182.1 0 273.5C0 379.5 78.8 448 176 448c27.33 0 51.21-6.516 66.11-36.79l19.93-40.5C268.3 358.6 278.1 352.4 288 352.1c9.9 .3711 19.7 6.501 25.97 18.63l19.93 40.5C348.8 441.5 372.7 448 400 448c97.2 0 176-68.51 176-174.5C576 182.1 536.5 64 288 64zM400 400c-18.12 0-19.56-2.924-23.04-9.986l-20.35-41.33c-13.87-26.86-38.85-43.53-66.82-44.57L288 304L286.2 304.1c-27.96 1.049-52.94 17.71-67.24 45.41l-19.93 40.49C195.6 397.1 194.1 400 176 400c-75.36 0-128-52.04-128-126.5C48 229.3 48 112 288 112s240 117.3 240 161.5C528 347.1 475.4 400 400 400zM160 192C124.7 192 96 220.7 96 256s28.65 64 64 64c35.35 0 64-28.65 64-64S195.3 192 160 192zM416 192c-35.35 0-64 28.65-64 64s28.65 64 64 64c35.35 0 64-28.65 64-64S451.3 192 416 192z"/></symbol>
|
|
||||||
<symbol id="icon-excel" fill="currentColor" stroke="none" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M365.3 93.38l-74.63-74.64C278.6 6.742 262.3 0 245.4 0H64C28.65 0 0 28.65 0 64l.0065 384c0 35.34 28.65 64 64 64H320c35.2 0 64-28.8 64-64V138.6C384 121.7 377.3 105.4 365.3 93.38zM336 448c0 8.836-7.164 16-16 16H64.02c-8.838 0-16-7.164-16-16L48 64.13c0-8.836 7.164-16 16-16h160L224 128c0 17.67 14.33 32 32 32h79.1V448zM229.1 233.3L192 280.9L154.9 233.3C146.8 222.8 131.8 220.9 121.3 229.1C110.8 237.2 108.9 252.3 117.1 262.8L161.6 320l-44.53 57.25c-8.156 10.47-6.25 25.56 4.188 33.69C125.7 414.3 130.8 416 135.1 416c7.156 0 14.25-3.188 18.97-9.25L192 359.1l37.06 47.65C233.8 412.8 240.9 416 248 416c5.125 0 10.31-1.656 14.72-5.062c10.44-8.125 12.34-23.22 4.188-33.69L222.4 320l44.53-57.25c8.156-10.47 6.25-25.56-4.188-33.69C252.2 220.9 237.2 222.8 229.1 233.3z"/></symbol>
|
|
||||||
<symbol id="icon-pdf" fill="currentColor" stroke="none" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M365.3 93.38l-74.63-74.64C278.6 6.742 262.3 0 245.4 0H64C28.65 0 0 28.65 0 64l.0065 384c0 35.34 28.65 64 64 64H320c35.2 0 64-28.8 64-64V138.6C384 121.7 377.3 105.4 365.3 93.38zM336 448c0 8.836-7.164 16-16 16H64.02c-8.838 0-16-7.164-16-16L48 64.13c0-8.836 7.164-16 16-16h160L224 128c0 17.67 14.33 32 32 32h79.1V448zM202 286.1c.877-2.688 1.74-5.398 2.582-8.145c1.434-5.762 7.488-31.54 7.488-52.47C212.1 207 197.1 192 178.6 192C160.1 192 145.1 207 145.1 225.5c0 .2969 .1641 28.81 13.85 62.3c-7.035 19.36-15.57 38.8-25.41 57.93c-21.49 10.11-39.24 22.23-52.8 36.07c-6.234 6.438-9.367 14.74-9.367 24.72c0 18.45 15.01 33.46 33.46 33.46c10.8 0 20.98-5.227 27.22-13.98c7.322-10.28 18.38-26.9 30.47-48.95c15.8-6.352 33.88-11.72 53.88-16c13.55 9.578 28.9 17.29 45.71 22.95c4.527 1.551 9.402 2.348 14.43 2.348c20.26 0 36.13-16.19 36.13-36.86c0-20.33-16.54-36.87-36.87-36.87h-3.705c-2.727 .125-20.51 1.141-45.37 5.367C216.9 308.9 208.6 298.3 202 286.1zM110.2 410.4c-3.273 4.688-12.03 2.777-12.03-5.312c0-1.754 .6289-3.43 1.729-4.555c9.02-9.219 19.94-17.05 31.85-23.72C122.3 393.1 114.3 404.7 110.2 410.4zM178.6 218.8c3.693 0 6.703 3.008 6.703 6.703c0 15.21-4.109 34.84-5.746 42.1C172.1 245 171.9 227.2 171.9 225.5C171.9 221.8 174.9 218.8 178.6 218.8zM162.3 348.3c6.611-13.48 13.22-28.46 19.38-44.7c6.389 10.92 14.56 21.86 24.96 31.97C192.6 338.8 177.4 342.9 162.3 348.3zM272.4 339.5h3.352c5.539 0 10.05 4.5 10.05 10.79c0 5.129-4.176 9.32-9.32 9.32c-2.029 0-4.059-.3164-5.852-.9414c-12.33-4.137-23.11-9.32-32.54-15.19C258.3 340.3 272.1 339.5 272.4 339.5z"/></symbol>
|
|
||||||
<symbol id="icon-file" fill="currentColor" stroke="none" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M365.3 93.38l-74.63-74.64C278.6 6.743 262.3 0 245.4 0L64-.0001c-35.35 0-64 28.65-64 64l.0065 384c0 35.35 28.65 64 64 64H320c35.2 0 64-28.8 64-64V138.6C384 121.7 377.3 105.4 365.3 93.38zM320 464H64.02c-8.836 0-15.1-7.163-16-15.1L48 64.13c-.0004-8.837 7.163-16 16-16h160L224 128c0 17.67 14.33 32 32 32h79.1v288C336 456.8 328.8 464 320 464z"/></symbol>
|
|
||||||
<symbol id="icon-sigma" fill="currentColor" stroke="none" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M335.1 136l.0005-56H83.59l165 158.7C253.3 243.2 255.1 249.5 255.1 256s-2.656 12.78-7.375 17.31L83.59 432h252.4l-.0005-56c0-13.25 10.75-24 24-24C373.2 352 384 362.8 384 376v80c0 13.25-10.75 24-24 24H23.99c-9.782 0-18.59-5.938-22.25-15.03s-1.438-19.47 5.625-26.28L197.4 256L7.364 73.31C.3015 66.5-1.917 56.13 1.739 47.03S14.21 32 23.99 32h336C373.2 32 384 42.75 384 56v80C384 149.3 373.2 160 359.1 160C346.7 160 335.1 149.3 335.1 136z"/></symbol>
|
|
||||||
<symbol id="icon-percent" fill="currentColor" stroke="none" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M376.1 71.03c-9.375-9.375-24.56-9.375-33.94 0l-336 336c-9.375 9.375-9.375 24.56 0 33.94C11.72 445.7 17.84 448 24 448s12.28-2.344 16.97-7.031l336-336C386.3 95.59 386.3 80.41 376.1 71.03zM64 176c26.51 0 48-21.49 48-48S90.51 80 64 80C37.49 80 16 101.5 16 128S37.49 176 64 176zM320 336c-26.51 0-48 21.49-48 48s21.49 48 48 48c26.51 0 48-21.49 48-48S346.5 336 320 336z"/></symbol>
|
|
||||||
<symbol id="icon-chair" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M288 16c26.5 0 48 21.5 48 48V256h16V64c0-35.3-28.7-64-64-64H160C124.7 0 96 28.7 96 64V256h16V64c0-26.5 21.5-48 48-48H288zM48 168c0-4.4-3.6-8-8-8s-8 3.6-8 8v80c0 4.4 3.6 8 8 8s8-3.6 8-8V168zm368 0c0-4.4-3.6-8-8-8s-8 3.6-8 8v80c0 4.4 3.6 8 8 8s8-3.6 8-8V168zM80 304H368c6.1 0 11.6 3.4 14.3 8.8l16 32c2.5 5 2.2 10.9-.7 15.6s-8.1 7.6-13.6 7.6H224 64c-5.5 0-10.7-2.9-13.6-7.6s-3.2-10.6-.7-15.6l16-32c2.7-5.4 8.3-8.8 14.3-8.8zm152 80H384c11.1 0 21.4-5.7 27.2-15.2s6.4-21.2 1.4-31.1l-16-32C391.2 294.8 380.1 288 368 288H80c-12.1 0-23.2 6.8-28.6 17.7l-16 32c-5 9.9-4.4 21.7 1.4 31.1S52.9 384 64 384H216v64H184c-30.9 0-56 25.1-56 56c0 4.4 3.6 8 8 8s8-3.6 8-8c0-22.1 17.9-40 40-40h32v40c0 4.4 3.6 8 8 8s8-3.6 8-8V464h32c22.1 0 40 17.9 40 40c0 4.4 3.6 8 8 8s8-3.6 8-8c0-30.9-25.1-56-56-56H232V384z"/></symbol>
|
|
||||||
<symbol id="icon-eye" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" /><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /></symbol>
|
|
||||||
<symbol id="icon-profile" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" d="M7.864 4.243A7.5 7.5 0 0119.5 10.5c0 2.92-.556 5.709-1.568 8.268M5.742 6.364A7.465 7.465 0 004.5 10.5a7.464 7.464 0 01-1.15 3.993m1.989 3.559A11.209 11.209 0 008.25 10.5a3.75 3.75 0 117.5 0c0 .527-.021 1.049-.064 1.565M12 10.5a14.94 14.94 0 01-3.6 9.75m6.633-4.596a18.666 18.666 0 01-2.485 5.33" /></symbol>
|
|
||||||
<symbol id="icon-chart" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 3v11.25A2.25 2.25 0 006 16.5h2.25M3.75 3h-1.5m1.5 0h16.5m0 0h1.5m-1.5 0v11.25A2.25 2.25 0 0118 16.5h-2.25m-7.5 0h7.5m-7.5 0l-1 3m8.5-3l1 3m0 0l.5 1.5m-.5-1.5h-9.5m0 0l-.5 1.5M9 11.25v1.5M12 9v3.75m3-6v6" /></symbol>
|
|
||||||
<symbol id="icon-info" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" /></symbol>
|
|
||||||
<symbol id="icon-star" fill="currentColor" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z" /></symbol>
|
|
||||||
<symbol id="icon-cancel" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M9.75 9.75l4.5 4.5m0-4.5l-4.5 4.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></symbol>
|
|
||||||
<symbol id="icon-bell" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0" /></symbol>
|
|
||||||
<symbol id="icon-folder" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path d="M2.25 12.75V12A2.25 2.25 0 014.5 9.75h15A2.25 2.25 0 0121.75 12v.75m-8.69-6.44l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9a2.25 2.25 0 00-2.25-2.25h-5.379a1.5 1.5 0 01-1.06-.44z" stroke-linecap="round" stroke-linejoin="round"/></symbol>
|
|
||||||
<symbol id="icon-power" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M5.636 5.636a9 9 0 1 0 12.728 0M12 3v9" /></symbol>
|
|
||||||
<symbol id="icon-no-symbol" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636" /></symbol>
|
|
||||||
<symbol id="icon-draggable" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M8.5 7C9.32843 7 10 6.32843 10 5.5C10 4.67157 9.32843 4 8.5 4C7.67157 4 7 4.67157 7 5.5C7 6.32843 7.67157 7 8.5 7ZM8.5 13.5C9.32843 13.5 10 12.8284 10 12C10 11.1716 9.32843 10.5 8.5 10.5C7.67157 10.5 7 11.1716 7 12C7 12.8284 7.67157 13.5 8.5 13.5ZM10 18.5C10 19.3284 9.32843 20 8.5 20C7.67157 20 7 19.3284 7 18.5C7 17.6716 7.67157 17 8.5 17C9.32843 17 10 17.6716 10 18.5ZM15.5 7C16.3284 7 17 6.32843 17 5.5C17 4.67157 16.3284 4 15.5 4C14.6716 4 14 4.67157 14 5.5C14 6.32843 14.6716 7 15.5 7ZM17 12C17 12.8284 16.3284 13.5 15.5 13.5C14.6716 13.5 14 12.8284 14 12C14 11.1716 14.6716 10.5 15.5 10.5C16.3284 10.5 17 11.1716 17 12ZM15.5 20C16.3284 20 17 19.3284 17 18.5C17 17.6716 16.3284 17 15.5 17C14.6716 17 14 17.6716 14 18.5C14 19.3284 14.6716 20 15.5 20Z"/></symbol>
|
|
||||||
<symbol id="icon-discount" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M10.0544 2.0941C11.1756 1.13856 12.8248 1.13855 13.9461 2.09411L15.2941 3.24286C15.4542 3.37935 15.6533 3.46182 15.8631 3.47856L17.6286 3.61945C19.0971 3.73663 20.2633 4.9028 20.3805 6.37131L20.5214 8.13679C20.5381 8.34654 20.6205 8.54568 20.757 8.70585L21.9058 10.0539C22.8614 11.1751 22.8614 12.8243 21.9058 13.9456L20.757 15.2935C20.6206 15.4537 20.538 15.6529 20.5213 15.8627L20.3805 17.6281C20.2633 19.0967 19.0971 20.2628 17.6286 20.3799L15.8631 20.5208C15.6533 20.5376 15.4542 20.6201 15.2941 20.7566L13.9461 21.9053C12.8248 22.8609 11.1756 22.8608 10.0543 21.9053L8.70631 20.7566C8.54615 20.6201 8.34705 20.5376 8.1373 20.5209L6.37184 20.3799C4.9033 20.2627 3.73716 19.0966 3.61997 17.6281L3.47906 15.8627C3.46232 15.6529 3.37983 15.4538 3.24336 15.2936L2.0946 13.9455C1.13905 12.8243 1.13904 11.1752 2.09458 10.0539L3.24334 8.70589C3.37983 8.54573 3.46234 8.34654 3.47907 8.13678L3.61996 6.3713C3.73714 4.90278 4.90327 3.73665 6.3718 3.61946L8.13729 3.47857C8.34705 3.46183 8.54619 3.37935 8.70636 3.24286L10.0544 2.0941ZM12.6488 3.61632C12.2751 3.29782 11.7253 3.29781 11.3516 3.61632L10.0036 4.76509C9.5231 5.17456 8.92568 5.42201 8.29637 5.47223L6.5309 5.61312C6.04139 5.65219 5.65268 6.04089 5.61362 6.53041L5.47272 8.29593C5.4225 8.92521 5.17505 9.52259 4.76559 10.0031L3.61683 11.3511C3.29832 11.7248 3.29831 12.2746 3.61683 12.6483L4.76559 13.9963C5.17506 14.4768 5.4225 15.0743 5.47275 15.7035L5.61363 17.469C5.65268 17.9585 6.04139 18.3473 6.53092 18.3863L8.29636 18.5272C8.92563 18.5774 9.5231 18.8249 10.0036 19.2344L11.3516 20.3831C11.7254 20.7016 12.2751 20.7016 12.6488 20.3831L13.9969 19.2343C14.4773 18.8249 15.0747 18.5774 15.704 18.5272L17.4695 18.3863C17.959 18.3472 18.3478 17.9585 18.3868 17.469L18.5277 15.7035C18.5779 15.0742 18.8253 14.4768 19.2349 13.9964L20.3836 12.6483C20.7022 12.2746 20.7021 11.7249 20.3836 11.3511L19.2348 10.0031C18.8253 9.52259 18.5779 8.92519 18.5277 8.2959L18.3868 6.53041C18.3478 6.0409 17.959 5.65219 17.4695 5.61312L15.704 5.47224C15.0748 5.42203 14.4773 5.17455 13.9968 4.76508L12.6488 3.61632ZM14.8284 7.75718L16.2426 9.1714L9.17154 16.2425L7.75733 14.8282L14.8284 7.75718ZM10.2322 10.232C9.64641 10.8178 8.69667 10.8178 8.11088 10.232C7.52509 9.6463 7.52509 8.69652 8.11088 8.11073C8.69667 7.52494 9.64641 7.52494 10.2322 8.11073C10.818 8.69652 10.818 9.6463 10.2322 10.232ZM13.7677 15.8889C14.3535 16.4747 15.3032 16.4747 15.889 15.8889C16.4748 15.3031 16.4748 14.3534 15.889 13.7676C15.3032 13.1818 14.3535 13.1818 13.7677 13.7676C13.1819 14.3534 13.1819 15.3031 13.7677 15.8889Z"/></symbol>
|
|
||||||
<symbol id="icon-stack" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M20.0833 15.1999L21.2854 15.9212C21.5221 16.0633 21.5989 16.3704 21.4569 16.6072C21.4146 16.6776 21.3557 16.7365 21.2854 16.7787L12.5144 22.0412C12.1977 22.2313 11.8021 22.2313 11.4854 22.0412L2.71451 16.7787C2.47772 16.6366 2.40093 16.3295 2.54301 16.0927C2.58523 16.0223 2.64413 15.9634 2.71451 15.9212L3.9166 15.1999L11.9999 20.0499L20.0833 15.1999ZM20.0833 10.4999L21.2854 11.2212C21.5221 11.3633 21.5989 11.6704 21.4569 11.9072C21.4146 11.9776 21.3557 12.0365 21.2854 12.0787L11.9999 17.6499L2.71451 12.0787C2.47772 11.9366 2.40093 11.6295 2.54301 11.3927C2.58523 11.3223 2.64413 11.2634 2.71451 11.2212L3.9166 10.4999L11.9999 15.3499L20.0833 10.4999ZM12.5144 1.30864L21.2854 6.5712C21.5221 6.71327 21.5989 7.0204 21.4569 7.25719C21.4146 7.32757 21.3557 7.38647 21.2854 7.42869L11.9999 12.9999L2.71451 7.42869C2.47772 7.28662 2.40093 6.97949 2.54301 6.7427C2.58523 6.67232 2.64413 6.61343 2.71451 6.5712L11.4854 1.30864C11.8021 1.11864 12.1977 1.11864 12.5144 1.30864ZM11.9999 3.33233L5.88723 6.99995L11.9999 10.6676L18.1126 6.99995L11.9999 3.33233Z"/></symbol>
|
|
||||||
<symbol id="icon-bus" xmlns="http://www.w3.org/2000/svg" stroke-width="1.5" viewBox="0 0 24 24" stroke="currentColor" fill="none"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M6 17m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M18 17m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M4 17h-2v-11a1 1 0 0 1 1 -1h14a5 7 0 0 1 5 7v5h-2m-4 0h-8" /><path d="M16 5l1.5 7l4.5 0" /><path d="M2 10l15 0" /><path d="M7 5l0 5" /><path d="M12 5l0 5" /></symbol>
|
|
||||||
<symbol id="icon-ski" fill="currentColor" stroke="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M380.7 48a48 48 0 1 1 96 0 48 48 0 1 1 -96 0zM2.7 268.9c6.1-11.8 20.6-16.3 32.4-10.2L239.8 365l49.6-74.5-82.7-87.9c-9.5-10.1-14.4-22.4-15.1-34.9l87.7 42 49.4 52.5c12.8 13.6 14.5 34.1 4.2 49.6l-50.2 75.4 135.8 70.5c13.6 7.1 29.8 7.2 43.6 .3l15.2-7.6c11.9-5.9 26.3-1.1 32.2 10.7s1.1 26.3-10.7 32.2l-15.2 7.6c-27.5 13.7-59.9 13.5-87.2-.7L12.9 301.3C1.2 295.2-3.4 280.7 2.7 268.9zM118.9 65.6L137 74.2l8.7-17.4c4-7.9 13.6-11.1 21.5-7.2s11.1 13.6 7.2 21.5l-8.5 16.9 55.5 26.6c1.7-.9 3.5-1.7 5.4-2.5l80.9-32.4c32.4-13 68.6 6.5 75.6 40.7l12.3 59.7 62.9 30.1c12 5.7 17 20 11.3 32s-20 17-32 11.3l-54.2-25.9c-1-.3-1.9-.6-2.8-1l-229.1-110-9.2 18.4c-4 7.9-13.6 11.1-21.5 7.2s-11.1-13.6-7.2-21.5l9-18-17.6-8.4c-8-3.8-11.3-13.4-7.5-21.3s13.4-11.3 21.3-7.5zm217.3 64.7c-1-4.9-6.2-7.7-10.8-5.8l-45.7 18.3 65.5 31.5-9-43.9z"/></symbol>
|
|
||||||
<symbol id="icon-snowboard" fill="currentColor" stroke="nonw" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M204.3 3c11.6-6.4 26.2-2.3 32.6 9.3l31.9 57.4c1.3 2.3 3.6 3.8 6.2 4.1l33.8 3.4c24.1 2.4 46.9 12 65.4 27.6L503.5 213.6c10.1 8.5 11.4 23.7 2.9 33.8s-23.7 11.4-33.8 2.9l-68.2-57.5-82.3 43.9 24.7 18.9c24.9 19 34.6 51.9 24.1 81.4l-28.2 79c-1.4 3.8-3.6 7-6.3 9.6l93.3 35.7c4.6 1.8 9.4 2.6 14.3 2.6H472c13.3 0 24 10.7 24 24s-10.7 24-24 24H443.8c-10.8 0-21.4-2-31.5-5.8L60.1 371.3c-11.5-4.4-22-11.2-30.8-20L7 329c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l22.4 22.4c4 4 8.7 7.1 14 9.1l19.4 7.4c-2.9-11.4 2.9-23.4 14-28.1L192 272l0-68.2c0-21.2 12-40.6 31-50.1L284.4 123l-14.1-1.4c-18.3-1.8-34.5-12.5-43.4-28.5L195 35.7c-6.4-11.6-2.3-26.2 9.3-32.6zm91.8 407.2c-.3-3.4 .1-6.9 1.3-10.3l28.2-79c3.5-9.8 .3-20.8-8-27.1L240 234.4l0 42.9c0 16.1-9.7 30.7-24.6 36.9L134 348.2l162.1 62.1zM259.6 189l20.5 15.7 84.6-45.1-17.2-14.5L259.6 189zM384 48a48 48 0 1 1 96 0 48 48 0 1 1 -96 0z"/></symbol>
|
|
||||||
<symbol id="icon-hourglass" fill="currentColor" stroke="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M24 0C10.7 0 0 10.7 0 24S10.7 48 24 48h8V67c0 40.3 16 79 44.5 107.5L158.1 256 76.5 337.5C48 366 32 404.7 32 445v19H24c-13.3 0-24 10.7-24 24s10.7 24 24 24H360c13.3 0 24-10.7 24-24s-10.7-24-24-24h-8V445c0-40.3-16-79-44.5-107.5L225.9 256l81.5-81.5C336 146 352 107.3 352 67V48h8c13.3 0 24-10.7 24-24s-10.7-24-24-24H24zM192 289.9l81.5 81.5C293 391 304 417.4 304 445v19H80V445c0-27.6 11-54 30.5-73.5L192 289.9zm0-67.9l-81.5-81.5C91 121 80 94.6 80 67V48H304V67c0 27.6-11 54-30.5 73.5L192 222.1z"/></symbol>
|
|
||||||
<symbol id="icon-car" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><circle cx="64" cy="176" r="24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><circle cx="192" cy="176" r="24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="88" y1="176" x2="168" y2="176" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M216,176h24a8,8,0,0,0,8-8V128a8,8,0,0,0-8-8H208L162.34,74.34A8,8,0,0,0,156.69,72H44.28a8,8,0,0,0-6.65,3.56L8,120v48a8,8,0,0,0,8,8H40" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="208" y1="120" x2="8" y2="120" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></symbol>
|
|
||||||
<symbol id="icon-flag-a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 480"><g fill-rule="evenodd"><path fill="#fff" d="M640 480H0V0h640z"/><path fill="#ed2939" d="M640 480H0V320h640zm0-319.9H0V.1h640z"/></g></symbol>
|
|
||||||
<symbol id="icon-flag-ch" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 480"><g fill-rule="evenodd" stroke-width="1pt"><path fill="#d52b1e" d="M0 0h640v480H0z"/><g fill="#fff"><path d="M170 195h300v90H170z"/><path d="M275 90h90v300h-90z"/></g></g></symbol>
|
|
||||||
<symbol id="icon-flag-d" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 480"><path fill="#ffce00" d="M0 320h640v160H0z"/><path d="M0 0h640v160H0z"/><path fill="#d00" d="M0 160h640v160H0z"/></symbol>
|
|
||||||
<symbol id="icon-flag-f" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 480"><g fill-rule="evenodd" stroke-width="1pt"><path fill="#fff" d="M0 0h640v480H0z"/><path fill="#00267f" d="M0 0h213.3v480H0z"/><path fill="#f31830" d="M426.7 0H640v480H426.7z"/></g></symbol>
|
|
||||||
<symbol id="icon-flag-i" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 480"><g fill-rule="evenodd" stroke-width="1pt"><path fill="#fff" d="M0 0h640v480H0z"/><path fill="#009246" d="M0 0h213.3v480H0z"/><path fill="#ce2b37" d="M426.7 0H640v480H426.7z"/></g></symbol>
|
|
||||||
<symbol id="icon-flag-nl" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 480"><g fill-rule="evenodd"><path fill="#21468b" d="M0 320h640v160H0z"/><path fill="#fff" d="M0 160h640v160H0z"/><path fill="#ae1c28" d="M0 0h640v160H0z"/></g></symbol>
|
|
||||||
<symbol id="icon-backpack" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><path d="M96,48h64a48,48,0,0,1,48,48V216a8,8,0,0,1-8,8H56a8,8,0,0,1-8-8V96A48,48,0,0,1,96,48Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M80,224V152a16,16,0,0,1,16-16h64a16,16,0,0,1,16,16v72" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M96,48V32a16,16,0,0,1,16-16h32a16,16,0,0,1,16,16V48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="112" y1="88" x2="144" y2="88" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="80" y1="168" x2="176" y2="168" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="144" y1="168" x2="144" y2="184" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></symbol>
|
|
||||||
<symbol id="icon-barricade" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><rect x="24" y="72" width="208" height="88" rx="8" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="84" y1="72" x2="172" y2="160" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="156" y1="72" x2="232" y2="148" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="24" y1="84" x2="100" y2="160" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="192" y1="160" x2="192" y2="200" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="64" y1="160" x2="64" y2="200" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></symbol>
|
|
||||||
<symbol id="icon-bed" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><path d="M112,168V80H216a32,32,0,0,1,32,32v56" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="24" y1="208" x2="24" y2="48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><polyline points="24 168 248 168 248 208" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="112" y1="80" x2="24" y2="80" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></symbol>
|
|
||||||
<symbol id="icon-boot" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><line x1="152" y1="120" x2="112" y2="120" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="152" y1="88" x2="112" y2="88" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M240,168v32a8,8,0,0,1-8,8H205.89a8.08,8.08,0,0,1-3.58-.84l-12.62-6.32a8.08,8.08,0,0,0-3.58-.84H169.89a8.08,8.08,0,0,0-3.58.84l-12.62,6.32a8.08,8.08,0,0,1-3.58.84H121.89a8.08,8.08,0,0,1-3.58-.84l-12.62-6.32a8.08,8.08,0,0,0-3.58-.84H85.89a8.08,8.08,0,0,0-3.58.84l-12.62,6.32a8.08,8.08,0,0,1-3.58.84H40a8,8,0,0,1-8-8V168c9.22-32.06,12-88.65,0-120H144a8,8,0,0,1,8,8v64h40A48,48,0,0,1,240,168Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="32" y1="168" x2="240" y2="168" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></symbol>
|
|
||||||
<symbol id="icon-lift" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><line x1="16" y1="72" x2="240" y2="32" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><rect x="40" y="104" width="176" height="112" rx="24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="40" y1="168" x2="216" y2="168" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="128" y1="52" x2="128" y2="104" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="160" y1="104" x2="160" y2="168" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="96" y1="104" x2="96" y2="168" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></symbol>
|
|
||||||
<symbol id="icon-chat" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><path d="M71.58,144,32,176V48a8,8,0,0,1,8-8H168a8,8,0,0,1,8,8v88a8,8,0,0,1-8,8Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M80,144v40a8,8,0,0,0,8,8h96.42L224,224V96a8,8,0,0,0-8-8H176" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></symbol>
|
|
||||||
<symbol id="icon-weather" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><line x1="87.66" y1="56.73" x2="83.5" y2="33.09" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="56.69" y1="76.46" x2="37.03" y2="62.69" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="48.73" y1="112.31" x2="25.09" y2="116.48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="123.52" y1="64.69" x2="137.28" y2="45.03" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M96,144a68.06,68.06,0,1,1,68,72H84a44,44,0,1,1,14.2-85.66" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M59.65,135.35a48,48,0,1,1,80.19-50.94" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></symbol>
|
|
||||||
<symbol id="icon-party" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><path d="M40.49,205.52,93,61.14a7.79,7.79,0,0,1,12.84-2.85l91.88,91.88A7.79,7.79,0,0,1,194.86,163L50.48,215.51A7.79,7.79,0,0,1,40.49,205.52Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M168,72s0-24,24-24,24-24,24-24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="144" y1="16" x2="144" y2="40" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="216" y1="112" x2="232" y2="128" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="216" y1="80" x2="240" y2="72" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="78.09" y1="102.09" x2="153.91" y2="177.91" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="101.11" y1="197.11" x2="58.89" y2="154.89" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></symbol>
|
|
||||||
<symbol id="icon-dice" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><rect width="256" height="256" fill="none"/><rect x="40" y="40" width="176" height="176" rx="24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><circle cx="92" cy="84" r="12"/><circle cx="164" cy="84" r="12"/><circle cx="92" cy="128" r="12"/><circle cx="164" cy="128" r="12"/><circle cx="92" cy="172" r="12"/><circle cx="164" cy="172" r="12"/></symbol>
|
|
||||||
<symbol id="icon-flag" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><line x1="48" y1="224" x2="48" y2="56" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M48,176c64-55.43,112,55.43,176,0V56C160,111.43,112,.57,48,56" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></symbol>
|
|
||||||
<symbol id="icon-burger" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><path d="M48.07,96a8,8,0,0,1-7.89-9.72C46.68,55.55,83.52,32,128,32s81.32,23.55,87.82,54.28A8,8,0,0,1,207.93,96Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><polyline points="24 176 68 160 108 176 148 160 188 176 232 160" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="24" y1="128" x2="232" y2="128" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M208,168.73V184a32,32,0,0,1-32,32H80a32,32,0,0,1-32-32V167.27" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></symbol>
|
|
||||||
<symbol id="icon-handshake" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><polyline points="200 152 160 192 96 176 40 136" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><polyline points="72.68 70.63 128 56 183.32 70.63" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M34.37,60.42,8.85,111.48a8,8,0,0,0,3.57,10.73L40,136,72.68,70.63,45.11,56.85A8,8,0,0,0,34.37,60.42Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M216,136l27.58-13.79a8,8,0,0,0,3.57-10.73L221.63,60.42a8,8,0,0,0-10.74-3.57L183.32,70.63Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M184,72H144L98.34,116.29a8,8,0,0,0,1.38,12.42C117.23,139.9,141,139.13,160,120l40,32,16-16" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><polyline points="124.06 216 82.34 205.57 56 186.75" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></symbol>
|
|
||||||
<symbol id="icon-badge" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><line x1="152" y1="112" x2="192" y2="112" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="152" y1="144" x2="192" y2="144" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><rect x="32" y="48" width="192" height="160" rx="8" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><circle cx="96" cy="120" r="24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M64,168c3.55-13.8,17.09-24,32-24s28.46,10.19,32,24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></symbol>
|
|
||||||
<symbol id="icon-parking" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><circle cx="128" cy="128" r="96" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M104,144h32a28,28,0,0,0,0-56H104v80" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></symbol>
|
|
||||||
<symbol id="icon-drinks" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><path d="M56,32H200L176.85,225A8,8,0,0,1,168.9,232H87.1A8,8,0,0,1,79.15,225Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="60.8" y1="72" x2="195.2" y2="72" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></symbol>
|
|
||||||
<symbol id="icon-sauna" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor"><path d="M153.599 76.8c-35.285 0-64 28.706-64 64s28.715 64 64 64 64-28.715 64-64-28.706-64-64-64Zm0 102.4c-21.205 0-38.4-17.195-38.4-38.4s17.195-38.4 38.4-38.4 38.4 17.195 38.4 38.4-17.194 38.4-38.4 38.4Zm152.824 315.623-51.2-140.8c-1.835-5.06-6.639-8.422-12.023-8.422h-89.6V230.4c0-7.074-5.726-12.8-12.8-12.8S128 223.326 128 230.4v128c0 7.074 5.726 12.8 12.8 12.8h93.44l48.137 132.378C284.263 508.775 289.178 512 294.4 512c1.451 0 2.927-.247 4.378-.776 6.638-2.415 10.077-9.754 7.645-16.401ZM209.919 396.8h-107.52c-7.074 0-12.8 5.726-12.8 12.8s5.726 12.8 12.8 12.8h107.52c7.074 0 12.8-5.726 12.8-12.8s-5.726-12.8-12.8-12.8ZM340.36 7.074c-3.157-6.323-10.863-8.875-17.169-5.726-6.323 3.166-8.892 10.846-5.726 17.178 22.886 45.79 12.228 85.726-.111 131.959-7.603 28.424-15.454 57.839-15.991 90.914-.111 7.074 5.521 12.902 12.587 13.013h.213c6.972 0 12.689-5.598 12.8-12.587.486-29.935 7.927-57.796 15.113-84.736 12.552-46.975 25.514-95.547-1.716-150.015Zm65.98 0c-3.174-6.323-10.837-8.875-17.178-5.726-6.323 3.166-8.892 10.846-5.726 17.178 22.903 45.79 12.237 85.726-.111 131.977-13.141 49.203-28.041 104.977-.324 174.251 1.997 5.009 6.81 8.047 11.887 8.047 1.587 0 3.2-.29 4.753-.913 6.562-2.628 9.762-10.078 7.134-16.64-24.559-61.389-11.426-110.575 1.271-158.148C420.599 110.123 433.578 61.551 406.34 7.074Z"/></symbol>
|
|
||||||
<symbol id="icon-shower" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M64,236a12,12,0,1,1-12-12A12,12,0,0,1,64,236Zm20-44a12,12,0,1,0,12,12A12,12,0,0,0,84,192Zm-64,0a12,12,0,1,0,12,12A12,12,0,0,0,20,192Zm32-32a12,12,0,1,0,12,12A12,12,0,0,0,52,160ZM256,40a8,8,0,0,1-8,8H219.31L191.46,75.86,169.8,202.65a16,16,0,0,1-27.09,8.66l-98-98a16,16,0,0,1,8.69-27.1L180.14,64.54l30.2-30.2A8,8,0,0,1,216,32h32A8,8,0,0,1,256,40ZM174.21,81.79,56,102l98,98Z"/></symbol>
|
|
||||||
<symbol id="icon-wifi" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><rect width="256" height="256" fill="none"/><circle cx="128" cy="204" r="12"/><path d="M232,93.19a164,164,0,0,0-208,0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M200,129a116,116,0,0,0-144,0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M168,165a68,68,0,0,0-80,0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></symbol>
|
|
||||||
<symbol id="icon-mountain" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><circle cx="164" cy="52" r="20" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M8,200,81.1,75.94a8,8,0,0,1,13.8,0L168,200Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="50.35" y1="128" x2="125.65" y2="128" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M146.61,163.71l33.06-55.79a8,8,0,0,1,13.76,0L248,200H168" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></symbol>
|
|
||||||
<symbol id="icon-spinner" viewBox="0 0 44 44" xmlns="http://www.w3.org/2000/svg" stroke="currentColor"><!-- By Sam Herbert (@sherb), for everyone. More @ http://goo.gl/7AJzbL --><g fill="none" fill-rule="evenodd" stroke-width="2"><circle cx="22" cy="22" r="1"><animate attributeName="r" begin="0s" dur="1.8s" values="1; 20" calcMode="spline" keyTimes="0; 1" keySplines="0.165, 0.84, 0.44, 1" repeatCount="indefinite" /><animate attributeName="stroke-opacity" begin="0s" dur="1.8s" values="1; 0" calcMode="spline" keyTimes="0; 1" keySplines="0.3, 0.61, 0.355, 1" repeatCount="indefinite" /></circle><circle cx="22" cy="22" r="1"><animate attributeName="r" begin="-0.9s" dur="1.8s" values="1; 20" calcMode="spline" keyTimes="0; 1" keySplines="0.165, 0.84, 0.44, 1" repeatCount="indefinite" /><animate attributeName="stroke-opacity" begin="-0.9s" dur="1.8s" values="1; 0" calcMode="spline" keyTimes="0; 1" keySplines="0.3, 0.61, 0.355, 1" repeatCount="indefinite" /></circle></g></symbol>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 55 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.6 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 8.4 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 9.2 KiB |
@@ -1,29 +0,0 @@
|
|||||||
export const useFetch = controller => {
|
|
||||||
Object.assign(controller, {
|
|
||||||
checkStatus(response) {
|
|
||||||
if (response.status >= 200 && response.status < 300) {
|
|
||||||
return response
|
|
||||||
} else {
|
|
||||||
const error = new Error(response.statusText)
|
|
||||||
error.response = response
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
},
|
|
||||||
parseJSON(response) {
|
|
||||||
return response.json()
|
|
||||||
},
|
|
||||||
parseHTML(response) {
|
|
||||||
return response.text()
|
|
||||||
},
|
|
||||||
getInitObject(method = 'get', body = null) {
|
|
||||||
return {
|
|
||||||
method,
|
|
||||||
body,
|
|
||||||
credentials: 'include',
|
|
||||||
headers: {
|
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -53,10 +53,6 @@
|
|||||||
background-image: url('../images/bg_5.jpg'), linear-gradient(190deg, rgba(24, 82, 123, 1) 0%, rgba(22, 88, 131, 1) 62%, rgba(0, 112, 224, 1) 100%);;
|
background-image: url('../images/bg_5.jpg'), linear-gradient(190deg, rgba(24, 82, 123, 1) 0%, rgba(22, 88, 131, 1) 62%, rgba(0, 112, 224, 1) 100%);;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-outer--summer {
|
|
||||||
background-image: url('../images/bg_summer.jpg'), linear-gradient(190deg, rgba(24, 82, 123, 1) 0%, rgba(22, 88, 131, 1) 62%, rgba(0, 112, 224, 1) 100%);;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sbw .bg-outer--2 {
|
.sbw .bg-outer--2 {
|
||||||
background-image: url('../images/bg_7.jpg'), linear-gradient(190deg, rgba(24, 82, 123, 1) 0%, rgba(22, 88, 131, 1) 62%, rgba(0, 112, 224, 1) 100%);;
|
background-image: url('../images/bg_7.jpg'), linear-gradient(190deg, rgba(24, 82, 123, 1) 0%, rgba(22, 88, 131, 1) 62%, rgba(0, 112, 224, 1) 100%);;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
@import "components/button.css";
|
|
||||||
@import "components/forms.css";
|
|
||||||
@import "components/gallery.css";
|
|
||||||
@import "components/pagination.css";
|
|
||||||
@import "components/price_calendar.css";
|
|
||||||
@import "components/toast.css";
|
|
||||||
@import "components/tooltip.css";
|
|
||||||
@import "components/typography.css";
|
@import "components/typography.css";
|
||||||
@import "components/rte-content.css";
|
@import "components/forms.css";
|
||||||
|
@import "components/button.css";
|
||||||
|
@import "components/tooltip.css";
|
||||||
|
@import "components/toast.css";
|
||||||
|
@import "components/pagination.css";
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
@import "components/autocomplete.css";
|
|
||||||
@import "components/badge.css";
|
|
||||||
@import "components/button.css";
|
|
||||||
@import "components/datatable.css";
|
|
||||||
@import "components/datepicker.css";
|
|
||||||
@import "components/forms.css";
|
|
||||||
@import "components/pagination.css";
|
|
||||||
@import "components/toast.css";
|
|
||||||
@import "components/tooltip.css";
|
|
||||||
@import "components/typography.css";
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
@layer utilities {
|
|
||||||
/* Reserve the scrollbar gutter so content does not shift width when a
|
|
||||||
scrollbar appears (e.g. when a collapsible section is opened). */
|
|
||||||
.scroll-stable {
|
|
||||||
scrollbar-gutter: stable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
@import "tailwindcss/base";
|
|
||||||
@import "_base.css";
|
|
||||||
|
|
||||||
@import "tailwindcss/components";
|
|
||||||
@import "_components_admin.css";
|
|
||||||
|
|
||||||
@import "tailwindcss/utilities";
|
|
||||||
@@ -5,4 +5,3 @@
|
|||||||
@import "_components.css";
|
@import "_components.css";
|
||||||
|
|
||||||
@import "tailwindcss/utilities";
|
@import "tailwindcss/utilities";
|
||||||
@import "_utilities.css";
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
.autocomplete {
|
|
||||||
@apply relative w-full;
|
|
||||||
}
|
|
||||||
|
|
||||||
.autocomplete[data-loading="true"]:after {
|
|
||||||
@apply block absolute top-0 right-0;
|
|
||||||
@apply absolute w-4 h-4 rounded-full right-0 mr-4;
|
|
||||||
content: "";
|
|
||||||
border: 3px solid rgba(0, 0, 0, 0.12);
|
|
||||||
border-right: 3px solid rgba(0, 0, 0, 0.48);
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
animation: rotate 1s infinite linear;
|
|
||||||
}
|
|
||||||
|
|
||||||
.autocomplete-icon {
|
|
||||||
@apply absolute top-1/2 right-0 transform -translate-y-1/2 mr-4;
|
|
||||||
}
|
|
||||||
.autocomplete[data-loading="true"] .autocomplete-icon {
|
|
||||||
@apply hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.autocomplete-result-list {
|
|
||||||
@apply bg-white shadow-md max-h-64 overflow-y-auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.autocomplete-result {
|
|
||||||
@apply p-2;
|
|
||||||
}
|
|
||||||
.autocomplete-result:hover,
|
|
||||||
.autocomplete-result[aria-selected="true"] {
|
|
||||||
@apply bg-gray-200;
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
.badge {
|
|
||||||
@apply inline-flex items-center rounded-md px-1.5 py-0.5 text-xs font-medium whitespace-nowrap;
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
.button {
|
.button {
|
||||||
@apply inline-flex items-center justify-center space-x-2 h-8 leading-8 md:h-10 md:leading-10 px-4 md:px-8 cursor-pointer;
|
@apply inline-flex items-center justify-center space-x-2 h-10 leading-10 px-4 md:px-8 cursor-pointer;
|
||||||
@apply uppercase leading-none text-center text-sm md:text-base;
|
@apply uppercase leading-none text-center;
|
||||||
@apply transition-colors outline-none focus:outline-2 focus:outline-offset-2 focus:outline-primary-dark;
|
@apply transition-colors outline-none focus:outline-2 focus:outline-offset-2 focus:outline-primary-light;
|
||||||
@apply rounded-md;
|
@apply rounded-md;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,52 +20,42 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.button--secondary {
|
.button--secondary {
|
||||||
@apply bg-none bg-primary-light text-white;
|
@apply bg-none bg-primary-dark text-white;
|
||||||
@apply hover:bg-primary-light/50;
|
@apply hover:bg-primary-light/80;
|
||||||
}
|
|
||||||
|
|
||||||
.button--neutral {
|
|
||||||
@apply bg-none bg-gray-200;
|
|
||||||
@apply hover:bg-gray-200/50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button--warning {
|
|
||||||
@apply bg-none bg-red-500 text-white;
|
|
||||||
@apply hover:bg-red-500/50;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sbw {
|
.sbw {
|
||||||
.button--primary {
|
.button--primary {
|
||||||
@apply bg-none bg-sbw-secondary text-white;
|
@apply bg-none bg-sbw-secondary text-white;
|
||||||
@apply hover:bg-none hover:bg-sbw-primary/50;
|
@apply hover:bg-none hover:bg-sbw-primary/80;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button--secondary {
|
.button--secondary {
|
||||||
@apply bg-none bg-sbw-primary-dark text-white;
|
@apply bg-none bg-sbw-primary-dark text-white;
|
||||||
@apply hover:bg-none hover:bg-sbw-primary/50;
|
@apply hover:bg-none hover:bg-sbw-primary/80;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.snz {
|
.snz {
|
||||||
.button--primary {
|
.button--primary {
|
||||||
@apply bg-none bg-snz-secondary text-white;
|
@apply bg-none bg-snz-secondary text-white;
|
||||||
@apply hover:bg-none hover:bg-snz-primary/50;
|
@apply hover:bg-none hover:bg-snz-primary/80;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button--secondary {
|
.button--secondary {
|
||||||
@apply bg-none bg-snz-primary-dark text-white;
|
@apply bg-none bg-snz-primary-dark text-white;
|
||||||
@apply hover:bg-none hover:bg-snz-primary/50;
|
@apply hover:bg-none hover:bg-snz-primary/80;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ser {
|
.ser {
|
||||||
.button--primary {
|
.button--primary {
|
||||||
@apply bg-none bg-ser-secondary text-white;
|
@apply bg-none bg-ser-secondary text-white;
|
||||||
@apply hover:bg-none hover:bg-ser-primary/50;
|
@apply hover:bg-none hover:bg-ser-primary/80;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button--secondary {
|
.button--secondary {
|
||||||
@apply bg-none bg-ser-primary text-white;
|
@apply bg-none bg-ser-primary text-white;
|
||||||
@apply hover:bg-none hover:bg-ser-primary/50;
|
@apply hover:bg-none hover:bg-ser-primary/80;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
.data-table-wrapper {
|
|
||||||
@apply overflow-x-auto lg:overflow-x-visible mb-8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-table-wrapper__inner {
|
|
||||||
@apply align-middle inline-block min-w-full overflow-hidden lg:overflow-visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-table {
|
|
||||||
@apply min-w-full;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-table tbody {
|
|
||||||
@apply bg-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-table tr {
|
|
||||||
@apply border border-gray-300;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-table th {
|
|
||||||
@apply px-4 py-2 bg-gray-200 text-left leading-4 font-bold text-primary align-top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-table td {
|
|
||||||
@apply px-4 py-2 leading-5 align-top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-table tbody tr {
|
|
||||||
@apply hover:bg-gray-100;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
@import "vanilla-calendar-pro/styles/index.css";
|
|
||||||
@import "vanilla-calendar-pro/styles/themes/light.css";
|
|
||||||
|
|
||||||
[data-vc-theme=light] .vc-date[data-vc-date-selected] .vc-date__btn,
|
|
||||||
[data-vc-theme=light] .vc-date[data-vc-date-selected] .vc-date__btn:hover {
|
|
||||||
background-color: theme('colors.secondary');
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
@import "glightbox/dist/css/glightbox.min.css";
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
.pc-day {
|
|
||||||
@apply relative w-full text-center py-1 px-0.5 text-sm leading-tight rounded text-white cursor-pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pc-day--out-of-month {
|
|
||||||
@apply pointer-events-none opacity-0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pc-day[data-status="ok"] {
|
|
||||||
@apply bg-emerald-500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pc-day[data-status="blocked-to-ok"] {
|
|
||||||
background: linear-gradient(to bottom right, theme('colors.gray.400') 50%, theme('colors.emerald.500') 50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pc-day[data-status="checkout-only"] {
|
|
||||||
background: linear-gradient(to bottom right, theme('colors.emerald.500') 50%, theme('colors.gray.400') 50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pc-day[data-status="past"] {
|
|
||||||
@apply bg-transparent text-gray-400 cursor-default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pc-day[data-status="blocked"] {
|
|
||||||
@apply bg-gray-400 cursor-default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pc-day[data-status=""], .pc-day:not([data-status]) {
|
|
||||||
@apply text-gray-400 cursor-default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pc-day.pc-day--pending-start,
|
|
||||||
.pc-day.pc-day--selected-start,
|
|
||||||
.pc-day.pc-day--selected-end {
|
|
||||||
@apply bg-none outline-none bg-secondary text-gray-700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pc-day.pc-day--in-range {
|
|
||||||
@apply bg-none bg-secondary text-gray-700 opacity-55;
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
.rte-content {
|
|
||||||
p {
|
|
||||||
@apply pb-4;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
@apply list-disc p-4;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol {
|
|
||||||
@apply list-decimal p-4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
### list custom fields
|
|
||||||
GET {{clickup_baseurl}}/list/{{clickup_list_id}}/field?include_applied_objects=true
|
|
||||||
Authorization: {{clickup_api_token}}
|
|
||||||
|
|
||||||
+41
-50
@@ -4,7 +4,7 @@
|
|||||||
"minimum-stability": "stable",
|
"minimum-stability": "stable",
|
||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.4",
|
"php": ">=8.1",
|
||||||
"ext-ctype": "*",
|
"ext-ctype": "*",
|
||||||
"ext-dom": "*",
|
"ext-dom": "*",
|
||||||
"ext-iconv": "*",
|
"ext-iconv": "*",
|
||||||
@@ -14,11 +14,8 @@
|
|||||||
"doctrine/doctrine-bundle": "^2.13",
|
"doctrine/doctrine-bundle": "^2.13",
|
||||||
"doctrine/doctrine-migrations-bundle": "^3.3",
|
"doctrine/doctrine-migrations-bundle": "^3.3",
|
||||||
"doctrine/orm": "^3.3",
|
"doctrine/orm": "^3.3",
|
||||||
"dompdf/dompdf": "^3.1",
|
"easycorp/easyadmin-bundle": "^4.27",
|
||||||
"fakerphp/faker": "^1.24",
|
"fakerphp/faker": "^1.24",
|
||||||
"flagception/flagception-bundle": "^6.1",
|
|
||||||
"knplabs/knp-menu-bundle": "^3.7",
|
|
||||||
"knplabs/knp-paginator-bundle": "^6.10",
|
|
||||||
"league/flysystem-bundle": "^3.4",
|
"league/flysystem-bundle": "^3.4",
|
||||||
"league/flysystem-sftp-v3": "^3.29",
|
"league/flysystem-sftp-v3": "^3.29",
|
||||||
"league/oauth2-server-bundle": "^1.0",
|
"league/oauth2-server-bundle": "^1.0",
|
||||||
@@ -29,43 +26,41 @@
|
|||||||
"phpstan/phpdoc-parser": "^2.0",
|
"phpstan/phpdoc-parser": "^2.0",
|
||||||
"spatie/crypto": "^2.1",
|
"spatie/crypto": "^2.1",
|
||||||
"symfony/apache-pack": "^1.0",
|
"symfony/apache-pack": "^1.0",
|
||||||
"symfony/asset": "7.4.*",
|
"symfony/asset": "6.4.*",
|
||||||
"symfony/console": "7.4.*",
|
"symfony/console": "6.4.*",
|
||||||
"symfony/doctrine-messenger": "7.4.*",
|
"symfony/doctrine-messenger": "6.4.*",
|
||||||
"symfony/dom-crawler": "7.4.*",
|
"symfony/dom-crawler": "6.4.*",
|
||||||
"symfony/dotenv": "7.4.*",
|
"symfony/dotenv": "6.4.*",
|
||||||
"symfony/expression-language": "7.4.*",
|
"symfony/expression-language": "6.4.*",
|
||||||
"symfony/flex": "^2",
|
"symfony/flex": "^2",
|
||||||
"symfony/form": "7.4.*",
|
"symfony/form": "6.4.*",
|
||||||
"symfony/framework-bundle": "7.4.*",
|
"symfony/framework-bundle": "6.4.*",
|
||||||
"symfony/html-sanitizer": "7.4.*",
|
"symfony/html-sanitizer": "6.4.*",
|
||||||
"symfony/http-client": "7.4.*",
|
"symfony/http-client": "6.4.*",
|
||||||
"symfony/intl": "7.4.*",
|
"symfony/intl": "6.4.*",
|
||||||
"symfony/lock": "7.4.*",
|
"symfony/lock": "6.4.*",
|
||||||
"symfony/mailer": "7.4.*",
|
"symfony/mailer": "6.4.*",
|
||||||
"symfony/mime": "7.4.*",
|
"symfony/mime": "6.4.*",
|
||||||
"symfony/monolog-bundle": "^3.0",
|
"symfony/monolog-bundle": "^3.0",
|
||||||
"symfony/notifier": "7.4.*",
|
"symfony/notifier": "6.4.*",
|
||||||
"symfony/process": "7.4.*",
|
"symfony/process": "6.4.*",
|
||||||
"symfony/property-access": "7.4.*",
|
"symfony/property-access": "6.4.*",
|
||||||
"symfony/property-info": "7.4.*",
|
"symfony/property-info": "6.4.*",
|
||||||
"symfony/runtime": "7.4.*",
|
"symfony/runtime": "6.4.*",
|
||||||
"symfony/security-bundle": "7.4.*",
|
"symfony/security-bundle": "6.4.*",
|
||||||
"symfony/serializer": "7.4.*",
|
"symfony/serializer": "6.4.*",
|
||||||
"symfony/stimulus-bundle": "^2.21",
|
"symfony/stimulus-bundle": "^2.21",
|
||||||
"symfony/string": "7.4.*",
|
"symfony/string": "6.4.*",
|
||||||
"symfony/translation": "7.4.*",
|
"symfony/translation": "6.4.*",
|
||||||
"symfony/twig-bundle": "7.4.*",
|
"symfony/twig-bundle": "6.4.*",
|
||||||
"symfony/uid": "7.4.*",
|
"symfony/uid": "6.4.*",
|
||||||
"symfony/ux-twig-component": "^2.36",
|
"symfony/validator": "6.4.*",
|
||||||
"symfony/validator": "7.4.*",
|
"symfony/web-link": "6.4.*",
|
||||||
"symfony/web-link": "7.4.*",
|
|
||||||
"symfony/webpack-encore-bundle": "^2.2",
|
"symfony/webpack-encore-bundle": "^2.2",
|
||||||
"symfony/yaml": "7.4.*",
|
"symfony/yaml": "6.4.*",
|
||||||
"twig/extra-bundle": "^2.12|^3.0",
|
"twig/extra-bundle": "^2.12|^3.0",
|
||||||
"twig/html-extra": "^3.15",
|
"twig/html-extra": "^3.15",
|
||||||
"twig/intl-extra": "*",
|
"twig/intl-extra": "*",
|
||||||
"twig/string-extra": "^3.24",
|
|
||||||
"twig/twig": "^2.12|^3.0",
|
"twig/twig": "^2.12|^3.0",
|
||||||
"zenstruck/schedule-bundle": "^1.8"
|
"zenstruck/schedule-bundle": "^1.8"
|
||||||
},
|
},
|
||||||
@@ -77,7 +72,7 @@
|
|||||||
},
|
},
|
||||||
"sort-packages": true,
|
"sort-packages": true,
|
||||||
"platform": {
|
"platform": {
|
||||||
"php": "8.4.24"
|
"php": "8.3.30"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@@ -100,8 +95,6 @@
|
|||||||
"symfony/polyfill-php81": "*"
|
"symfony/polyfill-php81": "*"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"rector": "vendor/bin/rector process",
|
|
||||||
"rector:dry-run": "vendor/bin/rector process --dry-run",
|
|
||||||
"auto-scripts": {
|
"auto-scripts": {
|
||||||
"cache:clear": "symfony-cmd",
|
"cache:clear": "symfony-cmd",
|
||||||
"assets:install %PUBLIC_DIR%": "symfony-cmd"
|
"assets:install %PUBLIC_DIR%": "symfony-cmd"
|
||||||
@@ -119,24 +112,22 @@
|
|||||||
"extra": {
|
"extra": {
|
||||||
"symfony": {
|
"symfony": {
|
||||||
"allow-contrib": true,
|
"allow-contrib": true,
|
||||||
"require": "7.4.*",
|
"require": "6.4.*",
|
||||||
"docker": true
|
"docker": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"deployer/deployer": "^7.5",
|
"deployer/deployer": "^7.5",
|
||||||
"friendsofphp/php-cs-fixer": "^3.75",
|
"friendsofphp/php-cs-fixer": "^3.75",
|
||||||
"phpstan/phpstan": "^2.1",
|
"phpunit/phpunit": "^9.5",
|
||||||
"phpstan/phpstan-doctrine": "^2.0",
|
"spatie/ray": "^1.43",
|
||||||
"phpstan/phpstan-symfony": "^2.0",
|
"spatie/x-ray": "^1.2",
|
||||||
"phpunit/phpunit": "^12",
|
"symfony/browser-kit": "6.4.*",
|
||||||
"rector/rector": "^2.4",
|
"symfony/css-selector": "6.4.*",
|
||||||
"symfony/browser-kit": "7.4.*",
|
"symfony/debug-bundle": "6.4.*",
|
||||||
"symfony/css-selector": "7.4.*",
|
|
||||||
"symfony/debug-bundle": "7.4.*",
|
|
||||||
"symfony/maker-bundle": "^1.0",
|
"symfony/maker-bundle": "^1.0",
|
||||||
"symfony/phpunit-bridge": "^7.4",
|
"symfony/phpunit-bridge": "^7.1",
|
||||||
"symfony/stopwatch": "7.4.*",
|
"symfony/stopwatch": "6.4.*",
|
||||||
"symfony/web-profiler-bundle": "7.4.*"
|
"symfony/web-profiler-bundle": "6.4.*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+2572
-3302
File diff suppressed because it is too large
Load Diff
+1
-3
@@ -16,8 +16,6 @@ return [
|
|||||||
League\FlysystemBundle\FlysystemBundle::class => ['all' => true],
|
League\FlysystemBundle\FlysystemBundle::class => ['all' => true],
|
||||||
League\Bundle\OAuth2ServerBundle\LeagueOAuth2ServerBundle::class => ['all' => true],
|
League\Bundle\OAuth2ServerBundle\LeagueOAuth2ServerBundle::class => ['all' => true],
|
||||||
Zenstruck\ScheduleBundle\ZenstruckScheduleBundle::class => ['all' => true],
|
Zenstruck\ScheduleBundle\ZenstruckScheduleBundle::class => ['all' => true],
|
||||||
Flagception\Bundle\FlagceptionBundle\FlagceptionBundle::class => ['all' => true],
|
|
||||||
Knp\Bundle\MenuBundle\KnpMenuBundle::class => ['all' => true],
|
|
||||||
Knp\Bundle\PaginatorBundle\KnpPaginatorBundle::class => ['all' => true],
|
|
||||||
Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true],
|
Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true],
|
||||||
|
EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle::class => ['all' => true],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ doctrine:
|
|||||||
profiling_collect_backtrace: '%kernel.debug%'
|
profiling_collect_backtrace: '%kernel.debug%'
|
||||||
use_savepoints: true
|
use_savepoints: true
|
||||||
orm:
|
orm:
|
||||||
enable_native_lazy_objects: true
|
auto_generate_proxy_classes: true
|
||||||
|
enable_lazy_ghost_objects: true
|
||||||
validate_xml_mapping: true
|
validate_xml_mapping: true
|
||||||
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
|
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
|
||||||
auto_mapping: true
|
auto_mapping: true
|
||||||
@@ -32,6 +33,8 @@ when@test:
|
|||||||
when@prod:
|
when@prod:
|
||||||
doctrine:
|
doctrine:
|
||||||
orm:
|
orm:
|
||||||
|
auto_generate_proxy_classes: false
|
||||||
|
proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies'
|
||||||
query_cache_driver:
|
query_cache_driver:
|
||||||
type: pool
|
type: pool
|
||||||
pool: doctrine.system_cache_pool
|
pool: doctrine.system_cache_pool
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
flagception:
|
|
||||||
features: {}
|
|
||||||
@@ -1,18 +1,21 @@
|
|||||||
flysystem:
|
flysystem:
|
||||||
storages:
|
storages:
|
||||||
xml_export.storage:
|
xml_export.storage:
|
||||||
local:
|
adapter: 'local'
|
||||||
directory: '%kernel.project_dir%/var/xmlexport'
|
options:
|
||||||
|
directory: '%env(resolve:XML_EXPORT_PATH)%'
|
||||||
xml_source.storage:
|
xml_source.storage:
|
||||||
sftp:
|
adapter: 'sftp'
|
||||||
host: '%env(SFTP_XML_SOURCE_HOST)%'
|
options:
|
||||||
port: '%env(int:SFTP_XML_SOURCE_PORT)%'
|
host: '%env(SFTP_XML_EXPORT_HOST)%'
|
||||||
username: '%env(SFTP_XML_SOURCE_USER)%'
|
port: '%env(int:SFTP_XML_EXPORT_PORT)%'
|
||||||
password: '%env(SFTP_XML_SOURCE_PASSWORD)%'
|
username: '%env(SFTP_XML_EXPORT_USER)%'
|
||||||
root: '%env(SFTP_XML_SOURCE_ROOT)%'
|
password: '%env(SFTP_XML_EXPORT_PASSWORD)%'
|
||||||
xml_dump.storage:
|
xml_dump.storage:
|
||||||
local:
|
adapter: 'local'
|
||||||
|
options:
|
||||||
directory: '%kernel.project_dir%/var/bpn'
|
directory: '%kernel.project_dir%/var/bpn'
|
||||||
json_export.storage:
|
json_export.storage:
|
||||||
local:
|
adapter: 'local'
|
||||||
directory: '%kernel.project_dir%/var/jsonexport'
|
options:
|
||||||
|
directory: '%env(resolve:JSON_EXPORT_PATH)%'
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# see https://symfony.com/doc/current/reference/configuration/framework.html
|
# see https://symfony.com/doc/current/reference/configuration/framework.html
|
||||||
framework:
|
framework:
|
||||||
secret: '%env(APP_SECRET)%'
|
secret: '%env(APP_SECRET)%'
|
||||||
|
annotations: false
|
||||||
http_method_override: false
|
http_method_override: false
|
||||||
handle_all_throwables: true
|
handle_all_throwables: true
|
||||||
|
|
||||||
@@ -8,8 +9,9 @@ framework:
|
|||||||
# Remove or comment this section to explicitly disable session support.
|
# Remove or comment this section to explicitly disable session support.
|
||||||
session:
|
session:
|
||||||
handler_id: null
|
handler_id: null
|
||||||
cookie_secure: true
|
cookie_secure: auto
|
||||||
cookie_samesite: lax
|
# cookie_samesite: lax
|
||||||
|
cookie_samesite: none # this is to make login work in staging environment. to be removed when going live!
|
||||||
|
|
||||||
#esi: true
|
#esi: true
|
||||||
#fragments: true
|
#fragments: true
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
knp_menu:
|
|
||||||
twig:
|
|
||||||
template: _partials/_menu.html.twig
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
knp_paginator:
|
|
||||||
default_options:
|
|
||||||
page_name: page
|
|
||||||
sort_field_name: sort
|
|
||||||
sort_direction_name: direction
|
|
||||||
distinct: true
|
|
||||||
filter_field_name: filterField
|
|
||||||
filter_value_name: filterValue
|
|
||||||
template:
|
|
||||||
pagination: '_partials/_paginator_sliding.html.twig'
|
|
||||||
sortable: '_partials/_paginator_sortable_link.html.twig'
|
|
||||||
@@ -3,12 +3,10 @@ league_oauth2_server:
|
|||||||
private_key: '%env(resolve:OAUTH_PRIVATE_KEY)%'
|
private_key: '%env(resolve:OAUTH_PRIVATE_KEY)%'
|
||||||
private_key_passphrase: null
|
private_key_passphrase: null
|
||||||
encryption_key: '%env(resolve:OAUTH_ENCRYPTION_KEY)%'
|
encryption_key: '%env(resolve:OAUTH_ENCRYPTION_KEY)%'
|
||||||
require_code_challenge_for_public_clients: true
|
|
||||||
enable_client_credentials_grant: true
|
enable_client_credentials_grant: true
|
||||||
enable_auth_code_grant: true
|
enable_auth_code_grant: true
|
||||||
enable_refresh_token_grant: true
|
enable_refresh_token_grant: false
|
||||||
enable_password_grant: false
|
enable_password_grant: false
|
||||||
enable_implicit_grant: false
|
|
||||||
access_token_ttl: PT10M
|
access_token_ttl: PT10M
|
||||||
resource_server:
|
resource_server:
|
||||||
public_key: '%env(resolve:OAUTH_PUBLIC_KEY)%'
|
public_key: '%env(resolve:OAUTH_PUBLIC_KEY)%'
|
||||||
@@ -17,8 +15,6 @@ league_oauth2_server:
|
|||||||
default: ['email']
|
default: ['email']
|
||||||
persistence:
|
persistence:
|
||||||
doctrine: null
|
doctrine: null
|
||||||
client:
|
|
||||||
allow_plaintext_secrets: false
|
|
||||||
|
|
||||||
when@test:
|
when@test:
|
||||||
league_oauth2_server:
|
league_oauth2_server:
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ framework:
|
|||||||
max_retries: 3
|
max_retries: 3
|
||||||
multiplier: 2
|
multiplier: 2
|
||||||
failed: 'doctrine://default?queue_name=failed'
|
failed: 'doctrine://default?queue_name=failed'
|
||||||
sync: 'sync://'
|
# sync: 'sync://'
|
||||||
|
|
||||||
default_bus: messenger.bus.default
|
default_bus: messenger.bus.default
|
||||||
|
|
||||||
@@ -21,12 +21,9 @@ framework:
|
|||||||
messenger.bus.default: []
|
messenger.bus.default: []
|
||||||
|
|
||||||
routing:
|
routing:
|
||||||
Symfony\Component\Mailer\Messenger\SendEmailMessage: sync
|
Symfony\Component\Mailer\Messenger\SendEmailMessage: async
|
||||||
Symfony\Component\Notifier\Message\ChatMessage: async
|
Symfony\Component\Notifier\Message\ChatMessage: async
|
||||||
Symfony\Component\Notifier\Message\SmsMessage: async
|
Symfony\Component\Notifier\Message\SmsMessage: async
|
||||||
App\Message\MailjetNewsletterEventMessage: async
|
|
||||||
App\Message\CreateClickUpBookingTaskMessage: async
|
|
||||||
App\Message\RoleNominationMessage: async
|
|
||||||
|
|
||||||
# Route your messages to the transports
|
# Route your messages to the transports
|
||||||
# 'App\Message\YourMessage': async
|
# 'App\Message\YourMessage': async
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ monolog:
|
|||||||
- bpn
|
- bpn
|
||||||
- core
|
- core
|
||||||
- auth
|
- auth
|
||||||
- admin
|
|
||||||
|
|
||||||
when@dev:
|
when@dev:
|
||||||
monolog:
|
monolog:
|
||||||
@@ -28,16 +27,11 @@ when@dev:
|
|||||||
path: "%kernel.logs_dir%/%kernel.environment%.core.log"
|
path: "%kernel.logs_dir%/%kernel.environment%.core.log"
|
||||||
level: debug
|
level: debug
|
||||||
channels: ["core"]
|
channels: ["core"]
|
||||||
admin:
|
|
||||||
type: stream
|
|
||||||
path: "%kernel.logs_dir%/%kernel.environment%.admin.log"
|
|
||||||
level: debug
|
|
||||||
channels: ["admin"]
|
|
||||||
main:
|
main:
|
||||||
type: stream
|
type: stream
|
||||||
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||||
level: debug
|
level: debug
|
||||||
channels: ["!event", "!bpn", "!core", "!auth", "!admin"]
|
channels: ["!event", "!bpn", "!core", "!auth"]
|
||||||
# uncomment to get logging in your browser
|
# uncomment to get logging in your browser
|
||||||
# you may have to allow bigger header sizes in your Web server configuration
|
# you may have to allow bigger header sizes in your Web server configuration
|
||||||
#firephp:
|
#firephp:
|
||||||
@@ -65,7 +59,7 @@ when@test:
|
|||||||
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||||
level: debug
|
level: debug
|
||||||
|
|
||||||
when@prod: &prod_config
|
when@prod:
|
||||||
monolog:
|
monolog:
|
||||||
handlers:
|
handlers:
|
||||||
database:
|
database:
|
||||||
@@ -90,19 +84,13 @@ when@prod: &prod_config
|
|||||||
path: "%kernel.logs_dir%/%kernel.environment%.core.log"
|
path: "%kernel.logs_dir%/%kernel.environment%.core.log"
|
||||||
level: info
|
level: info
|
||||||
channels: ["core"]
|
channels: ["core"]
|
||||||
admin:
|
|
||||||
type: rotating_file
|
|
||||||
max_files: 7
|
|
||||||
path: "%kernel.logs_dir%/%kernel.environment%.admin.log"
|
|
||||||
level: info
|
|
||||||
channels: ["admin"]
|
|
||||||
main:
|
main:
|
||||||
type: fingers_crossed
|
type: fingers_crossed
|
||||||
action_level: error
|
action_level: error
|
||||||
handler: nested
|
handler: nested
|
||||||
excluded_http_codes: [404, 405]
|
excluded_http_codes: [404, 405]
|
||||||
buffer_size: 50 # How many messages should be saved? Prevent memory leaks
|
buffer_size: 50 # How many messages should be saved? Prevent memory leaks
|
||||||
channels: ["!bpn", "!core", "!auth", "!admin"]
|
channels: ["!bpn", "!core", "!auth"]
|
||||||
nested:
|
nested:
|
||||||
type: rotating_file
|
type: rotating_file
|
||||||
path: "%kernel.logs_dir%/%kernel.environment%.framework.log"
|
path: "%kernel.logs_dir%/%kernel.environment%.framework.log"
|
||||||
@@ -117,5 +105,3 @@ when@prod: &prod_config
|
|||||||
channels: [deprecation]
|
channels: [deprecation]
|
||||||
path: php://stderr
|
path: php://stderr
|
||||||
formatter: monolog.formatter.json
|
formatter: monolog.formatter.json
|
||||||
|
|
||||||
when@staging: *prod_config
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
framework:
|
|
||||||
property_info:
|
|
||||||
with_constructor_extractor: true
|
|
||||||
@@ -2,12 +2,9 @@ framework:
|
|||||||
router:
|
router:
|
||||||
utf8: true
|
utf8: true
|
||||||
|
|
||||||
# How URLs are generated where there is no request to take the host from: scheduled
|
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
|
||||||
# commands and, notably, the Messenger workers that send mail off the request path.
|
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
|
||||||
# Without it those links would point at http://localhost. Only ever consulted in a
|
#default_uri: http://localhost
|
||||||
# non-HTTP context, so web requests keep using their own host — which matters here,
|
|
||||||
# since one codebase serves four brands.
|
|
||||||
default_uri: '%env(APP_BASE_URL)%'
|
|
||||||
|
|
||||||
when@prod:
|
when@prod:
|
||||||
framework:
|
framework:
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
security:
|
security:
|
||||||
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
|
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
|
||||||
password_hashers:
|
password_hashers:
|
||||||
Symfony\Component\Security\Core\User\InMemoryUser: 'plaintext'
|
|
||||||
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
|
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
|
||||||
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
|
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
|
||||||
providers:
|
providers:
|
||||||
@@ -9,18 +8,6 @@ security:
|
|||||||
entity:
|
entity:
|
||||||
class: App\Entity\User
|
class: App\Entity\User
|
||||||
property: email
|
property: email
|
||||||
mailjet_webhook_provider:
|
|
||||||
memory:
|
|
||||||
users:
|
|
||||||
mailjet:
|
|
||||||
password: '%env(MAILJET_WEBHOOK_BASIC_PASSWORD)%'
|
|
||||||
roles: [ ROLE_MAILJET_WEBHOOK ]
|
|
||||||
|
|
||||||
role_hierarchy:
|
|
||||||
ROLE_ADMIN:
|
|
||||||
- ROLE_GROUPS_ADMIN
|
|
||||||
ROLE_GROUPS_ADMIN:
|
|
||||||
- ROLE_GROUPS_MANAGER
|
|
||||||
firewalls:
|
firewalls:
|
||||||
dev:
|
dev:
|
||||||
pattern: ^/(_(profiler|wdt)|css|images|js)/
|
pattern: ^/(_(profiler|wdt)|css|images|js)/
|
||||||
@@ -32,15 +19,7 @@ security:
|
|||||||
pattern: ^/api
|
pattern: ^/api
|
||||||
security: true
|
security: true
|
||||||
stateless: true
|
stateless: true
|
||||||
provider: app_user_provider
|
|
||||||
oauth2: true
|
oauth2: true
|
||||||
mailjet_webhook:
|
|
||||||
pattern: ^/webhooks/mailjet/newsletter$
|
|
||||||
security: true
|
|
||||||
stateless: true
|
|
||||||
provider: mailjet_webhook_provider
|
|
||||||
http_basic:
|
|
||||||
realm: 'Mailjet Webhook'
|
|
||||||
main:
|
main:
|
||||||
lazy: true
|
lazy: true
|
||||||
provider: app_user_provider
|
provider: app_user_provider
|
||||||
@@ -53,8 +32,8 @@ security:
|
|||||||
# Easy way to control access for large sections of your site
|
# Easy way to control access for large sections of your site
|
||||||
# Note: Only the *first* access control that matches will be used
|
# Note: Only the *first* access control that matches will be used
|
||||||
access_control:
|
access_control:
|
||||||
- { path: ^/webhooks/mailjet/newsletter$, roles: ROLE_MAILJET_WEBHOOK, requires_channel: https }
|
|
||||||
- { path: ^/authorize, roles: IS_AUTHENTICATED_REMEMBERED, requires_channel: https }
|
- { path: ^/authorize, roles: IS_AUTHENTICATED_REMEMBERED, requires_channel: https }
|
||||||
|
- { path: ^/admin, roles: ROLE_ADMIN, requires_channel: https }
|
||||||
- { path: ^/, roles: PUBLIC_ACCESS, requires_channel: https }
|
- { path: ^/, roles: PUBLIC_ACCESS, requires_channel: https }
|
||||||
|
|
||||||
when@test:
|
when@test:
|
||||||
@@ -64,7 +43,6 @@ when@test:
|
|||||||
# important to generate secure password hashes. In tests however, secure hashes
|
# important to generate secure password hashes. In tests however, secure hashes
|
||||||
# are not important, waste resources and increase test times. The following
|
# are not important, waste resources and increase test times. The following
|
||||||
# reduces the work factor to the lowest possible values.
|
# reduces the work factor to the lowest possible values.
|
||||||
Symfony\Component\Security\Core\User\InMemoryUser: 'plaintext'
|
|
||||||
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
|
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
|
||||||
algorithm: auto
|
algorithm: auto
|
||||||
cost: 4 # Lowest possible value for bcrypt
|
cost: 4 # Lowest possible value for bcrypt
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
twig:
|
twig:
|
||||||
file_name_pattern: '*.twig'
|
file_name_pattern: '*.twig'
|
||||||
form_themes: ['forms.html.twig']
|
form_themes: ['forms.html.twig']
|
||||||
paths:
|
|
||||||
'%kernel.project_dir%/assets/images': images
|
|
||||||
globals:
|
globals:
|
||||||
htmx_change_trigger: 'change delay:100ms'
|
htmx_change_trigger: 'change delay:100ms'
|
||||||
website_base_url: '%env(APP_WEBSITE_BASE_URL)%'
|
website_base_url: '%env(APP_WEBSITE_BASE_URL)%'
|
||||||
@@ -10,7 +8,6 @@ twig:
|
|||||||
legal_notice_url: '%env(APP_LEGAL_NOTICE_URL)%'
|
legal_notice_url: '%env(APP_LEGAL_NOTICE_URL)%'
|
||||||
terms_and_conditions_url: '%env(APP_TERMS_AND_CONDITIONS_URL)%'
|
terms_and_conditions_url: '%env(APP_TERMS_AND_CONDITIONS_URL)%'
|
||||||
privacy_policy_url: '%env(APP_PRIVACY_POLICY_URL)%'
|
privacy_policy_url: '%env(APP_PRIVACY_POLICY_URL)%'
|
||||||
insurance_revocation_url: '%env(APP_INSURANCE_REVOCATION_URL)%'
|
|
||||||
|
|
||||||
when@test:
|
when@test:
|
||||||
twig:
|
twig:
|
||||||
|
|||||||
@@ -14,6 +14,4 @@ when@test:
|
|||||||
intercept_redirects: false
|
intercept_redirects: false
|
||||||
|
|
||||||
framework:
|
framework:
|
||||||
profiler:
|
profiler: { collect: false }
|
||||||
collect: false
|
|
||||||
collect_serializer_data: true
|
|
||||||
|
|||||||
@@ -3,14 +3,13 @@ zenstruck_schedule:
|
|||||||
|
|
||||||
mailer:
|
mailer:
|
||||||
service: mailer
|
service: mailer
|
||||||
default_to: [email protected]
|
default_to: [email protected]
|
||||||
default_from: noreply@ep-reisen.de
|
default_from: info@ep-reisen.de
|
||||||
subject_prefix: "[MyE&P]"
|
subject_prefix: "[MyE&P]"
|
||||||
|
|
||||||
schedule_extensions:
|
schedule_extensions:
|
||||||
email_on_failure:
|
email_on_failure:
|
||||||
enabled: true
|
enabled: true
|
||||||
to: [email protected]
|
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- task: app:cleanup:xml-dumps
|
- task: app:cleanup:xml-dumps
|
||||||
@@ -25,22 +24,6 @@ zenstruck_schedule:
|
|||||||
frequency: "30 1 * * *"
|
frequency: "30 1 * * *"
|
||||||
description: "Removes expired drafts"
|
description: "Removes expired drafts"
|
||||||
|
|
||||||
- task: app:cleanup:newsletter-opt-in-requests
|
|
||||||
frequency: "45 1 * * *"
|
|
||||||
description: "Removes expired pending newsletter double opt-in requests"
|
|
||||||
|
|
||||||
- task: league:oauth2-server:clear-expired-tokens
|
|
||||||
frequency: "0 * * * *"
|
|
||||||
description: "Removes expired oauth2 tokens hourly"
|
|
||||||
|
|
||||||
- task: app:bpn:refresh-travel-snapshot --refresh-after 60
|
|
||||||
frequency: "10 8-19 * * *"
|
|
||||||
description: "Syncs travel data snapshots with BusPro XML data hourly during peak hours"
|
|
||||||
|
|
||||||
- task: app:bpn:refresh-travel-snapshot --refresh-after 180
|
|
||||||
frequency: "10 20-23,0-7 * * *"
|
|
||||||
description: "Syncs travel data snapshots with BusPro XML data every 3 hours outside peak hours"
|
|
||||||
|
|
||||||
when@staging:
|
when@staging:
|
||||||
zenstruck_schedule:
|
zenstruck_schedule:
|
||||||
mailer:
|
mailer:
|
||||||
@@ -54,16 +37,8 @@ when@prod:
|
|||||||
tasks:
|
tasks:
|
||||||
- task: app:bpn:xml-sync
|
- task: app:bpn:xml-sync
|
||||||
frequency: "*/15 8-19 * * *"
|
frequency: "*/15 8-19 * * *"
|
||||||
description: "Syncs BusPro XML data every 15 min. during peak hours"
|
description: "Sync BusPro XML data every 15 min. during peak hours"
|
||||||
|
|
||||||
- task: app:bpn:xml-sync
|
- task: app:bpn:xml-sync
|
||||||
frequency: "0 20-23,0-7 * * *"
|
frequency: "0 20-23,0-7 * * *"
|
||||||
description: "Syncs BusPro XML data hourly outside peak hours"
|
description: "Sync BusPro XML data hourly outside peak hours"
|
||||||
|
|
||||||
- task: app:bpn:sync-contingents
|
|
||||||
frequency: "5-59/15 8-19 * * *"
|
|
||||||
description: "Syncs the full contingent horizon every 15 min. during peak hours"
|
|
||||||
|
|
||||||
- task: app:bpn:sync-contingents
|
|
||||||
frequency: "5 20-23,0-7 * * *"
|
|
||||||
description: "Syncs the full contingent horizon hourly outside peak hours"
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
easyadmin:
|
||||||
|
resource: .
|
||||||
|
type: easyadmin.routes
|
||||||
+28
-233
@@ -1,4 +1,3 @@
|
|||||||
# yaml-language-server: $schema=../vendor/symfony/dependency-injection/Loader/schema/services.schema.json
|
|
||||||
# This file is the entry point to configure your own services.
|
# This file is the entry point to configure your own services.
|
||||||
# Files in the packages/ subdirectory configure your dependencies.
|
# Files in the packages/ subdirectory configure your dependencies.
|
||||||
|
|
||||||
@@ -10,78 +9,26 @@ parameters:
|
|||||||
path_to_keys: '%kernel.project_dir%/config/secret'
|
path_to_keys: '%kernel.project_dir%/config/secret'
|
||||||
bpn_debug: '%env(APP_BPN_DEBUG)%'
|
bpn_debug: '%env(APP_BPN_DEBUG)%'
|
||||||
default_booking_status: '%env(DEFAULT_BOOKING_STATUS)%'
|
default_booking_status: '%env(DEFAULT_BOOKING_STATUS)%'
|
||||||
newsletter_confirmation_ttl_hours: '%env(int:APP_NEWSLETTER_CONFIRMATION_TTL_HOURS)%'
|
|
||||||
travel_snapshot_retention_buffer_days: '%env(int:APP_TRAVEL_SNAPSHOT_RETENTION_BUFFER_DAYS)%'
|
|
||||||
default_email_from: '%env(APP_DEFAULT_EMAIL_FROM)%'
|
|
||||||
default_email_to: '%env(APP_DEFAULT_EMAIL_TO)%'
|
|
||||||
accommodation_inquiry_email: '%env(ACCOMMODATION_INQUIRY_EMAIL)%'
|
|
||||||
|
|
||||||
# MailJet list ids and their labels
|
# Body dimensions choices for BodyDimensionsType
|
||||||
mailjet_lists:
|
body_dimensions.height_choices:
|
||||||
10321569: 'E&P Newsletter'
|
'bis 148cm': '-148'
|
||||||
10321382: 'Reisen-Alert Ahrntal'
|
'149 - 157cm': '149-157'
|
||||||
10321383: 'Reisen-Alert Davos'
|
'158 - 166cm': '158-166'
|
||||||
10321391: 'Reisen-Alert Familienreisen'
|
'167 - 178cm': '167-178'
|
||||||
10321384: 'Reisen-Alert Arosa-Lenzerheide'
|
'179 - 185cm': '179-185'
|
||||||
10321385: 'Reisen-Alert Les Deux Alpes'
|
'186 - 194cm': '186-194'
|
||||||
10321386: 'Reisen-Alert Montafon'
|
'195cm oder mehr': '195+'
|
||||||
10321387: 'Reisen-Alert Portes du Soleil'
|
body_dimensions.shoe_size_min: 36
|
||||||
10321388: 'Reisen-Alert Saalbach-Hinterglemm'
|
body_dimensions.shoe_size_max: 48
|
||||||
10538809: 'Reisen-Alert Scuol'
|
body_dimensions.weight_choices:
|
||||||
10321389: 'Reisen-Alert Stubaital'
|
'42 - 48kg': '42-48'
|
||||||
10554990: 'Reisen-Alert Ski & Boarderweek'
|
'49 - 57kg': '49-57'
|
||||||
|
'58 - 66kg': '58-66'
|
||||||
# Email domains whose accounts are members of staff. BusPro has no CRM selection expressing
|
'67 - 78kg': '67-78'
|
||||||
# "works here", so ROLE_EMPLOYEE is derived from the address instead. Matched exactly on the
|
'79 - 85kg': '79-85'
|
||||||
# domain part, never as a suffix.
|
'86 - 94kg': '86-94'
|
||||||
employee_email_domains:
|
'95kg oder mehr': '95+'
|
||||||
- 'ep-reisen.de'
|
|
||||||
|
|
||||||
# BusProNet "Hausleitung {CODE}" CRM selections, by selection id.
|
|
||||||
# DEPLOYMENT-CRITICAL: roles and hotel codes are synced on every login, so an id missing
|
|
||||||
# here does not merely fail to nominate a Hausleitung — it revokes the role and the hotel
|
|
||||||
# code from everyone holding it, one login at a time, with manual re-approval per user.
|
|
||||||
# Entries for houses that are not in use yet stay commented out.
|
|
||||||
bpn_crm_house_manager_ids:
|
|
||||||
1299: 'SSL'
|
|
||||||
1300: 'SST'
|
|
||||||
1301: 'MVK'
|
|
||||||
# 1302: 'ASB'
|
|
||||||
1303: 'LPJ'
|
|
||||||
1304: 'DKS'
|
|
||||||
1305: 'DGS'
|
|
||||||
1306: 'DPW'
|
|
||||||
1307: 'DKI'
|
|
||||||
1308: 'DWW'
|
|
||||||
1309: 'PMV'
|
|
||||||
# 1352: 'ASG'
|
|
||||||
1371: 'ASC'
|
|
||||||
1373: 'PCJ'
|
|
||||||
1374: 'SBW'
|
|
||||||
# 1375: 'UCH'
|
|
||||||
# 1376: 'SZO'
|
|
||||||
1377: 'KHH'
|
|
||||||
1459: 'SVS'
|
|
||||||
1461: 'SHM'
|
|
||||||
1462: 'AGR'
|
|
||||||
|
|
||||||
# MailJet contact metadata names for name synchronization
|
|
||||||
mailjet_contact_metadata_fields:
|
|
||||||
firstName: 'vorname'
|
|
||||||
lastName: 'nachname'
|
|
||||||
|
|
||||||
# Body dimensions ranges for BodyDimensionsType and ParticipantValidator
|
|
||||||
body_dimension_ranges:
|
|
||||||
height_min: 145
|
|
||||||
height_max: 210
|
|
||||||
weight_min: 40
|
|
||||||
weight_max: 120
|
|
||||||
shoe_size_min: 35
|
|
||||||
shoe_size_max: 50
|
|
||||||
|
|
||||||
# Season data
|
|
||||||
season_winter_from: '%env(APP_SEASON_WINTER_FROM)%'
|
|
||||||
season_summer_from: '%env(APP_SEASON_SUMMER_FROM)%'
|
|
||||||
|
|
||||||
# domain mapping for theme, gtm id and cmp url
|
# domain mapping for theme, gtm id and cmp url
|
||||||
domain_config:
|
domain_config:
|
||||||
@@ -148,73 +95,21 @@ services:
|
|||||||
arguments:
|
arguments:
|
||||||
$path: '%path_to_keys%'
|
$path: '%path_to_keys%'
|
||||||
|
|
||||||
App\Menu\AdminMenuBuilder:
|
|
||||||
arguments:
|
|
||||||
$factory: '@knp_menu.factory'
|
|
||||||
tags:
|
|
||||||
- name: knp_menu.menu_builder
|
|
||||||
method: createMainMenu
|
|
||||||
alias: admin_main
|
|
||||||
|
|
||||||
App\Menu\GroupsMenuBuilder:
|
|
||||||
arguments:
|
|
||||||
$factory: '@knp_menu.factory'
|
|
||||||
tags:
|
|
||||||
- name: knp_menu.menu_builder
|
|
||||||
method: createMainMenu
|
|
||||||
alias: groups_main
|
|
||||||
|
|
||||||
App\Service\BpnXmlSyncManager:
|
|
||||||
arguments:
|
|
||||||
$xmlSource: '@xml_source.storage'
|
|
||||||
$xmlExport: '@xml_export.storage'
|
|
||||||
$bpnCache: '@bpn.cache'
|
|
||||||
|
|
||||||
App\Command\BpnXmlCacheInvalidateCommand:
|
|
||||||
arguments:
|
|
||||||
$xmlExport: '@xml_export.storage'
|
|
||||||
$bpnCache: '@bpn.cache'
|
|
||||||
$cache: '@cache.app'
|
|
||||||
|
|
||||||
App\Security\Crypt:
|
App\Security\Crypt:
|
||||||
arguments:
|
arguments:
|
||||||
$path: '%path_to_keys%'
|
$path: '%path_to_keys%'
|
||||||
|
|
||||||
App\Service\TravelSnapshotManager:
|
App\Service\TravelDataService:
|
||||||
arguments:
|
|
||||||
$retentionBufferDays: '%travel_snapshot_retention_buffer_days%'
|
|
||||||
|
|
||||||
App\Service\TravelDataProvider:
|
|
||||||
arguments:
|
arguments:
|
||||||
$logger: '@monolog.logger.core'
|
$logger: '@monolog.logger.core'
|
||||||
$preferRemote: '%env(bool:APP_TRAVEL_PREFER_REMOTE)%'
|
$preferRemote: '%env(bool:APP_TRAVEL_PREFER_REMOTE)%'
|
||||||
$enableFallback: '%env(bool:APP_TRAVEL_ENABLE_FALLBACK)%'
|
$enableFallback: '%env(bool:APP_TRAVEL_ENABLE_FALLBACK)%'
|
||||||
|
|
||||||
App\Service\ParticipantFormSupport:
|
App\Service\BookingEditDataLoaderService:
|
||||||
arguments:
|
|
||||||
$bodyDimensionRanges: '%body_dimension_ranges%'
|
|
||||||
|
|
||||||
App\Service\BookingEditDataLoader:
|
|
||||||
arguments:
|
arguments:
|
||||||
$bpnCache: '@bpn.cache'
|
$bpnCache: '@bpn.cache'
|
||||||
|
|
||||||
App\BusProNet\XmlLoader\TravelLoader:
|
App\Service\VoucherValidationService:
|
||||||
arguments:
|
|
||||||
$cache: '@bpn.cache'
|
|
||||||
|
|
||||||
App\BusProNet\XmlLoader\HotelLoader:
|
|
||||||
arguments:
|
|
||||||
$cache: '@bpn.cache'
|
|
||||||
|
|
||||||
App\BusProNet\XmlLoader\PickupLoader:
|
|
||||||
arguments:
|
|
||||||
$cache: '@bpn.cache'
|
|
||||||
|
|
||||||
App\BusProNet\XmlLoader\InsuranceLoader:
|
|
||||||
arguments:
|
|
||||||
$cache: '@bpn.cache'
|
|
||||||
|
|
||||||
App\Service\VoucherValidator:
|
|
||||||
arguments:
|
arguments:
|
||||||
$cache: '@cache.app'
|
$cache: '@cache.app'
|
||||||
$logger: '@monolog.logger.bpn'
|
$logger: '@monolog.logger.bpn'
|
||||||
@@ -231,54 +126,38 @@ services:
|
|||||||
arguments:
|
arguments:
|
||||||
$handlers:
|
$handlers:
|
||||||
# Simple handlers with no external dependencies - instantiated directly
|
# Simple handlers with no external dependencies - instantiated directly
|
||||||
|
- 'App\Form\Service\ParticipantDateOfBirthFieldHandler'
|
||||||
- 'App\Form\Service\ParticipantAssignedRoomFieldHandler'
|
- 'App\Form\Service\ParticipantAssignedRoomFieldHandler'
|
||||||
- 'App\Form\Service\ParticipantRemarksRoomFieldHandler'
|
- 'App\Form\Service\ParticipantRemarksRoomFieldHandler'
|
||||||
- 'App\Form\Service\ParticipantAdditionalServicesFieldHandler'
|
- 'App\Form\Service\ParticipantAdditionalServicesFieldHandler'
|
||||||
- 'App\Form\Service\ParticipantCoursesFieldHandler'
|
- 'App\Form\Service\ParticipantCoursesFieldHandler'
|
||||||
- 'App\Form\Service\ParticipantBoardFieldHandler'
|
- 'App\Form\Service\ParticipantBoardFieldHandler'
|
||||||
- 'App\Form\Service\ParticipantVegFieldHandler'
|
|
||||||
- 'App\Form\Service\ParticipantRentalsFieldHandler'
|
- 'App\Form\Service\ParticipantRentalsFieldHandler'
|
||||||
- 'App\Form\Service\ParticipantSkiPassFieldHandler'
|
- 'App\Form\Service\ParticipantSkiPassFieldHandler'
|
||||||
- 'App\Form\Service\ParticipantTransportationOutboundFieldHandler'
|
- 'App\Form\Service\ParticipantTransportationOutboundFieldHandler'
|
||||||
- 'App\Form\Service\ParticipantTransportationInboundFieldHandler'
|
- 'App\Form\Service\ParticipantTransportationInboundFieldHandler'
|
||||||
- 'App\Form\Service\ParticipantPickupFieldHandler'
|
- 'App\Form\Service\ParticipantPickupFieldHandler'
|
||||||
- 'App\Form\Service\ParticipantDropOffFieldHandler'
|
- 'App\Form\Service\ParticipantDropOffFieldHandler'
|
||||||
|
- 'App\Form\Service\ParticipantParkingFieldHandler'
|
||||||
- 'App\Form\Service\ParticipantRentalInsuranceFieldHandler'
|
- 'App\Form\Service\ParticipantRentalInsuranceFieldHandler'
|
||||||
- 'App\Form\Service\ParticipantLicensePlateFieldHandler'
|
- 'App\Form\Service\ParticipantLicensePlateFieldHandler'
|
||||||
# Complex handlers with dependencies - use service references
|
# Complex handlers with dependencies - use service references
|
||||||
- '@App\Form\Service\ParticipantDateOfBirthFieldHandler'
|
|
||||||
- '@App\Form\Service\ParticipantTransportationDiscountReplacementFieldHandler'
|
- '@App\Form\Service\ParticipantTransportationDiscountReplacementFieldHandler'
|
||||||
- '@App\Form\Service\ParticipantBulkInsuranceFieldHandler'
|
- '@App\Form\Service\ParticipantBulkInsuranceFieldHandler'
|
||||||
- '@App\Form\Service\ParticipantInsuranceFieldHandler'
|
- '@App\Form\Service\ParticipantInsuranceFieldHandler'
|
||||||
- '@App\Form\Service\ParticipantPurchaseVoucherFieldHandler'
|
- '@App\Form\Service\ParticipantPurchaseVoucherFieldHandler'
|
||||||
- '@App\Form\Service\ParticipantPromoVoucherFieldHandler'
|
- '@App\Form\Service\ParticipantPromoVoucherFieldHandler'
|
||||||
- '@App\Form\Service\ParticipantParkingFieldHandler'
|
|
||||||
|
|
||||||
# Booking Status Rules
|
# Participant Status Rules
|
||||||
App\BusProNet\Service\StatusRule\Selection1473StatusRule: ~
|
|
||||||
App\BusProNet\Service\StatusRule\ChaperonServiceStatusRule: ~
|
App\BusProNet\Service\StatusRule\ChaperonServiceStatusRule: ~
|
||||||
|
|
||||||
# Booking Status Rule Registry
|
# Participant Status Rule Registry
|
||||||
App\BusProNet\Service\BookingStatusRuleRegistry:
|
App\BusProNet\Service\ParticipantStatusRuleRegistry:
|
||||||
arguments:
|
arguments:
|
||||||
$rules:
|
$rules:
|
||||||
- '@App\BusProNet\Service\StatusRule\Selection1473StatusRule'
|
|
||||||
- '@App\BusProNet\Service\StatusRule\ChaperonServiceStatusRule'
|
- '@App\BusProNet\Service\StatusRule\ChaperonServiceStatusRule'
|
||||||
|
|
||||||
# Dashboard Widgets
|
App\Service\CmsDataService:
|
||||||
App\Dashboard\Widget\OpenGroupBookingsWidgetProvider: ~
|
|
||||||
App\Dashboard\Widget\PendingRoleApprovalsWidgetProvider: ~
|
|
||||||
App\Dashboard\Widget\RecentLogEntriesWidgetProvider: ~
|
|
||||||
|
|
||||||
# Dashboard Widget Registry
|
|
||||||
App\Dashboard\DashboardWidgetRegistry:
|
|
||||||
arguments:
|
|
||||||
$providers:
|
|
||||||
- '@App\Dashboard\Widget\OpenGroupBookingsWidgetProvider'
|
|
||||||
- '@App\Dashboard\Widget\PendingRoleApprovalsWidgetProvider'
|
|
||||||
- '@App\Dashboard\Widget\RecentLogEntriesWidgetProvider'
|
|
||||||
|
|
||||||
App\Service\CmsDataProvider:
|
|
||||||
arguments:
|
arguments:
|
||||||
$httpClient: '@typo3.client'
|
$httpClient: '@typo3.client'
|
||||||
$apiKey: 'AbcAbc123'# dummy key, not yet implemented
|
$apiKey: 'AbcAbc123'# dummy key, not yet implemented
|
||||||
@@ -287,91 +166,7 @@ services:
|
|||||||
tags:
|
tags:
|
||||||
- { name: monolog.processor }
|
- { name: monolog.processor }
|
||||||
|
|
||||||
App\Service\NewsletterManager:
|
|
||||||
arguments:
|
|
||||||
$newsletterConfirmationTtlHours: '%newsletter_confirmation_ttl_hours%'
|
|
||||||
$mailjetLists: '%mailjet_lists%'
|
|
||||||
$defaultMailjetListId: '%env(default::MAILJET_DEFAULT_LIST_ID)%'
|
|
||||||
|
|
||||||
App\Controller\Api\NewsletterSubscriptionController:
|
|
||||||
arguments:
|
|
||||||
$mailjetLists: '%mailjet_lists%'
|
|
||||||
|
|
||||||
App\Controller\Webhook\MailjetNewsletterWebhookController:
|
|
||||||
arguments:
|
|
||||||
$mailjetLists: '%mailjet_lists%'
|
|
||||||
|
|
||||||
App\BusProNet\XmlParser\CrmAttributesResponseParser:
|
|
||||||
arguments:
|
|
||||||
$houseManagerIds: '%bpn_crm_house_manager_ids%'
|
|
||||||
|
|
||||||
App\Security\EmployeeDomainMatcher:
|
|
||||||
arguments:
|
|
||||||
$domains: '%employee_email_domains%'
|
|
||||||
|
|
||||||
App\Service\DomainConfigProvider:
|
App\Service\DomainConfigProvider:
|
||||||
arguments:
|
arguments:
|
||||||
$domainConfig: '%domain_config%'
|
$domainConfig: '%domain_config%'
|
||||||
|
|
||||||
App\Email\Mailer:
|
|
||||||
arguments:
|
|
||||||
$defaults:
|
|
||||||
from: '%default_email_from%'
|
|
||||||
to: '%default_email_to%'
|
|
||||||
|
|
||||||
App\Service\AccommodationBookingService:
|
|
||||||
arguments:
|
|
||||||
$accommodationEmail: '%accommodation_inquiry_email%'
|
|
||||||
|
|
||||||
App\Service\AccommodationTermsUrlProvider:
|
|
||||||
arguments:
|
|
||||||
$config:
|
|
||||||
AT: '%env(ACCOMMODATION_TERMS_AT)%'
|
|
||||||
CH: '%env(ACCOMMODATION_TERMS_CH)%'
|
|
||||||
IT: '%env(ACCOMMODATION_TERMS_IT)%'
|
|
||||||
|
|
||||||
App\Service\GroupsPriceCalculator:
|
|
||||||
arguments:
|
|
||||||
$config:
|
|
||||||
runningCostsEur: '%env(float:APP_RUNNING_COSTS_FACTOR_EUR)%'
|
|
||||||
runningCostsChf: '%env(float:APP_RUNNING_COSTS_FACTOR_CHF)%'
|
|
||||||
undersubscription30Eur: '%env(float:APP_UNDERSUBSCRIPTION_SURCHARGE_30_EUR)%'
|
|
||||||
undersubscription30Chf: '%env(float:APP_UNDERSUBSCRIPTION_SURCHARGE_30_CHF)%'
|
|
||||||
undersubscription40Eur: '%env(float:APP_UNDERSUBSCRIPTION_SURCHARGE_40_EUR)%'
|
|
||||||
undersubscription40Chf: '%env(float:APP_UNDERSUBSCRIPTION_SURCHARGE_40_CHF)%'
|
|
||||||
|
|
||||||
App\Validator\Constraints\ParticipantValidator:
|
|
||||||
arguments:
|
|
||||||
$bodyDimensionRanges: '%body_dimension_ranges%'
|
|
||||||
|
|
||||||
App\BpnConnect\AbstractApiClient:
|
|
||||||
abstract: true
|
|
||||||
arguments:
|
|
||||||
$baseUrl: '%env(default::BPN_CONNECT_BASE_URL)%'
|
|
||||||
$apiKey: '%env(default::BPN_CONNECT_API_KEY)%'
|
|
||||||
|
|
||||||
App\BpnConnect\ContingentsClient:
|
|
||||||
parent: App\BpnConnect\AbstractApiClient
|
|
||||||
|
|
||||||
App\ClickUp\ApiClient:
|
|
||||||
arguments:
|
|
||||||
$apiToken: '%env(default::CLICKUP_API_TOKEN)%'
|
|
||||||
$listId: '%env(default::CLICKUP_LIST_ID)%'
|
|
||||||
$templateId: '%env(default::CLICKUP_TEMPLATE_ID)%'
|
|
||||||
$baseUrl: '%env(default::CLICKUP_API_BASE_URL)%'
|
|
||||||
|
|
||||||
App\Mailjet\ApiClient:
|
|
||||||
arguments:
|
|
||||||
$apiKey: '%env(default::MAILJET_API_KEY)%'
|
|
||||||
$apiSecret: '%env(default::MAILJET_API_SECRET)%'
|
|
||||||
$apiBaseUrl: '%env(default::MAILJET_API_BASE_URL)%'
|
|
||||||
$defaultListId: '%env(default::MAILJET_DEFAULT_LIST_ID)%'
|
|
||||||
$contactMetadataFields: '%mailjet_contact_metadata_fields%'
|
|
||||||
|
|
||||||
App\EventListener\MaintenanceModeListener:
|
|
||||||
arguments:
|
|
||||||
$options:
|
|
||||||
enabled: '%env(bool:MAINTENANCE_MODE_ENABLED)%'
|
|
||||||
whitelisted_ips: '%env(csv:MAINTENANCE_MODE_IP_WHITELIST)%'
|
|
||||||
whitelisted_routes: '%env(csv:MAINTENANCE_MODE_ROUTE_WHITELIST)%'
|
|
||||||
whitelisted_paths: '%env(csv:MAINTENANCE_MODE_PATH_WHITELIST)%'
|
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ const {defineConfig} = require("cypress");
|
|||||||
|
|
||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
e2e: {
|
e2e: {
|
||||||
baseUrl: 'https://myep.ddev.site',
|
baseUrl: 'https://myep-next-booking.ddev.site',
|
||||||
supportFile: false,
|
supportFile: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
+21
-10
@@ -77,19 +77,31 @@ $rsyncOptions = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
host('prod')
|
host('prod')
|
||||||
->setHostname('dedi10193.your-server.de')
|
->setHostname('185.243.135.29')
|
||||||
->setRemoteUser('myepsf')
|
->setRemoteUser('p704161')
|
||||||
->setForwardAgent(true)
|
->setForwardAgent(true)
|
||||||
->setSshMultiplexing(true)
|
->setSshMultiplexing(true)
|
||||||
->setDeployPath('/usr/home/myepsf/public_html/prod')
|
->setDeployPath('/home/www/p704161/html/myep')
|
||||||
->set('bin/php', '/usr/bin/php')
|
->set('writable_mode', 'chmod')
|
||||||
->set('http_user', 'myepsf')
|
->set('http_user', 'p704161')
|
||||||
|
->set('bin/php', '/usr/local/bin/php')
|
||||||
->set('rsync_src', __DIR__)
|
->set('rsync_src', __DIR__)
|
||||||
->set('rsync', $rsyncOptions)
|
->set('rsync', $rsyncOptions)
|
||||||
->set('cachetool_args', '--web=SymfonyHttpClient --web-path={{current_path}}/public/ --web-url=https://my.ep-reisen.de')
|
->set('cachetool_args', '--web=SymfonyHttpClient --web-path={{current_path}}/public/ --web-url=https://my.ep-reisen.de')
|
||||||
->add('shared_files', [
|
;
|
||||||
'public/.htpasswd',
|
|
||||||
])
|
host('staging')
|
||||||
|
->setHostname('185.243.135.29')
|
||||||
|
->setRemoteUser('p704161')
|
||||||
|
->setForwardAgent(true)
|
||||||
|
->setSshMultiplexing(true)
|
||||||
|
->setDeployPath('/home/www/p704161/html/myep-staging')
|
||||||
|
->set('writable_mode', 'chmod')
|
||||||
|
->set('http_user', 'p704161')
|
||||||
|
->set('bin/php', '/usr/local/bin/php')
|
||||||
|
->set('rsync_src', __DIR__)
|
||||||
|
->set('rsync', $rsyncOptions)
|
||||||
|
->set('cachetool_args', '--web=SymfonyHttpClient --web-path={{current_path}}/public/ --web-url=https://my.ep-reisen.net')
|
||||||
;
|
;
|
||||||
|
|
||||||
task('deploy', [
|
task('deploy', [
|
||||||
@@ -109,8 +121,7 @@ task('deploy', [
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
task('deploy:assets', function () {
|
task('deploy:assets', function () {
|
||||||
runLocally('ddev exec npm ci');
|
runLocally('npm ci && npm run build');
|
||||||
runLocally('ddev exec npm run build');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
task('deploy:stop-workers', function () {
|
task('deploy:stop-workers', function () {
|
||||||
|
|||||||
@@ -0,0 +1,484 @@
|
|||||||
|
# Symfony 7.4 Upgrade Plan
|
||||||
|
|
||||||
|
**Project:** MyEP Next Booking
|
||||||
|
**Current Version:** Symfony 6.4.x (6.4.33)
|
||||||
|
**Target Version:** Symfony 7.4 (LTS)
|
||||||
|
**Date:** 2026-02-23
|
||||||
|
**Status:** Ready for Upgrade
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Executive Summary
|
||||||
|
|
||||||
|
**Estimated Effort: LOW**
|
||||||
|
**Risk Level: LOW**
|
||||||
|
**Status: READY FOR UPGRADE**
|
||||||
|
|
||||||
|
The MyEP Next Booking application is well-positioned for a Symfony 7.4 upgrade. The codebase follows modern Symfony best practices, uses PHP 8 features extensively, and avoids most deprecated patterns. All pre-upgrade fixes have been completed.
|
||||||
|
|
||||||
|
**Key Facts:**
|
||||||
|
- Symfony 7.4 was released on November 27, 2025
|
||||||
|
- Requires PHP 8.2.0+ (current: PHP 8.3.30)
|
||||||
|
- LTS version with bug fixes until November 2028, security fixes until November 2029
|
||||||
|
- Symfony 8.0 = Symfony 7.4 minus deprecations (same features, PHP 8.4 required)
|
||||||
|
|
||||||
|
**Pre-Upgrade Status:**
|
||||||
|
- PHP version: 8.3.30 (compatible; Symfony 7.4 requires 8.2+)
|
||||||
|
- OAuth2 Server Bundle: 1.1.0 (compatible)
|
||||||
|
- Stimulus Bundle: 2.32.0 (compatible)
|
||||||
|
- Doctrine ORM: 3.6.0 (compatible)
|
||||||
|
- All 433 tests passing
|
||||||
|
- Email validation mode: Configured (html5)
|
||||||
|
- 18 test-only deprecation warnings (cosmetic, do not affect runtime)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Previous Plan Status
|
||||||
|
|
||||||
|
The previous upgrade plan (dated 2025-12-06) has been updated. All pre-upgrade tasks are now complete.
|
||||||
|
|
||||||
|
### Completed Items
|
||||||
|
| Item | Previous Status | Current Status |
|
||||||
|
|------|----------------|----------------|
|
||||||
|
| OAuth2 Server Bundle | 1.0.0 | **1.1.0 installed** |
|
||||||
|
| Stimulus Bundle | 2.31.0 | **2.32.0 installed** |
|
||||||
|
| PHP 8.2 deprecations (ParticipantDto::$touched) | Planned | **Completed** |
|
||||||
|
| Doctrine config (controller_resolver) | Planned | **Completed** |
|
||||||
|
| SecurityController null safety | Needs verification | **Completed** (uses `?->` and `??`) |
|
||||||
|
| Email validator "loose" mode deprecation | Needs fix | **Completed** (validator.yaml configured) |
|
||||||
|
| Failing test in BookingDataProcessorTest | Unrelated to upgrade | **Resolved** (test now passes) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Current State Analysis
|
||||||
|
|
||||||
|
### PHP Version Compatibility
|
||||||
|
|
||||||
|
- **Current Runtime:** PHP 8.3.30
|
||||||
|
- **Composer Platform:** PHP 8.3.30
|
||||||
|
- **Symfony 7.4 Required:** PHP 8.2.0+
|
||||||
|
- **Status:** Compatible
|
||||||
|
|
||||||
|
### Installed Dependencies
|
||||||
|
|
||||||
|
#### Core Symfony Packages (6.4.x)
|
||||||
|
All 33 Symfony packages currently at 6.4.x need version constraint updates to 7.4.*
|
||||||
|
|
||||||
|
#### Third-Party Bundles - Compatible
|
||||||
|
| Package | Installed | Symfony 7.4 Compatible |
|
||||||
|
|---------|-----------|------------------------|
|
||||||
|
| `doctrine/orm` | 3.6.0 | Yes |
|
||||||
|
| `doctrine/doctrine-bundle` | 2.18.1 | Yes |
|
||||||
|
| `doctrine/doctrine-migrations-bundle` | 3.7.0 | Yes |
|
||||||
|
| `league/oauth2-server-bundle` | 1.1.0 | Yes |
|
||||||
|
| `symfony/stimulus-bundle` | 2.32.0 | Yes |
|
||||||
|
| `symfony/webpack-encore-bundle` | 2.4.0 | Yes |
|
||||||
|
| `symfony/monolog-bundle` | 3.11.0 | Yes |
|
||||||
|
| `zenstruck/schedule-bundle` | 1.9.0 | Yes |
|
||||||
|
| `league/flysystem-bundle` | 3.6.1 | Yes |
|
||||||
|
| `league/flysystem-sftp-v3` | 3.30.0 | Yes |
|
||||||
|
| `nelexa/zip` | 4.0.2 | Yes (verify post-upgrade) |
|
||||||
|
| `spatie/crypto` | 2.1.1 | Yes |
|
||||||
|
| `easycorp/easyadmin-bundle` | 4.29.1 (^4.27) | Yes |
|
||||||
|
|
||||||
|
### Code Quality Assessment
|
||||||
|
|
||||||
|
The codebase implements modern Symfony/PHP patterns:
|
||||||
|
- All controllers use PHP 8 attributes (`#[Route]`)
|
||||||
|
- Constructor property promotion used throughout
|
||||||
|
- Strict types enforced on all files
|
||||||
|
- Typed properties and return type declarations
|
||||||
|
- Entities use PHP 8 attributes for ORM mapping
|
||||||
|
- Modern security system (AbstractLoginFormAuthenticator)
|
||||||
|
- No deprecated annotation usage
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pre-Upgrade Fixes - COMPLETED
|
||||||
|
|
||||||
|
### 1. Email Validator "loose" Mode Deprecation - DONE
|
||||||
|
|
||||||
|
**Issue:** The default mode for `#[Assert\Email]` is "loose", which is deprecated in Symfony 6.2+ and removed in Symfony 7.0.
|
||||||
|
|
||||||
|
**Resolution:** Framework default configured in `config/packages/validator.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
framework:
|
||||||
|
validation:
|
||||||
|
email_validation_mode: html5
|
||||||
|
```
|
||||||
|
|
||||||
|
All Email constraints in the codebase also explicitly use `mode: 'strict'`:
|
||||||
|
- `src/Form/Model/ParticipantDto.php` - Has `mode: 'strict'`
|
||||||
|
- `src/Form/Model/RegistrationDto.php` - Has `mode: 'strict'`
|
||||||
|
- `src/BusProNet/Model/Communication.php` - Has `mode: 'strict'`
|
||||||
|
- `src/Controller/ResetPasswordController.php` - Has `mode: 'strict'`
|
||||||
|
|
||||||
|
### 2. Test Deprecation Warnings - COSMETIC ONLY
|
||||||
|
|
||||||
|
**Issue:** 18 deprecation warnings appear during test runs.
|
||||||
|
|
||||||
|
**Cause:** `ParticipantEditDtoTest` creates a standalone validator using `Validation::createValidatorBuilder()` which bypasses the framework's `validator.yaml` configuration.
|
||||||
|
|
||||||
|
**Impact:** These warnings are cosmetic and do not affect:
|
||||||
|
- Runtime behavior (all Email constraints use explicit `mode: 'strict'`)
|
||||||
|
- Symfony 7.4 compatibility (no code changes needed)
|
||||||
|
- Test results (all 433 tests pass)
|
||||||
|
|
||||||
|
**Note:** Warnings can be silenced with `SYMFONY_DEPRECATIONS_HELPER=disabled` if desired.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Required Changes
|
||||||
|
|
||||||
|
### 1. Update Composer Version Constraints
|
||||||
|
|
||||||
|
**File:** `composer.json`
|
||||||
|
|
||||||
|
#### Symfony Core Packages
|
||||||
|
Update all packages from `6.4.*` to `7.4.*`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"require": {
|
||||||
|
"symfony/asset": "7.4.*",
|
||||||
|
"symfony/console": "7.4.*",
|
||||||
|
"symfony/doctrine-messenger": "7.4.*",
|
||||||
|
"symfony/dom-crawler": "7.4.*",
|
||||||
|
"symfony/dotenv": "7.4.*",
|
||||||
|
"symfony/expression-language": "7.4.*",
|
||||||
|
"symfony/form": "7.4.*",
|
||||||
|
"symfony/framework-bundle": "7.4.*",
|
||||||
|
"symfony/html-sanitizer": "7.4.*",
|
||||||
|
"symfony/http-client": "7.4.*",
|
||||||
|
"symfony/intl": "7.4.*",
|
||||||
|
"symfony/lock": "7.4.*",
|
||||||
|
"symfony/mailer": "7.4.*",
|
||||||
|
"symfony/mime": "7.4.*",
|
||||||
|
"symfony/notifier": "7.4.*",
|
||||||
|
"symfony/process": "7.4.*",
|
||||||
|
"symfony/property-access": "7.4.*",
|
||||||
|
"symfony/property-info": "7.4.*",
|
||||||
|
"symfony/runtime": "7.4.*",
|
||||||
|
"symfony/security-bundle": "7.4.*",
|
||||||
|
"symfony/serializer": "7.4.*",
|
||||||
|
"symfony/string": "7.4.*",
|
||||||
|
"symfony/translation": "7.4.*",
|
||||||
|
"symfony/twig-bundle": "7.4.*",
|
||||||
|
"symfony/uid": "7.4.*",
|
||||||
|
"symfony/validator": "7.4.*",
|
||||||
|
"symfony/web-link": "7.4.*",
|
||||||
|
"symfony/yaml": "7.4.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Development Packages
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"require-dev": {
|
||||||
|
"symfony/browser-kit": "7.4.*",
|
||||||
|
"symfony/css-selector": "7.4.*",
|
||||||
|
"symfony/debug-bundle": "7.4.*",
|
||||||
|
"symfony/maker-bundle": "^1.0",
|
||||||
|
"symfony/phpunit-bridge": "7.4.*",
|
||||||
|
"symfony/stopwatch": "7.4.*",
|
||||||
|
"symfony/web-profiler-bundle": "7.4.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Keep `symfony/maker-bundle` at `^1.0` (compatible with Symfony 7.4).
|
||||||
|
|
||||||
|
### 2. Update Symfony Flex Configuration
|
||||||
|
|
||||||
|
**File:** `composer.json`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"extra": {
|
||||||
|
"symfony": {
|
||||||
|
"allow-contrib": true,
|
||||||
|
"require": "7.4.*",
|
||||||
|
"docker": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Potential Breaking Changes
|
||||||
|
|
||||||
|
### 1. Symfony Core Changes (Symfony 7.0 -> 7.4)
|
||||||
|
|
||||||
|
Symfony 7.0 removed all deprecated features from 6.4. Our codebase is fully compliant:
|
||||||
|
|
||||||
|
- **Native Return Types:** Already implemented
|
||||||
|
- **Attribute-based Configuration:** Already implemented
|
||||||
|
- **Security System:** Modern authenticator pattern in use
|
||||||
|
- **Form System:** Modern approach in use
|
||||||
|
- **No Annotation Usage:** All using PHP 8 attributes
|
||||||
|
|
||||||
|
### 2. Email Validation Mode
|
||||||
|
|
||||||
|
**Breaking Change:** The default `mode` for `Email` constraint changes from `loose` to `html5` in Symfony 7.0.
|
||||||
|
|
||||||
|
**Status:** Already handled - all constraints use explicit `mode: 'strict'` and framework default is set to `html5`.
|
||||||
|
|
||||||
|
### 3. Deprecation Cleanup
|
||||||
|
|
||||||
|
Deprecations accumulated in Symfony 7.1-7.4 will need review:
|
||||||
|
- Check Symfony Profiler after upgrade for any new deprecation warnings
|
||||||
|
- Review [UPGRADE-7.0.md](https://github.com/symfony/symfony/blob/7.0/UPGRADE-7.0.md)
|
||||||
|
- Review [UPGRADE-7.4.md](https://github.com/symfony/symfony/blob/7.4/UPGRADE-7.4.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Upgrade Procedure
|
||||||
|
|
||||||
|
### Phase 1: Preparation (15 minutes)
|
||||||
|
|
||||||
|
1. **Create Feature Branch**
|
||||||
|
```bash
|
||||||
|
git checkout -b feature/symfony-7.4-upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Backup Current State**
|
||||||
|
```bash
|
||||||
|
git tag before-symfony-7.4-upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Dependency Updates (30 minutes - 1 hour)
|
||||||
|
|
||||||
|
1. **Update composer.json**
|
||||||
|
- Update all Symfony package constraints to `7.4.*`
|
||||||
|
- Update `extra.symfony.require` to `"7.4.*"`
|
||||||
|
- Consider tightening `php` to `>=8.2` (Symfony 7.4 requires 8.2+; optional if already on 8.3)
|
||||||
|
|
||||||
|
2. **Run Composer Update**
|
||||||
|
```bash
|
||||||
|
ddev composer update
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Review Recipe Updates**
|
||||||
|
```bash
|
||||||
|
ddev composer recipes
|
||||||
|
```
|
||||||
|
Review any recipe updates from Symfony Flex and apply as needed.
|
||||||
|
|
||||||
|
4. **Clear All Caches**
|
||||||
|
```bash
|
||||||
|
ddev exec bin/console cache:clear
|
||||||
|
ddev exec bin/console cache:warmup
|
||||||
|
ddev exec "php -r 'opcache_reset();'"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Testing & Validation (1-2 hours)
|
||||||
|
|
||||||
|
1. **Run PHPUnit Test Suite**
|
||||||
|
```bash
|
||||||
|
ddev exec ./vendor/bin/phpunit
|
||||||
|
```
|
||||||
|
Check for:
|
||||||
|
- Test failures
|
||||||
|
- Deprecation warnings
|
||||||
|
- Fatal errors
|
||||||
|
|
||||||
|
2. **Run PHP-CS-Fixer**
|
||||||
|
```bash
|
||||||
|
ddev php vendor/bin/php-cs-fixer fix --dry-run --diff
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Manual Testing - Critical Paths**
|
||||||
|
- [ ] User authentication (BPN API login)
|
||||||
|
- [ ] OAuth2 token generation (`/token` endpoint)
|
||||||
|
- [ ] OAuth2 authorization flow (`/authorize` endpoint)
|
||||||
|
- [ ] API endpoints (all `/api/*` routes)
|
||||||
|
- [ ] Booking creation flow (Steps 1-4)
|
||||||
|
- [ ] Participant form rendering and submission
|
||||||
|
- [ ] Dynamic field updates (HTMX)
|
||||||
|
- [ ] Real-time pricing calculations
|
||||||
|
- [ ] Insurance selection and filtering
|
||||||
|
- [ ] Transportation service selection
|
||||||
|
- [ ] Payment method selection
|
||||||
|
- [ ] Final booking submission to BPN API
|
||||||
|
- [ ] Booking edit functionality
|
||||||
|
- [ ] PDF/XML download functionality
|
||||||
|
- [ ] User registration
|
||||||
|
- [ ] Password reset
|
||||||
|
- [ ] Personal data management
|
||||||
|
- [ ] EasyAdmin CRUD / admin panels
|
||||||
|
|
||||||
|
4. **Check Symfony Profiler**
|
||||||
|
- Review deprecation warnings (should be minimal)
|
||||||
|
- Check performance metrics
|
||||||
|
- Verify no errors in logs
|
||||||
|
|
||||||
|
5. **Test Frontend Assets**
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
- Verify Stimulus controllers load correctly
|
||||||
|
- Test HTMX interactions
|
||||||
|
- Verify Toastify notifications
|
||||||
|
- Check TailwindCSS compilation
|
||||||
|
|
||||||
|
6. **Verify ZIP File Functionality**
|
||||||
|
- Test any features using `nelexa/zip` (package last updated 2022)
|
||||||
|
- Verify document generation and downloads work correctly
|
||||||
|
|
||||||
|
### Phase 4: Bug Fixes (0-2 hours)
|
||||||
|
|
||||||
|
Address any issues discovered during testing:
|
||||||
|
|
||||||
|
1. **Review Error Logs**
|
||||||
|
```bash
|
||||||
|
ddev logs
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Fix Any Remaining Deprecations**
|
||||||
|
- Use Symfony Profiler to identify deprecations
|
||||||
|
- Update code to use modern alternatives
|
||||||
|
|
||||||
|
### Phase 5: Deployment (1 hour)
|
||||||
|
|
||||||
|
1. **Deploy to Staging**
|
||||||
|
```bash
|
||||||
|
ddev exec vendor/bin/dep deploy staging
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Staging Validation**
|
||||||
|
- Full regression testing
|
||||||
|
- Performance testing
|
||||||
|
|
||||||
|
3. **Production Deployment**
|
||||||
|
- Schedule maintenance window
|
||||||
|
- Deploy during low-traffic period
|
||||||
|
- Monitor error logs closely
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rollback Plan
|
||||||
|
|
||||||
|
### Immediate Rollback
|
||||||
|
|
||||||
|
If critical issues are discovered:
|
||||||
|
|
||||||
|
1. **Git Revert**
|
||||||
|
```bash
|
||||||
|
git checkout master
|
||||||
|
git reset --hard before-symfony-7.4-upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Restore Dependencies**
|
||||||
|
```bash
|
||||||
|
ddev composer install
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Clear Caches**
|
||||||
|
```bash
|
||||||
|
ddev exec bin/console cache:clear
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production Rollback
|
||||||
|
|
||||||
|
If issues occur in production:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ddev exec vendor/bin/dep rollback production
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing Checklist
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
- [ ] All PHPUnit tests pass (433 tests)
|
||||||
|
- [ ] No new deprecation warnings
|
||||||
|
- [ ] Code coverage maintained
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
- [ ] BusProNet API integration
|
||||||
|
- [ ] XML parsing and data processing
|
||||||
|
- [ ] Booking price calculations
|
||||||
|
- [ ] Insurance assignment logic
|
||||||
|
- [ ] Field handler system
|
||||||
|
|
||||||
|
### Functional Tests
|
||||||
|
- [ ] User authentication flow
|
||||||
|
- [ ] OAuth2 token generation
|
||||||
|
- [ ] API endpoints respond correctly
|
||||||
|
- [ ] Booking creation (all steps)
|
||||||
|
- [ ] Form validation
|
||||||
|
- [ ] HTMX partial updates
|
||||||
|
- [ ] Dynamic field state changes
|
||||||
|
- [ ] Real-time pricing updates
|
||||||
|
- [ ] Toast notifications
|
||||||
|
|
||||||
|
### Browser Testing
|
||||||
|
- [ ] Chrome (latest)
|
||||||
|
- [ ] Firefox (latest)
|
||||||
|
- [ ] Safari (latest)
|
||||||
|
- [ ] Mobile browsers
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Timeline Estimate
|
||||||
|
|
||||||
|
| Phase | Estimated Time | Notes |
|
||||||
|
|-------|---------------|-------|
|
||||||
|
| **Phase 1:** Preparation | 15 min | Branch creation |
|
||||||
|
| **Phase 2:** Dependency Updates | 30 min - 1 hour | composer.json updates, composer update |
|
||||||
|
| **Phase 3:** Testing & Validation | 1-2 hours | Automated + manual testing |
|
||||||
|
| **Phase 4:** Bug Fixes | 0-2 hours | Address any issues |
|
||||||
|
| **Phase 5:** Deployment | 1 hour | Staging deployment |
|
||||||
|
| **Total** | **2.5-6 hours** | Average: **3.5 hours** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Future Considerations: Symfony 8.0
|
||||||
|
|
||||||
|
Symfony 8.0 was released alongside 7.4 (November 2025) with identical features but:
|
||||||
|
- Requires PHP 8.4
|
||||||
|
- No deprecation layers (all deprecated code removed)
|
||||||
|
- Shorter support window (8 months)
|
||||||
|
|
||||||
|
**Recommendation:** Stay on Symfony 7.4 LTS for stability. Plan PHP 8.4 upgrade separately, then consider Symfony 8.x path.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
- [ ] All PHPUnit tests pass (433 tests)
|
||||||
|
- [ ] No deprecation warnings in Symfony Profiler
|
||||||
|
- [ ] All critical user flows functional
|
||||||
|
- [ ] OAuth2 authentication working
|
||||||
|
- [ ] BPN API integration operational
|
||||||
|
- [ ] Booking creation flow complete
|
||||||
|
- [ ] Frontend assets compile and load
|
||||||
|
- [ ] Performance metrics comparable or better
|
||||||
|
- [ ] No errors in production logs after 24 hours
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Additional Resources
|
||||||
|
|
||||||
|
### Official Symfony Documentation
|
||||||
|
- [Upgrade Major Version Guide](https://symfony.com/doc/current/setup/upgrade_major.html)
|
||||||
|
- [UPGRADE-7.0.md](https://github.com/symfony/symfony/blob/7.0/UPGRADE-7.0.md)
|
||||||
|
- [UPGRADE-7.4.md](https://github.com/symfony/symfony/blob/7.4/UPGRADE-7.4.md)
|
||||||
|
- [Symfony 7.4 Release Notes](https://symfony.com/releases/7.4)
|
||||||
|
- [Preparing for Symfony 7.4 and 8.0](https://symfony.com/blog/preparing-for-symfony-7-4-and-symfony-8-0)
|
||||||
|
|
||||||
|
### Bundle-Specific Documentation
|
||||||
|
- [OAuth2 Server Bundle](https://github.com/thephpleague/oauth2-server-bundle)
|
||||||
|
- [Doctrine ORM 3.x Documentation](https://www.doctrine-project.org/projects/orm.html)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Document Version:** 7.0
|
||||||
|
**Previous Version:** 6.0 (dated 2026-01-09)
|
||||||
|
**Last Updated:** 2026-02-23
|
||||||
|
**Status:** Ready for Symfony 7.4 Upgrade (updated for current project state)
|
||||||
@@ -1,414 +0,0 @@
|
|||||||
# API Consumer Guide
|
|
||||||
|
|
||||||
This document describes the public JSON API exposed under `/api`, written for developers implementing a **client** in another application. It is self-contained: no access to this codebase is required to use it.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
A Symfony application exposing a read-mostly JSON API under the `/api` prefix. All `/api/*` requests are stateless and authenticated via OAuth2 Bearer tokens (League OAuth2 Server, JWT access tokens). Responses are `application/json`. HTTPS is enforced on all routes.
|
|
||||||
|
|
||||||
## Authentication
|
|
||||||
|
|
||||||
- Token endpoint: `POST /token` (unauthenticated).
|
|
||||||
- Enabled grants: **client_credentials**, **authorization_code** (PKCE required for public clients), **refresh_token**. Password and implicit grants are disabled.
|
|
||||||
- Access token TTL: **10 minutes** — clients must refresh/re-request frequently.
|
|
||||||
- Available scopes: `email`, `id`, `profile`, `roles`, `api`. Default scope if none requested: `email`.
|
|
||||||
- Each granted scope maps to a role `ROLE_OAUTH2_<SCOPE_UPPERCASE>` (e.g. scope `api` → `ROLE_OAUTH2_API`).
|
|
||||||
- Send the token as `Authorization: Bearer <access_token>`.
|
|
||||||
|
|
||||||
Scope required per endpoint:
|
|
||||||
|
|
||||||
| Scope needed | Endpoints |
|
|
||||||
|---|---|
|
|
||||||
| `api` | everything except the two below |
|
|
||||||
| `profile` | `GET /api/crm-attributes` |
|
|
||||||
| `email` | `GET /api/userinfo` (optional extra scopes `id`, `profile`, `roles` widen the response) |
|
|
||||||
|
|
||||||
`/api/userinfo` and `/api/crm-attributes` act on the **authenticated end user**, so they require an authorization_code token issued for a user. All other endpoints are machine-to-machine and work with client_credentials.
|
|
||||||
|
|
||||||
### Machine-to-machine example
|
|
||||||
|
|
||||||
```
|
|
||||||
POST /token
|
|
||||||
Content-Type: application/x-www-form-urlencoded
|
|
||||||
|
|
||||||
grant_type=client_credentials&client_id=...&client_secret=...&scope=api
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/hotels
|
|
||||||
Authorization: Bearer eyJ0eXAi...
|
|
||||||
```
|
|
||||||
|
|
||||||
## Error conventions
|
|
||||||
|
|
||||||
There is no single error envelope; three shapes occur:
|
|
||||||
|
|
||||||
- `{"message": "..."}` — commonly with 404 / 400.
|
|
||||||
- `{"error": "...", "type": "api_error"}` — upstream failures on some endpoints. **Caution:** on `/api/products` and `/api/travels/{dateId}/{hotelId}/{dateTo}/availability` this shape is returned with HTTP **200**, so clients must check for the presence of an `error` key, not only the status code.
|
|
||||||
- `{"success": false, "message": "..."}` — the write endpoints (contact form, newsletter).
|
|
||||||
|
|
||||||
Validation failures on endpoints using mapped query strings / payloads (`/api/contingents/*`, `/api/newsletter-subscriptions`) return **400** with Symfony's constraint-violation body. Missing/invalid token yields **401**; insufficient scope yields **403**.
|
|
||||||
|
|
||||||
The HTTP methods listed below are the intended ones. Several read routes do not restrict methods server-side; clients should still use `GET` for them.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Endpoints
|
|
||||||
|
|
||||||
### Reference data (BusProNet master data)
|
|
||||||
|
|
||||||
#### `GET /api/hotels` — scope `api`
|
|
||||||
|
|
||||||
List of hotels. Array of objects: `id` (int), `code` (string), `name`, `city`, `street`, `phone`, `country`.
|
|
||||||
|
|
||||||
#### `GET /api/hotels/{id}` — scope `api`
|
|
||||||
|
|
||||||
Single hotel by numeric id, same field set as the list. `404 {"message":"Not found"}` if unknown.
|
|
||||||
|
|
||||||
#### `GET /api/countries` — scope `api`
|
|
||||||
|
|
||||||
Array of `{id, name, token, nationality}`.
|
|
||||||
|
|
||||||
#### `GET /api/products` — scope `api`
|
|
||||||
|
|
||||||
Product master data from the upstream BusProNet system, cached server-side for 1 hour. On upstream failure returns `{"error": "...", "type": "api_error"}` **with status 200**.
|
|
||||||
|
|
||||||
#### `GET /api/last-update` — scope `api`
|
|
||||||
|
|
||||||
Timestamp of the last upstream data export: `{"timestamp": "YYYY-MM-DD HH:MM:SS"}`. Note: if no export marker file exists the current implementation errors (500) rather than returning null — treat a non-JSON/500 response as "unknown".
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Pickups (bus boarding points)
|
|
||||||
|
|
||||||
#### `GET /api/pickups` — scope `api`
|
|
||||||
|
|
||||||
Array of `{id, code, city, postalCode, street}`.
|
|
||||||
|
|
||||||
#### `GET /api/pickups/{id}` — scope `api`
|
|
||||||
|
|
||||||
Single pickup by numeric id, same fields as the list.
|
|
||||||
|
|
||||||
#### `GET /api/pickups-planning/{travelCode}` — scope `api`
|
|
||||||
|
|
||||||
Planned outbound (HIN) boarding stops for one travel code. Returns an array, chronologically sorted:
|
|
||||||
|
|
||||||
```json
|
|
||||||
[{"city": "...", "location": "...", "datetime": "YYYY-MM-DD HH:MM", "busNumber": "..."}]
|
|
||||||
```
|
|
||||||
|
|
||||||
`404 {"error":"Planning data not available"}` if no planning has been uploaded yet, `404 {"error":"Travel code not found"}` for an unknown code, `500 {"error":"Failed to read planning data"}` on storage errors.
|
|
||||||
|
|
||||||
#### `POST /api/pickups-planning` — scope `api`
|
|
||||||
|
|
||||||
Bulk-replaces the planning dataset. Body: a JSON **array** of raw planning rows using the upstream German keys:
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"PlanungsArt": "HIN",
|
|
||||||
"Reisecode": "ABC123",
|
|
||||||
"Zustieg/Ausstieg Ort": "Köln",
|
|
||||||
"Zustieg/Ausstieg Strasse": "Hauptbahnhof",
|
|
||||||
"Zeit": "24.12.2026 07:30",
|
|
||||||
"BusNummer": "1"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
Rows without `PlanungsArt === "HIN"` or without `Reisecode` are dropped; duplicates (same city + location + datetime + bus) are de-duplicated; results are grouped by `Reisecode` and sorted by time. Response `{"status":"ok"}` (200). `400 {"error":"Invalid JSON payload"}`, `500 {"error":"Failed to write pickups planning data"}`.
|
|
||||||
|
|
||||||
This is a full overwrite, not a merge.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Travels
|
|
||||||
|
|
||||||
#### `GET /api/travels` — scope `api`
|
|
||||||
|
|
||||||
Index/mapping of all known travel dates. Object keyed by numeric travel id, each value `{id, code, label, dateFrom, dateTo, hotels: [...]}`.
|
|
||||||
|
|
||||||
#### `GET /api/travels/{dateId}/{hotelId}` — scope `api`
|
|
||||||
|
|
||||||
Single travel by **numeric** ids (`hotelId` optional). Route matches only when both segments are digits.
|
|
||||||
|
|
||||||
#### `GET /api/travels/{dateCode}/{hotelCode}` — scope `api`
|
|
||||||
|
|
||||||
Same, addressed by **string** codes (`hotelCode` optional). The date code is sanitized (separators stripped) before lookup. `404 {"message":"Not found"}` if the date code is unknown, `404 {"message":"Travel not found"}` if no data exists.
|
|
||||||
|
|
||||||
Both variants accept optional query parameters:
|
|
||||||
|
|
||||||
- `source=local` — force the local XML/snapshot data.
|
|
||||||
- `source=remote` — force a live upstream call.
|
|
||||||
- `prefer_remote=1` — used only when `source` is absent; prefers upstream with local fallback.
|
|
||||||
|
|
||||||
Response fields: `id`, `code`, `productCode`, `productId`, `label`, `hotelId`, `hotel` (hotel object), `dateFrom`, `dateTo`, `type`, `status`, `allowedBookingStatus[]`, `priceFrom`, `selectionGroups[]`, `additionalServices[]`, `transportationServices[]`, `pickups[]`, `dropOffs[]`, `rooms[]`, `guide`, `insurances[]`.
|
|
||||||
|
|
||||||
#### `GET /api/travels/{dateId}/{hotelId}/{dateTo}/availability` — scope `api`
|
|
||||||
|
|
||||||
Live hotel availability from the upstream system. `dateTo` must be `Y-m-d`. On upstream failure returns `{"error":"...","type":"api_error"}` **with status 200**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Contingents & accommodation prices
|
|
||||||
|
|
||||||
#### `GET /api/contingents/prices?hotelCode=…&year=…` — scope `api`
|
|
||||||
|
|
||||||
Price timeline for one accommodation and calendar year, collapsed into contiguous segments (adjacent days with the same effective price are merged into one row). The timeline never starts before today; if the requested year is entirely in the past the response is `[]`.
|
|
||||||
|
|
||||||
- `hotelCode`: required, max 16 chars, `[A-Za-z0-9_-]+` (matches the accommodation's calendar code).
|
|
||||||
- `year`: required int, 1000–9999.
|
|
||||||
|
|
||||||
Each row:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"dateFrom": "2026-06-01",
|
|
||||||
"dateTo": "2026-06-14",
|
|
||||||
"season": "high|low|...|null",
|
|
||||||
"includedPax": 2,
|
|
||||||
"pricePerNight": 89.5,
|
|
||||||
"priceAdditionalPerson": 25.0,
|
|
||||||
"defaultPricePerNight": 99.0,
|
|
||||||
"defaultPriceAdditionalPerson": 30.0,
|
|
||||||
"currency": "EUR",
|
|
||||||
"type": "discount|null"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Prices are **decimal major units** (converted from integer minor units server-side). `default*` fields are non-null only when the winning price is a discount, and then carry the undiscounted price. `400 {"error":"Hotel not found for hotelCode."}` for an unknown code; `400` with violations for invalid parameters.
|
|
||||||
|
|
||||||
#### `GET /api/contingents/calendar?hotelCode=…&dateFrom=…&dateTo=…` — scope `api`
|
|
||||||
|
|
||||||
Per-day availability enriched with prices.
|
|
||||||
|
|
||||||
`dateFrom` and `dateTo` are **optional, and must be supplied together** — one without the other is a `400`. Both are `Y-m-d` and **inclusive** (`dateTo` is the last night, not the checkout day); when supplied, the range may not exceed 366 days and `dateTo` must not precede `dateFrom`.
|
|
||||||
|
|
||||||
**Omit both to get the full priced span.** The range is then derived from the accommodation's persisted prices, `MIN(dateFrom)` to `MAX(dateTo)`, and never starts before today. This is the way to fetch everything a hotel sells in one call, and it is not subject to the 366-day cap — a hotel priced over two seasons returns well over a year of entries.
|
|
||||||
|
|
||||||
Full-span responses are contiguous, exactly like ranged ones: every day between the derived bounds gets an entry, including days no price covers. Those come back `BLOCKED` with `null` price fields — a hotel closed over winter reports the whole closure day by day rather than skipping it, so a consumer keying off `status` always finds one.
|
|
||||||
|
|
||||||
The response is `[]` — a `200`, not a `502` — when the hotel has no prices at all, or when every priced period has already ended.
|
|
||||||
|
|
||||||
Availability is served from a **local snapshot** refreshed over a 24-month horizon — every 15 minutes during the day, hourly overnight — not fetched from the upstream contingent service per request. The endpoint therefore responds in single-digit milliseconds and stays available during an upstream outage, at the cost of being at most one sync interval stale.
|
|
||||||
|
|
||||||
The response format is unchanged from the previous upstream-backed implementation: same fields, same order, same types, same `status` values. One behavioural note — the endpoint returns **one entry per day of the range**, whether that range came from `dateFrom`/`dateTo` or was derived from the prices. Previously days the upstream service did not mention were simply absent, so a response may contain days it would not have contained before. It is a superset, never a different shape.
|
|
||||||
|
|
||||||
Two kinds of day resolve to `BLOCKED` regardless of the underlying contingent:
|
|
||||||
|
|
||||||
- **days in the past** — the snapshot is maintained from today forward only (`prices` likewise refuses past ranges)
|
|
||||||
- **days beyond the synced horizon** — currently 24 months out. Prices are often maintained further ahead than the contingent is synced, so a full-span response can end in a run of `BLOCKED` days that simply have no availability data yet.
|
|
||||||
|
|
||||||
Each entry:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"date": "2026-06-01",
|
|
||||||
"status": "Free|Blocked|...",
|
|
||||||
"type": "discount|null",
|
|
||||||
"pricePerNight": 89.5,
|
|
||||||
"defaultPricePerNight": 99.0,
|
|
||||||
"priceAdditionalPerson": 25.0,
|
|
||||||
"defaultPriceAdditionalPerson": 30.0,
|
|
||||||
"currency": "EUR",
|
|
||||||
"includedPax": 2,
|
|
||||||
"minNights": 2
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Important rule: **a day with no maintained price is forced to `Blocked`** regardless of what the upstream contingent says. Price fields may be `null` in that case. This applies to both modes.
|
|
||||||
|
|
||||||
Errors: `400 {"error":"Hotel not found for hotelCode."}`, `400` with violations for invalid parameters.
|
|
||||||
|
|
||||||
`502 {"error":"Failed to fetch contingent data."}` is still returned — with the same body as before — but it now means **"no usable local snapshot for this hotel"** rather than "the upstream call failed": either the hotel has never synced successfully, or its last successful sync is more than 6 hours old. Treat it as before: the data is unavailable, not empty. This matters because the alternative would be a `200` reporting every day as `BLOCKED`, which is indistinguishable from a genuinely fully-booked hotel.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Operating the snapshot (deployment)
|
|
||||||
|
|
||||||
The snapshot is a new table populated by the scheduler, so it is **empty immediately after a deploy that runs the migration**. Until the first successful sync every hotel returns `502` — deliberately, so no consumer caches a fully-blocked calendar as if it were real availability.
|
|
||||||
|
|
||||||
Rollout order:
|
|
||||||
|
|
||||||
1. **Confirm `BPN_CONNECT_API_KEY` is valid for the target environment before deploying.** If it is rejected the sync cannot populate anything and the endpoint stays at `502`.
|
|
||||||
2. Deploy; `database:migrate` creates the empty tables.
|
|
||||||
3. Run `bin/console app:bpn:sync-contingents` on the host, promptly and ideally off-peak — a consumer that page-caches whatever it renders from a `502` may hold that for the length of its own cache period.
|
|
||||||
4. Verify: `contingent_sync_state.synced_at` populated and `failure_count` at 0 for every hotel, then spot-check one calendar response.
|
|
||||||
|
|
||||||
Thereafter the scheduler keeps it current; a sync failing for one hotel puts only that hotel into `502` and leaves the rest serving normally.
|
|
||||||
|
|
||||||
### Freshness and propagation
|
|
||||||
|
|
||||||
There is deliberately **no change-notification endpoint**. Availability changes slowly, and the measured cost of simply refetching on a TTL is low enough that a polling/revision mechanism was not worth the extra moving parts on either side.
|
|
||||||
|
|
||||||
That leaves two levers, and consumers should know both:
|
|
||||||
|
|
||||||
- **Their own cache TTL** bounds how stale a rendered calendar can be. A one-hour TTL is the current agreed budget.
|
|
||||||
- **Direct cache invalidation** is the only fast path when something must propagate sooner — a hotel closing, or bad availability that got cached. Consumers should keep this targeted rather than flushing everything; the TYPO3 site, for example, tags its cached calendar and price entries per hotel, so a single hotel can be flushed and re-warmed on its own.
|
|
||||||
|
|
||||||
On this side, staleness is bounded by the sync cadence and hard-capped by the 6-hour guard: past that, the endpoint returns `502` rather than presenting stale availability as current.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Accommodation bookings (group bookings)
|
|
||||||
|
|
||||||
#### `GET /api/accommodation-bookings/{uuid}` — scope `api`
|
|
||||||
|
|
||||||
Single booking by UUID. `404 {"message":"Not found"}` if unknown.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"uuid": "...",
|
|
||||||
"status": "...",
|
|
||||||
"type": "...",
|
|
||||||
"dateFrom": "2026-06-01",
|
|
||||||
"dateTo": "2026-06-08",
|
|
||||||
"nights": 7,
|
|
||||||
"paxCount": 40,
|
|
||||||
"minorsCount": 12,
|
|
||||||
"childrenCount": 3,
|
|
||||||
"groupName": "...",
|
|
||||||
"acceptedAt": "2026-05-01T10:00:00+00:00",
|
|
||||||
"personalData": {},
|
|
||||||
"accommodation": {},
|
|
||||||
"boardService": {},
|
|
||||||
"additionalServices": [],
|
|
||||||
"accommodationDiscount": null,
|
|
||||||
"boardServiceDiscount": null,
|
|
||||||
"additionalServicesDiscount": null,
|
|
||||||
"totalDiscountAmount": null,
|
|
||||||
"totalDiscountLabel": null,
|
|
||||||
"totalPrice": 123456,
|
|
||||||
"pricingCurrency": "EUR",
|
|
||||||
"pricingVersion": 3,
|
|
||||||
"priceBreakdown": {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`totalPrice` and `totalDiscountAmount` are integer **minor units** (cents) — unlike the contingent endpoints. The three `*Discount` fields are whole **percentages** (1–100) or `null`, not amounts. `dateFrom`/`dateTo` are `Y-m-d`; `acceptedAt` is a full ISO-8601 datetime and is `null` until accepted. `priceBreakdown` is a computed nested structure.
|
|
||||||
|
|
||||||
The first three discounts each apply to one section of the breakdown: `accommodationDiscount` to `basePrice + additionalPersonsPrice`, `boardServiceDiscount` to `boardPrice`, `additionalServicesDiscount` to `servicesPrice`. Surcharges and running costs sit in `total` but in no section, so they are never discounted.
|
|
||||||
|
|
||||||
`totalDiscountAmount` is a freely named discount on the whole price, labelled by `totalDiscountLabel`. It is a fixed sum, not a percentage, and is subtracted **after** the three section discounts have reduced `total`. It is capped at what is left of that subtotal, so `discountedTotal` is never negative and the amount shown in the breakdown may be smaller than `totalDiscountAmount` itself. The section discounts are each rounded to the nearest cent as they are applied.
|
|
||||||
|
|
||||||
`priceBreakdown` carries the resulting rows, all in minor units:
|
|
||||||
|
|
||||||
| field | meaning |
|
|
||||||
| --- | --- |
|
|
||||||
| `discounts` | list of `{label, percent, amount}` for the section discounts; entries rounding to `0` are omitted, and `label` already includes its `Rabatt ` prefix |
|
|
||||||
| `discountSubtotal` | `total` minus the section discounts — present only when `totalDiscountAmount` applies *and* at least one section discount did, otherwise `null` |
|
|
||||||
| `totalDiscountDetails` | `{label, amount}` for the total discount, or `null`; `amount` is the capped figure actually subtracted |
|
|
||||||
| `discountedTotal` | the final price, equal to the booking's `totalPrice` |
|
|
||||||
|
|
||||||
#### `POST /api/accommodation-bookings/{uuid}/accept` — scope `api`
|
|
||||||
|
|
||||||
Marks the booking as accepted (server-side side effects: status change, `acceptedAt`, notifications). No request body. Returns the same booking representation as the GET. `404` if unknown. Not idempotent in terms of side effects — call once.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Contact form
|
|
||||||
|
|
||||||
#### `POST /api/contactform` — scope `api`
|
|
||||||
|
|
||||||
Creates or matches an address record in the upstream CRM.
|
|
||||||
|
|
||||||
Request body (all fields optional strings; the upstream system decides what is required):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"lastName": "...", "firstName": "...", "gender": "...",
|
|
||||||
"street": "...", "zipCode": "...", "city": "...",
|
|
||||||
"email": "...", "phone": "..."
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- `201` — `{"success": true, "addressId": 123, "personId": 456, "isNewRecord": true}`
|
|
||||||
- `409` — `{"success": false, "message": "<upstream notification>"}` (upstream rejected/conflicted)
|
|
||||||
- `400` — `{"success": false, "message": "<error>"}` (upstream call failed)
|
|
||||||
- `422` — malformed payload
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Newsletter
|
|
||||||
|
|
||||||
#### `GET /api/newsletters` — scope `api`
|
|
||||||
|
|
||||||
The configured newsletter lists as an array of `{id, name, ...}` mapping objects. Use the returned `id` values as `listIds` below.
|
|
||||||
|
|
||||||
#### `POST /api/newsletter-subscriptions` — scope `api`
|
|
||||||
|
|
||||||
Request:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"email": "[email protected]",
|
|
||||||
"firstName": "Ada",
|
|
||||||
"lastName": "Lovelace",
|
|
||||||
"listIds": [1, 2]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- `email`: required, strict email validation.
|
|
||||||
- `firstName` / `lastName`: optional, max 255 chars.
|
|
||||||
- `listIds`: required, at least one positive integer; every id must be one of the configured lists.
|
|
||||||
|
|
||||||
Responses:
|
|
||||||
|
|
||||||
- `200` — subscription completed, `state: "subscribed"`.
|
|
||||||
- `202` — a double-opt-in confirmation mail was requested, `state: "confirmation_requested"`.
|
|
||||||
- Body in both cases:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"email": "[email protected]",
|
|
||||||
"lists": [{"id": 1, "name": "...", "state": "success|pending|already_registered"}],
|
|
||||||
"state": "subscribed|pending_confirmation|confirmation_requested",
|
|
||||||
"confirmationRequested": true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- `400` — unknown or disallowed list ids: `{"success": false, "message": "...", "listIds": [...], "unknownListIds": [...]}`, or validation violations.
|
|
||||||
- `503` — `{"success": false, "email": "...", "listIds": [...], "message": "Newsletter subscription request could not be processed."}` when the newsletter provider is unavailable. Safe to retry.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### End-user endpoints (require a user-bound token)
|
|
||||||
|
|
||||||
#### `GET /api/userinfo` — scope `email` (+ optional `id`, `profile`, `roles`)
|
|
||||||
|
|
||||||
OIDC-style claims for the authenticated user. The response contains only the claims covered by the granted scopes:
|
|
||||||
|
|
||||||
- always: `email`
|
|
||||||
- scope `id`: `person_id`, `address_id`
|
|
||||||
- scope `roles`: `roles` (array; only the roles that actually grant something are exported — the implicit baseline role is stripped, and so are the `*_PENDING` markers of roles the BusPro CRM claims but nobody has approved yet, see `docs/user-roles.md`)
|
|
||||||
- scope `profile`: `profile` object:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"first_name": "...", "last_name": "...", "title": "...", "salutation": "...",
|
|
||||||
"gender": "...", "date_of_birth": "1990-01-01", "nationality": "...",
|
|
||||||
"address": {"street": "...", "postcode": "...", "city": "...", "district": "...", "country": "..."},
|
|
||||||
"communication": {"email": "...", "mobile": "...", "phone": "...", "newsletter": true},
|
|
||||||
"hotel_codes": ["..."], "height": null, "weight": null, "shoe_size": null, "remarks": "..."
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: `profile.communication.email` comes from the CRM address record and is **not necessarily** the login email — a single address can hold several contacts. Use the top-level `email` claim for identity.
|
|
||||||
|
|
||||||
`400 {"message": "...", "code": "..."}` when the upstream lookup fails or rejects the credentials.
|
|
||||||
|
|
||||||
#### `GET /api/crm-attributes` — scope `profile`
|
|
||||||
|
|
||||||
CRM selection attributes/segments for the authenticated user. `400 {"message": "...", "code": "..."}` on upstream failure.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Client implementation notes
|
|
||||||
|
|
||||||
1. **Token lifetime is 10 minutes** — implement refresh/re-fetch with a safety margin and retry once on 401.
|
|
||||||
2. **Check for an `error` key even on 200 responses** for `/api/products` and the travel availability endpoint.
|
|
||||||
3. **Money units are inconsistent across endpoints**: contingent endpoints return decimal major units; accommodation-booking fields return integer cents. Normalize at the client boundary.
|
|
||||||
4. **Dates**: `Y-m-d` everywhere except `acceptedAt` (ISO-8601 datetime) and pickup planning (`Y-m-d H:i`). Contingent `dateFrom`/`dateTo` are inclusive night boundaries.
|
|
||||||
5. **Server-side caching** (1 h on products) means upstream changes may not be visible immediately. The contingent calendar is snapshot-backed instead: it is at most one sync interval behind upstream. There is no change-notification endpoint — clients are expected to cache on their own TTL and, when something must propagate sooner, invalidate their cache directly.
|
|
||||||
6. **Route ambiguity**: `/api/travels/{a}/{b}` resolves to the id-based route when both segments are numeric, otherwise to the code-based route. Non-numeric codes are safe; purely numeric *codes* will hit the id route.
|
|
||||||
@@ -1,898 +0,0 @@
|
|||||||
# bpn-connect Query Reference
|
|
||||||
|
|
||||||
> **Scope: endpoint design and the XML→SQL migration plan for the `bpn-connect` Go proxy.**
|
|
||||||
> For schema semantics, conventions and confidence marks see
|
|
||||||
> [`docs/buspro-database-reference.md`](buspro-database-reference.md), which is authoritative
|
|
||||||
> and wins on any conflict. The table descriptions below are kept for endpoint context.
|
|
||||||
|
|
||||||
This document summarizes the BusProNet schema exports in
|
|
||||||
`docs/buspronet-schema` for future `bpn-connect` endpoint/query development.
|
|
||||||
|
|
||||||
Generated from:
|
|
||||||
|
|
||||||
- `docs/buspronet-schema/BusPronet_20260601-1517.csv`: table columns
|
|
||||||
- `docs/buspronet-schema/BusPronet_20260601-1518.csv`: foreign keys
|
|
||||||
|
|
||||||
Current limitation: the visible exports do not include primary-key or index
|
|
||||||
catalog data, even though those were expected. Identity flags are not a reliable
|
|
||||||
primary-key substitute in this database because many `ID...` columns are not
|
|
||||||
marked as identity. Add PK/index exports before finalizing performance-sensitive
|
|
||||||
queries.
|
|
||||||
|
|
||||||
## Naming Conventions
|
|
||||||
|
|
||||||
- Primary identifiers usually follow `ID<TableName>`, for example
|
|
||||||
`Buchung.IDBuchung`, `Adresse.IDAdresse`, `AdressePerson.IDAdressePerson`.
|
|
||||||
- Foreign key columns usually include `_FS`, for example
|
|
||||||
`BuchungTN.IDBuchung_FS`.
|
|
||||||
- Address data is split into:
|
|
||||||
- `Adresse`: address/customer account root.
|
|
||||||
- `AdressePerson`: person attached to an address.
|
|
||||||
- `AdresseAnschrift`: postal address.
|
|
||||||
- `AdresseKommunikation`: communication values such as email/phone.
|
|
||||||
- Booking data is split into:
|
|
||||||
- `Buchung`: booking header.
|
|
||||||
- `BuchungTN`: booking participants.
|
|
||||||
- `BuchungTNFerienziel`, `BuchungTNLeistung`: participant travel/service rows.
|
|
||||||
- Public booking number appears to be `Buchung.Vorgang`.
|
|
||||||
- Internal booking id is `Buchung.IDBuchung`.
|
|
||||||
- Many tables have `Status` fields. Based on observed data, `Status = 'L'`
|
|
||||||
appears to be a soft-delete marker and should be excluded from portal lookup
|
|
||||||
queries. Other status values still need table-specific business validation.
|
|
||||||
|
|
||||||
## Core Tables
|
|
||||||
|
|
||||||
### `Buchung`
|
|
||||||
|
|
||||||
Booking header. Important columns:
|
|
||||||
|
|
||||||
| Column | Type | Nullable | Notes |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `IDBuchung` | `bigint` | no | Internal booking id. |
|
|
||||||
| `Vorgang` | `nvarchar(100)` | no | Public booking number/reference. |
|
|
||||||
| `IDReise_FS` | `bigint` | yes | Links to `Reise.IDReise`; no FK was present in the export. |
|
|
||||||
| `IDAdresse_FS_Anmelder` | `bigint` | yes | Applicant address id; no FK was present in the export. |
|
|
||||||
| `IDAdresseAnschrift_FS_Anmelder` | `bigint` | yes | Applicant postal address id; no FK was present in the export. |
|
|
||||||
| `IdAdressePerson_FS_Anmelder` | `bigint` | yes | Applicant person id; FK exists. |
|
|
||||||
| `IDAdresse_FS_Agentur` | `bigint` | no | Agency address; FK exists. |
|
|
||||||
| `IDAdresse_FS_Mandant` | `bigint` | no | Tenant/client address; FK exists. |
|
|
||||||
| `LoginWert1` | `nvarchar(100)` | no | Possible login-related value; semantics unknown. |
|
|
||||||
| `LoginWert2` | `nvarchar(100)` | no | Possible login-related value; semantics unknown. |
|
|
||||||
| `LoginWert3` | `nvarchar(100)` | no | Possible login-related value; semantics unknown. |
|
|
||||||
|
|
||||||
Known exported foreign keys:
|
|
||||||
|
|
||||||
- `Buchung.IdAdressePerson_FS_Anmelder -> AdressePerson.IDAdressePerson`
|
|
||||||
- `Buchung.IDAdresse_FS_Agentur -> Adresse.IDAdresse`
|
|
||||||
- `Buchung.IDAdresse_FS_ErsterSachb -> Adresse.IDAdresse`
|
|
||||||
- `Buchung.IDAdresse_FS_LetzterSachb -> Adresse.IDAdresse`
|
|
||||||
- `Buchung.IDAdresse_FS_Mandant -> Adresse.IDAdresse`
|
|
||||||
|
|
||||||
Notable missing FKs in the export:
|
|
||||||
|
|
||||||
- `Buchung.IDAdresse_FS_Anmelder`
|
|
||||||
- `Buchung.IDAdresseAnschrift_FS_Anmelder`
|
|
||||||
- `Buchung.IDReise_FS`
|
|
||||||
|
|
||||||
Treat those as likely relationships but verify against real data.
|
|
||||||
|
|
||||||
### `BuchungTN`
|
|
||||||
|
|
||||||
Booking participant.
|
|
||||||
|
|
||||||
| Column | Type | Nullable | Notes |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `IDBuchungTN` | `bigint` | no | Participant row id. |
|
|
||||||
| `IDBuchung_FS` | `bigint` | no | Booking header id. |
|
|
||||||
| `IDAdressePerson_FS` | `bigint` | no | Participant person id. |
|
|
||||||
| `IDAdresseAnschrift_FS` | `bigint` | yes | Participant postal address id. |
|
|
||||||
| `Status` | `nvarchar(2)` | no | Participant status; values need business validation. |
|
|
||||||
| `BuchungsDatum` | `datetime` | no | Booking date. |
|
|
||||||
| `StornoStatusKV` | `nvarchar(2)` | yes | Cancellation-related status. |
|
|
||||||
| `StornoDatum` | `datetime` | yes | Cancellation date. |
|
|
||||||
| `Position` | `smallint` | no | Participant position within booking. |
|
|
||||||
|
|
||||||
Known exported foreign keys:
|
|
||||||
|
|
||||||
- `BuchungTN.IDBuchung_FS -> Buchung.IDBuchung`
|
|
||||||
- `BuchungTN.IDAdressePerson_FS -> AdressePerson.IDAdressePerson`
|
|
||||||
|
|
||||||
### `Adresse`
|
|
||||||
|
|
||||||
Customer/address root.
|
|
||||||
|
|
||||||
| Column | Type | Nullable | Notes |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `IDAdresse` | `bigint` | no | Address/customer id needed by the local user record. |
|
|
||||||
| `SuchName` | `nvarchar(402)` | no | Search/display name. |
|
|
||||||
| `Status` | `nvarchar(2)` | no | Address status; active values need validation. |
|
|
||||||
| `Art` | `tinyint` | no | Address type; values need validation. |
|
|
||||||
| `IDAdresseAnschrift_FS_Versandanschrift` | `bigint` | no | Default shipping/postal address. |
|
|
||||||
| `IDAdressePerson_FS_ReferenzM` | `bigint` | no | Reference person. |
|
|
||||||
| `DatumLöschungDSGVO` | `datetime` | yes | GDPR deletion marker. Exclude rows where this is not null for portal access. |
|
|
||||||
|
|
||||||
### `AdressePerson`
|
|
||||||
|
|
||||||
Person attached to an address.
|
|
||||||
|
|
||||||
| Column | Type | Nullable | Notes |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `IDAdressePerson` | `bigint` | no | Person id needed by the local user record. |
|
|
||||||
| `IDAdresse_FS` | `bigint` | no | Parent address id. |
|
|
||||||
| `Name` | `nvarchar(200)` | no | Last name. |
|
|
||||||
| `Vorname` | `nvarchar(200)` | yes | First name. |
|
|
||||||
| `Geschlecht` | `nvarchar(2)` | no | Gender/salutation code. |
|
|
||||||
| `Status` | `nvarchar(2)` | no | Person status; active values need validation. |
|
|
||||||
| `Passwort` | `nvarchar(200)` | no | Legacy BusProNet password field. Do not use from `bpn-connect`; password migration remains on the BusProNet XML API until the algorithm is known. |
|
|
||||||
| `Registrierung` | `bit` | no | Registration flag; semantics need validation. |
|
|
||||||
| `IDFremd` | `nvarchar(100)` | no | External id field. |
|
|
||||||
|
|
||||||
Known exported foreign key:
|
|
||||||
|
|
||||||
- `AdressePerson.IDAdresse_FS -> Adresse.IDAdresse`
|
|
||||||
|
|
||||||
### `AdresseKommunikation`
|
|
||||||
|
|
||||||
Communication values for an address/person.
|
|
||||||
|
|
||||||
| Column | Type | Nullable | Notes |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `IDAdresseKommunikation` | `bigint` | no | Communication row id. |
|
|
||||||
| `IDAdresse_FS` | `bigint` | no | Address id. |
|
|
||||||
| `IDAdressePerson_FS` | `bigint` | no | Person id. No FK was present in the export, but the name strongly implies `AdressePerson.IDAdressePerson`. |
|
|
||||||
| `Wert` | `nvarchar(512)` | no | Communication value, likely email/phone depending on type. |
|
|
||||||
| `WertTelefonie` | `nvarchar(100)` | yes | Phone-specific normalized value. |
|
|
||||||
| `IDArtKommunikation_FS` | `bigint` | no | Communication type. |
|
|
||||||
| `Status` | `nvarchar(2)` | no | Communication status; active values need validation. |
|
|
||||||
| `Newsletter` | `bit` | no | Newsletter flag. |
|
|
||||||
| `DoubleOptIn` | `bit` | no | Double opt-in flag. |
|
|
||||||
|
|
||||||
Known exported foreign keys:
|
|
||||||
|
|
||||||
- `AdresseKommunikation.IDAdresse_FS -> Adresse.IDAdresse`
|
|
||||||
- `AdresseKommunikation.IDArtKommunikation_FS -> ArtKommunikation.IDArtKommunikation`
|
|
||||||
|
|
||||||
Important missing FK:
|
|
||||||
|
|
||||||
- `AdresseKommunikation.IDAdressePerson_FS` is not exported as a FK. Join it to
|
|
||||||
`AdressePerson.IDAdressePerson` only after validating with real data.
|
|
||||||
|
|
||||||
### `ArtKommunikation`
|
|
||||||
|
|
||||||
Communication type lookup.
|
|
||||||
|
|
||||||
| Column | Type | Nullable | Notes |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `IDArtKommunikation` | `bigint` | no | Type id. |
|
|
||||||
| `Langtext` | `nvarchar(200)` | yes | Long label. |
|
|
||||||
| `Kurztext` | `nvarchar(40)` | yes | Short label. |
|
|
||||||
| `Code` | `nvarchar(20)` | yes | Type code. Find the email code/id here. |
|
|
||||||
| `Status` | `nvarchar(2)` | yes | Type status. |
|
|
||||||
|
|
||||||
Before relying on email lookup queries, run:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT IDArtKommunikation, Langtext, Kurztext, Code, Status
|
|
||||||
FROM dbo.ArtKommunikation
|
|
||||||
ORDER BY IDArtKommunikation;
|
|
||||||
```
|
|
||||||
|
|
||||||
Then hard-code or configure the email type ids/codes in `bpn-connect`.
|
|
||||||
|
|
||||||
### `AdresseAnschrift`
|
|
||||||
|
|
||||||
Postal address.
|
|
||||||
|
|
||||||
| Column | Type | Nullable | Notes |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `IDAdresseAnschrift` | `bigint` | no | Postal address id. |
|
|
||||||
| `IDAdresse_FS` | `bigint` | no | Address id. |
|
|
||||||
| `IDAdressePerson_FS` | `bigint` | yes | Person id. |
|
|
||||||
| `Anrede` | `nvarchar(100)` | yes | Salutation. |
|
|
||||||
| `Name` | `nvarchar(400)` | yes | Postal name. |
|
|
||||||
| `Strasse` | `nvarchar(200)` | no | Street. |
|
|
||||||
| `PLZ` | `nvarchar(20)` | no | Postal code. |
|
|
||||||
| `Ort` | `nvarchar(200)` | no | City. |
|
|
||||||
| `Status` | `nvarchar(2)` | no | Postal address status. |
|
|
||||||
|
|
||||||
No direct FK from `AdresseAnschrift` to `Adresse` or `AdressePerson` was present
|
|
||||||
in the export subset shown by the current FK file. Treat these as likely
|
|
||||||
relationships and validate them with data.
|
|
||||||
|
|
||||||
### `Reise` and `Produkt`
|
|
||||||
|
|
||||||
Travel/product metadata.
|
|
||||||
|
|
||||||
`Reise` important columns:
|
|
||||||
|
|
||||||
- `IDReise`
|
|
||||||
- `IDProdukt_FS`
|
|
||||||
- `Bezeichnung`
|
|
||||||
- `Code`
|
|
||||||
- `TerminVon`
|
|
||||||
- `TerminBis`
|
|
||||||
- `Status`
|
|
||||||
- `StornoDatum`
|
|
||||||
|
|
||||||
`Produkt` important columns:
|
|
||||||
|
|
||||||
- `IDProdukt`
|
|
||||||
- `Bezeichnung`
|
|
||||||
- `Code`
|
|
||||||
- `Suchfeld`
|
|
||||||
- `Status`
|
|
||||||
|
|
||||||
Known exported foreign key:
|
|
||||||
|
|
||||||
- `Reise.IDProdukt_FS -> Produkt.IDProdukt`
|
|
||||||
|
|
||||||
## Relationship Map
|
|
||||||
|
|
||||||
Most relevant joins for portal access:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Buchung.IDBuchung
|
|
||||||
-> BuchungTN.IDBuchung_FS
|
|
||||||
-> BuchungTN.IDAdressePerson_FS
|
|
||||||
-> AdressePerson.IDAdressePerson
|
|
||||||
-> AdressePerson.IDAdresse_FS
|
|
||||||
-> Adresse.IDAdresse
|
|
||||||
```
|
|
||||||
|
|
||||||
Applicant joins:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Buchung.IdAdressePerson_FS_Anmelder
|
|
||||||
-> AdressePerson.IDAdressePerson
|
|
||||||
-> AdressePerson.IDAdresse_FS
|
|
||||||
-> Adresse.IDAdresse
|
|
||||||
```
|
|
||||||
|
|
||||||
Email joins to validate:
|
|
||||||
|
|
||||||
```text
|
|
||||||
AdresseKommunikation.IDAdressePerson_FS
|
|
||||||
-> AdressePerson.IDAdressePerson
|
|
||||||
|
|
||||||
AdresseKommunikation.IDAdresse_FS
|
|
||||||
-> Adresse.IDAdresse
|
|
||||||
|
|
||||||
AdresseKommunikation.IDArtKommunikation_FS
|
|
||||||
-> ArtKommunikation.IDArtKommunikation
|
|
||||||
```
|
|
||||||
|
|
||||||
## Required bpn-connect Endpoints
|
|
||||||
|
|
||||||
All endpoints are private and authenticated by API key:
|
|
||||||
|
|
||||||
```http
|
|
||||||
X-API-Key: {apiKey}
|
|
||||||
Accept: application/json
|
|
||||||
```
|
|
||||||
|
|
||||||
Keep responses narrow. `bpn-connect` should return only fields needed by the
|
|
||||||
Symfony portal and should never return BusProNet passwords, bank data, credit
|
|
||||||
card data, or broad customer records by default.
|
|
||||||
|
|
||||||
Authentication rule: Symfony owns portal passwords after a user has been
|
|
||||||
bootstrapped locally. The BusProNet legacy password algorithm is currently
|
|
||||||
unknown, so `bpn-connect` must not verify passwords from the database. Keep the
|
|
||||||
existing BusProNet XML password check only for first-time legacy migration. Once
|
|
||||||
the local Symfony password hash exists, all subsequent login/profile refresh
|
|
||||||
logic should use local authentication plus `bpn-connect` read endpoints.
|
|
||||||
|
|
||||||
### `GET /api/bookings/applicant-lookup`
|
|
||||||
|
|
||||||
Purpose: resolve an applicant by email and public booking number. This is the
|
|
||||||
core endpoint for local user creation after a booking and for the portal access
|
|
||||||
request flow.
|
|
||||||
|
|
||||||
Request:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /api/bookings/applicant-lookup?email={email}&bookingNumber={vorgang}
|
|
||||||
```
|
|
||||||
|
|
||||||
Response `200`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"bookingNumber": "111386",
|
|
||||||
"bookingId": 123456,
|
|
||||||
"addressId": 98765,
|
|
||||||
"personId": 54321,
|
|
||||||
"email": "[email protected]",
|
|
||||||
"firstName": "Example",
|
|
||||||
"lastName": "Customer"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Response rules:
|
|
||||||
|
|
||||||
- Return `200` when `email` belongs to `Buchung.IdAdressePerson_FS_Anmelder`
|
|
||||||
for the given `Buchung.Vorgang`.
|
|
||||||
- Return `404` when there is no matching non-deleted applicant.
|
|
||||||
- Return `409` if the query returns more than one applicant row after
|
|
||||||
deduplication. This should be rare and indicates inconsistent BusProNet data.
|
|
||||||
- Normalize input email by trimming and lowercasing before querying.
|
|
||||||
|
|
||||||
SQL:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
DECLARE @Email nvarchar(512) = LOWER(LTRIM(RTRIM(@email)));
|
|
||||||
DECLARE @Vorgang nvarchar(100) = LTRIM(RTRIM(@bookingNumber));
|
|
||||||
|
|
||||||
SELECT TOP (2)
|
|
||||||
b.Vorgang AS bookingNumber,
|
|
||||||
b.IDBuchung AS bookingId,
|
|
||||||
a.IDAdresse AS addressId,
|
|
||||||
ap.IDAdressePerson AS personId,
|
|
||||||
@Email AS email,
|
|
||||||
ap.Vorname AS firstName,
|
|
||||||
ap.Name AS lastName
|
|
||||||
FROM dbo.Buchung b
|
|
||||||
INNER JOIN dbo.AdressePerson ap
|
|
||||||
ON ap.IDAdressePerson = b.IdAdressePerson_FS_Anmelder
|
|
||||||
INNER JOIN dbo.Adresse a
|
|
||||||
ON a.IDAdresse = ap.IDAdresse_FS
|
|
||||||
WHERE b.Vorgang = @Vorgang
|
|
||||||
AND a.DatumLöschungDSGVO IS NULL
|
|
||||||
AND a.Status <> 'L'
|
|
||||||
AND ap.Status <> 'L'
|
|
||||||
AND EXISTS (
|
|
||||||
SELECT 1
|
|
||||||
FROM dbo.AdresseKommunikation ak
|
|
||||||
WHERE ak.IDAdresse_FS = ap.IDAdresse_FS
|
|
||||||
AND ak.IDAdressePerson_FS = ap.IDAdressePerson
|
|
||||||
AND LOWER(LTRIM(RTRIM(ak.Wert))) = @Email
|
|
||||||
AND ak.Status <> 'L'
|
|
||||||
-- TODO: restrict to validated email communication type.
|
|
||||||
-- AND ak.IDArtKommunikation_FS IN (...)
|
|
||||||
)
|
|
||||||
ORDER BY ap.IDAdressePerson;
|
|
||||||
```
|
|
||||||
|
|
||||||
### `GET /api/customers/{addressId}/persons/{personId}`
|
|
||||||
|
|
||||||
Purpose: fetch the personal data for a known BusProNet person. Use this after a
|
|
||||||
local user exists and already stores `addressId` and `personId`. Do not use email
|
|
||||||
as the identifier because multiple `Adresse` rows can share an email address.
|
|
||||||
|
|
||||||
Request:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /api/customers/{addressId}/persons/{personId}
|
|
||||||
```
|
|
||||||
|
|
||||||
Response `200`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"addressId": 98765,
|
|
||||||
"personId": 54321,
|
|
||||||
"firstName": "Example",
|
|
||||||
"lastName": "Customer",
|
|
||||||
"email": "[email protected]",
|
|
||||||
"genderCode": "F",
|
|
||||||
"title": null,
|
|
||||||
"birthDate": "1980-01-01T00:00:00Z",
|
|
||||||
"nationality": "deutsch",
|
|
||||||
"nationalityCountryName": "Deutschland",
|
|
||||||
"street": "Example Street 1",
|
|
||||||
"postalCode": "12345",
|
|
||||||
"city": "Example City",
|
|
||||||
"addressCountryName": "Deutschland"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Response rules:
|
|
||||||
|
|
||||||
- Return `200` for a non-deleted address/person pair.
|
|
||||||
- Return `404` if the address/person pair does not exist or has `Status = 'L'`.
|
|
||||||
- Return only portal-editable or portal-visible fields.
|
|
||||||
|
|
||||||
SQL:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
DECLARE @AddressId bigint = @addressId;
|
|
||||||
DECLARE @PersonId bigint = @personId;
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
a.IDAdresse AS addressId,
|
|
||||||
ap.IDAdressePerson AS personId,
|
|
||||||
ap.Name AS lastName,
|
|
||||||
ap.Vorname AS firstName,
|
|
||||||
ap.Geschlecht AS genderCode,
|
|
||||||
ap.Titel AS title,
|
|
||||||
ap.Geburtsdatum AS birthDate,
|
|
||||||
ap.Briefanrede AS letterSalutation,
|
|
||||||
ap.Anrede AS salutation,
|
|
||||||
ap.Newsletter AS newsletter,
|
|
||||||
email.Wert AS email,
|
|
||||||
|
|
||||||
manifest.IDArtNationalitaet_FS AS nationalityId,
|
|
||||||
person_nat.Langtext AS nationalityCountryName,
|
|
||||||
person_nat.Kurztext AS nationality,
|
|
||||||
person_nat.Code AS nationalityCode,
|
|
||||||
|
|
||||||
aa.IDAdresseAnschrift AS postalAddressId,
|
|
||||||
aa.Anrede AS postalSalutation,
|
|
||||||
aa.Name AS postalName,
|
|
||||||
aa.Strasse AS street,
|
|
||||||
aa.PLZ AS postalCode,
|
|
||||||
aa.Ort AS city,
|
|
||||||
aa.Ortsteil AS district,
|
|
||||||
aa.IDArtNationalitaet_FS_Land AS addressCountryId,
|
|
||||||
address_country.Langtext AS addressCountryName,
|
|
||||||
address_country.Kurztext AS addressCountryNationality,
|
|
||||||
address_country.Code AS addressCountryCode,
|
|
||||||
|
|
||||||
ad.TelefonOK AS phoneAllowed,
|
|
||||||
ad.MailOK AS mailAllowed,
|
|
||||||
ad.PostOK AS postAllowed
|
|
||||||
FROM dbo.AdressePerson ap
|
|
||||||
INNER JOIN dbo.Adresse a
|
|
||||||
ON a.IDAdresse = ap.IDAdresse_FS
|
|
||||||
OUTER APPLY (
|
|
||||||
SELECT TOP (1)
|
|
||||||
ak.Wert
|
|
||||||
FROM dbo.AdresseKommunikation ak
|
|
||||||
WHERE ak.IDAdresse_FS = ap.IDAdresse_FS
|
|
||||||
AND ak.IDAdressePerson_FS = ap.IDAdressePerson
|
|
||||||
AND ak.Status <> 'L'
|
|
||||||
-- TODO: restrict to validated email communication type.
|
|
||||||
-- AND ak.IDArtKommunikation_FS IN (...)
|
|
||||||
ORDER BY ak.IDAdresseKommunikation DESC
|
|
||||||
) email
|
|
||||||
LEFT JOIN dbo.AdresseAnschrift aa
|
|
||||||
ON aa.IDAdresseAnschrift = ap.IDAdresseAnschrift_FS_Versandanschrift
|
|
||||||
AND aa.Status <> 'L'
|
|
||||||
LEFT JOIN dbo.ArtNationalitaet address_country
|
|
||||||
ON address_country.IDArtNationalitaet = aa.IDArtNationalitaet_FS_Land
|
|
||||||
AND COALESCE(address_country.Status, '') <> 'L'
|
|
||||||
OUTER APPLY (
|
|
||||||
SELECT TOP (1)
|
|
||||||
m.IDArtNationalitaet_FS
|
|
||||||
FROM dbo.AdressePersonManifest m
|
|
||||||
WHERE m.IDAdressePerson_FS = ap.IDAdressePerson
|
|
||||||
AND m.IDAdresse_FS = ap.IDAdresse_FS
|
|
||||||
AND COALESCE(m.Status, '') <> 'L'
|
|
||||||
ORDER BY m.IDAdressePersonManifest DESC
|
|
||||||
) manifest
|
|
||||||
LEFT JOIN dbo.ArtNationalitaet person_nat
|
|
||||||
ON person_nat.IDArtNationalitaet = manifest.IDArtNationalitaet_FS
|
|
||||||
AND COALESCE(person_nat.Status, '') <> 'L'
|
|
||||||
LEFT JOIN dbo.AdresseDatenschutz ad
|
|
||||||
ON ad.IdAdresse_FS = a.IDAdresse
|
|
||||||
WHERE a.IDAdresse = @AddressId
|
|
||||||
AND ap.IDAdressePerson = @PersonId
|
|
||||||
AND a.DatumLöschungDSGVO IS NULL
|
|
||||||
AND a.Status <> 'L'
|
|
||||||
AND ap.Status <> 'L';
|
|
||||||
```
|
|
||||||
|
|
||||||
### Optional Debug Endpoint: `GET /api/customers/by-email`
|
|
||||||
|
|
||||||
Purpose: manual investigation only. Email is not unique across `Adresse`, so this
|
|
||||||
endpoint should return a list and should not be used as a local user identifier.
|
|
||||||
|
|
||||||
Request:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /api/customers/by-email?email={email}
|
|
||||||
```
|
|
||||||
|
|
||||||
Use the same joins as `GET /api/customers/{addressId}/persons/{personId}`, but
|
|
||||||
start from `AdresseKommunikation` and return all non-deleted matching persons.
|
|
||||||
Do not use this for account creation unless the user also provides a booking
|
|
||||||
number or another disambiguating key.
|
|
||||||
|
|
||||||
## Symfony Login And Bootstrap Replacement Plan
|
|
||||||
|
|
||||||
Current Symfony login behavior:
|
|
||||||
|
|
||||||
- Existing local users authenticate against the local Symfony password hash.
|
|
||||||
- Existing legacy local users may still have an encrypted legacy password in the
|
|
||||||
local `User.password` column. Symfony validates it locally once, then replaces
|
|
||||||
it with a Symfony password hash.
|
|
||||||
- If no local user exists, `BpnAuthenticator::bootstrapUser()` currently calls
|
|
||||||
the BusProNet XML API with email and password:
|
|
||||||
- `getPersonalDataWithPassword(email, password)`
|
|
||||||
- `getCrmAttributesWithPassword(email, password)`
|
|
||||||
- After any successful local login, `BpnAuthenticator::refreshBpnSnapshot()`
|
|
||||||
calls the BusProNet XML API by local `addressId` and `personId`:
|
|
||||||
- `getPersonalData(email, addressId, personId)`
|
|
||||||
- `getCrmAttributes(email, addressId, personId)`
|
|
||||||
|
|
||||||
Replacement rule:
|
|
||||||
|
|
||||||
- Keep login password validation local to Symfony.
|
|
||||||
- Keep the current XML password-based bootstrap for users that exist in
|
|
||||||
BusProNet but do not yet have a local Symfony user.
|
|
||||||
- On successful XML legacy verification, Symfony creates a local user with a
|
|
||||||
Symfony password hash from the submitted password.
|
|
||||||
- Future logins use only the local hash and must not call XML for password
|
|
||||||
verification.
|
|
||||||
- Replace post-login snapshot refresh with `bpn-connect` id-based read endpoints.
|
|
||||||
|
|
||||||
### Login Endpoint Usage
|
|
||||||
|
|
||||||
No `bpn-connect` endpoint is needed to authenticate a password.
|
|
||||||
|
|
||||||
Existing local user login should do:
|
|
||||||
|
|
||||||
1. Find local `User` by normalized email.
|
|
||||||
2. Validate local Symfony password hash.
|
|
||||||
3. If the local row still has a legacy encrypted local password, migrate it to a
|
|
||||||
Symfony hash without contacting BusProNet.
|
|
||||||
4. Call `GET /api/customers/{addressId}/persons/{personId}` to refresh profile
|
|
||||||
completeness.
|
|
||||||
5. Call `GET /api/customers/{addressId}/crm-attributes` to refresh roles and
|
|
||||||
hotel codes.
|
|
||||||
|
|
||||||
Missing local user login should do:
|
|
||||||
|
|
||||||
1. Call the existing BusProNet XML password-based bootstrap.
|
|
||||||
2. If valid, create a local `User` with a Symfony hash of the submitted password.
|
|
||||||
3. Store returned `addressId`, `personId`, `roles`, `hotelCodes`, and
|
|
||||||
`profileComplete`.
|
|
||||||
4. Persist and continue login.
|
|
||||||
5. If invalid, fail with the generic invalid-login message.
|
|
||||||
|
|
||||||
Do not add a `bpn-connect` password verification endpoint until the BusProNet
|
|
||||||
password algorithm is known and validated against real examples. The observed
|
|
||||||
database values do not currently match simple MD5/SHA/Base64 derivations.
|
|
||||||
|
|
||||||
### `GET /api/customers/{addressId}/crm-attributes`
|
|
||||||
|
|
||||||
Purpose: replace XML `SelektionCRM` reads used during login refresh and by the
|
|
||||||
OAuth CRM attributes endpoint.
|
|
||||||
|
|
||||||
Request:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /api/customers/{addressId}/crm-attributes
|
|
||||||
```
|
|
||||||
|
|
||||||
Minimal response:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"roles": ["ROLE_CUSTOMER"],
|
|
||||||
"hotelCodes": []
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Detailed response may also include selection groups and CRM actions if the
|
|
||||||
current `/api/crm-attributes` response must remain compatible.
|
|
||||||
|
|
||||||
Known role mapping from the XML parser:
|
|
||||||
|
|
||||||
- `Selektionsstamm.IDSelektionsstamm = 1292` selected: `ROLE_ADMIN` and
|
|
||||||
`ROLE_HOUSE_MANAGER`
|
|
||||||
- `Selektionsstamm.IDSelektionsstamm = 1293` selected: `ROLE_MANAGER`
|
|
||||||
- `Selektionsstamm.IDSelektionsstamm = 1070` selected: `ROLE_TEAMER`
|
|
||||||
- selected `Selektionsstamm.Selektion` matching `Hausleitung {CODE}`:
|
|
||||||
`ROLE_HOUSE_MANAGER` and hotel code `{CODE}`
|
|
||||||
- admin users also receive default hotel code `SSL`
|
|
||||||
- no matched role means `ROLE_CUSTOMER`
|
|
||||||
|
|
||||||
Candidate SQL for selected customer selections:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
DECLARE @AddressId bigint = @addressId;
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
sg.IDSelektionsgruppenstamm AS groupId,
|
|
||||||
sg.Selektionsgruppe AS groupLabel,
|
|
||||||
s.IDSelektionsstamm AS selectionId,
|
|
||||||
s.Selektion AS selectionLabel,
|
|
||||||
CAST(1 AS bit) AS selected
|
|
||||||
FROM dbo.Selektionen sel
|
|
||||||
INNER JOIN dbo.Selektionsstamm s
|
|
||||||
ON s.IDSelektionsstamm = sel.IDSelektionsstamm_FS
|
|
||||||
LEFT JOIN dbo.Selektionsgruppe sg_map
|
|
||||||
ON sg_map.IDSelektionsstamm_FS = s.IDSelektionsstamm
|
|
||||||
LEFT JOIN dbo.Selektionsgruppenstamm sg
|
|
||||||
ON sg.IDSelektionsgruppenstamm = sg_map.IDSelektionsgruppenstamm_FS
|
|
||||||
WHERE sel.IDAdresse_FS = @AddressId
|
|
||||||
AND COALESCE(s.Status, '') <> 'L'
|
|
||||||
AND COALESCE(sg.Status, '') <> 'L'
|
|
||||||
ORDER BY sg.Position, sg.Selektionsgruppe, sg_map.Position, s.Selektion;
|
|
||||||
```
|
|
||||||
|
|
||||||
The Go service can derive `roles` and `hotelCodes` from this result with the
|
|
||||||
mapping above. Before shipping, verify whether XML `SelektionCRM` includes
|
|
||||||
unselected mutable options; if the portal only needs roles/hotel codes, selected
|
|
||||||
rows are enough.
|
|
||||||
|
|
||||||
Candidate SQL for CRM actions if compatibility is required:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
DECLARE @AddressId bigint = @addressId;
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
ca.IDCRMAktion AS id,
|
|
||||||
ca.Code AS code,
|
|
||||||
ca.Bezeichnung AS label,
|
|
||||||
ca.Aktiv AS selected
|
|
||||||
FROM dbo.CRMZuweisung cz
|
|
||||||
INNER JOIN dbo.CRMAktion ca
|
|
||||||
ON ca.IDCRMAktion = cz.IDCRMAktion_FS
|
|
||||||
WHERE cz.IDAdresse_FS = @AddressId
|
|
||||||
AND ca.Status <> 'L'
|
|
||||||
ORDER BY ca.Bezeichnung;
|
|
||||||
```
|
|
||||||
|
|
||||||
This needs data validation against the XML response before compatibility is
|
|
||||||
guaranteed.
|
|
||||||
|
|
||||||
## XML Calls To Replace For Local User Processes
|
|
||||||
|
|
||||||
| Current Symfony call | Current XML purpose | Replacement |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `getPersonalData(email, addressId, personId)` | Load profile by known user ids. | `GET /api/customers/{addressId}/persons/{personId}` |
|
|
||||||
| `updatePersonalData(email, personalData)` | Persist profile edits. | Future `PATCH /api/customers/{addressId}/persons/{personId}` with transaction-safe updates. |
|
|
||||||
| `getCrmAttributes(email, addressId, personId)` | Refresh roles/hotel codes and expose CRM attributes. | `GET /api/customers/{addressId}/crm-attributes` |
|
|
||||||
| `getPersonalDataWithPassword(email, password)` | Missing-local-user bootstrap by BPN password. | Keep XML until the local user has been created/migrated. |
|
|
||||||
| `getCrmAttributesWithPassword(email, password)` | Roles for missing-local-user bootstrap by BPN password. | Keep XML until the local user has been created/migrated. |
|
|
||||||
| `register(registrationData)` | Create BusProNet address/person and local user. | Future `POST /api/customers` if direct DB writes are allowed; otherwise keep XML until write rules are known. |
|
|
||||||
| `createAddress(personalData)` | Create BusProNet address/person without mail. | Future `POST /api/customers` if direct DB writes are allowed; otherwise keep XML until write rules are known. |
|
|
||||||
|
|
||||||
Recommended migration sequence:
|
|
||||||
|
|
||||||
1. Keep XML for legacy password bootstrap only.
|
|
||||||
2. Replace booking applicant lookup for guest provisioning and portal access.
|
|
||||||
3. Replace post-login profile/CRM refresh reads.
|
|
||||||
4. Replace personal-data page reads.
|
|
||||||
5. Replace OAuth/userinfo and CRM API reads.
|
|
||||||
6. Replace booking form prefill reads.
|
|
||||||
7. Defer personal-data writes until update rules are validated.
|
|
||||||
8. Defer registration/create-address until the insert graph is known.
|
|
||||||
|
|
||||||
### Phase 1: Keep XML For Legacy Password Bootstrap
|
|
||||||
|
|
||||||
Keep these XML calls temporarily:
|
|
||||||
|
|
||||||
- `getPersonalDataWithPassword(email, password)`
|
|
||||||
- `getCrmAttributesWithPassword(email, password)`
|
|
||||||
|
|
||||||
Scope:
|
|
||||||
|
|
||||||
- Only used when no local Symfony user exists yet.
|
|
||||||
- On success, create the local user with a Symfony password hash from the
|
|
||||||
submitted password.
|
|
||||||
- Store `addressId`, `personId`, `roles`, `hotelCodes`, and `profileComplete`.
|
|
||||||
- Future logins for that user use only the local Symfony password hash.
|
|
||||||
|
|
||||||
Do not implement a `bpn-connect` password verification endpoint until the
|
|
||||||
BusProNet password algorithm is known and verified.
|
|
||||||
|
|
||||||
### Phase 2: Booking Applicant Lookup
|
|
||||||
|
|
||||||
Replace XML/fragile booking lookup plumbing with:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /api/bookings/applicant-lookup?email={email}&bookingNumber={vorgang}
|
|
||||||
```
|
|
||||||
|
|
||||||
Symfony integration points:
|
|
||||||
|
|
||||||
- `BookingGuestAccountManager::provisionGuestAccountForBooking()`
|
|
||||||
- `BookingGuestAccountManager::requestPortalAccess()`
|
|
||||||
- booking-created welcome account provisioning
|
|
||||||
|
|
||||||
This endpoint returns only the fields needed for local user creation:
|
|
||||||
|
|
||||||
- `bookingNumber`
|
|
||||||
- `bookingId`
|
|
||||||
- `addressId`
|
|
||||||
- `personId`
|
|
||||||
- `email`
|
|
||||||
- `firstName`
|
|
||||||
- `lastName`
|
|
||||||
|
|
||||||
### Phase 3: Post-login Snapshot Refresh
|
|
||||||
|
|
||||||
Replace XML reads in `BpnAuthenticator::refreshBpnSnapshot()` with:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /api/customers/{addressId}/persons/{personId}
|
|
||||||
GET /api/customers/{addressId}/crm-attributes
|
|
||||||
```
|
|
||||||
|
|
||||||
Symfony keeps local password validation. `bpn-connect` only refreshes ids,
|
|
||||||
profile completeness, roles, and hotel codes after the password has already been
|
|
||||||
validated locally.
|
|
||||||
|
|
||||||
### Phase 4: Personal-data Page Reads
|
|
||||||
|
|
||||||
Replace XML read in `PersonalDataController::loadPersonalData()` with:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /api/customers/{addressId}/persons/{personId}
|
|
||||||
```
|
|
||||||
|
|
||||||
Keep `updatePersonalData()` on XML for now. Reads are low-risk; writes require
|
|
||||||
validated BusProNet update semantics.
|
|
||||||
|
|
||||||
### Phase 5: OAuth/Userinfo And CRM Reads
|
|
||||||
|
|
||||||
Replace XML reads in:
|
|
||||||
|
|
||||||
- `UserinfoController`
|
|
||||||
- `CrmAttributeController`
|
|
||||||
|
|
||||||
with:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /api/customers/{addressId}/persons/{personId}
|
|
||||||
GET /api/customers/{addressId}/crm-attributes
|
|
||||||
```
|
|
||||||
|
|
||||||
### Phase 6: Booking Form Prefill Reads
|
|
||||||
|
|
||||||
Replace XML read in `ParticipantDataPrefiller::prefillApplicantFromUser()` with:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /api/customers/{addressId}/persons/{personId}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Phase 7: Defer Personal-data Writes
|
|
||||||
|
|
||||||
Keep XML call:
|
|
||||||
|
|
||||||
- `updatePersonalData(email, personalData)`
|
|
||||||
|
|
||||||
Future replacement:
|
|
||||||
|
|
||||||
```http
|
|
||||||
PATCH /api/customers/{addressId}/persons/{personId}
|
|
||||||
```
|
|
||||||
|
|
||||||
Do this only after write rules are validated for:
|
|
||||||
|
|
||||||
- `AdressePerson`
|
|
||||||
- `AdresseAnschrift`
|
|
||||||
- `AdresseKommunikation`
|
|
||||||
- `AdressePersonManifest`
|
|
||||||
- `AdresseZusatzfeld` or other tables holding height, weight, shoe size, and
|
|
||||||
remarks if those are not in the core person/address tables.
|
|
||||||
|
|
||||||
### Phase 8: Defer Registration And Create-address
|
|
||||||
|
|
||||||
Keep XML calls:
|
|
||||||
|
|
||||||
- `register(registrationData)`
|
|
||||||
- `createAddress(personalData)`
|
|
||||||
|
|
||||||
Future replacement:
|
|
||||||
|
|
||||||
```http
|
|
||||||
POST /api/customers
|
|
||||||
```
|
|
||||||
|
|
||||||
This is higher risk than read replacement because it requires the full BusProNet
|
|
||||||
insert graph, required defaults, and side effects.
|
|
||||||
|
|
||||||
## Candidate SQL: Find Email Communication Type
|
|
||||||
|
|
||||||
Run once during `bpn-connect` development:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT
|
|
||||||
IDArtKommunikation,
|
|
||||||
Langtext,
|
|
||||||
Kurztext,
|
|
||||||
Code,
|
|
||||||
Status
|
|
||||||
FROM dbo.ArtKommunikation
|
|
||||||
WHERE
|
|
||||||
LOWER(COALESCE(Langtext, '')) LIKE '%mail%'
|
|
||||||
OR LOWER(COALESCE(Kurztext, '')) LIKE '%mail%'
|
|
||||||
OR LOWER(COALESCE(Code, '')) LIKE '%mail%'
|
|
||||||
ORDER BY IDArtKommunikation;
|
|
||||||
```
|
|
||||||
|
|
||||||
If the data uses German labels, also inspect all rows manually. The schema alone
|
|
||||||
cannot prove the canonical email type.
|
|
||||||
|
|
||||||
## Candidate SQL: Validate Missing Relationships
|
|
||||||
|
|
||||||
Use these checks before shipping lookup logic:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- Check whether AdresseKommunikation.IDAdressePerson_FS is a real person link.
|
|
||||||
SELECT TOP (20)
|
|
||||||
ak.IDAdresseKommunikation,
|
|
||||||
ak.IDAdresse_FS,
|
|
||||||
ak.IDAdressePerson_FS,
|
|
||||||
ap.IDAdressePerson,
|
|
||||||
ap.IDAdresse_FS AS PersonAddressId,
|
|
||||||
ak.Wert
|
|
||||||
FROM dbo.AdresseKommunikation ak
|
|
||||||
LEFT JOIN dbo.AdressePerson ap
|
|
||||||
ON ap.IDAdressePerson = ak.IDAdressePerson_FS
|
|
||||||
WHERE ak.IDAdressePerson_FS <> 0
|
|
||||||
AND ap.IDAdressePerson IS NULL;
|
|
||||||
```
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- Check whether Buchung.IDAdresse_FS_Anmelder matches the applicant person's address.
|
|
||||||
SELECT TOP (100)
|
|
||||||
b.IDBuchung,
|
|
||||||
b.Vorgang,
|
|
||||||
b.IDAdresse_FS_Anmelder,
|
|
||||||
b.IdAdressePerson_FS_Anmelder,
|
|
||||||
ap.IDAdresse_FS AS ApplicantPersonAddressId
|
|
||||||
FROM dbo.Buchung b
|
|
||||||
LEFT JOIN dbo.AdressePerson ap
|
|
||||||
ON ap.IDAdressePerson = b.IdAdressePerson_FS_Anmelder
|
|
||||||
WHERE b.IDAdresse_FS_Anmelder IS NOT NULL
|
|
||||||
AND ap.IDAdresse_FS <> b.IDAdresse_FS_Anmelder;
|
|
||||||
```
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- Inspect status values before adding active-only predicates.
|
|
||||||
SELECT 'Adresse' AS table_name, Status, COUNT(*) AS rows
|
|
||||||
FROM dbo.Adresse
|
|
||||||
GROUP BY Status
|
|
||||||
UNION ALL
|
|
||||||
SELECT 'AdressePerson', Status, COUNT(*)
|
|
||||||
FROM dbo.AdressePerson
|
|
||||||
GROUP BY Status
|
|
||||||
UNION ALL
|
|
||||||
SELECT 'AdresseKommunikation', Status, COUNT(*)
|
|
||||||
FROM dbo.AdresseKommunikation
|
|
||||||
GROUP BY Status
|
|
||||||
UNION ALL
|
|
||||||
SELECT 'BuchungTN', Status, COUNT(*)
|
|
||||||
FROM dbo.BuchungTN
|
|
||||||
GROUP BY Status
|
|
||||||
ORDER BY table_name, Status;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Indexes To Confirm Or Add
|
|
||||||
|
|
||||||
The current CSV files do not include index definitions. For the lookup endpoint,
|
|
||||||
confirm whether these access paths already exist:
|
|
||||||
|
|
||||||
- `Buchung(Vorgang)` including `IDBuchung`, `IdAdressePerson_FS_Anmelder`
|
|
||||||
- `BuchungTN(IDBuchung_FS)` including `IDAdressePerson_FS`, `Status`
|
|
||||||
- `AdressePerson(IDAdressePerson)` including `IDAdresse_FS`, `Vorname`, `Name`,
|
|
||||||
`Status`
|
|
||||||
- `AdresseKommunikation(IDAdressePerson_FS, Wert)` including `IDAdresse_FS`,
|
|
||||||
`IDArtKommunikation_FS`, `Status`
|
|
||||||
- `AdresseKommunikation(IDAdresse_FS, Wert)` including `IDAdressePerson_FS`,
|
|
||||||
`IDArtKommunikation_FS`, `Status`
|
|
||||||
|
|
||||||
If the database collation is case-sensitive and email lookup must be
|
|
||||||
case-insensitive, avoid `LOWER(Wert)` on large tables unless a computed
|
|
||||||
normalized-email column or suitable index exists.
|
|
||||||
|
|
||||||
## Future Endpoint Candidates
|
|
||||||
|
|
||||||
The XML plumbing can be replaced incrementally by small read endpoints backed by
|
|
||||||
targeted SQL:
|
|
||||||
|
|
||||||
| Endpoint | Purpose | Core tables |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `GET /api/bookings/lookup` | Resolve email + public booking number to booking/person/address ids. | `Buchung`, `BuchungTN`, `AdressePerson`, `AdresseKommunikation`, `Adresse` |
|
|
||||||
| `GET /api/bookings/{bookingId}` | Fetch booking header/detail by internal id. | `Buchung`, `Reise`, `Produkt`, `BuchungTN` |
|
|
||||||
| `GET /api/bookings/{bookingId}/participants` | Fetch participants for a booking. | `BuchungTN`, `AdressePerson`, `AdresseAnschrift` |
|
|
||||||
| `GET /api/customers/{addressId}/persons/{personId}` | Fetch a minimal customer/person profile. | `Adresse`, `AdressePerson`, `AdresseKommunikation`, `AdresseAnschrift` |
|
|
||||||
|
|
||||||
Keep endpoint responses narrow. Return only ids and fields needed by the Symfony
|
|
||||||
portal so the Go service does not become a broad BusProNet data mirror.
|
|
||||||
|
|
||||||
## Regeneration Notes
|
|
||||||
|
|
||||||
When new catalog exports are added, regenerate this document with:
|
|
||||||
|
|
||||||
1. Columns export from `sys.tables`, `sys.schemas`, `sys.columns`, `sys.types`.
|
|
||||||
2. Foreign key export from `sys.foreign_keys` and related FK columns.
|
|
||||||
3. Primary-key export from `sys.key_constraints`.
|
|
||||||
4. Index export from `sys.indexes`, `sys.index_columns`, and `sys.columns`.
|
|
||||||
|
|
||||||
Primary keys and indexes should be added to the table sections and the lookup
|
|
||||||
query should be revisited once the real access paths are known.
|
|
||||||
@@ -1,382 +0,0 @@
|
|||||||
# BusProNet XML API — working reference
|
|
||||||
|
|
||||||
Condensed from the vendor PDF `docs/bpn_docs.pdf` (41 pages, German), keeping what this project
|
|
||||||
uses or plausibly could. The PDF stays authoritative; a few of its sentences are mangled by a
|
|
||||||
broken font encoding, so anything unclear there is flagged rather than guessed at.
|
|
||||||
|
|
||||||
> **The PDF lags the live API.** The vendor has shipped elements since it was written —
|
|
||||||
> `<ausstiege>` (return boarding points) is one confirmed example. So *absent from the PDF* means
|
|
||||||
> "not in this snapshot", never "unsupported". Treat the document as a floor on what the API
|
|
||||||
> accepts, not a ceiling, and prefer a live `buchungsart=Anfrage` probe over an argument from
|
|
||||||
> silence.
|
|
||||||
|
|
||||||
Read this alongside `buspro-database-reference.md` (what BusPro actually stores) — the two answer
|
|
||||||
different questions, and where they disagree the schema is the record of what happened.
|
|
||||||
|
|
||||||
**Use it for:** what an element is called and what it may contain; which codes a failure can
|
|
||||||
return; which documented capabilities we are not using; and which elements we send that the
|
|
||||||
vendor never documented — those are the ones that can break without warning.
|
|
||||||
|
|
||||||
## Envelope
|
|
||||||
|
|
||||||
Every request is `<anfrage>` with `<user>`, `<key>` and `<satz typ="…"/>`.
|
|
||||||
|
|
||||||
```
|
|
||||||
key = md5( user + password + date("YYYYMMDD") + satz-typ )
|
|
||||||
```
|
|
||||||
|
|
||||||
Per satz type and per day. `ApiClient::createKey()` implements it, and `sendRawXml()` regenerates
|
|
||||||
the key before sending, so a hand-built payload can carry a placeholder.
|
|
||||||
|
|
||||||
## Satz types
|
|
||||||
|
|
||||||
**Documented:** `ADRESSFELDER`, `ADRESSFELDERGUTSCHEIN`, `ADRESSFELDERKATALOGANFRAGE`,
|
|
||||||
`AGENTUREN`, `AGENTURKONTO`, `AKTIONSGUTSCHEIN`, `BUCHUNG`, `BUCHUNGAENDERUNG`, `CRM`,
|
|
||||||
`CRMKATALOGANFRAGE`, `GUTSCHEINBUCHUNG`, `GUTSCHEINDRUCK`, `GUTSCHEINPRUEFUNGEINLOESUNG`,
|
|
||||||
`GUTSCHEINVORLAGEN`, `GUTSCHEINVORSCHAU`, `GUTSCHEINZAHLUNGSARTEN`, `KATALOGANFRAGE`,
|
|
||||||
`KUNDENKONTO`, `KUNDENLISTE`, `MOEGLICHEAENDERUNGEN`, `PRODUKTDATEN`, `PRODUKTE`, `SITZPLATZ`,
|
|
||||||
`STAMMHOTELS`, `STAMMLAENDER`, `STAMMVERSICHERUNGEN`, `STAMMZUSTIEGE`, `VERFUEGBARKEIT`,
|
|
||||||
`VERFUEGBARKEITHOTEL`, `ZAHLUNG`, `ZAHLUNG2`, `ZAHLUNGSARTEN`, `ZAHLUNGSARTEN2`.
|
|
||||||
`SITZPLATZAUSWAHL` and `ZAHLUNG4` are named in prose without their own section.
|
|
||||||
|
|
||||||
**Used here but undocumented:** `VERFUEGBARKEIT2` (`ApiClient::TYPE_AVAILABILITY_EXTENDED`) — the
|
|
||||||
extended availability call returning `buchungstatusmoeglich` and per-service `pflicht`/`frei`.
|
|
||||||
Its contract is observed, not specified.
|
|
||||||
|
|
||||||
**Documented but unused here:** `SITZPLATZ`/`SITZPLATZAUSWAHL`, `KUNDENLISTE`, `ZAHLUNG2`,
|
|
||||||
`ADRESSFELDER`, `STAMMHOTELS`, `STAMMVERSICHERUNGEN`, `STAMMZUSTIEGE`, `KATALOGANFRAGE`.
|
|
||||||
|
|
||||||
## BUCHUNG — the create contract
|
|
||||||
|
|
||||||
`<buchungsart>` decides what the call does:
|
|
||||||
|
|
||||||
| Value | Effect |
|
|
||||||
|---|---|
|
|
||||||
| `Anfrage` | Enquiry. Validates and **returns the full price breakdown**. Nothing is stored. |
|
|
||||||
| `Buchung` | Stores the booking. |
|
|
||||||
|
|
||||||
`<status>` at the root: `F` Festbuchung · `O` Optionsbuchung · `A` Anfragebuchung. Gated by
|
|
||||||
error `962` ("Buchungsstatus #1# kann nicht verarbeitet werden, nur Status #2# möglich"), and by
|
|
||||||
`982`/`983` when the travel forbids options or enquiries outright.
|
|
||||||
|
|
||||||
### Two accommodation models
|
|
||||||
|
|
||||||
The base payload books accommodation as an ordinary service:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<unterbringungen>
|
|
||||||
<unterbringung idleistung="22341" anzahl="1" zuordnung="1,2"/>
|
|
||||||
</unterbringungen>
|
|
||||||
```
|
|
||||||
|
|
||||||
For the **Ferienziel** domain — what this app books — `unterbringungen` is *replaced* by
|
|
||||||
`ferienzielunterbringungen`, **and** a root `<idpartner>` added:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<ferienzielunterbringungen>
|
|
||||||
<ferienzielunterbringung idzimmer="2" kategorie="Ik" idverpflegung="2"
|
|
||||||
anreise="30.01.2018" abreise="06.02.2018"
|
|
||||||
anzahl="1" zuordnung="1,2"/>
|
|
||||||
</ferienzielunterbringungen>
|
|
||||||
…
|
|
||||||
<idpartner>78687</idpartner>
|
|
||||||
```
|
|
||||||
|
|
||||||
> `<idpartner>` is introduced as the element you add when switching to the Ferienziel model, and
|
|
||||||
> `914 Partner-ID fehlt` guards it. Note it is **not** confined to bookings that have a room: a
|
|
||||||
> teamer booking with zero `BuchungTNFerienziel` rows still returns `<idpartner>222925</idpartner>`
|
|
||||||
> from `Vorgang_Details`. Where that comes from is **unresolved** — `Buchung` has no partner FK, so
|
|
||||||
> either it is stored on `Buchung.IDAdresse_FS_ExtraFerienzielPartner`, or it is derived from the
|
|
||||||
> travel. That booking's product has exactly one hotel, so the response cannot distinguish the two;
|
|
||||||
> one read-only query settles it.
|
|
||||||
|
|
||||||
Accommodation is **mandatory** — `913 Unterbringungen fehlen` / `916 Ferienziel-Unterbringungen
|
|
||||||
fehlen`. Confirmed empirically on the test system 2026-09-02, not just documented: see
|
|
||||||
"Validation gate order" below. Room dates are cross-checked against the transport legs (`921`,
|
|
||||||
`923`).
|
|
||||||
|
|
||||||
### Full documented request shape
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<anfrage>
|
|
||||||
<user/><key/><satz typ="BUCHUNG"/>
|
|
||||||
<buchungsart>Anfrage|Buchung</buchungsart>
|
|
||||||
<status>F|O|A</status>
|
|
||||||
<idagentur/> <idcrm/> <idreise/> <idpartner/>
|
|
||||||
<fremdvorgang/> <!-- your own reference, stored as Buchung.FremdVorgang -->
|
|
||||||
<datenschutztfmp/> <!-- ad consent, combinable: T Telefon, F Telefax, M EMail, P Post -->
|
|
||||||
<anmelder>
|
|
||||||
name, vorname, anrede, titel, geschlecht, nationalitaet, geburtsdatum,
|
|
||||||
<anschrift>strasse, plz, ort, ortsteil, land</anschrift>
|
|
||||||
<kommunikation>telefonmobil, email, telefonprivat</kommunikation>
|
|
||||||
<bemerkung/>
|
|
||||||
</anmelder>
|
|
||||||
<teilnehmerliste>
|
|
||||||
<teilnehmer id="1">
|
|
||||||
…same personal fields…
|
|
||||||
<wünsche><unterbringungswunsch/><beförderungswunsch/></wünsche>
|
|
||||||
<aktionscode/>
|
|
||||||
</teilnehmer>
|
|
||||||
</teilnehmerliste>
|
|
||||||
<beförderungen><beförderung idleistung anzahl zuordnung/></beförderungen>
|
|
||||||
<ferienzielunterbringungen>…</ferienzielunterbringungen>
|
|
||||||
<zusatzleistungen><zusatzleistung idleistung anzahl zuordnung/></zusatzleistungen>
|
|
||||||
<zustiege><zustieg idzustieg anzahl zuordnung/></zustiege>
|
|
||||||
<sitzpläne><sitzplan idleistung idsitzplan sitzplätze="1B;1A"/></sitzpläne>
|
|
||||||
<versicherungen><versicherung idversicherung anzahl zuordnung/></versicherungen>
|
|
||||||
<gutscheine><gutschein gutscheinnr einloesecode/></gutscheine>
|
|
||||||
<zuschlaege>
|
|
||||||
<zuschlag text betrag anzahl zuordnung
|
|
||||||
provision altersrabatt gruppenrabatt frühbucherrabatt bzberechnung/>
|
|
||||||
</zuschlaege>
|
|
||||||
<zahlung idzahlungsart bezeichnung art>
|
|
||||||
<bankverbindung kreditinstitut bic iban kontoinhaber/>
|
|
||||||
</zahlung>
|
|
||||||
</anfrage>
|
|
||||||
```
|
|
||||||
|
|
||||||
`zuordnung` is a comma-joined list of 1-based `teilnehmer/@id` values throughout.
|
|
||||||
|
|
||||||
### Responses
|
|
||||||
|
|
||||||
`buchungsart=Anfrage` → `<buchung>möglich</buchung>` plus `<preise>`: one `<preis>` per line with
|
|
||||||
`position`, `art`, `unterart`, `bezeichnung`, `terminvon`/`terminbis`, `anzahl`, `zuordnung`,
|
|
||||||
`preis`, `gesamtpreis`, `id`; then `<gesamtpreis>` and `<zahlungsbedingungen>` with `<anzahlung>`
|
|
||||||
and `<restzahlung>` (each `betrag` + `termin`). `art` values seen: `BEF` transport, `UNT`
|
|
||||||
accommodation, `SON` other, `ERM` discount, `VER` insurance.
|
|
||||||
|
|
||||||
`buchungsart=Buchung` → `<buchung>erfolgt</buchung>`, `<vorgang>`, `<rechnung>`, `<idbuchung>`,
|
|
||||||
`<email>`, `<pdfpasswort>`.
|
|
||||||
|
|
||||||
### Validation gate order (measured)
|
|
||||||
|
|
||||||
Established with `buchungsart=Anfrage` against the test system on 2026-09-02, by mirroring a real
|
|
||||||
room-less teamer booking (Vorgang 110628) element for element:
|
|
||||||
|
|
||||||
1. **Accommodation.** No `ferienzielunterbringungen` → `916`, regardless of everything else being
|
|
||||||
valid. Nothing further is evaluated.
|
|
||||||
2. **Personal data**, once a room is present → `650` with the field list *"Fehlende
|
|
||||||
Anmelder/Teilnehmer Daten … Nachname, Vorname, Strasse, Plz, Ort, Telefon-Mobil, Mail-Adresse,
|
|
||||||
Geburtsdatum, Nationalität"*, for the Anmelder **and every participant**.
|
|
||||||
|
|
||||||
So **sending `idadresse`/`idadresseperson` alone is not enough on create** — BusPro validates the
|
|
||||||
personal fields whether or not it could resolve them from the ids. Echoing the record back
|
|
||||||
(`idadresse` + `idadresseperson` + full data) narrows the complaint to whatever that record is
|
|
||||||
genuinely missing, which is a useful way to audit an address: the placeholder account 19465:41003
|
|
||||||
fails on `Telefon-Mobil` and `Nationalität` because its `Vorgang_Details` record has only
|
|
||||||
`telefonprivat` and no `nationalitaet`.
|
|
||||||
|
|
||||||
Note `650` again: both of those are hard rejections delivered under the code
|
|
||||||
`Notification::isError()` treats as success.
|
|
||||||
|
|
||||||
## BUCHUNGAENDERUNG — the edit contract
|
|
||||||
|
|
||||||
Structurally identical to `BUCHUNG` plus `<idbuchung>`, and the responses follow `buchungsart`
|
|
||||||
the same way. Ask `MOEGLICHEAENDERUNGEN` first (`<idreise>`), which answers:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<änderungen>
|
|
||||||
<änderung art="anzahl_teilnehmer" möglich="False"/>
|
|
||||||
<änderung art="beförderung" möglich="False"/>
|
|
||||||
<änderung art="zustieg" möglich="False"/>
|
|
||||||
<änderung art="sitztplatz" möglich="False"/> <!-- vendor's typo -->
|
|
||||||
<änderung art="unterbringung" möglich="False"/>
|
|
||||||
<änderung art="zusatzleistung" möglich="True" möglichbiszum="23.02.2020"/>
|
|
||||||
<änderung art="teilnehmerdaten" möglich="False"/>
|
|
||||||
</änderungen>
|
|
||||||
```
|
|
||||||
|
|
||||||
## How this app maps onto the contract
|
|
||||||
|
|
||||||
`BookingPayloadBuilder::buildCreatePayload()` builds create; `buildBasePayload()` + the
|
|
||||||
`addTo…Payload()` helpers build edit; `ApiClient::createBooking()`/`updateBooking()` wrap them.
|
|
||||||
|
|
||||||
| Documented element | Sent on create | Sent on edit | Notes |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `buchungsart` | yes | yes | Edit **hardcodes `Buchung`** (`ApiClient.php:233`) |
|
|
||||||
| `status` | yes | yes | Edit replays the value read back from BusPro — see the caveat below |
|
|
||||||
| `idreise`, `idpartner`, `idagentur` | yes | yes | `idpartner` sent unconditionally, even with no room |
|
|
||||||
| `anmelder` | yes | yes | Create builds it from participant[0]; edit uses `PersonalData::toPayload()` |
|
|
||||||
| `anmelder/bemerkung` | — | yes | Via `PersonalData::toPayload()` |
|
|
||||||
| `teilnehmerliste` | yes | yes | |
|
|
||||||
| `wünsche` (`unterbringungswunsch`, `beförderungswunsch`) | yes | yes | |
|
|
||||||
| `aktionscode` | yes | yes | Promo code, paired with `AKTIONSGUTSCHEIN` |
|
|
||||||
| `beförderungen`, `zusatzleistungen`, `zustiege` | yes | yes | Built by `ServiceMappingCollector` |
|
|
||||||
| `ferienzielunterbringungen` | yes | yes | Create omits the node entirely when no room maps; edit always emits the empty skeleton |
|
|
||||||
| `versicherungen` | yes | yes | |
|
|
||||||
| `gutscheine` | yes | yes | Paired with `GUTSCHEINPRUEFUNGEINLOESUNG` |
|
|
||||||
| `zahlung` + `bankverbindung` | yes | yes | |
|
|
||||||
| `idcrm` | no | no | |
|
|
||||||
| `fremdvorgang` | no | no | Would give our own bookings a stable external reference |
|
|
||||||
| `datenschutztfmp` | no | no | Advertising consent — we track newsletter consent locally instead |
|
|
||||||
| `sitzpläne` | no | no | See Seats |
|
|
||||||
| `zuschlaege` | no | no | Free-text manual surcharge or discount with its own amount |
|
|
||||||
|
|
||||||
### Newer than the PDF
|
|
||||||
|
|
||||||
These are absent from the document but work against the live API — vendor additions that
|
|
||||||
post-date it. Listed so nobody mistakes the PDF's silence for a contract violation, and so the
|
|
||||||
gaps in this reference are known.
|
|
||||||
|
|
||||||
| Element | Where | Note |
|
|
||||||
|---|---|---|
|
|
||||||
| `<ausstiege><ausstieg idzustieg …/></ausstiege>` | create and edit | Return boarding points. Confirmed as a recent vendor addition; the PDF documents only `<zustiege>`. |
|
|
||||||
| `<status>` inside `<teilnehmer>` | create (hardcoded `F`) and edit | Not in the PDF in either direction, though `Vorgang_Details` returns `status_teilnehmer`. |
|
|
||||||
| `<login>J</login>` on `KUNDENKONTO` | probe command | Passwordless read escape hatch; see `bpn-login-j-passwordless-reads`. |
|
|
||||||
| `VERFUEGBARKEIT2` | availability path | Whole satz type post-dates the PDF. Returns `buchungstatusmoeglich` and per-service `pflicht`. |
|
|
||||||
|
|
||||||
When one of these needs pinning down, the authoritative move is a live `Anfrage` call, not a
|
|
||||||
reading of this file.
|
|
||||||
|
|
||||||
### Caveat: the edit flow replays a derived status
|
|
||||||
|
|
||||||
`buildBasePayload()` sends `'status' => $bookingData->status`, and `Booking::$status` is parsed
|
|
||||||
from the response's root `<status>` (`BookingParser.php:38`). But `Buchung` has **no status
|
|
||||||
column** — the root value is an aggregate over `BuchungTN.Status`, the only real store
|
|
||||||
(`<status>F</status>` beside `<status_teilnehmer>F/F/…/F</status_teilnehmer>` in
|
|
||||||
`tests/Resources/booking_data.xml`). So on a booking whose participants do *not* all share one
|
|
||||||
status, the edit flow sends an aggregate back as a directive. Worth confirming against a mixed
|
|
||||||
booking before relying on it.
|
|
||||||
|
|
||||||
### Gap: edit never dry-runs
|
|
||||||
|
|
||||||
Create is two-phase — `Anfrage` to validate and price, then `Buchung`. Edit sends
|
|
||||||
`buchungsart=Buchung` directly (`ApiClient.php:233`), even though `BUCHUNGAENDERUNG` supports
|
|
||||||
`Anfrage` identically. An enquiry pass would surface `913`/`924`/`962` and return the recalculated
|
|
||||||
price *before* mutating a live booking.
|
|
||||||
|
|
||||||
## Seats
|
|
||||||
|
|
||||||
`SITZPLATZ` asks BusPro to pick seats:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<satz typ="SITZPLATZ"/>
|
|
||||||
<idreise/><idleistung_hin/><idleistung_rueck/><anzahl_personen/>
|
|
||||||
<wunsch_vornemittehinten>V|M|H</wunsch_vornemittehinten>
|
|
||||||
<wunsch_linksrechts>L|R</wunsch_linksrechts>
|
|
||||||
<wunsch_fenstergang>F|G</wunsch_fenstergang>
|
|
||||||
```
|
|
||||||
|
|
||||||
Response: one `<sitzplan>` per direction with `sitzplaetze="0D;1D;1C"`, `idsitzplan`,
|
|
||||||
`richtung="H|R"`, `bezeichnung`, `idleistung`, and `grafik` (base64 PNG of the seat map). Where
|
|
||||||
seats carry surcharges the response adds `<zuschläge>` (`id`, `bezeichnung`, `preis`, colours) and
|
|
||||||
the request must then carry `idzuschlag`. `SITZPLATZAUSWAHL` returns the raw plan structure
|
|
||||||
instead of a picture.
|
|
||||||
|
|
||||||
Chosen seats go back into a booking via `<sitzpläne>`, landing in
|
|
||||||
`BuchungTNLeistungZusatz.Sitzplatz` / `IDSitzplan_FS`. Guarded by `933 Sitzplan mit ID #1# bei
|
|
||||||
Leistung #2# nicht freigegeben` and `304 Es konnte nicht für alle Teilnehmer ein Sitzplatz
|
|
||||||
ermittelt werden`.
|
|
||||||
|
|
||||||
## KUNDENKONTO
|
|
||||||
|
|
||||||
`<art>`: `Adressdaten`, `Adressdaten_Ändern`, `Adresse_Neu`, `SelektionCRM`,
|
|
||||||
`SelektionCRM_Ändern`, `Vorgänge`, `Vorgangdruck`, `Voucherdruck`, `Dokumentdruck`,
|
|
||||||
`Passwort_Anfrage`, `Newsletter`, `Adressdokument`, `Vorgang_Details`, `Prüfung`, `PrüfungEmail`,
|
|
||||||
`Ausweisdaten_Ändern`, `Stornoanfrage`, `Storno`, `Versicherungspolice`.
|
|
||||||
|
|
||||||
`Stornoanfrage` answers `stornierung_moeglich` + `stornopreis`; `Storno` answers `storniert` —
|
|
||||||
the documented way to cancel, including cleaning up a test booking. `Ausweisdaten_Ändern` takes a
|
|
||||||
`<personen>` list with `<ausweis>` data and answers `änderung`. `Versicherungspolice` returns
|
|
||||||
per-policy document URLs.
|
|
||||||
|
|
||||||
## AGENTURKONTO
|
|
||||||
|
|
||||||
`<art>`: `Vorgänge`, `Vorgangdruck`, `Rechnungdruck`, `Hotelgutscheine`, `Hotelgutscheindruck`,
|
|
||||||
`OptionInFest` — the last converts a held option into a firm booking, answering
|
|
||||||
`<optioninfestumgewandelt>true</optioninfestumgewandelt>`. `Vorgangdruck` takes `idbuchung`,
|
|
||||||
`Rechnungdruck` takes `idrechnung`, `Hotelgutscheindruck` takes `idgutschein`; print arts return
|
|
||||||
base64 PDFs.
|
|
||||||
|
|
||||||
## Availability and payment
|
|
||||||
|
|
||||||
`VERFUEGBARKEIT` takes `<idreise>` plus a `<leistungen><leistung id="…"/></leistungen>` filter and
|
|
||||||
answers `<reise id status>` plus per-service `status` ("buchbar"), `frei`, `preis`,
|
|
||||||
`preisfaellig`. `VERFUEGBARKEIT2`, which we actually use, additionally reports
|
|
||||||
`buchungstatusmoeglich` and `pflicht` but is undocumented.
|
|
||||||
|
|
||||||
`ZAHLUNGSARTEN2` lists payment methods; `ZAHLUNG2` records a payment against a booking
|
|
||||||
(`vorgang`, `idbuchung`, `datum`, `betrag`, `idzahlungsart`, `zahlungstext` — all mandatory).
|
|
||||||
`ZAHLUNG` reports a credit-card authorisation after booking; the amount must equal the confirmed
|
|
||||||
`anzahlung` or errors `1011`-`1018` follow.
|
|
||||||
|
|
||||||
## Error codes
|
|
||||||
|
|
||||||
Returned as `<satz typ="HINWEIS"><nr>…</nr><text>…</text></satz>`. `#1#`/`#2#` are substituted at
|
|
||||||
runtime.
|
|
||||||
|
|
||||||
> **`650` is not a success code.** Its documented text is a bare `#1#` — a generic message
|
|
||||||
> carrier whose entire content is substituted. `Notification::isError()`
|
|
||||||
> (`src/BusProNet/Model/Notification.php:33-36`) treats it as success, which happens to hold on
|
|
||||||
> the `KUNDENKONTO` paths but not on `BUCHUNG`/`BUCHUNGAENDERUNG`, where the same code delivers
|
|
||||||
> real rejections such as *"Es wurde nicht jedem Teilnehmer eine Pflichtleistung zugeordnet"*.
|
|
||||||
> `BookingEditSubmitter.php:81` therefore reports some genuine failures as INFO.
|
|
||||||
|
|
||||||
**Envelope / auth** — 100 Anfrageknoten fehlt · 101 Satzknoten fehlt · 102 Ungültiger Satztyp ·
|
|
||||||
103/104 User fehlt/fehlerhaft · 105/106 Key fehlt/fehlerhaft · 999 Systemfehler
|
|
||||||
|
|
||||||
**Lookup** — 200 Keine Einträge · 201/202 Partner · 203 Ungültiger Termin · 300 IDReise fehlt ·
|
|
||||||
301 Reise nicht gefunden · 302 IDLeistung (Hin & Rück) fehlt · 303 IDLeistung passt nicht zu
|
|
||||||
IDReise · 304 Kein Sitzplatz für alle Teilnehmer ermittelbar · 305 Anzahl Personen fehlt ·
|
|
||||||
306/307 Leistungen · 308 Keine Zahlungsarten · 400 IDPartner fehlt · 401 Partner nicht gefunden ·
|
|
||||||
402 Bis-Termin fehlt · 403 Keine Unterbringungsleistungen gefunden
|
|
||||||
|
|
||||||
**Customer / address** — 500 Art fehlt · 502 Keine Kunden · 840-842 Personen · 850 IDAdresse und
|
|
||||||
EMail fehlen · 851 Passwort fehlt · 852 Art fehlt · 853 ID, EMail oder Passwort falsch · 855 Keine
|
|
||||||
Daten für Adresse · 860 Adressdaten fehlen · 862-867 Selektionsmerkmale / CRM-Aktionen ·
|
|
||||||
870-875 Newsletter · 880-882 Dokumente · 885-891 Pflichtfelder (Name, Vorname, Geschlecht, EMail,
|
|
||||||
PLZ, Straße) · 889 Kunde bereits angelegt (ID #1#)
|
|
||||||
|
|
||||||
**Booking (create and edit)** — 900 Buchungsart fehlt · 901 Buchungsart falsch · 902 Status
|
|
||||||
fehlt · 903 Status falsch · 904/905 Agentur · 906 Reise-ID fehlt · 907 Reise nicht gefunden ·
|
|
||||||
908 Reise ist storniert · **909 Beförderungen fehlen** · 910 Leistung gehört nicht zur Reise ·
|
|
||||||
911 Leistung gehört nicht zum Produkt · 912 Beförderungsleistung fehlt · **913 Unterbringungen
|
|
||||||
fehlen** · **914 Partner-ID fehlt** · 915 Partner nicht gefunden · **916 Ferienziel-Unterbringungen
|
|
||||||
fehlen** · 917 Zimmer-IDZ fehlt · 918 Kategorie fehlt · 919 Verpflegungs-ID fehlt · 920 Anreise
|
|
||||||
fehlt · **921 Anreise passt nicht zur Beförderungsleistung (Hinfahrt)** · 922 Abreise fehlt ·
|
|
||||||
**923 Abreise passt nicht zur Beförderungsleistung (Rückfahrt)** · **924 Ferienziel: Keine Preise
|
|
||||||
zu den Daten gefunden** · 925 Preis zu den Daten nicht gefunden · 930 Zustiege fehlen ·
|
|
||||||
931 Zustieg nicht gefunden · 932 Zustieg bei Leistung nicht freigegeben · 933 Sitzplan bei
|
|
||||||
Leistung nicht freigegeben · 935-938 Versicherungen · 940-942 Zahlungsart · 950 Anmelder fehlt ·
|
|
||||||
951 Teilnehmerliste fehlt · 952 Teilnehmer-ID fehlt · 953 Keine Teilnehmer übergeben ·
|
|
||||||
960 Preisfehler · 961 Buchung konnte nicht gespeichert werden · **962 Buchungsstatus #1# kann
|
|
||||||
nicht verarbeitet werden, nur Status #2# möglich** · 970 CRM-Merkmal nicht gültig · 980 Reise ist
|
|
||||||
fürs Internet gesperrt · 981 Reise ist nicht mehr buchbar (#1#) · **982 Optionsbuchungen nicht
|
|
||||||
zugelassen** · 983 Anfragebuchung nicht zugelassen · 984 Leistung nicht fürs Internet buchbar ·
|
|
||||||
985 Zustieg nicht fürs Internet buchbar · 986/987 Rechnung · 990-994 Agenturkonto ·
|
|
||||||
995/996 Gutschein · 997/998 Buchung-ID · 1001 Reisedatum fehlerhaft · 1002 Buchungsdatum
|
|
||||||
fehlerhaft · 1003 Status im Vorgang nicht korrekt · 1011-1018 Zahlungsabgleich
|
|
||||||
|
|
||||||
**Vouchers** — 600-608 Gutscheine · 610/611 Buchungsart · 612-621 Gutscheinart / Stamm /
|
|
||||||
Kapazität · 622 Einlösecode fehlt · 630-632 Aktionsgutschein
|
|
||||||
|
|
||||||
## Applying this: the teamer placeholder booking
|
|
||||||
|
|
||||||
**Answered 2026-09-02: `BUCHUNG` cannot create it.** A payload mirroring the real teamer booking
|
|
||||||
(travel 12429, hotel 222925, agency 77876, both bus legs, ski pass, Ortstaxe, Keycard-Pfand,
|
|
||||||
pickup and drop-off, root and participant status `O`, ids-only identity, no room) returns
|
|
||||||
`916 Ferienziel-Unterbringungen fehlen`. Adding a room to the identical payload clears that gate
|
|
||||||
and moves on to `650`, which proves accommodation was the only structural objection.
|
|
||||||
|
|
||||||
Two blockers that were *not* the problem:
|
|
||||||
|
|
||||||
- **Status `O` is fine here.** Travel 12429 reports `buchungstatusmoeglich="F, O, A"`. The `982`
|
|
||||||
refusal seen earlier is per travel, not a global setting.
|
|
||||||
- **`NurFahrt` never came up** — the request never got far enough for it to matter.
|
|
||||||
|
|
||||||
What the real booking shows about how the office builds one, worth reproducing if the work moves
|
|
||||||
to a direct write:
|
|
||||||
|
|
||||||
- `<idpartner>` **is** present despite there being no accommodation — 222925 is the ordinary
|
|
||||||
hotel of the travel (`Adresse.IDAdresse`, "NEU Sportclub Weissfluh Midweeks und Wochenreisen",
|
|
||||||
the sole hotel of product 1429, with real bookable rooms), not a special no-accommodation
|
|
||||||
partner. Whether the booking stores it or the response derives it is still open.
|
|
||||||
- Each teamer seat is its **own `AdressePerson`** (407725, 407726) under the shared address 41003,
|
|
||||||
with an empty `<anschrift/>`.
|
|
||||||
- `<wünsche><beförderungswunsch>` carries the role label — "Reiseleitung + Busbegleitung".
|
|
||||||
- Per participant, `<optionsdatum>` sets the option expiry.
|
|
||||||
- The total is driven to zero with a **manual `<zuschlag>`**: Ortstaxe 44,00 + Keycard-Pfand 10,80
|
|
||||||
+ Abfahrtsortzuschlag 25,80 = 80,60, offset by an unnamed −80,60 line. `<zuschlaege>` is part of
|
|
||||||
the documented `BUCHUNG` payload, so this part *is* expressible over XML.
|
|
||||||
|
|
||||||
See `.ddev/plans/teamer-placeholder-booking-plan.md`.
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -1,971 +0,0 @@
|
|||||||
"foreign_key_name";"child_schema";"child_table";"child_column";"parent_schema";"parent_table";"parent_column"
|
|
||||||
"FK_Tarif_Schulamt";"dbo";"ABOSummeFahrscheine";"IDAdresse_FS_Schulamt";"dbo";"Schulamt";"IDSchulamt"
|
|
||||||
"FK_Tarif_Schule";"dbo";"ABOSummeFahrscheine";"IDAdresse_FS_Schule";"dbo";"Schule";"IDSchule"
|
|
||||||
"FK_Tarif_Schuljahr";"dbo";"ABOSummeFahrscheine";"IDABOSchuljahr_FS";"dbo";"ABOSchuljahr";"IDABOSchuljahr"
|
|
||||||
"FK_AdressAuswertungProtokoll_Adresse";"dbo";"AdressAuswertungProtokoll";"IDAdresse_FS_Sachbearbeiter";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseAgenturKatalog_Adresse";"dbo";"AdresseAgenturKatalog";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseAgenturKatalog_ArtKatalog";"dbo";"AdresseAgenturKatalog";"IDArtKatalog_FS";"dbo";"ArtKatalog";"IDArtKatalog"
|
|
||||||
"FK_AdresseAgenturOrganisation_Adresse";"dbo";"AdresseAgenturOrganisation";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseAgenturOrganisation_ArtAgenturOrganisation";"dbo";"AdresseAgenturOrganisation";"IDArtAgenturOrganisation_FS";"dbo";"ArtAgenturOrganisation";"IDArtAgenturOrganisation"
|
|
||||||
"FK_AdresseAgenturProvision_Adresse";"dbo";"AdresseAgenturProvision";"IDArtProvisionsstaffel_FS";"dbo";"ArtProvisionsstaffel";"IDArtProvisionsstaffel"
|
|
||||||
"FK_AdresseAgenturProvision_Adresse1";"dbo";"AdresseAgenturProvision";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseAgenturZusatz_Adresse";"dbo";"AdresseAgenturZusatz";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseAgenturZusatz_ArtAgenturPreisgruppe";"dbo";"AdresseAgenturZusatz";"IDArtAgenturPreisgruppe_FS";"dbo";"ArtAgenturPreisgruppe";"IDArtAgenturPreisgruppe"
|
|
||||||
"FK_AdresseAgenturZusatz_ArtPreisschema";"dbo";"AdresseAgenturZusatz";"IDArtPreisschema_FS";"dbo";"ArtPreisschema";"IDArtPreisschema"
|
|
||||||
"FK_AdresseAnruf_Adresse";"dbo";"AdresseAnruf";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseAnschriftKoordinate_AdresseAnschrift";"dbo";"AdresseAnschriftKoordinate";"IDAdresseAnschrift_FS";"dbo";"AdresseAnschrift";"IDAdresseAnschrift"
|
|
||||||
"FK_AdresseAnschriftKoordinate_ArtZustiegKoordinaten";"dbo";"AdresseAnschriftKoordinate";"IDArtZustiegKoordinaten_FS";"dbo";"ArtZustiegKoordinaten";"IDArtZustiegKoordinaten"
|
|
||||||
"FK_Adresse_Bankverbindung";"dbo";"AdresseBankverbindung";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Adresse_Bearbeitung";"dbo";"AdresseBearbeitung";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Adresse_Bearbeitung_ErsterSachbearbeiter";"dbo";"AdresseBearbeitung";"IDAdresse_FS_ErsterSachbearbeiter";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Adresse_Bearbeitung_LetzterSachbearbeiter";"dbo";"AdresseBearbeitung";"IDAdresse_FS_LetzterSachbearbeiter";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseCRS_Adresse";"dbo";"AdresseCRS";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseCRS_ArtCRS";"dbo";"AdresseCRS";"IDArtCRS_FS";"dbo";"ArtCRS";"IDArtCRS"
|
|
||||||
"FK_AdresseDatenschutz_Adresse";"dbo";"AdresseDatenschutz";"IdAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseEmpfehlerKonto_Adresse";"dbo";"AdresseEmpfehlerKonto";"IDAdresse_FS_Empfehler";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseEmpfehlerKonto_Adresse1";"dbo";"AdresseEmpfehlerKonto";"IDAdresse_FS_Empfohlener";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseHinweis_Adresse";"dbo";"AdresseHinweis";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseHistorie_Adresse";"dbo";"AdresseHistorie";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseHistorieBuchungTN_Adresse";"dbo";"AdresseHistorieBuchungTN";"IDAdresseHistorie_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseHistorieMietbus_Adresse";"dbo";"AdresseHistorieMietbus";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Adresse_Kommunikation";"dbo";"AdresseKommunikation";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseKommunikation_ArtKommunikation";"dbo";"AdresseKommunikation";"IDArtKommunikation_FS";"dbo";"ArtKommunikation";"IDArtKommunikation"
|
|
||||||
"FK_Adresse_Kreditkarte";"dbo";"AdresseKreditkarte";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseLandSteuer_Adresse";"dbo";"AdresseLandSteuer";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseLandSteuer_Land";"dbo";"AdresseLandSteuer";"IDLand_FS";"dbo";"Land";"IDLand"
|
|
||||||
"FK_AdresseLinieAuftraggeber_Adresse";"dbo";"AdresseLinieAuftraggeber";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseLinieAuftraggeber_ArtStatistikGruppe";"dbo";"AdresseLinieAuftraggeber";"IDArtStatistikGruppe_FS";"dbo";"ArtStatistikGruppe";"IDArtStatistikGruppe"
|
|
||||||
"FK_AdresseLinieAuftraggeberBlock_Adresse";"dbo";"AdresseLinieAuftraggeberBlock";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseLinieAuftraggeberBlock_AdresseLinieAuftraggeber";"dbo";"AdresseLinieAuftraggeberBlock";"IDAdresseLinieAuftraggeber_FS";"dbo";"AdresseLinieAuftraggeber";"IDAdresseLinieAuftraggeber"
|
|
||||||
"FK_AdresseLinieAuftraggeberLinie_Adresse";"dbo";"AdresseLinieAuftraggeberLinie";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseLinieAuftraggeberLinie_AdresseLinieAuftraggeber";"dbo";"AdresseLinieAuftraggeberLinie";"IDAdresseLinieAuftraggeber_FS";"dbo";"AdresseLinieAuftraggeber";"IDAdresseLinieAuftraggeber"
|
|
||||||
"FK_AdresseLinieAuftraggeberTour_Adresse";"dbo";"AdresseLinieAuftraggeberTour";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseLinieAuftraggeberTour_AdresseLinieAuftraggeber";"dbo";"AdresseLinieAuftraggeberTour";"IDAdresseLinieAuftraggeber_FS";"dbo";"AdresseLinieAuftraggeber";"IDAdresseLinieAuftraggeber"
|
|
||||||
"FK_AdressMandant_Adresse";"dbo";"AdresseMandant";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseMandantDokumentAusgabe_Adresse";"dbo";"AdresseMandantDokumentAusgabe";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseMandantDokumentAusgabe_ArtDokumentAusgabe";"dbo";"AdresseMandantDokumentAusgabe";"IDArtDokumentAusgabe_FS";"dbo";"ArtDokumentAusgabe";"IDArtDokumentAusgabe"
|
|
||||||
"FK_AdresseMandantERechnung_Adresse";"dbo";"AdresseMandantERechnung";"IDAdresse_FS_Mandant";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseMandantERechnungBV_AdresseBankverbindung";"dbo";"AdresseMandantERechnungBV";"IDAdresseBankverbindung_FS";"dbo";"AdresseBankverbindung";"IDAdresseBankverbindung"
|
|
||||||
"FK_AdresseMandantERechnungBV_AdresseMandantERechnung";"dbo";"AdresseMandantERechnungBV";"IDAdresseMandantERechnung_FS";"dbo";"AdresseMandantERechnung";"IDAdresseMandantERechnung"
|
|
||||||
"FK_AdresseMandantNummernkreis_Adresse";"dbo";"AdresseMandantNummernkreis";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseMandantNummernkreis_Nummernkreis";"dbo";"AdresseMandantNummernkreis";"IDNummernkreis_FS";"dbo";"Nummernkreis";"IDNummernkreis"
|
|
||||||
"FK_Adresse_Person";"dbo";"AdressePerson";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdressePersonal_Adresse";"dbo";"AdressePersonal";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdressePersonal_ArtBetriebshof";"dbo";"AdressePersonal";"IDArtBetriebshof_FS";"dbo";"ArtBetriebshof";"IDArtBetriebshof"
|
|
||||||
"FK_AdressePersonal_ArtLohnMandant";"dbo";"AdressePersonal";"IDArtLohnMandant_FS";"dbo";"ArtLohnMandant";"IDArtLohnMandant"
|
|
||||||
"FK_AdressePersonal_ArtPersonalEinsatz";"dbo";"AdressePersonal";"IDArtPersonalEinsatz_FS";"dbo";"ArtPersonalEinsatz";"IDArtPersonalEinsatz"
|
|
||||||
"FK_AdressePersonal_ArtPersonalGruppeKompetenz";"dbo";"AdressePersonal";"IDArtPersonalGruppeKompetenz_FS";"dbo";"ArtPersonalGruppeKompetenz";"IDArtPersonalGruppeKompetenz"
|
|
||||||
"FK_AdressePersonalArt_Adresse";"dbo";"AdressePersonalArt";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdressePersonalGruppeProdukt_Adresse";"dbo";"AdressePersonalGruppeProdukt";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdressePersonalGruppeProdukt_ArtPersonalGruppeProdukt";"dbo";"AdressePersonalGruppeProdukt";"IDArtPersonalGruppeProdukt_FS";"dbo";"ArtPersonalGruppeProdukt";"IDArtPersonalGruppeProdukt"
|
|
||||||
"FK_AdressePersonalKleidung_Adresse";"dbo";"AdressePersonalKleidung";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdressePersonalKleidung_ArtPersonalKleidung";"dbo";"AdressePersonalKleidung";"IDArtPersonalKleidung_FS";"dbo";"ArtPersonalKleidung";"IDArtPersonalKleidung"
|
|
||||||
"FK_AdressePersonalNummer_Adresse";"dbo";"AdressePersonalNummer";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdressePersonalNummer_ArtPersonalNummer";"dbo";"AdressePersonalNummer";"IDArtPersonalNummer_FS";"dbo";"ArtPersonalNummer";"IDArtPersonalNummer"
|
|
||||||
"FK_AdressePersonalPunkte_Adresse";"dbo";"AdressePersonalPunkte";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdressePersonalSonstGegenstaende_Adresse";"dbo";"AdressePersonalSonstGegenstaende";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdressePersonalSonstGegenstaende_ArtPersonalSonstGegenstaende";"dbo";"AdressePersonalSonstGegenstaende";"IDArtPersonalSonstGegenstaende_FS";"dbo";"ArtPersonalSonstGegenstaende";"IDArtPersonalSonstGegenstaende"
|
|
||||||
"FK_AdressePersonalSperrKasse_Adresse";"dbo";"AdressePersonalSperrKasse";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdressePersonalSperrKasse_ZahlungStamm";"dbo";"AdressePersonalSperrKasse";"IDZahlungStamm_FS";"dbo";"ZahlungStamm";"IDZahlungStamm"
|
|
||||||
"FK_AdressePersonalUnfall_Adresse";"dbo";"AdressePersonalUnfall";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdressePersonalVerwendung_Adresse";"dbo";"AdressePersonalVerwendung";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdressePersonalZusatz_Adresse";"dbo";"AdressePersonalZusatz";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdressePersonalZusatz_SpracheMaske";"dbo";"AdressePersonalZusatz";"IDSpracheMaske_FS";"dbo";"SpracheMaske";"IDSpracheMaske"
|
|
||||||
"FK_AdressePunkte_Adresse";"dbo";"AdressePunkte";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdressePunkte_Adresse1";"dbo";"AdressePunkte";"IDAdresse_FS_Sachbearbeiter";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseSteueraufteilungDetail_Adresse";"dbo";"AdresseSteueraufteilungDetail";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseSteueraufteilungDetail_Steueraufteilung";"dbo";"AdresseSteueraufteilungDetail";"IDSteueraufteilungDetail_FS";"dbo";"SteueraufteilungDetail";"IDSteueraufteilungDetail"
|
|
||||||
"FK_AdresseZusatz_Adresse";"dbo";"AdresseZusatz";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseZusatzfeld_Adresse";"dbo";"AdresseZusatzfeld";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AdresseZusatzfeld_AdressePerson";"dbo";"AdresseZusatzfeld";"IDAdressePerson_FS";"dbo";"AdressePerson";"IDAdressePerson"
|
|
||||||
"FK_AdresseZusatzfeld_ArtAdresseZusatzfeld";"dbo";"AdresseZusatzfeld";"IDArtAdresseZusatzfeld_FS";"dbo";"ArtAdresseZusatzfeld";"IDArtAdresseZusatzfeld"
|
|
||||||
"FK_AdresseMietbusZusatz_Adresse";"dbo";"AdresseZusatzMietbus";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Adresse_AgenturSelektion";"dbo";"AgenturSelektionenKunde";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Selektionsstamm_AgenturSelektion";"dbo";"AgenturSelektionenKunde";"IDSelektionsstamm_FS";"dbo";"Selektionsstamm";"IDSelektionsstamm"
|
|
||||||
"FK_ArbeitswertMaterial_Arbeitswert";"dbo";"ArbeitswertMaterial";"IDArbeitswert_FS";"dbo";"Arbeitswert";"IDArbeitswert"
|
|
||||||
"FK_ArbeitswertMaterial_Artikel";"dbo";"ArbeitswertMaterial";"IDArtikel_FS";"dbo";"Artikel";"IDArtikel"
|
|
||||||
"FK_ArbeitswertPersonal_Arbeitswert";"dbo";"ArbeitswertPersonal";"IDArbeitswert_FS";"dbo";"Arbeitswert";"IDArbeitswert"
|
|
||||||
"FK_ArbeitswertPersonal_Arbeitszeitwert";"dbo";"ArbeitswertPersonal";"IDArbeitszeitwert_FS";"dbo";"Arbeitszeitwert";"IDArbeitszeitwert"
|
|
||||||
"FK_ArtArbeitszeitZusatzEinst_ArtArbeitszeitZusatz";"dbo";"ArtArbeitszeitZusatzEinst";"IDArtArbeitszeitZusatz_FS";"dbo";"ArtArbeitszeitzusatz";"IDArtArbeitszeitzusatz"
|
|
||||||
"FK_ArtikelBewegung_Artikel";"dbo";"ArtikelBewegung";"IDArtikel_FS";"dbo";"Artikel";"IDArtikel"
|
|
||||||
"FK_ArtikelImport_ArtArtikelImport";"dbo";"ArtikelImport";"IDArtArtikelImport_FS";"dbo";"ArtArtikelImport";"IDArtArtikelImport"
|
|
||||||
"FK_Artikelzusatz_ArtEinheit";"dbo";"Artikelzusatz";"IDArtEinheit_FS";"dbo";"ArtEinheit";"IDArtEinheit"
|
|
||||||
"FK_ArtikelZuweisungWarengruppe_Artikel";"dbo";"ArtikelZuweisungWarengruppe";"IDArtikel_FS";"dbo";"Artikel";"IDArtikel"
|
|
||||||
"FK_ArtikelZuweisungWarengruppe_Warengruppe";"dbo";"ArtikelZuweisungWarengruppe";"IDArtWarengruppe_FS";"dbo";"ArtWarengruppe";"IDArtWarengruppe"
|
|
||||||
"FK_Auftrag_Adresse3";"dbo";"Auftrag";"IDAdresse_FS_Kunde";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Auftrag_Auftragsart";"dbo";"Auftrag";"IDAuftragsart_FS";"dbo";"Auftragsart";"IDAuftragsart"
|
|
||||||
"FK_AuftragBlock_Auftrag";"dbo";"AuftragBlock";"IDAuftrag_FS";"dbo";"Auftrag";"IDAuftrag"
|
|
||||||
"FK_AuftragFahrer_Auftrag";"dbo";"AuftragFahrer";"IDAuftrag_FS";"dbo";"Auftrag";"IDAuftrag"
|
|
||||||
"FK_AuftragFahrer_AuftragFahrzeug";"dbo";"AuftragFahrer";"IDAuftragFahrzeug_FS";"dbo";"AuftragFahrzeug";"IDAuftragFahrzeug"
|
|
||||||
"FK_AuftragFahrer_AuftragTeilauftrag";"dbo";"AuftragFahrer";"IDAuftragTeilauftrag_FS";"dbo";"AuftragTeilauftrag";"IDAuftragTeilauftrag"
|
|
||||||
"FK_AuftragFahrerArbeitszeit_ArtArbeitszeitzusatz";"dbo";"AuftragFahrerArbeitszeit";"IDArtArbeitszeitzusatz_FS";"dbo";"ArtArbeitszeitzusatz";"IDArtArbeitszeitzusatz"
|
|
||||||
"FK_AuftragFahrerArbeitszeit_AuftragFahrer";"dbo";"AuftragFahrerArbeitszeit";"IDAuftragFahrer_FS";"dbo";"AuftragFahrer";"IDAuftragFahrer"
|
|
||||||
"FK_AuftragFahrerArbeitszeitSpesen_AuftragFahrer";"dbo";"AuftragFahrerArbeitszeitSpesen";"IDAuftragFahrer_FS";"dbo";"AuftragFahrer";"IDAuftragFahrer"
|
|
||||||
"FK_AuftragFahrerTagesStunden_AuftragFahrer";"dbo";"AuftragFahrerTagesStunden";"IDAuftragFahrer_FS";"dbo";"AuftragFahrer";"IDAuftragFahrer"
|
|
||||||
"FK_AuftragFahrzeug_Auftrag";"dbo";"AuftragFahrzeug";"IDAuftrag_FS";"dbo";"Auftrag";"IDAuftrag"
|
|
||||||
"FK_AuftragFahrzeug_AuftragTeilauftrag";"dbo";"AuftragFahrzeug";"IDAuftragTeilauftrag_FS";"dbo";"AuftragTeilauftrag";"IDAuftragTeilauftrag"
|
|
||||||
"FK_AuftragFahrzeugNavi_AuftragFahrzeug";"dbo";"AuftragFahrzeugNavi";"IDAuftragFahrzeug_FS";"dbo";"AuftragFahrzeug";"IDAuftragFahrzeug"
|
|
||||||
"FK_AuftragFahrzeugNavi_Fahrzeug";"dbo";"AuftragFahrzeugNavi";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_AuftragFahrzeugNaviGrenzuebertritt_AuftragFahrzeug";"dbo";"AuftragFahrzeugNaviGrenzuebertritt";"IDAuftragFahrzeug_FS";"dbo";"AuftragFahrzeug";"IDAuftragFahrzeug"
|
|
||||||
"FK_AuftragFahrzeugTagesKM_AuftragFahrzeug";"dbo";"AuftragFahrzeugTagesKM";"IDAuftragFahrzeug_FS";"dbo";"AuftragFahrzeug";"IDAuftragFahrzeug"
|
|
||||||
"FK_AuftragInkludierteLeistung_Auftrag";"dbo";"AuftragInkludierteLeistung";"IDAuftrag_FS";"dbo";"Auftrag";"IDAuftrag"
|
|
||||||
"FK_AuftragInkludierteLeistung_AuftragTeilauftrag";"dbo";"AuftragInkludierteLeistung";"IDAuftragTeilauftrag_FS";"dbo";"AuftragTeilauftrag";"IDAuftragTeilauftrag"
|
|
||||||
"FK_AuftragNebenleistung_Auftrag";"dbo";"AuftragInkludierteLeistung";"IDAuftrag_FS";"dbo";"Auftrag";"IDAuftrag"
|
|
||||||
"FK_AuftragNebenleistung_AuftragTeilauftrag";"dbo";"AuftragInkludierteLeistung";"IDAuftragTeilauftrag_FS";"dbo";"AuftragTeilauftrag";"IDAuftragTeilauftrag"
|
|
||||||
"FK_AuftragKalkulationswert_Auftrag";"dbo";"AuftragKalkulationswert";"IDAuftrag_FS";"dbo";"Auftrag";"IDAuftrag"
|
|
||||||
"FK_AuftragKalkulationswert_AuftragTeilauftrag";"dbo";"AuftragKalkulationswert";"IDAuftragTeilauftrag_FS";"dbo";"AuftragTeilauftrag";"IDAuftragTeilauftrag"
|
|
||||||
"FK_AuftragKilometer_Auftrag";"dbo";"AuftragKilometer";"IDAuftrag_FS";"dbo";"Auftrag";"IDAuftrag"
|
|
||||||
"FK_AuftragKilometer_AuftragFahrzeug";"dbo";"AuftragKilometer";"IDAuftragFahrzeug_FS";"dbo";"AuftragFahrzeug";"IDAuftragFahrzeug"
|
|
||||||
"FK_AuftragKilometer_AuftragTeilauftrag";"dbo";"AuftragKilometer";"IDAuftragTeilauftrag_FS";"dbo";"AuftragTeilauftrag";"IDAuftragTeilauftrag"
|
|
||||||
"FK_AuftragKilometerLand_Auftrag";"dbo";"AuftragKilometerLand";"IDAuftrag_FS";"dbo";"Auftrag";"IDAuftrag"
|
|
||||||
"FK_AuftragKilometerLand_AuftragFahrzeug";"dbo";"AuftragKilometerLand";"IDAuftragFahrzeug_FS";"dbo";"AuftragFahrzeug";"IDAuftragFahrzeug"
|
|
||||||
"FK_AuftragKilometerLand_AuftragKilometer";"dbo";"AuftragKilometerLand";"IDAuftragKilometer_FS";"dbo";"AuftragKilometer";"IDAuftragKilometer"
|
|
||||||
"FK_AuftragKilometerLand_AuftragTeilauftrag";"dbo";"AuftragKilometerLand";"IDAuftragTeilauftrag_FS";"dbo";"AuftragTeilauftrag";"IDAuftragTeilauftrag"
|
|
||||||
"FK_AuftragKilometerLand_Land";"dbo";"AuftragKilometerLand";"IDLand_FS";"dbo";"Land";"IDLand"
|
|
||||||
"FK_AuftragsartMwst_Auftragsart";"dbo";"AuftragsartMwst";"IDAuftragsart_FS";"dbo";"Auftragsart";"IDAuftragsart"
|
|
||||||
"FK_AuftragSuche_Adresse1";"dbo";"AuftragSuche";"IDAdresse_FS_Kunde";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AuftragSuche_Auftrag";"dbo";"AuftragSuche";"IDAuftrag_FS";"dbo";"Auftrag";"IDAuftrag"
|
|
||||||
"FK_AuftragSuche_Auftragsart";"dbo";"AuftragSuche";"IDAuftragsArt_FS";"dbo";"Auftragsart";"IDAuftragsart"
|
|
||||||
"FK_AuftragTAAbholstelle_AuftragTeilauftrag";"dbo";"AuftragTAAbholstelle";"IDAuftragTeilauftrag_FS";"dbo";"AuftragTeilauftrag";"IDAuftragTeilauftrag"
|
|
||||||
"FK_AuftragTARoute_Auftrag";"dbo";"AuftragTARoute";"IDAuftrag_FS";"dbo";"Auftrag";"IDAuftrag"
|
|
||||||
"FK_AuftragTARoute_AuftragTeilauftrag";"dbo";"AuftragTARoute";"IDAuftragTeilauftrag_FS";"dbo";"AuftragTeilauftrag";"IDAuftragTeilauftrag"
|
|
||||||
"FK_AuftragTARoute_Land";"dbo";"AuftragTARoute";"IDLand_FS";"dbo";"Land";"IDLand"
|
|
||||||
"FK_AuftragTeilauftrag_Auftrag";"dbo";"AuftragTeilauftrag";"IDAuftrag_FS";"dbo";"Auftrag";"IDAuftrag"
|
|
||||||
"FK_AuftragTeilauftragPreis_Auftrag";"dbo";"AuftragTeilauftragPreis";"IDAuftrag_FS";"dbo";"Auftrag";"IDAuftrag"
|
|
||||||
"FK_AuftragTeilauftragPreis_AuftragTeilauftrag";"dbo";"AuftragTeilauftragPreis";"IDAuftragTeilauftrag_FS";"dbo";"AuftragTeilauftrag";"IDAuftragTeilauftrag"
|
|
||||||
"FK_AuftragTeilauftragPreisErweitert_AuftragTeilauftrag";"dbo";"AuftragTeilauftragPreisErweitert";"IDAuftragTeilauftrag_FS";"dbo";"AuftragTeilauftrag";"IDAuftragTeilauftrag"
|
|
||||||
"FK_AuftragTeilauftragPreisErweitert_Ausstattung";"dbo";"AuftragTeilauftragPreisErweitert";"IDAusstattung_FS";"dbo";"Ausstattung";"IDAusstattung"
|
|
||||||
"FK_AuftragText_ArtAuftragstext";"dbo";"AuftragText";"IDArtAuftragstext_FS";"dbo";"ArtAuftragstext";"IDArtAuftragstext"
|
|
||||||
"FK_AuftragText_Auftrag";"dbo";"AuftragText";"IDAuftrag_FS";"dbo";"Auftrag";"IDAuftrag"
|
|
||||||
"FK_AuftragZusatz_ArtStornogrund";"dbo";"AuftragZusatz";"IDArtStornogrund_FS";"dbo";"ArtStornogrund";"IDArtStornogrund"
|
|
||||||
"FK_AuftragZusatz_Auftrag";"dbo";"AuftragZusatz";"IDAuftrag_FS";"dbo";"Auftrag";"IDAuftrag"
|
|
||||||
"FK_AuftragZusatzleistung_Auftrag";"dbo";"AuftragZusatzleistung";"IDAuftrag_FS";"dbo";"Auftrag";"IDAuftrag"
|
|
||||||
"FK_AuftragZusatzleistung_AuftragTeilauftrag";"dbo";"AuftragZusatzleistung";"IDAuftragTeilauftrag_FS";"dbo";"AuftragTeilauftrag";"IDAuftragTeilauftrag"
|
|
||||||
"FK_AusstattungFahrzeugAusstattung_ArtFahrzeugAusstattungMerkmal";"dbo";"AusstattungFahrzeugAusstattung";"IDArtFahrzeugAusstattungMerkmal_FS";"dbo";"ArtFahrzeugAusstattungMerkmal";"IDArtFahrzeugAusstattungMerkmal"
|
|
||||||
"FK_AusstattungFahrzeugAusstattung_Ausstattung";"dbo";"AusstattungFahrzeugAusstattung";"IDAusstattung_FS";"dbo";"Ausstattung";"IDAusstattung"
|
|
||||||
"FK_AusstattungKalkulationsWert_Ausstattung";"dbo";"AusstattungKalkulationsWert";"IDAusstattung_FS";"dbo";"Ausstattung";"IDAusstattung"
|
|
||||||
"FK_AusstattungKalkulationsWert_KalkulationswertBezeichnung";"dbo";"AusstattungKalkulationsWert";"IDKalkulationsWertBezeichnung_FS";"dbo";"KalkulationswertBezeichnung";"IDKalkulationswertBezeichnung"
|
|
||||||
"FK_AusstattungKalkulationswertPersonen_AusstattungKalkulationsWert";"dbo";"AusstattungKalkulationswertPersonen";"IDAusstattungKalkulationswert_FS";"dbo";"AusstattungKalkulationsWert";"IDAusstattungKalkulationsWert"
|
|
||||||
"FK_AZKImport_Adresse";"dbo";"AZKImport";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_AZKImport_AZKImportEinstellung";"dbo";"AZKImport";"IDAZKImportEinstellung_FS";"dbo";"AZKImportEinstellung";"IDAZKImportEinstellung"
|
|
||||||
"FK_Befoerderungsvertrag_Adresse";"dbo";"Befoerderungsvertrag";"IDAdresse_FS_Partner";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Befoerderungsvertrag_AdresseAnschrift";"dbo";"Befoerderungsvertrag";"IDAdresseAnschrift_FS_Partner";"dbo";"AdresseAnschrift";"IDAdresseAnschrift"
|
|
||||||
"FK_BefoerderungsvertragTeilauftrag_AuftragTeilauftrag";"dbo";"BefoerderungsvertragTeilauftrag";"IDAuftragTeilauftrag_FS";"dbo";"AuftragTeilauftrag";"IDAuftragTeilauftrag"
|
|
||||||
"FK_BefoerderungsvertragTeilauftrag_Befoerderungsvertrag";"dbo";"BefoerderungsvertragTeilauftrag";"IDBefoerderungsvertrag_FS";"dbo";"Befoerderungsvertrag";"IDBefoerderungsvertrag"
|
|
||||||
"FK_Block_Adresse";"dbo";"Block";"IDAdresse_FS_Fahrer";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Block_ArtBetriebshof";"dbo";"Block";"IDArtBetriebshof_FS_Anfang";"dbo";"ArtBetriebshof";"IDArtBetriebshof"
|
|
||||||
"FK_Block_ArtBetriebshof1";"dbo";"Block";"IDArtBetriebshof_FS_Ende";"dbo";"ArtBetriebshof";"IDArtBetriebshof"
|
|
||||||
"FK_Block_ArtBlockArt";"dbo";"Block";"IDArtBlockart_FS";"dbo";"ArtBlockArt";"IDArtBlockArt"
|
|
||||||
"FK_Block_ArtFahrplanVersion";"dbo";"Block";"IDArtFahrplanVersion_FS";"dbo";"ArtFahrplanVersion";"IDArtFahrplanVersion"
|
|
||||||
"FK_Block_ArtFremdunternehmen";"dbo";"Block";"IDArtFremdunternehmen_FS";"dbo";"ArtFremdunternehmen";"IDArtFremdunternehmen"
|
|
||||||
"FK_Block_ArtReichweitentyp";"dbo";"Block";"IDArtReichweitentyp_FS";"dbo";"ArtReichweitentyp";"IDArtReichweitentyp"
|
|
||||||
"FK_Block_Fahrzeug";"dbo";"Block";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_Block_Ort_AbstellAnfang";"dbo";"Block";"IDOrt_FS_AbstellAnfang";"dbo";"Ort";"IDOrt"
|
|
||||||
"FK_Block_Ort_AbstellEnde";"dbo";"Block";"IDOrt_FS_AbstellEnde";"dbo";"Ort";"IDOrt"
|
|
||||||
"FK_BlockFahrzeugtyp_ArtFahrzeugTypen";"dbo";"BlockFahrzeugtyp";"IDArtFahrzeugTypen_FS";"dbo";"ArtFahrzeugTypen";"IDArtFahrzeugTypen"
|
|
||||||
"FK_BlockFahrzeugtyp_Block";"dbo";"BlockFahrzeugtyp";"IDBlock_FS";"dbo";"Block";"IDBlock"
|
|
||||||
"FK_BlockGruppeVerkehrstag_ArtGruppeVerkehrstag";"dbo";"BlockGruppeVerkehrstag";"IDArtGruppeVerkehrstag_FS";"dbo";"ArtGruppeVerkehrstag";"IDArtGruppeVerkehrstag"
|
|
||||||
"FK_BlockGruppeVerkehrstag_Block";"dbo";"BlockGruppeVerkehrstag";"IDBlock_FS";"dbo";"Block";"IDBlock"
|
|
||||||
"FK_Blockkombination_ArtFahrplanVersion";"dbo";"Blockkombination";"IDArtFahrplanVersion_FS";"dbo";"ArtFahrplanVersion";"IDArtFahrplanVersion"
|
|
||||||
"FK_BlockkombinationBlock_Block";"dbo";"BlockkombinationBlock";"IDBlock_FS";"dbo";"Block";"IDBlock"
|
|
||||||
"FK_BlockkombinationBlock_Blockkombination";"dbo";"BlockkombinationBlock";"IDBlockkombination_FS";"dbo";"Blockkombination";"IDBlockkombination"
|
|
||||||
"FK_BlockkombinationGruppeVerkehrstag_ArtGruppeVerkehrstag";"dbo";"BlockkombinationGruppeVerkehrstag";"IDArtGruppeVerkehrstag_FS";"dbo";"ArtGruppeVerkehrstag";"IDArtGruppeVerkehrstag"
|
|
||||||
"FK_BlockkombinationGruppeVerkehrstag_Blockkombination";"dbo";"BlockkombinationGruppeVerkehrstag";"IDBlockkombination_FS";"dbo";"Blockkombination";"IDBlockkombination"
|
|
||||||
"FK_BlockPause_Block";"dbo";"BlockPause";"IDBlock_FS";"dbo";"Block";"IDBlock"
|
|
||||||
"FK_BlockPause_Ort";"dbo";"BlockPause";"IDOrt_FS";"dbo";"Ort";"IDOrt"
|
|
||||||
"FK_BlockPlanung_Adresse";"dbo";"BlockPlanung";"IDAdresse_FS_Fahrer";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_BlockPlanung_Adresse2";"dbo";"BlockPlanung";"LoeschenManuellIDAdresse_FS_SB";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_BlockPlanung_ArtBetriebshof";"dbo";"BlockPlanung";"IDArtBetriebshof_FS_Anfang";"dbo";"ArtBetriebshof";"IDArtBetriebshof"
|
|
||||||
"FK_BlockPlanung_ArtBetriebshof1";"dbo";"BlockPlanung";"IDArtBetriebshof_FS_Ende";"dbo";"ArtBetriebshof";"IDArtBetriebshof"
|
|
||||||
"FK_BlockPlanung_ArtBlockArt";"dbo";"BlockPlanung";"IDArtBlockart_FS";"dbo";"ArtBlockArt";"IDArtBlockArt"
|
|
||||||
"FK_BlockPlanung_ArtFahrplanVersion";"dbo";"BlockPlanung";"IDArtFahrplanVersion_FS";"dbo";"ArtFahrplanVersion";"IDArtFahrplanVersion"
|
|
||||||
"FK_BlockPlanung_ArtFremdunternehmen";"dbo";"BlockPlanung";"IDArtFremdunternehmen_FS";"dbo";"ArtFremdunternehmen";"IDArtFremdunternehmen"
|
|
||||||
"FK_BlockPlanung_ArtReichweitentyp";"dbo";"BlockPlanung";"IDArtReichweitentyp_FS";"dbo";"ArtReichweitentyp";"IDArtReichweitentyp"
|
|
||||||
"FK_BlockPlanung_Block";"dbo";"BlockPlanung";"IDBlock_FS_Basis";"dbo";"Block";"IDBlock"
|
|
||||||
"FK_BlockPlanung_Fahrzeug";"dbo";"BlockPlanung";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_BlockPlanung_Ort_AbstellAnfang";"dbo";"BlockPlanung";"IDOrt_FS_AbstellAnfang";"dbo";"Ort";"IDOrt"
|
|
||||||
"FK_BlockPlanung_Ort_AbstellEnde";"dbo";"BlockPlanung";"IDOrt_FS_AbstellEnde";"dbo";"Ort";"IDOrt"
|
|
||||||
"FK_BlockPlanungFahrzeugtyp_ArtFahrzeugTypen";"dbo";"BlockPlanungFahrzeugtyp";"IDArtFahrzeugTypen_FS";"dbo";"ArtFahrzeugTypen";"IDArtFahrzeugTypen"
|
|
||||||
"FK_BlockPlanungFahrzeugtyp_BlockPlanung";"dbo";"BlockPlanungFahrzeugtyp";"IDBlockPlanung_FS";"dbo";"BlockPlanung";"IDBlockPlanung"
|
|
||||||
"FK_BlockPlanungPause_BlockPlanung";"dbo";"BlockPlanungPause";"IDBlockPlanung_FS";"dbo";"BlockPlanung";"IDBlockPlanung"
|
|
||||||
"FK_BlockPlanungPause_Ort";"dbo";"BlockPlanungPause";"IDOrt_FS";"dbo";"Ort";"IDOrt"
|
|
||||||
"FK_BlockPlanungPreisMehrMinder_TourPlanung";"dbo";"BlockPlanungPreisMehrMinder";"IDTourPlanung_FS";"dbo";"TourPlanung";"IDTourPlanung"
|
|
||||||
"FK_BlockPlanungZusatz_BlockPlanung";"dbo";"BlockPlanungZusatz";"IDBlockPlanung_FS";"dbo";"BlockPlanung";"IDBlockPlanung"
|
|
||||||
"FK_BlockSitzenbleiber_Block";"dbo";"BlockSitzenbleiber";"IDBlock_FS";"dbo";"Block";"IDBlock"
|
|
||||||
"FK_BlockSitzenbleiber_TourNach";"dbo";"BlockSitzenbleiber";"IdTour_FS_Nach";"dbo";"Tour";"IDTour"
|
|
||||||
"FK_BlockSitzenbleiber_TourVon";"dbo";"BlockSitzenbleiber";"IdTour_FS_Von";"dbo";"Tour";"IDTour"
|
|
||||||
"FK_BlockTour_Block";"dbo";"BlockTour";"IDBlock_FS";"dbo";"Block";"IDBlock"
|
|
||||||
"FK_BlockTour_Tour";"dbo";"BlockTour";"IDTour_FS";"dbo";"Tour";"IDTour"
|
|
||||||
"FK_BlockZusatz_Block";"dbo";"BlockZusatz";"IDBlock_FS";"dbo";"Block";"IDBlock"
|
|
||||||
"FK_Buchung_Adresse";"dbo";"Buchung";"IDAdresse_FS_Agentur";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Buchung_Adresse1";"dbo";"Buchung";"IDAdresse_FS_ErsterSachb";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Buchung_Adresse2";"dbo";"Buchung";"IDAdresse_FS_LetzterSachb";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Buchung_Adresse3";"dbo";"Buchung";"IDAdresse_FS_Mandant";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Buchung_IdAdressePerson";"dbo";"Buchung";"IdAdressePerson_FS_Anmelder";"dbo";"AdressePerson";"IDAdressePerson"
|
|
||||||
"FK_BuchungBemerkung_Buchung";"dbo";"BuchungBemerkung";"IDBuchung_FS";"dbo";"Buchung";"IDBuchung"
|
|
||||||
"FK_BuchungDokument_Adresse";"dbo";"BuchungDokument";"IDAdresse_FS_Sachbearbeiter";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_BuchungDokument_ArtDokument";"dbo";"BuchungDokument";"IDArtDokument_FS";"dbo";"ArtDokument";"IDArtDokument"
|
|
||||||
"FK_BuchungDokument_Buchung";"dbo";"BuchungDokument";"IDBuchung_FS";"dbo";"Buchung";"IDBuchung"
|
|
||||||
"FK_BuchungDruck_Buchung";"dbo";"BuchungDruck";"IDBuchung_FS";"dbo";"Buchung";"IDBuchung"
|
|
||||||
"FK_BuchungMeldung_Adresse";"dbo";"BuchungMeldung";"IDAdresse_FS_Mitarbeiter_Eingabe";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_BuchungMeldung_Adresse1";"dbo";"BuchungMeldung";"IDAdresse_FS_Mitarbeiter_Meldung";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_BuchungMeldung_ArtMeldungArt";"dbo";"BuchungMeldung";"IDArtMeldungart_FS";"dbo";"ArtMeldungArt";"IDArtMeldungArt"
|
|
||||||
"FK_BuchungMeldung_Buchung";"dbo";"BuchungMeldung";"IDBuchung_FS";"dbo";"Buchung";"IDBuchung"
|
|
||||||
"FK_BuchungMeldung_BuchungTNFerienziel";"dbo";"BuchungMeldung";"IDBuchungTNFerienziel_FS";"dbo";"BuchungTNFerienziel";"IDBuchungTNFerienziel"
|
|
||||||
"FK_BuchungMeldung_BuchungTNLeistung";"dbo";"BuchungMeldung";"IDBuchungTNLeistung_FS";"dbo";"BuchungTNLeistung";"IDBuchungTNLeistung"
|
|
||||||
"FK_BuchungReisebüro_Buchung";"dbo";"BuchungReisebüro";"IDBuchung_FS";"dbo";"Buchung";"IDBuchung"
|
|
||||||
"FK_BuchungReisebüroVersicherungsGruppe_ArtVersicherungsGruppe";"dbo";"BuchungReisebüroVersicherungsGruppe";"IDArtVersicherungsGruppe_FS";"dbo";"ArtVersicherungsGruppe";"IDArtVersicherungsGruppe"
|
|
||||||
"FK_BuchungReisebüroVersicherungsGruppe_BuchungReisebüro";"dbo";"BuchungReisebüroVersicherungsGruppe";"IDBuchungReisebüro_FS";"dbo";"BuchungReisebüro";"IDBuchungReisebüro"
|
|
||||||
"FK_BuchungReisebüroVisum_BuchungReisebüro";"dbo";"BuchungReisebüroVisum";"IDBuchungReisebüro_FS";"dbo";"BuchungReisebüro";"IDBuchungReisebüro"
|
|
||||||
"FK_BuchungTextbaustein_Buchung";"dbo";"BuchungTextbaustein";"IDBuchung_FS";"dbo";"Buchung";"IDBuchung"
|
|
||||||
"FK_BuchungTextbaustein_Textbaustein";"dbo";"BuchungTextbaustein";"IDTextbaustein_FS";"dbo";"Textbaustein";"IDTextbaustein"
|
|
||||||
"FK_BuchungTN_AdressePerson";"dbo";"BuchungTN";"IDAdressePerson_FS";"dbo";"AdressePerson";"IDAdressePerson"
|
|
||||||
"FK_BuchungTN_ArtTeilnahmeStornoGrund";"dbo";"BuchungTN";"IDArtTeilnahmeStornoGrund_FS";"dbo";"ArtTeilnahmeStornoGrund";"IDArtTeilnahmeStornoGrund"
|
|
||||||
"FK_BuchungTN_Buchung";"dbo";"BuchungTN";"IDBuchung_FS";"dbo";"Buchung";"IDBuchung"
|
|
||||||
"FK_BuchungTNFerienziel_Adresse";"dbo";"BuchungTNFerienziel";"IDAdresse_FS_Partner";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_BuchungTNFerienziel_ArtVerpflegung";"dbo";"BuchungTNFerienziel";"IDArtVerpflegung_FS";"dbo";"ArtVerpflegung";"IDArtVerpflegung"
|
|
||||||
"FK_BuchungTNFerienziel_BuchungTN";"dbo";"BuchungTNFerienziel";"IDBuchungTN_FS";"dbo";"BuchungTN";"IDBuchungTN"
|
|
||||||
"FK_BuchungTNFerienziel_Zimmerart";"dbo";"BuchungTNFerienziel";"IDZimmerart_FS";"dbo";"Zimmerart";"IDZimmerart"
|
|
||||||
"FK_BuchungTNFerienzielPreis_BuchungTNFerienziel";"dbo";"BuchungTNFerienzielPreis";"IDBuchungTNFerienziel_FS";"dbo";"BuchungTNFerienziel";"IDBuchungTNFerienziel"
|
|
||||||
"FK_BuchungTNFerienzielPreis_BuchungTNPreis";"dbo";"BuchungTNFerienzielPreis";"IDBuchungTNPreis_FS";"dbo";"BuchungTNPreis";"IDBuchungTNPreis"
|
|
||||||
"FK_BuchungTNLeistung_BuchungTN";"dbo";"BuchungTNLeistung";"IDBuchungTN_FS";"dbo";"BuchungTN";"IDBuchungTN"
|
|
||||||
"FK_BuchungTNLeistung_Leistung";"dbo";"BuchungTNLeistung";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_BuchungTNLeistungPreis_BuchungTNLeistung";"dbo";"BuchungTNLeistungPreis";"IDBuchungTNLeistung_FS";"dbo";"BuchungTNLeistung";"IDBuchungTNLeistung"
|
|
||||||
"FK_BuchungTNLeistungPreis_BuchungTNPreis";"dbo";"BuchungTNLeistungPreis";"IDBuchungTNPreis_FS";"dbo";"BuchungTNPreis";"IDBuchungTNPreis"
|
|
||||||
"FK_BuchungTNLeistungZusatz_BuchungTNLeistung";"dbo";"BuchungTNLeistungZusatz";"IDBuchungTNLeistung_FS";"dbo";"BuchungTNLeistung";"IDBuchungTNLeistung"
|
|
||||||
"FK_BuchungTNPreis_Adresse";"dbo";"BuchungTNPreis";"IDAdresse_FS_Sachb";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_BuchungTNPreis_ArtPreisschema";"dbo";"BuchungTNPreis";"IDArtPreisschema_FS";"dbo";"ArtPreisschema";"IDArtPreisschema"
|
|
||||||
"FK_BuchungTNPreis_BuchungTN";"dbo";"BuchungTNPreis";"IDBuchungTN_FS";"dbo";"BuchungTN";"IDBuchungTN"
|
|
||||||
"FK_BuchungTNPreisErm_BuchungTNPreis";"dbo";"BuchungTNPreisErm";"IDBuchungTNPreis_FS";"dbo";"BuchungTNPreis";"IDBuchungTNPreis"
|
|
||||||
"FK_BuchungTNVersicherung_BuchungTN";"dbo";"BuchungTNVersicherung";"IDBuchungTN_FS";"dbo";"BuchungTN";"IDBuchungTN"
|
|
||||||
"FK_BuchungTNVersicherung_Leistung";"dbo";"BuchungTNVersicherung";"IDLeistung_FS_Versicherung";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_BuchungTNVersicherungPreis_BuchungTNPreis";"dbo";"BuchungTNVersicherungPreis";"IDBuchungTNPreis_FS";"dbo";"BuchungTNPreis";"IDBuchungTNPreis"
|
|
||||||
"FK_BuchungTNVersicherungPreis_BuchungTNVersicherung";"dbo";"BuchungTNVersicherungPreis";"IDBuchungTNVersicherung_FS";"dbo";"BuchungTNVersicherung";"IDBuchungTNVersicherung"
|
|
||||||
"FK_BuchungTNWunsch_BuchungTN";"dbo";"BuchungTNWunsch";"IDBuchungTN_FS";"dbo";"BuchungTN";"IDBuchungTN"
|
|
||||||
"FK_BuchungTNZahlung_BuchungTN";"dbo";"BuchungTNZahlung";"IDBuchungTN_FS";"dbo";"BuchungTN";"IDBuchungTN"
|
|
||||||
"FK_BuchungTNZahlung_Zahlung";"dbo";"BuchungTNZahlung";"IDZahlung_FS";"dbo";"Zahlung";"IDZahlung"
|
|
||||||
"FK_BuchungTNZuschlag_BuchungTN";"dbo";"BuchungTNZuschlag";"IDBuchungTN_FS";"dbo";"BuchungTN";"IDBuchungTN"
|
|
||||||
"FK_BuchungTNZuschlagPreis_BuchungTNPreis";"dbo";"BuchungTNZuschlagPreis";"IDBuchungTNPreis_FS";"dbo";"BuchungTNPreis";"IDBuchungTNPreis"
|
|
||||||
"FK_BuchungTNZuschlagPreis_BuchungTNZuschlag";"dbo";"BuchungTNZuschlagPreis";"IDBuchungTNZuschlag_FS";"dbo";"BuchungTNZuschlag";"IDBuchungTNZuschlag"
|
|
||||||
"FK_BuchungTNZuschlagZusatz_BuchungTNZuschlag";"dbo";"BuchungTNZuschlagZusatz";"IDBuchungTNZuschlag_FS";"dbo";"BuchungTNZuschlag";"IDBuchungTNZuschlag"
|
|
||||||
"FK_BuchungUnterlagenStatus_Adresse";"dbo";"BuchungUnterlagenStatus";"IDAdresse_FS_Sachbearbeiter";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_BuchungUnterlagenStatus_ArtUnterlagenStatus";"dbo";"BuchungUnterlagenStatus";"IDArtUnterlagenStatus_FS";"dbo";"ArtUnterlagenStatus";"IDArtUnterlagenStatus"
|
|
||||||
"FK_BuchungUnterlagenStatus_Buchung";"dbo";"BuchungUnterlagenStatus";"IDBuchung_FS";"dbo";"Buchung";"IDBuchung"
|
|
||||||
"FK_BuchungVersicherungAngebot_Buchung";"dbo";"BuchungVersicherungAngebot";"IDBuchung_FS";"dbo";"Buchung";"IDBuchung"
|
|
||||||
"FK_BuchungVersicherungAngebot_Leistung";"dbo";"BuchungVersicherungAngebot";"IDLeistung_FS_Versicherung";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_BuchungZahlung_ArtZahlungsart";"dbo";"BuchungZahlung";"IDArtZahlungsart_FS";"dbo";"ArtZahlungsart";"IDArtZahlungsart"
|
|
||||||
"FK_BuchungZahlung_Buchung";"dbo";"BuchungZahlung";"IDBuchung_FS";"dbo";"Buchung";"IDBuchung"
|
|
||||||
"FK_BundeslandPLZOrt_ArtBundesland";"dbo";"BundeslandPLZOrt";"IDArtBundesland_FS";"dbo";"ArtBundesland";"IDArtBundesland"
|
|
||||||
"FK_ConnectChat_ArtConnectChatKategorie";"dbo";"ConnectChat";"IDArtConnectChatKategorie_FS";"dbo";"ArtConnectChatKategorie";"IDArtConnectChatKategorie"
|
|
||||||
"FK_ConnectChatKategorieKompetenz_ArtPersonalGruppeKompetenz";"dbo";"ConnectChatKategorieKompetenz";"IDArtPersonalGruppeKompetenz_FS";"dbo";"ArtPersonalGruppeKompetenz";"IDArtPersonalGruppeKompetenz"
|
|
||||||
"FK_ChatNachricht_ChatRaum";"dbo";"ConnectChatNachricht";"IDChat_FS";"dbo";"ConnectChat";"IDChat"
|
|
||||||
"FK_ChatTeilnehmer_ChatRaum";"dbo";"ConnectChatTeilnehmer";"IDChat_FS";"dbo";"ConnectChat";"IDChat"
|
|
||||||
"FK_ConnectDokumentReferenzen_ConnectDokument";"dbo";"ConnectDokumentReferenzen";"IDConnectDokument_FS";"dbo";"ConnectDokument";"IDConnectDokument"
|
|
||||||
"FK_ConnectKompetenz_ArtPersonalGruppeKompetenz";"dbo";"ConnectKompetenz";"IDArtPersonalGruppeKompetenz_FS";"dbo";"ArtPersonalGruppeKompetenz";"IDArtPersonalGruppeKompetenz"
|
|
||||||
"FK_ConnectNotificationsReceiver_ConnectNotifications";"dbo";"ConnectNotificationsReceiver";"IDConnectNotification";"dbo";"ConnectNotifications";"Id"
|
|
||||||
"FK_ConnectOrdner_ConnectOrdner";"dbo";"ConnectOrdner";"IDConnectOrdner_FS_Parent";"dbo";"ConnectOrdner";"IDConnectOrdner"
|
|
||||||
"FK_Connect_Planungsabruf_Adresse_Aenderung";"dbo";"ConnectPlanungsabruf";"IDAdresse_FS_Aenderung";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Connect_Planungsabruf_Adresse_Fahrer";"dbo";"ConnectPlanungsabruf";"IDAdresse_FS_Fahrer";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_ConnectPlanungsabrufHistorie_Adresse";"dbo";"ConnectPlanungsabrufHistorie";"IDAdresse_FS_Fahrer";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_ConnectRueckmeldungMB_AuftragFahrer";"dbo";"ConnectRueckmeldungMB";"IDAuftragFahrer_FS";"dbo";"AuftragFahrer";"IDAuftragFahrer"
|
|
||||||
"FK_ConnectRueckmeldungMBFoto_ConnectRueckmeldungMB";"dbo";"ConnectRueckmeldungMBFoto";"IDConnectRueckmeldungMB_FS";"dbo";"ConnectRueckmeldungMB";"IDConnectRueckmeldungMB"
|
|
||||||
"FK_ConnectRueckmeldungMBKilometer_ConnectRueckmeldungMB";"dbo";"ConnectRueckmeldungMBKilometer";"IDConnectRueckmeldungMB_FS";"dbo";"ConnectRueckmeldungMB";"IDConnectRueckmeldungMB"
|
|
||||||
"FK_ArtConnectRueckmeldungMBText_ConnectRueckmeldungMBText";"dbo";"ConnectRueckmeldungMBText";"IDArtConnectRueckmeldungMBText_FS";"dbo";"ArtConnectRueckmeldungMBText";"IDArtConnectRueckmeldungMBText"
|
|
||||||
"FK_ConnectRueckmeldungMBText_ConnectRueckmeldungMB";"dbo";"ConnectRueckmeldungMBText";"IDConnectRueckmeldungMB_FS";"dbo";"ConnectRueckmeldungMB";"IDConnectRueckmeldungMB"
|
|
||||||
"FK_CRMAktionDesignSeite_CRMAktion";"dbo";"CRMAktionDesignSeite";"IDCRMAktion_FS";"dbo";"CRMAktion";"IDCRMAktion"
|
|
||||||
"FK_CRMAktionDesignSeiteZuweisung_CRMAktionDesignSeite";"dbo";"CRMAktionDesignSeiteZuweisung";"IDCRMAktionDesignSeite_FS";"dbo";"CRMAktionDesignSeite";"IDCRMAktionDesignSeite"
|
|
||||||
"FK_CRMAktionPLZ_CRMAktion";"dbo";"CRMAktionPLZ";"IDCRMAktion_FS";"dbo";"CRMAktion";"IDCRMAktion"
|
|
||||||
"FK_CRMAktionZuGruppe_CRMAktion";"dbo";"CRMAktionZuGruppe";"IDCRMAktion_FS";"dbo";"CRMAktion";"IDCRMAktion"
|
|
||||||
"FK_CRMAktionZuGruppe_CRMAktionZuGruppe";"dbo";"CRMAktionZuGruppe";"IDArtCRMGruppe_FS";"dbo";"ArtCRMGruppe";"IDArtCRMGruppe"
|
|
||||||
"FK_CRMAktionZuProdukt_CRMAktion";"dbo";"CRMAktionZuProdukt";"IDCRMAktion_FS";"dbo";"CRMAktion";"IDCRMAktion"
|
|
||||||
"FK_CRMAktionZuProdukt_Produkt";"dbo";"CRMAktionZuProdukt";"IDProdukt_FS";"dbo";"Produkt";"IDProdukt"
|
|
||||||
"FK_CRMAktionZuReise_CRMAktion";"dbo";"CRMAktionZuReise";"IDCRMAktion_FS";"dbo";"CRMAktion";"IDCRMAktion"
|
|
||||||
"FK_CRMAktionZuReise_Reise";"dbo";"CRMAktionZuReise";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_CRMDesignSegment_CRMDesign";"dbo";"CRMDesignSegment";"IDCRMDesign_FS";"dbo";"CRMDesign";"IDCRMDesign"
|
|
||||||
"FK_CRMInternetZuweisung_ArtCRMInternet";"dbo";"CRMInternetZuweisung";"IDArtCRMInternet_FS";"dbo";"ArtCRMInternet";"IDArtCRMInternet"
|
|
||||||
"FK_CRMInternetZuweisung_Buchung";"dbo";"CRMInternetZuweisung";"IDBuchung_FS";"dbo";"Buchung";"IDBuchung"
|
|
||||||
"FK_CRMWerbepartner_Adresse";"dbo";"CRMWerbepartner";"IDAdresse_FS_Partner";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_CRMWerbepartnerDesign_CRMDesign";"dbo";"CRMWerbepartnerDesign";"IDCRMDesign_FS";"dbo";"CRMDesign";"IDCRMDesign"
|
|
||||||
"FK_CRMWerbepartnerDesign_CRMWerbepartner";"dbo";"CRMWerbepartnerDesign";"IDCRMWerbepartner_FS";"dbo";"CRMWerbepartner";"IDCRMWerbepartner"
|
|
||||||
"FK_CRMWerbepartnerPLZ_CRMWerbepartner";"dbo";"CRMWerbepartnerPLZ";"IDCRMWerbepartner_FS";"dbo";"CRMWerbepartner";"IDCRMWerbepartner"
|
|
||||||
"FK_CRMZuweisung_Adresse";"dbo";"CRMZuweisung";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_CRMZuweisung_Adresse1";"dbo";"CRMZuweisung";"IDAdresse_FS_VonPersonal";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_DienstplandruckExport_ArtDienstplandruckExport";"dbo";"DienstplandruckExport";"IDArtDienstplandruckExport_FS";"dbo";"ArtDienstplandruckExport";"IDArtDienstplandruckExport"
|
|
||||||
"FK_DispoAenderung_Adresse";"dbo";"DispoAenderung";"IDAdresse_FS_Disponent";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_DispoAenderungDisponent_DispoAenderung";"dbo";"DispoAenderungDisponent";"IDDispoAenderung_FS";"dbo";"DispoAenderung";"IDDispoAenderung"
|
|
||||||
"FK_DispoAenderungDisponent_DispoAnmeldungDisponent";"dbo";"DispoAenderungDisponent";"IDDispoAnmeldungDisponent_FS";"dbo";"DispoAnmeldungDisponent";"IDDispoAnmeldungDisponent"
|
|
||||||
"FK_DispoAnmeldungDisponent_Adresse";"dbo";"DispoAnmeldungDisponent";"IDAdresse_FS_Disponent";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_DispoEinstellungKompetenzen_ArtPersonalGruppeKompetenz";"dbo";"DispoEinstellungKompetenz";"IDArtPersonalGruppeKompetenz_FS";"dbo";"ArtPersonalGruppeKompetenz";"IDArtPersonalGruppeKompetenz"
|
|
||||||
"FK_DispoEinstellungKompetenzBetriebshofAnzeige_ArtBetriebshof";"dbo";"DispoEinstellungKompetenzBetriebshofAnzeige";"IDArtBetriebshof_FS";"dbo";"ArtBetriebshof";"IDArtBetriebshof"
|
|
||||||
"FK_DispoEinstellungKompetenzBetriebshofAnzeige_DispoEinstellungKompetenz";"dbo";"DispoEinstellungKompetenzBetriebshofAnzeige";"IDDispoEinstellungKompetenz_FS";"dbo";"DispoEinstellungKompetenz";"IDDispoEinstellungKompetenz"
|
|
||||||
"FK_DispoEinstellungKompetenzMandantAnzeige_Adresse";"dbo";"DispoEinstellungKompetenzMandantAnzeige";"IDAdresse_FS_Mandant";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_DispoEinstellungKompetenzMandantAnzeige_DispoEinstellungKompetenzen";"dbo";"DispoEinstellungKompetenzMandantAnzeige";"IDDispoEinstellungKompetenz_FS";"dbo";"DispoEinstellungKompetenz";"IDDispoEinstellungKompetenz"
|
|
||||||
"FK_Dokument_Adresse";"dbo";"Dokument";"IDAdresse_FS_Sachbearbeiter";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Dokument_ArtDokument";"dbo";"Dokument";"IDArtDokument_FS";"dbo";"ArtDokument";"IDArtDokument"
|
|
||||||
"FK_DruckArchiv_Adresse";"dbo";"DruckArchiv";"IDAdresse_FS_SB";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_DruckArchivZIP_Adresse";"dbo";"DruckArchivZIP";"IDAdresse_FS_Sachbearbeiter";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_DTAMeldung_RechnungHaupt";"dbo";"DTAMeldung";"IDRechnungHaupt_FS";"dbo";"RechnungHaupt";"IDRechnungHaupt"
|
|
||||||
"FK_EinreisebestimmungText_Einreisebestimmung";"dbo";"EinreisebestimmungText";"IDEinreisebestimmung_FS";"dbo";"Einreisebestimmung";"IDEinreisebestimmung"
|
|
||||||
"FK_ExportInternetSpalte_ExportInternet";"dbo";"ExportInternetSpalte";"IDExportInternetZeile_FS";"dbo";"ExportInternetZeile";"IDExportInternetZeile"
|
|
||||||
"FK_ExportInternetZeile_ExportInternetZeile";"dbo";"ExportInternetZeile";"IDExportInternet_FS";"dbo";"ExportInternet";"IDExportInternet"
|
|
||||||
"FK_FahrtArt_ArtFahrtArt";"dbo";"FahrtArtKonten";"IdArtFahrtArt_FS";"dbo";"ArtFahrtArt";"IDArtFahrtArt"
|
|
||||||
"FK_Fahrzeug_ArtFahrzeugBriefOrt";"dbo";"Fahrzeug";"IDArtFahrzeugBriefOrt_FS";"dbo";"ArtFahrzeugBriefOrt";"IDArtFahrzeugBriefOrt"
|
|
||||||
"FK_Fahrzeug_ArtReichweitentyp";"dbo";"Fahrzeug";"IDArtReichweitentyp_FS";"dbo";"ArtReichweitentyp";"IDArtReichweitentyp"
|
|
||||||
"FK_Fahrzeug_FahrzeugBetriebshof";"dbo";"Fahrzeug";"IDArtBetriebshof_FS";"dbo";"ArtBetriebshof";"IDArtBetriebshof"
|
|
||||||
"FK_Fahrzeug_FahrzeugEinsatz";"dbo";"Fahrzeug";"IDArtFahrzeugEinsatz_FS";"dbo";"ArtFahrzeugEinsatz";"IDArtFahrzeugEinsatz"
|
|
||||||
"FK_Fahrzeug_FahrzeugTypen";"dbo";"Fahrzeug";"IDArtFahrzeugTypen_FS";"dbo";"ArtFahrzeugTypen";"IDArtFahrzeugTypen"
|
|
||||||
"FK_FahrzeugAusstattung_AusstattungsMerkmale";"dbo";"FahrzeugAusstattung";"IDArtFahrzeugAusstattungMerkmal_FS";"dbo";"ArtFahrzeugAusstattungMerkmal";"IDArtFahrzeugAusstattungMerkmal"
|
|
||||||
"FK_FahrzeugAusstattung_Fahrzeug";"dbo";"FahrzeugAusstattung";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_FahrzeugEinbauten_Fahrzeug";"dbo";"FahrzeugEinbauten";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_FahrzeugGerät_ArtFahrzeugGerätTyp";"dbo";"FahrzeugGerät";"IDArtFahrzeugGerätTyp_FS";"dbo";"ArtFahrzeugGerätTyp";"IDArtFahrzeugGerätTyp"
|
|
||||||
"FK_FahrzeugGerätHistorie_ArtFahrzeugGerätOrt";"dbo";"FahrzeugGerätHistorie";"IDArtFahrzeugGerätOrt_FS";"dbo";"ArtFahrzeugGerätOrt";"IDArtFahrzeugGerätOrt"
|
|
||||||
"FK_FahrzeugGerätHistorie_Fahrzeug";"dbo";"FahrzeugGerätHistorie";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_FahrzeugGerätHistorie_FahrzeugGerät";"dbo";"FahrzeugGerätHistorie";"IDFahrzeugGerät_FS";"dbo";"FahrzeugGerät";"IDFahrzeugGerät"
|
|
||||||
"FK_FahrzeugLeasing_Fahrzeug";"dbo";"FahrzeugLeasing";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_FahrzeugNummer_ArtFahrzeugNummer";"dbo";"FahrzeugNummer";"IDArtFahrzeugNummer_FS";"dbo";"ArtFahrzeugNummer";"IDArtFahrzeugNummer"
|
|
||||||
"FK_FahrzeugNummer_Fahrzeug";"dbo";"FahrzeugNummer";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_FahrzeugStatistikAnmelder_Fahrzeug";"dbo";"FahrzeugStatistikAnmelder";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_FahrzeugTerminKettenAufbau_FahrzeugTermin";"dbo";"FahrzeugTerminKettenAufbau";"IDFahrzeugTermin_FS";"dbo";"FahrzeugTermin";"IDFahrzeugTermin"
|
|
||||||
"FK_FahrzeugTerminKettenAufbau_FahrzeugTerminHaeufigkeit";"dbo";"FahrzeugTerminKettenAufbau";"IDFahrzeugTerminHaeufigkeit_FS";"dbo";"FahrzeugTerminHaeufigkeit";"IDFahrzeugTerminHaeufigkeit"
|
|
||||||
"FK_FahrzeugTerminKettenAufbau_FahrzeugTerminkette";"dbo";"FahrzeugTerminKettenAufbau";"IDFahrzeugTerminkette_FS";"dbo";"FahrzeugTerminkette";"IDFahrzeugTerminkette"
|
|
||||||
"FK_FahrzeugTerminZuweisung_Fahrzeug";"dbo";"FahrzeugTerminZuweisung";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_FahrzeugTerminZuweisung_FahrzeugTerminKettenAufbau";"dbo";"FahrzeugTerminZuweisung";"IDFahrzeugTerminKettenAufbau_FS";"dbo";"FahrzeugTerminKettenAufbau";"IDFahrzeugTerminKettenAufbau"
|
|
||||||
"FK_FahrzeugVermietung_Fahrzeug";"dbo";"FahrzeugVermietung";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_FahrzeugVersicherung_Fahrzeug";"dbo";"FahrzeugVersicherung";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_FahrzeugVerwendung_Fahrzeug";"dbo";"FahrzeugVerwendung";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_FahrzeugWerbung_Fahrzeug";"dbo";"FahrzeugWerbung";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_FehlerProtokoll_Adresse";"dbo";"FehlerProtokoll";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_FehlerProtokoll_FehlerProtokollTyp";"dbo";"FehlerProtokoll";"IDFehlerProtokollTyp_FS";"dbo";"FehlerProtokollTyp";"IDFehlerProtokollTyp"
|
|
||||||
"FK_FehlerProtokollTypEinstellung_FehlerProtokollTyp_FS";"dbo";"FehlerProtokollTypEinstellung";"IDFehlerProtokollTyp_FS";"dbo";"FehlerProtokollTyp";"IDFehlerProtokollTyp"
|
|
||||||
"FK_FehlerProtokollTypEinstellung_Kommunikation_FS";"dbo";"FehlerProtokollTypEinstellung";"IDKommunikation_FS";"dbo";"Kommunikation";"IDKommunikation"
|
|
||||||
"FK_FerienzielZwischenPartner_Adresse";"dbo";"FerienzielZwischenPartner";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_FerienzielZwischenPartner_FerienzielZwischenZeitraum";"dbo";"FerienzielZwischenPartner";"IDFerienzielZwischenZeitraum_FS";"dbo";"FerienzielZwischenZeitraum";"IDFerienzielZwischenZeitraum"
|
|
||||||
"FK_FerienzielZwischenZeitraum_Produkt";"dbo";"FerienzielZwischenZeitraum";"IDProdukt_FS";"dbo";"Produkt";"IDProdukt"
|
|
||||||
"FK_Fibuaufteilung_ArtFibuaufteilung";"dbo";"Fibuaufteilung";"IdArtFibuaufteilung_FS";"dbo";"ArtFibuaufteilung";"IDArtFibuaufteilung"
|
|
||||||
"FK_Fibuaufteilung_Kontenplan";"dbo";"Fibuaufteilung";"IdKontenplan_FS";"dbo";"Kontenplan";"IDKontenplan"
|
|
||||||
"FK_FibuAufteilungWerte_Buchung";"dbo";"FibuAufteilungWerte";"IDBuchung_FS";"dbo";"Buchung";"IDBuchung"
|
|
||||||
"FK_FibuAufteilungWerte_Fibuaufteilung";"dbo";"FibuAufteilungWerte";"IDFibuAufteilung_FS";"dbo";"Fibuaufteilung";"IdFibuaufteilung"
|
|
||||||
"FK_FPAllgemein_ArtFahrplanVersion";"dbo";"FPAllgemein";"IDArtFahrplanVersion_FS";"dbo";"ArtFahrplanVersion";"IDArtFahrplanVersion"
|
|
||||||
"FK_FPLinie_ArtFahrplanVersion";"dbo";"FPLinie";"IDArtFahrplanVersion_FS";"dbo";"ArtFahrplanVersion";"IDArtFahrplanVersion"
|
|
||||||
"FK_FPLinie_ArtFPLinienTyp";"dbo";"FPLinie";"IDArtFPLinienTyp_FS";"dbo";"ArtFPLinienTyp";"IDArtFPLinienTyp"
|
|
||||||
"FK_FPLinie_ArtLinieVerbund";"dbo";"FPLinie";"IDArtLinieVerbund_FS";"dbo";"ArtLinieVerbund";"IDArtLinieVerbund"
|
|
||||||
"FK_FPLinie_ArtLinieVUBereich";"dbo";"FPLinie";"IDArtLinieVUBereich_FS";"dbo";"ArtLinieVUBereich";"IDArtLinieVUBereich"
|
|
||||||
"FK_FPLinie_ArtStatistikGruppe";"dbo";"FPLinie";"IDArtStatistikgruppe_FS";"dbo";"ArtStatistikGruppe";"IDArtStatistikGruppe"
|
|
||||||
"FK_FPLinieAnschluss_FPLinie";"dbo";"FPLinieAnschluss";"IDFPLinie_FS";"dbo";"FPLinie";"IDFPLinie"
|
|
||||||
"FK_FPLinieAnschluss_FPLinie_Bringer";"dbo";"FPLinieAnschluss";"IDFPLinie_FS_Bringer";"dbo";"FPLinie";"IDFPLinie"
|
|
||||||
"FK_FPLinieAnschluss_FPLinieHaltestelle";"dbo";"FPLinieAnschluss";"IDFPLinieHaltestelle_FS_Position";"dbo";"FPLinieHaltestelle";"IDFPLinieHaltestelle"
|
|
||||||
"FK_FPLinieAnschluss_FPLinieHaltestelleZubringer";"dbo";"FPLinieAnschluss";"IDFPLinieHaltestelle_FS_Position_Zubringer";"dbo";"FPLinieHaltestelle";"IDFPLinieHaltestelle"
|
|
||||||
"FK_FPLinieAnschluss_FPLinieSymbol";"dbo";"FPLinieAnschluss";"IDFPLinieSymbol_FS";"dbo";"FPLinieSymbol";"IDFPLinieSymbol"
|
|
||||||
"FK_FPLinieAnschlussDefinition_ArtFahrplanVersion";"dbo";"FPLinieAnschlussDefinition";"IDArtFahrplanVersion_FS";"dbo";"ArtFahrplanVersion";"IDArtFahrplanVersion"
|
|
||||||
"FK_FPLinieAnschlussDefinition_FPLinie_Abbringer";"dbo";"FPLinieAnschlussDefinition";"IDFPLinie_FS_Abbringer";"dbo";"FPLinie";"IDFPLinie"
|
|
||||||
"FK_FPLinieAnschlussDefinition_FPLinie_Zubringer";"dbo";"FPLinieAnschlussDefinition";"IDFPLinie_FS_Zubringer";"dbo";"FPLinie";"IDFPLinie"
|
|
||||||
"FK_FPLinieAnschlussDefinition_Haltepunkt_Abbringer";"dbo";"FPLinieAnschlussDefinition";"IDHaltepunkt_FS_Abbringer";"dbo";"Haltepunkt";"IDHaltepunkt"
|
|
||||||
"FK_FPLinieAnschlussDefinition_Haltepunkt_Zubringer";"dbo";"FPLinieAnschlussDefinition";"IDHaltepunkt_FS_Zubringer";"dbo";"Haltepunkt";"IDHaltepunkt"
|
|
||||||
"FK_FPLinieAnschlussDefinition_Haltestelle_Abbringer";"dbo";"FPLinieAnschlussDefinition";"IDHaltestelle_FS_Abbringer";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_FPLinieAnschlussDefinition_Haltestelle_Zubringer";"dbo";"FPLinieAnschlussDefinition";"IDHaltestelle_FS_Zubringer";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_FPLinieAnschlussDefinitionUmstieg_FPLinieAnschlussDefinition";"dbo";"FPLinieAnschlussDefinitionUmstieg";"IDFPLinieAnschlussDefinition_FS";"dbo";"FPLinieAnschlussDefinition";"IDFPLinieAnschlussDefinition"
|
|
||||||
"FK_FPLinieAnschlussDefinitionUmstiegGruppeVT_FPLinieAnschlussDefinitionUmstieg";"dbo";"FPLinieAnschlussDefinitionUmstiegGruppeVT";"IDFPLinieAnschlussDefinitionUmstieg_FS";"dbo";"FPLinieAnschlussDefinitionUmstieg";"IDFPLinieAnschlussDefinitionUmstieg"
|
|
||||||
"FK_FPLinieBeschraenkung_ArtLaufzeitschema";"dbo";"FPLinieBeschraenkung";"IDArtLaufzeitschema_FS";"dbo";"ArtLaufzeitschema";"IDArtLaufzeitschema"
|
|
||||||
"FK_FPLinieBeschraenkung_FPSondererklaerung";"dbo";"FPLinieBeschraenkung";"IDFPSondererklaerung_FS";"dbo";"FPSondererklaerung";"IDFPSondererklaerung"
|
|
||||||
"FK_FPLinieBeschraenkungFerienFeier_FPLinieBeschraenkung";"dbo";"FPLinieBeschraenkungFerienFeier";"IDFPLinieBeschraenkung_FS";"dbo";"FPLinieBeschraenkung";"IDFPLinieBeschraenkung"
|
|
||||||
"FK_FPLinieBeschraenkungLaufzeit_FPLinieBeschraenkung";"dbo";"FPLinieBeschraenkungLaufzeit";"IDFPLinieBeschraenkung_FS";"dbo";"FPLinieBeschraenkung";"IDFPLinieBeschraenkung"
|
|
||||||
"FK_FPLinieBeschraenkungWochentyp_ArtWochentyp";"dbo";"FPLinieBeschraenkungWochentyp";"IDArtWochentyp_FS";"dbo";"ArtWochentyp";"IDArtWochentyp"
|
|
||||||
"FK_FPLinieBeschraenkungWochentyp_FPLinieBeschraenkung";"dbo";"FPLinieBeschraenkungWochentyp";"IDFPLinieBeschraenkung_FS";"dbo";"FPLinieBeschraenkung";"IDFPLinieBeschraenkung"
|
|
||||||
"FK_FPLinieDruck_FPLinie";"dbo";"FPLinieDruck";"IDFPLinie_FS";"dbo";"FPLinie";"IDFPLinie"
|
|
||||||
"FK_FPLinieFahrplanAdresse_FahrplanAdresse";"dbo";"FPLinieFahrplanAdresse";"IDFahrplanAdresse_FS";"dbo";"FahrplanAdresse";"IDFahrplanAdresse"
|
|
||||||
"FK_FPLinieFahrplanAdresse_FPLinie";"dbo";"FPLinieFahrplanAdresse";"IDFPLinie_FS";"dbo";"FPLinie";"IDFPLinie"
|
|
||||||
"FK_FPLinieFahrt_ArtFremdunternehmen";"dbo";"FPLinieFahrt";"IDArtFremdunternehmen_FS";"dbo";"ArtFremdunternehmen";"IDArtFremdunternehmen"
|
|
||||||
"FK_FPLinieFahrt_FPLinie";"dbo";"FPLinieFahrt";"IDFPLinie_FS";"dbo";"FPLinie";"IDFPLinie"
|
|
||||||
"FK_FPLinieFahrt_FPLinieFahrt";"dbo";"FPLinieFahrt";"IDFPLinieFahrt_FS_VerstaerkerVon";"dbo";"FPLinieFahrt";"IDFPLinieFahrt"
|
|
||||||
"FK_FPLinieFahrt_FPLinieFahrt_TeilenOriginal";"dbo";"FPLinieFahrt";"IDFPLinieFahrt_FS_TeilenOriginal";"dbo";"FPLinieFahrt";"IDFPLinieFahrt"
|
|
||||||
"FK_FPLinieFahrt_FPLinieZeitart";"dbo";"FPLinieFahrt";"IDFPLinieZeitart_FS";"dbo";"FPLinieZeitart";"IDFPLinieZeitart"
|
|
||||||
"FK_FPLinieFahrtAnschluss_FPLinieAnschluss";"dbo";"FPLinieFahrtAnschluss";"IDFPLinieAnschluss_FS";"dbo";"FPLinieAnschluss";"IDFPLinieAnschluss"
|
|
||||||
"FK_FPLinieFahrtAnschluss_FPLinieFahrt";"dbo";"FPLinieFahrtAnschluss";"IDFPLinieFahrt_FS";"dbo";"FPLinieFahrt";"IDFPLinieFahrt"
|
|
||||||
"FK_FPLinieFahrtAnschluss_FPLinieFahrt_Bringer";"dbo";"FPLinieFahrtAnschluss";"IDFPLinieFahrt_FS_Bringer";"dbo";"FPLinieFahrt";"IDFPLinieFahrt"
|
|
||||||
"FK_FPLinieFahrtAnschluss_FPLinieHaltestelle_Bringer";"dbo";"FPLinieFahrtAnschluss";"IDFPLinieHaltestelle_FS_Bringer";"dbo";"FPLinieHaltestelle";"IDFPLinieHaltestelle"
|
|
||||||
"FK_FPLinieFahrtAnschlussBeschraenkung_FPLinieBeschraenkung";"dbo";"FPLinieFahrtAnschlussBeschraenkung";"IDFPLinieBeschraenkung_FS";"dbo";"FPLinieBeschraenkung";"IDFPLinieBeschraenkung"
|
|
||||||
"FK_FPLinieFahrtAnschlussBeschraenkung_FPLinieFahrtAnschluss";"dbo";"FPLinieFahrtAnschlussBeschraenkung";"IDFPLinieFahrtAnschluss_FS";"dbo";"FPLinieFahrtAnschluss";"IDFPLinieFahrtAnschluss"
|
|
||||||
"FK_FPLinieFahrtBeschraenkung_FPLinieBeschraenkung";"dbo";"FPLinieFahrtBeschraenkung";"IDFPLinieBeschraenkung_FS";"dbo";"FPLinieBeschraenkung";"IDFPLinieBeschraenkung"
|
|
||||||
"FK_FPLinieFahrtBeschraenkung_FPLinieFahrt";"dbo";"FPLinieFahrtBeschraenkung";"IDFPLinieFahrt_FS";"dbo";"FPLinieFahrt";"IDFPLinieFahrt"
|
|
||||||
"FK_FPLinieHaltestelle_ArtKreisGebiet";"dbo";"FPLinieHaltestelle";"IDArtKreisGebiet_FS";"dbo";"ArtKreisGebiet";"IDArtKreisGebiet"
|
|
||||||
"FK_FPLinieHaltestelle_FPLinie";"dbo";"FPLinieHaltestelle";"IDFPLinie_FS";"dbo";"FPLinie";"IDFPLinie"
|
|
||||||
"FK_FPLinieHaltestelle_FPLinieHaltestelle";"dbo";"FPLinieHaltestelle";"IDFPLinieHaltestelle_FS_VerschiebenAuf";"dbo";"FPLinieHaltestelle";"IDFPLinieHaltestelle"
|
|
||||||
"FK_FPLinieHaltestelle_Haltepunkt";"dbo";"FPLinieHaltestelle";"IDHaltepunkt_FS";"dbo";"Haltepunkt";"IDHaltepunkt"
|
|
||||||
"FK_FPLinieHaltestelle_Haltestelle";"dbo";"FPLinieHaltestelle";"IDHaltestelle_FS";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_FPLinieHaltestelleBemerkung_FPLinieHaltestelle";"dbo";"FPLinieHaltestelleBemerkung";"IDFPLinieHaltestelle_FS";"dbo";"FPLinieHaltestelle";"IDFPLinieHaltestelle"
|
|
||||||
"FK_FPLinieHaltestelleBemerkung_HaltestelleBemerkung";"dbo";"FPLinieHaltestelleBemerkung";"IDHaltestelleBemerkung_FS";"dbo";"HaltestelleBemerkung";"IDHaltestelleBemerkung"
|
|
||||||
"FK_FPLinieHaltestelleZeitart_ArtKreisGebiet";"dbo";"FPLinieHaltestelleZeitart";"IDArtKreisGebiet_FS";"dbo";"ArtKreisGebiet";"IDArtKreisGebiet"
|
|
||||||
"FK_FPLinieHaltestelleZeitart_FPLinieHaltestelle";"dbo";"FPLinieHaltestelleZeitart";"IDFPLinieHaltestelle_FS";"dbo";"FPLinieHaltestelle";"IDFPLinieHaltestelle"
|
|
||||||
"FK_FPLinieHaltestelleZeitart_FPLinieZeitart";"dbo";"FPLinieHaltestelleZeitart";"IDFPLinieZeitart_FS";"dbo";"FPLinieZeitart";"IDFPLinieZeitart"
|
|
||||||
"FK_FPLinieHaltestelleZeitart_HaltestelleBemerkung";"dbo";"FPLinieHaltestelleZeitart";"IDHaltestelleBemerkung_FS";"dbo";"HaltestelleBemerkung";"IDHaltestelleBemerkung"
|
|
||||||
"FK_FPLinieHaltestelleZeitart_LinieZiel";"dbo";"FPLinieHaltestelleZeitart";"IDLinieZiel_FS";"dbo";"LinieZiel";"IDLinieZiel"
|
|
||||||
"FK_FPLinieZeitart_ArtStatistikGruppe";"dbo";"FPLinieZeitart";"IDArtStatistikgruppe_FS";"dbo";"ArtStatistikGruppe";"IDArtStatistikGruppe"
|
|
||||||
"FK_FPLinieZeitart_FPLinie";"dbo";"FPLinieZeitart";"IDFPLinie_FS";"dbo";"FPLinie";"IDFPLinie"
|
|
||||||
"FK_FPLinieZeitart_FPLinieHaltestelle_TeilenAbschnittBis";"dbo";"FPLinieZeitart";"IDFPLinieHaltestelle_FS_TeilenAbschnittBis";"dbo";"FPLinieHaltestelle";"IDFPLinieHaltestelle"
|
|
||||||
"FK_FPLinieZeitart_FPLinieHaltestelle_TeilenAbschnittVon";"dbo";"FPLinieZeitart";"IDFPLinieHaltestelle_FS_TeilenAbschnittVon";"dbo";"FPLinieHaltestelle";"IDFPLinieHaltestelle"
|
|
||||||
"FK_FPLinieZeitart_FPLinieZeitart";"dbo";"FPLinieZeitart";"IDFPLinieZeitart_FS_TeilenOriginal";"dbo";"FPLinieZeitart";"IDFPLinieZeitart"
|
|
||||||
"FK_FPSchuelerAbsage_Adresse";"dbo";"FPSchuelerAbsage";"IdAdresse_FS_Bearbeitet";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_FPSchuelerAbsage_FPSchueler";"dbo";"FPSchuelerAbsage";"IDFPSchueler_FS";"dbo";"FPSchueler";"IDFPSchueler"
|
|
||||||
"FK_FPSchuelerBefoerderung_FPSchueler";"dbo";"FPSchuelerBefoerderung";"IDFPSchueler_FS";"dbo";"FPSchueler";"IDFPSchueler"
|
|
||||||
"FK_FPSchuelerBefoerderung_FPSchule";"dbo";"FPSchuelerBefoerderung";"IDFPSchule_FS";"dbo";"FPSchule";"IDFPSchule"
|
|
||||||
"FK_FPSchuelerBefoerderung_FPSchuljahr";"dbo";"FPSchuelerBefoerderung";"IDFPSchuljahr_FS";"dbo";"FPSchuljahr";"IDFPSchuljahr"
|
|
||||||
"FK_FPSchuelerBefoerderungBewegung_Haltepunkt_Ausstieg";"dbo";"FPSchuelerBefoerderungBewegung";"IDHaltepunkt_FS_Ausstieg";"dbo";"Haltepunkt";"IDHaltepunkt"
|
|
||||||
"FK_FPSchuelerBefoerderungBewegung_Haltepunkt_Einstieg";"dbo";"FPSchuelerBefoerderungBewegung";"IDHaltepunkt_FS_Einstieg";"dbo";"Haltepunkt";"IDHaltepunkt"
|
|
||||||
"FK_FPSchuelerBefoerderungBewegung_Haltestelle_Ausstieg";"dbo";"FPSchuelerBefoerderungBewegung";"IDHaltestelle_FS_Ausstieg";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_FPSchuelerBefoerderungBewegung_Haltestelle_Einstieg";"dbo";"FPSchuelerBefoerderungBewegung";"IDHaltestelle_FS_Einstieg";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_FPSchuelerBefoerderungBewegung_IDFPSchuelerBefoerderung";"dbo";"FPSchuelerBefoerderungBewegung";"IDFPSchuelerBefoerderung_FS";"dbo";"FPSchuelerBefoerderung";"IDFPSchuelerBefoerderung"
|
|
||||||
"FK_FPSchuelerBefoerderungBewegungAbsage_FPSchuelerAbsage";"dbo";"FPSchuelerBefoerderungBewegungAbsage";"IDFPSchuelerAbsage_FS";"dbo";"FPSchuelerAbsage";"IDFPSchuelerAbsage"
|
|
||||||
"FK_FPSchuelerBefoerderungBewegungAbsage_FPSchuelerBefoerderungBewegung";"dbo";"FPSchuelerBefoerderungBewegungAbsage";"IDFPSchuelerBefoerderungBewegung_FS";"dbo";"FPSchuelerBefoerderungBewegung";"IDFPSchuelerBefoerderungBewegung"
|
|
||||||
"FK_FPSchuelerBefoerderungBewegungZuordnung_FPLinieFahrt";"dbo";"FPSchuelerBefoerderungBewegungZuordnung";"IDFPLinieFahrt_FS";"dbo";"FPLinieFahrt";"IDFPLinieFahrt"
|
|
||||||
"FK_FPSchuelerBefoerderungBewegungZuordnung_FPLinieHaltestelle";"dbo";"FPSchuelerBefoerderungBewegungZuordnung";"IDFPLinieHaltestelle_FS";"dbo";"FPLinieHaltestelle";"IDFPLinieHaltestelle"
|
|
||||||
"FK_FPSchuelerBefoerderungBewegungZuordnung_FPSchuelerBefoerderungBewegung";"dbo";"FPSchuelerBefoerderungBewegungZuordnung";"IDFPSchuelerBefoerderungBewegung_FS";"dbo";"FPSchuelerBefoerderungBewegung";"IDFPSchuelerBefoerderungBewegung"
|
|
||||||
"FK_FPSchuelerBefoerderungBewegungZuordnung_FPZeitart";"dbo";"FPSchuelerBefoerderungBewegungZuordnung";"IDFPLinieZeitart_FS";"dbo";"FPLinieZeitart";"IDFPLinieZeitart"
|
|
||||||
"FK_FPSchuleHaltestelleHaltepunkt_FPSchule";"dbo";"FPSchuleHaltestelleHaltepunkt";"IDFPSchule_FS";"dbo";"FPSchule";"IDFPSchule"
|
|
||||||
"FK_FPSchuleHaltestelleHaltepunkt_Haltepunkt";"dbo";"FPSchuleHaltestelleHaltepunkt";"IDHaltepunkt_FS";"dbo";"Haltepunkt";"IDHaltepunkt"
|
|
||||||
"FK_FPSchuleHaltestelleHaltepunkt_Haltestelle";"dbo";"FPSchuleHaltestelleHaltepunkt";"IDHaltestelle_FS";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_FreibescheinigungManuelleEingabe_Adresse";"dbo";"FreibescheinigungManuelleEingabe";"IDAdresse_FS_Fahrer";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_FreibescheinigungRuhetag_Adresse";"dbo";"FreibescheinigungRuhetag";"IDAdresse_FS_Fahrer";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_GruppeVerkehrstag_ArtGruppeVerkehrstag";"dbo";"GruppeVerkehrstag";"IDArtGruppeVerkehrstag_FS";"dbo";"ArtGruppeVerkehrstag";"IDArtGruppeVerkehrstag"
|
|
||||||
"FK_GruppeVerkehrstagFerien_ArtGruppeVerkehrstag";"dbo";"GruppeVerkehrstagFerien";"IDArtGruppeVerkehrstag_FS";"dbo";"ArtGruppeVerkehrstag";"IDArtGruppeVerkehrstag"
|
|
||||||
"FK_Gutschein_ArtGutscheinversand";"dbo";"Gutschein";"IDArtGutscheinversand_FS";"dbo";"ArtGutscheinversand";"IDArtGutscheinversand"
|
|
||||||
"FK_GutscheinlayoutInternet_ArtGutscheinlayout";"dbo";"GutscheinlayoutInternet";"IDArtGutscheinlayout_FS";"dbo";"ArtGutscheinlayout";"IDArtGutscheinlayout"
|
|
||||||
"FK_Gutscheinzuschlag_Gutschein";"dbo";"GutscheinZuschlag";"IDGutschein_FS";"dbo";"Gutschein";"IDGutschein"
|
|
||||||
"FK_Gutscheinzuschlag_Kontenplan";"dbo";"GutscheinZuschlag";"IDKontenplan_FS";"dbo";"Kontenplan";"IDKontenplan"
|
|
||||||
"FK_Haltepunkt_ArtHaltepunktArt";"dbo";"Haltepunkt";"IDArtHaltepunktArt_FS";"dbo";"ArtHaltepunktArt";"IDArtHaltepunktArt"
|
|
||||||
"FK_Haltepunkt_Haltestelle";"dbo";"Haltepunkt";"IDHaltestelle_FS";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_HaltepunktFahrplankasten_ArtHaltepunktFahrplankasten";"dbo";"HaltepunktFahrplankasten";"IDArtHaltepunktFahrplankasten_FS";"dbo";"ArtHaltepunktFahrplankasten";"IDArtHaltepunktFahrplankasten"
|
|
||||||
"FK_HaltepunktFahrplankasten_HaltestelleHaltepunkt";"dbo";"HaltepunktFahrplankasten";"IDHaltestelleHaltepunkt_FS";"dbo";"HaltestelleHaltepunkt";"IDHaltestelleHaltepunkt"
|
|
||||||
"FK_Haltestelle_ArtHaltestelleZwischenpunkttyp";"dbo";"Haltestelle";"IDArtHaltestelleZwischenpunkttyp_FS";"dbo";"ArtHaltestelleZwischenpunkttyp";"IDArtHaltestelleZwischenpunkttyp"
|
|
||||||
"FK_Haltestelle_HaltestelleNach";"dbo";"Haltestelle";"IDHaltestelle_FS_ZwNach";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_Haltestelle_HaltestelleVon";"dbo";"Haltestelle";"IDHaltestelle_FS_ZwVon";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_HaltestelleDruck_Haltestelle";"dbo";"HaltestelleDruck";"IDHaltestelle_FS";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_HaltestelleFahrplanAdresse_FahrplanAdresse";"dbo";"HaltestelleFahrplanAdresse";"IDFahrplanAdresse_FS";"dbo";"FahrplanAdresse";"IDFahrplanAdresse"
|
|
||||||
"FK_HaltestelleFahrplanAdresse_Haltestelle";"dbo";"HaltestelleFahrplanAdresse";"IDHaltestelle_FS";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_HaltestelleHaltepunkt_ArtBetriebshof";"dbo";"HaltestelleHaltepunkt";"IDArtBetriebshof_FS";"dbo";"ArtBetriebshof";"IDArtBetriebshof"
|
|
||||||
"FK_HaltestelleHaltepunkt_ArtHaltepunktAbfallbehaelter";"dbo";"HaltestelleHaltepunkt";"IDArtHaltepunktAbfallbehaelter_FS";"dbo";"ArtHaltepunktAbfallbehaelter";"IDArtHaltepunktAbfallbehaelter"
|
|
||||||
"FK_HaltestelleHaltepunkt_ArtHaltepunktBlindenleitsystem";"dbo";"HaltestelleHaltepunkt";"IDArtHaltepunktBlindenleitsystem_FS";"dbo";"ArtHaltepunktBlindenleitsystem";"IDArtHaltepunktBlindenleitsystem"
|
|
||||||
"FK_HaltestelleHaltepunkt_ArtHaltepunktBlinkaufforderung";"dbo";"HaltestelleHaltepunkt";"IDArtHaltepunktBlinkaufforderung_FS";"dbo";"ArtHaltepunktBlinkaufforderung";"IDArtHaltepunktBlinkaufforderung"
|
|
||||||
"FK_HaltestelleHaltepunkt_ArtHaltepunktBusbordstein";"dbo";"HaltestelleHaltepunkt";"IDArtHaltepunktBusbordstein_FS";"dbo";"ArtHaltepunktBusbordstein";"IDArtHaltepunktBusbordstein"
|
|
||||||
"FK_HaltestelleHaltepunkt_Haltepunkt";"dbo";"HaltestelleHaltepunkt";"IDHaltepunkt_FS";"dbo";"Haltepunkt";"IDHaltepunkt"
|
|
||||||
"FK_HaltestelleHaltepunkt_Haltestelle";"dbo";"HaltestelleHaltepunkt";"IDHaltestelle_FS";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_HaltestelleHaltepunkt_Ort";"dbo";"HaltestelleHaltepunkt";"IDOrt_FS";"dbo";"Ort";"IDOrt"
|
|
||||||
"FK_ImportAdressen_ArtImportAdressen";"dbo";"ImportAdressen";"IDArtImportAdressen_FS";"dbo";"ArtImportAdressen";"IDArtImportAdressen"
|
|
||||||
"FK_ImportMatrix_ArtBuchungsImport";"dbo";"ImportMatrix";"IDArtBuchungsImport_FS";"dbo";"ArtBuchungsImport";"IDArtBuchungsImport"
|
|
||||||
"FK_InternetCRMZuweisung_ArtCRMGruppe";"dbo";"InternetCRMZuweisung";"IDArtCRMGruppe_FS";"dbo";"ArtCRMGruppe";"IDArtCRMGruppe"
|
|
||||||
"FK_InternetCRMZuweisung_ArtCRMInternet";"dbo";"InternetCRMZuweisung";"IDArtCRMInternet_FS";"dbo";"ArtCRMInternet";"IDArtCRMInternet"
|
|
||||||
"FK_KalenderFahrplanversion_ArtFahrplanVersion";"dbo";"KalenderFahrplanversion";"IDArtFahrplanversion_FS";"dbo";"ArtFahrplanVersion";"IDArtFahrplanVersion"
|
|
||||||
"FK_KalenderLaufzeitschema_ArtLaufzeitschema";"dbo";"KalenderLaufzeitschema";"IDArtLaufzeitschema_FS";"dbo";"ArtLaufzeitschema";"IDArtLaufzeitschema"
|
|
||||||
"FK_KalenderSondergruppen_ArtGruppeVerkehrstag";"dbo";"KalenderSondergruppen";"IDArtSondergruppen_FS";"dbo";"ArtGruppeVerkehrstag";"IDArtGruppeVerkehrstag"
|
|
||||||
"FK_KalenderWochentypen_ArtWochentyp";"dbo";"KalenderWochentypen";"IDArtWochentyp_FS";"dbo";"ArtWochentyp";"IDArtWochentyp"
|
|
||||||
"FK_KalkulationBeförderung_Kalkulation";"dbo";"KalkulationBeförderung";"IDKalkulation_FS";"dbo";"Kalkulation";"IDKalkulation"
|
|
||||||
"FK_KalkulationBeförderungLand_KalkulationBeförderung";"dbo";"KalkulationBeförderungLand";"IDKalkulationBeförderung_FS";"dbo";"KalkulationBeförderung";"IDKalkulationBeförderung"
|
|
||||||
"FK_KalkulationBusleistung_Kalkulation";"dbo";"KalkulationBusleistung";"IDKalkulation_FS";"dbo";"Kalkulation";"IDKalkulation"
|
|
||||||
"FK_KalkulationBusleistungPreis_KalkulationBusleistung";"dbo";"KalkulationBusleistungPreis";"IDKalkulationBusleistung_FS";"dbo";"KalkulationBusleistung";"IDKalkulationBusleistung"
|
|
||||||
"FK_KalkulationBusleistungPreis_Kalkulationswertbezeichnung";"dbo";"KalkulationBusleistungPreis";"IDKalkulationswertbezeichnung_FS";"dbo";"KalkulationswertBezeichnung";"IDKalkulationswertBezeichnung"
|
|
||||||
"FK_KalkulationErgebnis_Kalkulation";"dbo";"KalkulationErgebnis";"IDKalkulation_FS";"dbo";"Kalkulation";"IDKalkulation"
|
|
||||||
"FK_KalkulationErgebnisM_Kalkulation";"dbo";"KalkulationErgebnisM";"IDKalkulation_FS";"dbo";"Kalkulation";"IDKalkulation"
|
|
||||||
"FK_KalkulationErgebnisZimmerPreis_KalkulationErgebnis";"dbo";"KalkulationErgebnisZimmerPreis";"IDKalkulationErgebnis_FS";"dbo";"KalkulationErgebnis";"IDKalkulationErgebnis"
|
|
||||||
"FK_KalkulationErgebnisZimmerPreis_Zimmerart";"dbo";"KalkulationErgebnisZimmerPreis";"IDZimmerart_FS";"dbo";"Zimmerart";"IDZimmerart"
|
|
||||||
"FK_KalkulationFZAusgrenzung_KalkulationFZ";"dbo";"KalkulationFZAusgrenzung";"IDKalkulationFZ_FS";"dbo";"KalkulationFZ";"IDKalkulationFZ"
|
|
||||||
"FK_KalkulationFZBeförderung_KalkulationFZ";"dbo";"KalkulationFZBeförderung";"IDKalkulationFZ_FS";"dbo";"KalkulationFZ";"IDKalkulationFZ"
|
|
||||||
"FK_KalkulationFZErgebnis_ArtPartnerPreisCode";"dbo";"KalkulationFZErgebnis";"IdArtPartnerPreisCode_FS";"dbo";"ArtPartnerPreisCode";"IDArtPartnerPreisCode"
|
|
||||||
"FK_KalkulationFZErgebnis_ArtVerpflegung";"dbo";"KalkulationFZErgebnis";"IDArtVerpflegung_FS";"dbo";"ArtVerpflegung";"IDArtVerpflegung"
|
|
||||||
"FK_KalkulationFZErgebnis_KalkulationFZ";"dbo";"KalkulationFZErgebnis";"IDKalkulationFZ_FS";"dbo";"KalkulationFZ";"IDKalkulationFZ"
|
|
||||||
"FK_KalkulationFZTag_KalkulationFZ";"dbo";"KalkulationFZTag";"IDKalkulationFZ_FS";"dbo";"KalkulationFZ";"IDKalkulationFZ"
|
|
||||||
"FK_KalkulationPax_Kalkulation";"dbo";"KalkulationPax";"IDKalkulation_FS";"dbo";"Kalkulation";"IDKalkulation"
|
|
||||||
"FK_KalkulationReise_Kalkulation";"dbo";"KalkulationReise";"IDKalkulation_FS";"dbo";"Kalkulation";"IDKalkulation"
|
|
||||||
"FK_KalkulationReise_Reise";"dbo";"KalkulationReise";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_KalkulationSonstiges_Kalkulation";"dbo";"KalkulationSonstiges";"IDKalkulation_FS";"dbo";"Kalkulation";"IDKalkulation"
|
|
||||||
"FK_KalkulationSonstigesPreis_KalkulationSonstiges";"dbo";"KalkulationSonstigesPreis";"IDKalkulationSonstiges_FS";"dbo";"KalkulationSonstiges";"IDKalkulationSonstiges"
|
|
||||||
"FK_KalkulationUnterbringung_Kalkulation";"dbo";"KalkulationUnterbringung";"IDKalkulation_FS";"dbo";"Kalkulation";"IDKalkulation"
|
|
||||||
"FK_KalkulationUnterbringungM_Kalkulation";"dbo";"KalkulationUnterbringungM";"IDKalkulation_FS";"dbo";"Kalkulation";"IDKalkulation"
|
|
||||||
"FK_KalkulationUnterbringungMPreis_KalkulationUnterbringungM";"dbo";"KalkulationUnterbringungMPreis";"IDKalkulationUnterbringungM_FS";"dbo";"KalkulationUnterbringungM";"IDKalkulationUnterbringungM"
|
|
||||||
"FK_KalkulationUnterbringungPartner_Adresse";"dbo";"KalkulationUnterbringungPartner";"IDAdresse_FS_Partner";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_KalkulationUnterbringungPartner_KalkulationUnterbringung";"dbo";"KalkulationUnterbringungPartner";"IDKalkulationUnterbringung_FS";"dbo";"KalkulationUnterbringung";"IDKalkulationUnterbringung"
|
|
||||||
"FK_KalkulationUnterbringungPreis_KalkulationUnterbringung";"dbo";"KalkulationUnterbringungPreis";"IDKalkulationUnterbringung_FS";"dbo";"KalkulationUnterbringung";"IDKalkulationUnterbringung"
|
|
||||||
"FK_KalkulationUnterbringungPreis_KalkulationUnterbringungPartner";"dbo";"KalkulationUnterbringungPreis";"IDKalkulationUnterbringungPartner_FS";"dbo";"KalkulationUnterbringungPartner";"IDKalkulationUnterbringungPartner"
|
|
||||||
"FK_KalkulationUnterbringungPreis_KalkulationUnterbringungZimmer";"dbo";"KalkulationUnterbringungPreis";"IDKalkulationUnterbringungZimmer_FS";"dbo";"KalkulationUnterbringungZimmer";"IDKalkulationUnterbringungZimmer"
|
|
||||||
"FK_KalkulationUnterbringungZimmer_KalkulationUnterbringung";"dbo";"KalkulationUnterbringungZimmer";"IDKalkulationUnterbringung_FS";"dbo";"KalkulationUnterbringung";"IDKalkulationUnterbringung"
|
|
||||||
"FK_KalkulationUnterbringungZimmer_Zimmerart";"dbo";"KalkulationUnterbringungZimmer";"IDZimmerart_FS";"dbo";"Zimmerart";"IDZimmerart"
|
|
||||||
"FK_KalkulationUnterbringungZimmerPreis_KalkulationUnterbringung";"dbo";"KalkulationUnterbringungZimmerPreis";"IDKalkulationUnterbringung_FS";"dbo";"KalkulationUnterbringung";"IDKalkulationUnterbringung"
|
|
||||||
"FK_KalkulationUnterbringungZimmerPreis_KalkulationUnterbringungZimmer";"dbo";"KalkulationUnterbringungZimmerPreis";"IDKalkulationUnterbringungZimmerPreis";"dbo";"KalkulationUnterbringungZimmer";"IDKalkulationUnterbringungZimmer"
|
|
||||||
"FK_KalkulationZuschlag_Kalkulation";"dbo";"KalkulationZuschlag";"IDKalkulation_FS";"dbo";"Kalkulation";"IDKalkulation"
|
|
||||||
"FK_KalkulationZuschlagPosten_KalkulationZuschlag";"dbo";"KalkulationZuschlagPosten";"IDKalkulationZuschlag_FS";"dbo";"KalkulationZuschlag";"IDKalkulationZuschlag"
|
|
||||||
"FK_KommunikationEinstellung_Kommunikation";"dbo";"KommunikationEinstellung";"IDKommunikation_FS";"dbo";"Kommunikation";"IDKommunikation"
|
|
||||||
"FK_KonstantenErmäßigungProdukt_KonstantenErmäßigung";"dbo";"KonstantenErmäßigungProdukt";"IDKonstantenErmäßigungStaffel_FS";"dbo";"KonstantenErmäßigungStaffel";"IDKonstantenErmäßigungStaffel"
|
|
||||||
"FK_KonstantenErmäßigungProdukt_Produkt";"dbo";"KonstantenErmäßigungProdukt";"IDProdukt_FS";"dbo";"Produkt";"IDProdukt"
|
|
||||||
"FK_KonstantenErmäßigungStaffel_KonstantenErmäßigung";"dbo";"KonstantenErmäßigungStaffel";"IDKonstantenErmäßigung_FS";"dbo";"KonstantenErmäßigung";"IDKonstantenErmäßigung"
|
|
||||||
"FK_KonstantenErmäßigungZimmer_KonstantenErmäßigung";"dbo";"KonstantenErmäßigungZimmer";"IDKonstantenErmäßigungStaffel_FS";"dbo";"KonstantenErmäßigungStaffel";"IDKonstantenErmäßigungStaffel"
|
|
||||||
"FK_KonstantenErmäßigungZimmer_Zimmerart";"dbo";"KonstantenErmäßigungZimmer";"IDZimmerart_FS";"dbo";"Zimmerart";"IDZimmerart"
|
|
||||||
"FK_KonstantenErmäßigungZustieg_KonstantenErmäßigung";"dbo";"KonstantenErmäßigungZustieg";"IDKonstantenErmäßigungStaffel_FS";"dbo";"KonstantenErmäßigungStaffel";"IDKonstantenErmäßigungStaffel"
|
|
||||||
"FK_KonstantenErmäßigungZustieg_Zustieg";"dbo";"KonstantenErmäßigungZustieg";"IDZustieg_FS";"dbo";"Zustieg";"IDZustieg"
|
|
||||||
"FK_KonstantenFlexTarifStaffel_KonstantenFlexTarif";"dbo";"KonstantenFlexTarifStaffel";"IDKonstantenFlexTarif_FS";"dbo";"KonstantenFlexTarif";"IDKonstantenFlexTarif"
|
|
||||||
"FK_KonstantenFlexTarifStaffel_Leistung";"dbo";"KonstantenFlexTarifStaffel";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_KonstantenGruppenermStaffel_KonstantenGruppenerm";"dbo";"KonstantenGruppenermStaffel";"IDKonstantenGruppenerm_FS";"dbo";"KonstantenGruppenerm";"IDKonstantenGruppenerm"
|
|
||||||
"FK_KonstantenRabattStaffelEingabe_KonstantenRabattStaffel";"dbo";"KonstantenRabattStaffelEingabe";"IDKonstantenRabattStaffel_FS";"dbo";"KonstantenRabattStaffel";"IDKonstantenRabattStaffel"
|
|
||||||
"FK_KonstantenStAuftZuschlag_Land";"dbo";"KonstantenStAuftZuschlag";"IDLand_FS";"dbo";"Land";"IDLand"
|
|
||||||
"FK_KonstantenSteueraufteilung_SteueraufteilungArt";"dbo";"KonstantenSteueraufteilung";"IDSteueraufteilungArt_FS";"dbo";"SteueraufteilungArt";"IDSteueraufteilungArt"
|
|
||||||
"FK_KonstantenSteueraufteilungDetail_KonstantenSteueraufteilung";"dbo";"KonstantenSteueraufteilungDetail";"IDKonstantenSteueraufteilung_FS";"dbo";"KonstantenSteueraufteilung";"IDKonstantenSteueraufteilung"
|
|
||||||
"FK_KonstantenSteueraufteilungDetail_Steueraufteilung";"dbo";"KonstantenSteueraufteilungDetail";"IDSteueraufteilungDetail_FS";"dbo";"SteueraufteilungDetail";"IDSteueraufteilungDetail"
|
|
||||||
"FK_KonstantenStornoStaffelTage_KonstantenStornoStaffel";"dbo";"KonstantenStornoStaffelTage";"IDKonstantenStornoStaffel_FS";"dbo";"KonstantenStornoStaffel";"IDKonstantenStornoStaffel"
|
|
||||||
"FK_ArtikelLagerort_Artikel";"dbo";"LagerRegal";"IDLager_FS";"dbo";"Lager";"IDLager"
|
|
||||||
"FK_LandFiskal_Land";"dbo";"LandFiskal";"IDLand_Fs";"dbo";"Land";"IDLand"
|
|
||||||
"FK_LandHinweis_Land";"dbo";"LandHinweis";"IDLand_FS";"dbo";"Land";"IDLand"
|
|
||||||
"FK_LandKonto_Land";"dbo";"LandKonto";"IDLand_FS";"dbo";"Land";"IDLand"
|
|
||||||
"FK_LandMwst_Land";"dbo";"LandMwst";"IDLand_FS";"dbo";"Land";"IDLand"
|
|
||||||
"FK_LandVerpflegungSpesen_ArtSpesenLand";"dbo";"LandVerpflegungSpesen";"IDArtSpesenLand_FS";"dbo";"ArtSpesenLand";"IDArtSpesenLand"
|
|
||||||
"FK_LandVerpflegungSpesen_ArtSpesenVerpflegung";"dbo";"LandVerpflegungSpesen";"IDArtSpesenVerpflegung_FS";"dbo";"ArtSpesenVerpflegung";"IDArtSpesenVerpflegung"
|
|
||||||
"FK_Leistung_ArtLeistungSonstigesUnterart";"dbo";"Leistung";"IDArtLeistungSonstigesUnterart_FS";"dbo";"ArtLeistungSonstigesUnterart";"IDArtLeistungSonstigesUnterart"
|
|
||||||
"FK_Leistung_LeistungBefoerderung";"dbo";"LeistungBefoerderung";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_Leistung_LeistungExternSystem";"dbo";"LeistungExternSystem";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_LeistungHotelMoeglich_Adresse";"dbo";"LeistungHotelGesperrt";"IDAdresse_FS_Partner";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_LeistungHotelMoeglich_LeistungBefoerderung";"dbo";"LeistungHotelGesperrt";"IDLeistungBefoerderung_FS";"dbo";"LeistungBefoerderung";"IDLeistungBefoerderung"
|
|
||||||
"FK_Adresse_LeistungMandantMoeglich";"dbo";"LeistungMandantMoeglich";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Leistung_LeistungMandantMoeglich";"dbo";"LeistungMandantMoeglich";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_Adresse_LeistungPartnerMoeglich";"dbo";"LeistungPartnerMoeglich";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Leistung_LeistungPartnerMoeglich";"dbo";"LeistungPartnerMoeglich";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_LeistungPreis_ArtPreisschema";"dbo";"LeistungPreis";"IDArtPreisSchema_FS";"dbo";"ArtPreisschema";"IDArtPreisschema"
|
|
||||||
"FK_LeistungPreis_Leistung";"dbo";"LeistungPreis";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_LeistungProdukt_Leistung";"dbo";"LeistungProdukt";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_LeistungProdukt_Produkt";"dbo";"LeistungProdukt";"IDProdukt_FS";"dbo";"Produkt";"IDProdukt"
|
|
||||||
"FK_Leistung_LeistungSelektion";"dbo";"LeistungSelektionenKunde";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_Selektionsstamm_LeistungSelektion";"dbo";"LeistungSelektionenKunde";"IDSelektionsstamm_FS";"dbo";"Selektionsstamm";"IDSelektionsstamm"
|
|
||||||
"FK_LeistungSitzplan_Leistung";"dbo";"LeistungSitzplan";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_LeistungSitzplan_Sitzplan";"dbo";"LeistungSitzplan";"IDSitzplan_FS";"dbo";"Sitzplan";"IDSitzplan"
|
|
||||||
"FK_Leistung_LeistungText";"dbo";"LeistungText";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_Leistung_LeistungTicket";"dbo";"LeistungTicket";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_LeistungTicketZusatz_LeistungTicket";"dbo";"LeistungTicketZusatz";"IDLeistungTicket_FS";"dbo";"LeistungTicket";"IDLeistungTicket"
|
|
||||||
"FK_LeistungVersicherung_Leistung";"dbo";"LeistungVersicherung";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_LeistungVersicherung_Versicherungsbedingung_Leistung";"dbo";"LeistungVersicherung_Versicherungsbedingung";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_LeistungVersicherung_Versicherungsbedingung_Versicherungsbedingung";"dbo";"LeistungVersicherung_Versicherungsbedingung";"IDVersicherungsbedingung_FS";"dbo";"Versicherungsbedingung";"IDVersicherungsbedingung"
|
|
||||||
"FK_LeistungVersicherungsGruppe_ArtVersicherungsGruppe";"dbo";"LeistungVersicherungsGruppe";"IDArtVersicherungsGruppe_FS";"dbo";"ArtVersicherungsGruppe";"IDArtVersicherungsGruppe"
|
|
||||||
"FK_LeistungVersicherungsGruppe_LeistungVersicherung";"dbo";"LeistungVersicherungsGruppe";"IDLeistungVersicherung_FS";"dbo";"LeistungVersicherung";"IDLeistungVersicherung"
|
|
||||||
"FK_Leistung_LeistungVoucher";"dbo";"LeistungVoucher";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_LeistungZusatz_ArtAgenturPreisgruppe";"dbo";"LeistungZusatz";"IDArtAgenturPreisgruppe_FS";"dbo";"ArtAgenturPreisgruppe";"IDArtAgenturPreisgruppe"
|
|
||||||
"FK_LeistungZusatz_KonstantenSteueraufteilung";"dbo";"LeistungZusatz";"IDKonstantenSteueraufteilung_FS";"dbo";"KonstantenSteueraufteilung";"IDKonstantenSteueraufteilung"
|
|
||||||
"FK_LeistungZusatz_Leistung";"dbo";"LeistungZusatz";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_LeistungZustiegMoeglich_LeistungBefoerderung";"dbo";"LeistungZustiegMoeglichGesperrt";"IDLeistungBefoerderung_FS";"dbo";"LeistungBefoerderung";"IDLeistungBefoerderung"
|
|
||||||
"FK_Zustieg_LeistungZustiegMoeglich";"dbo";"LeistungZustiegMoeglichGesperrt";"IDZustieg_FS";"dbo";"Zustieg";"IDZustieg"
|
|
||||||
"FK_LiniePlanungEinstellung_Adresse";"dbo";"LiniePlanungEinstellung";"IDAdresse_FS_Personal";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_LiniePlanungEinstellung_ArtPersonalGruppeKompetenz";"dbo";"LiniePlanungEinstellung";"IDArtPersonalGruppeKompetenz_FS";"dbo";"ArtPersonalGruppeKompetenz";"IDArtPersonalGruppeKompetenz"
|
|
||||||
"FK_LinieStatistikAufteilungSpalteZuordnung_ArtStatistikGruppe";"dbo";"LinieStatistikAufteilungSpalteZuordnung";"IDArtStatistikGruppe_FS";"dbo";"ArtStatistikGruppe";"IDArtStatistikGruppe"
|
|
||||||
"FK_LinieStatistikAufteilungSpalteZuordnung_Auftragsart";"dbo";"LinieStatistikAufteilungSpalteZuordnung";"IDAuftragsart_FS";"dbo";"Auftragsart";"IDAuftragsart"
|
|
||||||
"FK_LinieStatistikAufteilungSpalteZuordnung_LinieStatistikAufteilungSpalte";"dbo";"LinieStatistikAufteilungSpalteZuordnung";"IDLinieStatistikAufteilungSpalte_FS";"dbo";"LinieStatistikAufteilungSpalte";"IDLinieStatistikAufteilungSpalte"
|
|
||||||
"FK_LinieStatistikSummeSpalte_ArtLinieStatistikSumme";"dbo";"LinieStatistikSummeSpalte";"IDArtLinieStatistikSumme_FS";"dbo";"ArtLinieStatistikSumme";"IDArtLinieStatistikSumme"
|
|
||||||
"FK_LinieStatistikSummeSpalteZuordnung_ArtStatistikGruppe";"dbo";"LinieStatistikSummeSpalteZuordnung";"IDArtStatistikGruppe_FS";"dbo";"ArtStatistikGruppe";"IDArtStatistikGruppe"
|
|
||||||
"FK_LinieStatistikSummeSpalteZuordnung_Auftragsart";"dbo";"LinieStatistikSummeSpalteZuordnung";"IDAuftragsart_FS";"dbo";"Auftragsart";"IDAuftragsart"
|
|
||||||
"FK_LinieStatistikSummeSpalteZuordnung_LinieStatistikSummeSpalte";"dbo";"LinieStatistikSummeSpalteZuordnung";"IDLinieStatistikSummeSpalte_FS";"dbo";"LinieStatistikSummeSpalte";"IDLinieStatistikSummeSpalte"
|
|
||||||
"FK_LinieWegPunkteGM_Haltestelle";"dbo";"LinieWegPunkteGM";"IDHaltestelle_FS_von";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_LinieWegPunkteGM_Haltestelle1";"dbo";"LinieWegPunkteGM";"IDHaltestelle_FS_nach";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_LinieZiel_ArtLinieZielSonderzeichen";"dbo";"LinieZiel";"IDArtLinieZielSonderzeichen_FS";"dbo";"ArtLinieZielSonderzeichen";"IDArtLinieZielSonderzeichen"
|
|
||||||
"FK_LinieZusatzKilometer_ArtStatistikGruppe";"dbo";"LinieZusatzKilometer";"IDArtStatistikgruppe_FS";"dbo";"ArtStatistikGruppe";"IDArtStatistikGruppe"
|
|
||||||
"FK_LinieZusatzKilometer_Auftragsart";"dbo";"LinieZusatzKilometer";"IDAuftragsart_FS";"dbo";"Auftragsart";"IDAuftragsart"
|
|
||||||
"FK_LinieZusatzKilometer_Fahrzeug";"dbo";"LinieZusatzKilometer";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_ListenterminalEinstellung_Adresse";"dbo";"ListenterminalEinstellung";"IDAdresse_FS_Personal";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_ListenterminalEinstellung_ArtPersonalGruppeKompetenz";"dbo";"ListenterminalEinstellung";"IDArtPersonalGruppeKompetenz_FS";"dbo";"ArtPersonalGruppeKompetenz";"IDArtPersonalGruppeKompetenz"
|
|
||||||
"FK_Lohnart_ArtLohnsystem";"dbo";"Lohnart";"IDArtLohnsystem_FS";"dbo";"ArtLohnsystem";"IDArtLohnsystem"
|
|
||||||
"FK_LohnartPersonalSperrung_Adresse";"dbo";"LohnartPersonalSperrung";"IDAdresse_FS_Fahrer";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_LohnartPersonalSperrung_Lohnart";"dbo";"LohnartPersonalSperrung";"IDLohnart_FS";"dbo";"Lohnart";"IDLohnart"
|
|
||||||
"FK_LohnartTag_Lohnart";"dbo";"LohnartTag";"IDLohnart_FS";"dbo";"Lohnart";"IDLohnart"
|
|
||||||
"FK_LohnDruckLobuPersonalSperrung_Adresse";"dbo";"LohnDruckLobuPersonalSperrung";"IDAdresse_FS_Fahrer";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_LohnDruckLobuPersonalSperrung_ArtLohnDruckLobu";"dbo";"LohnDruckLobuPersonalSperrung";"IDArtLohnDruckLobu_FS";"dbo";"ArtLohnDruckLobu";"IDArtLohnDruckLobu"
|
|
||||||
"FK_Lohnextra_Adresse";"dbo";"Lohnextra";"IDAdresse_FS_Personal";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Lohnextra_ArtLohnextra";"dbo";"Lohnextra";"IDArtLohnextra_FS";"dbo";"ArtLohnextra";"IDArtLohnextra"
|
|
||||||
"FK_LohnParameter_Adresse";"dbo";"LohnParameter";"IDAdresse_FS_Fahrer";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_LohnParameter_ArtLohnsystem";"dbo";"LohnParameter";"IDArtLohnsystem_FS";"dbo";"ArtLohnsystem";"IDArtLohnsystem"
|
|
||||||
"FK_LohnStundenkonto_Adresse";"dbo";"LohnStundenkonto";"IDAdresse_FS_Fahrer";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_LohnStundenkonto_ArtLohnStundenkonto";"dbo";"LohnStundenkonto";"IDArtLohnStundenkonto_FS";"dbo";"ArtLohnStundenkonto";"IDArtLohnStundenkonto"
|
|
||||||
"FK_LohnsystemPersonalSperrung_Adresse";"dbo";"LohnsystemPersonalSperrung";"IDAdresse_FS_Fahrer";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_LohnsystemPersonalSperrung_ArtLohnsystem";"dbo";"LohnsystemPersonalSperrung";"IDArtLohnsystem_FS";"dbo";"ArtLohnsystem";"IDArtLohnsystem"
|
|
||||||
"FK_LohnTagesdaten_Adresse";"dbo";"LohnTagesdaten";"IDAdresse_FS_Personal";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_LohnUebergabe_Adresse";"dbo";"LohnUebergabe";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_LohnUebergabe_ArtLohnsystem";"dbo";"LohnUebergabe";"IDArtLohnsystem_FS";"dbo";"ArtLohnsystem";"IDArtLohnsystem"
|
|
||||||
"FK_MandantZuordnungZahlung_Adresse";"dbo";"MandantZuordnungKontenplan";"IDAdresse_FS_Mandant";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_MandantZuordnungZahlung_ZahlungCode";"dbo";"MandantZuordnungKontenplan";"IDMandantZuordnung_FS";"dbo";"Kontenplan";"IDKontenplan"
|
|
||||||
"FK_MandantZuordnungReise_Adresse";"dbo";"MandantZuordnungReise";"IDAdresse_FS_Mandant";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_MandantZuordnungReise_Reise";"dbo";"MandantZuordnungReise";"IDMandantZuordnung_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_MandantZuordnungZustieg_Adresse";"dbo";"MandantZuordnungZustieg";"IDAdresse_FS_Mandant";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_MandantZuordnungZustieg_Zustieg";"dbo";"MandantZuordnungZustieg";"IDMandantZuordnung_FS";"dbo";"Zustieg";"IDZustieg"
|
|
||||||
"FK_MitgliedAdresse_ArtMitgliedsstufen";"dbo";"MitgliedsstufeAdresse";"IDArtMitgliedsstufe_FS";"dbo";"ArtMitgliedsstufe";"IDArtMitgliedsstufe"
|
|
||||||
"FK_MitgliedAdresse_ArtZahlungsart";"dbo";"MitgliedsstufeAdresse";"IDArtZahlungsart_FS";"dbo";"ArtZahlungsart";"IDArtZahlungsart"
|
|
||||||
"FK_MitgliedsstufeAdresse_Adresse";"dbo";"MitgliedsstufeAdresse";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_MitgliedsEinstellungBetrag_ArtMitgliedsstufen";"dbo";"MitgliedsstufeBetrag";"IDArtMitgliedsstufe_FS";"dbo";"ArtMitgliedsstufe";"IDArtMitgliedsstufe"
|
|
||||||
"FK_MitgliedVorteilBezeichnungArtMitgliedsstufe_ArtMitgliedsstufe";"dbo";"MitgliedsVorteilBezeichnungArtMitgliedsstufe";"IDArtMitgliedsstufe_FS";"dbo";"ArtMitgliedsstufe";"IDArtMitgliedsstufe"
|
|
||||||
"FK_Newsletter_Adresse";"dbo";"Newsletter";"IDAdresse_FS_ErsterSB";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Newsletter_Adresse1";"dbo";"Newsletter";"IDAdresse_FS_LetzterSB";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_NewsletterAdressat_Adresse";"dbo";"NewsletterAdressat";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_NewsletterAdressat_Newsletter";"dbo";"NewsletterAdressat";"IDNewsletter_FS";"dbo";"Newsletter";"IDNewsletter"
|
|
||||||
"FK_NewsletterAdressat_NewsletterVersand";"dbo";"NewsletterAdressat";"IDNewsletterVersand_FS";"dbo";"NewsletterVersand";"IDNewsletterVersand"
|
|
||||||
"FK_NewsletterBlock_Newsletter";"dbo";"NewsletterBlock";"IDNewsletter_FS";"dbo";"Newsletter";"IDNewsletter"
|
|
||||||
"FK_NewsletterVersand_Adresse";"dbo";"NewsletterVersand";"IDAdresse_FS_Sachbearbeiter";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_NewsletterVersand_Newsletter";"dbo";"NewsletterVersand";"IDNewsletter_FS";"dbo";"Newsletter";"IDNewsletter"
|
|
||||||
"FK_NotizFahrerFahrzeug_ArtNotizart";"dbo";"NotizFahrerFahrzeug";"IDArtNotizart_FS";"dbo";"ArtNotizart";"IDArtNotizart"
|
|
||||||
"FK_NummernVerwaltung_Nummernkreis";"dbo";"NummernVerwaltung";"IDNummernkreis_FS";"dbo";"Nummernkreis";"IDNummernkreis"
|
|
||||||
"FK_OrtBeziehung_Ort";"dbo";"OrtBeziehung";"IDOrt_FS_Ort1";"dbo";"Ort";"IDOrt"
|
|
||||||
"FK_OrtBeziehung_Ort1";"dbo";"OrtBeziehung";"IDOrt_FS_Ort2";"dbo";"Ort";"IDOrt"
|
|
||||||
"FK_PartnerBefoederungsvertragspreis_Adresse";"dbo";"PartnerBefoederungsvertragspreis";"IDAdresse_FS_Partner";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_PartnerDokument_Adresse";"dbo";"PartnerDokument";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_PartnerGebiet_Land";"dbo";"PartnerGebiet";"IDLand_FS";"dbo";"Land";"IDLand"
|
|
||||||
"FK_PartnerGebietPreis_PartnerGebiet";"dbo";"PartnerGebietPreis";"IDPartnergebiet_FS";"dbo";"PartnerGebiet";"IDPartnergebiet"
|
|
||||||
"FK_PartnerKontingent_Adresse";"dbo";"PartnerKontingent";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_PartnerLeistung_Adresse";"dbo";"PartnerLeistung";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_PartnerLeistungPreis_PartnerLeistung";"dbo";"PartnerLeistungPreis";"IDPartnerLeistung_FS";"dbo";"PartnerLeistung";"IDPartnerLeistung"
|
|
||||||
"FK_PartnerLeistungPreis_PartnerSaisontafel";"dbo";"PartnerLeistungPreis";"IDPartnerSaisontafel_FS";"dbo";"PartnerSaisontafel";"IDPartnerSaisontafel"
|
|
||||||
"FK_PartnerPreis_ArtPreisschema";"dbo";"PartnerPreis";"IDPartnerSaisonTafel_FS";"dbo";"PartnerSaisontafel";"IDPartnerSaisontafel"
|
|
||||||
"FK_PartnerPreis_PartnerPreisKategorie";"dbo";"PartnerPreis";"IDPartnerPreisKategorie_FS";"dbo";"PartnerPreisKategorie";"IDPartnerPreisKategorie"
|
|
||||||
"FK_PartnerPreisKategorie_Adresse";"dbo";"PartnerPreisKategorie";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_PartnerPreisKategorie_ArtPartnerPreisCode";"dbo";"PartnerPreisKategorie";"IDArtPartnerPreisCode_FS";"dbo";"ArtPartnerPreisCode";"IDArtPartnerPreisCode"
|
|
||||||
"FK_PartnerPreisKategorie_ArtVerpflegung";"dbo";"PartnerPreisKategorie";"IDArtVerpflegung_FS";"dbo";"ArtVerpflegung";"IDArtVerpflegung"
|
|
||||||
"FK_PartnerProdukt_Adresse";"dbo";"PartnerProdukt";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_PartnerProdukt_Produkt";"dbo";"PartnerProdukt";"IDProdukt_FS";"dbo";"Produkt";"IDProdukt"
|
|
||||||
"FK_PartnerSaisontafel_Adresse";"dbo";"PartnerSaisontafel";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_PartnerSaisonzeit_Adresse";"dbo";"PartnerSaisonzeit";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_PartnerSaisonzeit_PartnerSaisontafel";"dbo";"PartnerSaisonzeit";"IDPartnerSaisonTafel_FS";"dbo";"PartnerSaisontafel";"IDPartnerSaisontafel"
|
|
||||||
"FK_Adresse_PartnerSelektion";"dbo";"PartnerSelektionenKunde";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Selektionsstamm_PartnerSelektion";"dbo";"PartnerSelektionenKunde";"IDSelektionsstamm_FS";"dbo";"Selektionsstamm";"IDSelektionsstamm"
|
|
||||||
"FK_PartnerZimmer_Adresse";"dbo";"PartnerZimmer";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_PartnerZimmer_ArtPartnerPreisCode";"dbo";"PartnerZimmer";"IDArtPartnerPreisCode_FS";"dbo";"ArtPartnerPreisCode";"IDArtPartnerPreisCode"
|
|
||||||
"FK_PartnerZimmer_Zimmerart";"dbo";"PartnerZimmer";"IDZimmerart_FS";"dbo";"Zimmerart";"IDZimmerart"
|
|
||||||
"FK_PartnerZusatz_Adresse";"dbo";"PartnerZusatz";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_PartnerZusatz_ArtAgenturPreisgruppe";"dbo";"PartnerZusatz";"IDArtAgenturPreisgruppe_FS";"dbo";"ArtAgenturPreisgruppe";"IDArtAgenturPreisgruppe"
|
|
||||||
"FK_PartnerZusatz_KonstantenSteueraufteilung";"dbo";"PartnerZusatz";"IDKonstantenSteueraufteilung_FS";"dbo";"KonstantenSteueraufteilung";"IDKonstantenSteueraufteilung"
|
|
||||||
"FK_PersonalGruppeDatenschutz_ArtPersonalGruppeKompetenz";"dbo";"PersonalGruppeDatenschutz";"IDArtPersonalGruppeKompetenz_FS";"dbo";"ArtPersonalGruppeKompetenz";"IDArtPersonalGruppeKompetenz"
|
|
||||||
"FK_PersonalGruppeDatenschutzDarstellung_ArtPersonalGruppeKompetenz";"dbo";"PersonalGruppeDatenschutzDarstellung";"IDArtPersonalGruppeKompetenz_FS";"dbo";"ArtPersonalGruppeKompetenz";"IDArtPersonalGruppeKompetenz"
|
|
||||||
"FK_PersonalGruppeDatenschutzLöschung_ArtPersonalGruppeKompetenz";"dbo";"PersonalGruppeDatenschutzLöschung";"IDArtPersonalGruppeKompetenz_FS";"dbo";"ArtPersonalGruppeKompetenz";"IDArtPersonalGruppeKompetenz"
|
|
||||||
"FK_PersonalGruppeKompetenz_ArtPersonalGruppeKompetenz";"dbo";"PersonalGruppeKompetenz";"IDArtPersonalGruppeKompetenz_FS";"dbo";"ArtPersonalGruppeKompetenz";"IDArtPersonalGruppeKompetenz"
|
|
||||||
"FK_Personalkapazitaet_Adresse";"dbo";"Personalkapazitaet";"IDAdresse_FS_Personal";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Personalkapazitaet_ArtPersonalkapazitaet";"dbo";"Personalkapazitaet";"IDArtPersonalkapazitaet_FS";"dbo";"ArtPersonalkapazitaet";"IDArtPersonalkapazitaet"
|
|
||||||
"FK_PersonalkapazitaetArbeitszeit_ArtPersonalkapazitaet";"dbo";"PersonalkapazitaetArbeitszeit";"IDArtPersonalkapazitaet_FS";"dbo";"ArtPersonalkapazitaet";"IDArtPersonalkapazitaet"
|
|
||||||
"FK_PersonalkapazitaetEingabe_Adresse";"dbo";"PersonalkapazitaetEingabe";"IDAdresse_FS_Personal";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_PersonalkapazitaetMindestbesetzung_ArtBetriebshof";"dbo";"PersonalkapazitaetMindestbesetzung";"IDArtBetriebshof_FS";"dbo";"ArtBetriebshof";"IDArtBetriebshof"
|
|
||||||
"FK_PersonalkapazitaetMindestbesetzung_ArtMindestbesetzungZeitraum";"dbo";"PersonalkapazitaetMindestbesetzung";"IDArtMindestbesetzungZeitraum_FS";"dbo";"ArtMindestbesetzungZeitraum";"IDArtMindestbesetzungZeitraum"
|
|
||||||
"FK_PersonalkapazitaetMindestbesetzungZeitraum_ArtMindestbesetzungZeitraum";"dbo";"PersonalkapazitaetMindestbesetzungZeitraum";"IDArtMindestbesetzungZeitraum_FS";"dbo";"ArtMindestbesetzungZeitraum";"IDArtMindestbesetzungZeitraum"
|
|
||||||
"FK_PlanungSoll_Adresse";"dbo";"PlanungSoll";"IDAdresse_FS_Fahrer";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_PlanungSoll_ArtPersonalkapazitaet";"dbo";"PlanungSoll";"IDArtPersonalkapazitaet_FS_Schema";"dbo";"ArtPersonalkapazitaet";"IDArtPersonalkapazitaet"
|
|
||||||
"FK_PlanungSollBlock_BlockPlanung";"dbo";"PlanungSollBlock";"IDBlockPlanung_FS";"dbo";"BlockPlanung";"IDBlockPlanung"
|
|
||||||
"FK_PlanungSollBlock_PlanungSoll";"dbo";"PlanungSollBlock";"IDPlanungSoll_FS";"dbo";"PlanungSoll";"IDPlanungSoll"
|
|
||||||
"FK_PlanungSollLohnarten_Lohnart";"dbo";"PlanungSollLohnart";"IDLohnart_FS";"dbo";"Lohnart";"IDLohnart"
|
|
||||||
"FK_PlanungSollLohnarten_PlanungSoll";"dbo";"PlanungSollLohnart";"IDPlanungSoll_FS";"dbo";"PlanungSoll";"IDPlanungSoll"
|
|
||||||
"FK_PortalBemerkung_PortalBuchung";"dbo";"PortalBemerkung";"IDPortalBuchung_FS";"dbo";"PortalBuchung";"IDPortalBuchung"
|
|
||||||
"FK_PortalLeistung_PortalBuchung";"dbo";"PortalLeistung";"IDPortalBuchung_FS";"dbo";"PortalBuchung";"IDPortalBuchung"
|
|
||||||
"FK_PortalLeistungTN_PortalLeistung";"dbo";"PortalLeistungTN";"IDPortalLeistung_FS";"dbo";"PortalLeistung";"IDPortalLeistung"
|
|
||||||
"FK_PortalLeistungTN_PortalTN";"dbo";"PortalLeistungTN";"IDPortalTN_FS";"dbo";"PortalTN";"IDPortalTN"
|
|
||||||
"FK_PortalLeistungZusatz_PortalBuchung";"dbo";"PortalLeistungZusatz";"IDPortalBuchung_FS";"dbo";"PortalBuchung";"IDPortalBuchung"
|
|
||||||
"FK_PortalTN_PortalBuchung";"dbo";"PortalTN";"IDPortalBuchung_FS";"dbo";"PortalBuchung";"IDPortalBuchung"
|
|
||||||
"FK_Produkt_KonstantenSteueraufteilung";"dbo";"Produkt";"IDKonstantenSteueraufteilung_FS";"dbo";"KonstantenSteueraufteilung";"IDKonstantenSteueraufteilung"
|
|
||||||
"FK_ProduktArtPersonalGruppe_ArtPersonalGruppeProdukt";"dbo";"ProduktArtPersonalGruppe";"IDArtPersonalGruppeProdukt_FS";"dbo";"ArtPersonalGruppeProdukt";"IDArtPersonalGruppeProdukt"
|
|
||||||
"FK_ProduktArtPersonalGruppe_Produkt";"dbo";"ProduktArtPersonalGruppe";"IDProdukt_FS";"dbo";"Produkt";"IDProdukt"
|
|
||||||
"FK_ProduktBeschreibungInhalt_ProduktBeschreibung";"dbo";"ProduktBeschreibungInhalt";"IDProduktBeschreibung_FS";"dbo";"ProduktBeschreibung";"IDProduktBeschreibung"
|
|
||||||
"FK_ProduktbeschreibungKategorie_ProduktBeschreibung";"dbo";"ProduktbeschreibungKategorie";"IDProduktbeschreibung_FS";"dbo";"ProduktBeschreibung";"IDProduktBeschreibung"
|
|
||||||
"FK_ProduktbeschreibungKategorie_WWSuchKategorie";"dbo";"ProduktbeschreibungKategorie";"IDWWSuchKategorie_FS";"dbo";"WWSuchKategorie";"IDWWSuchKategorie"
|
|
||||||
"FK_ProduktbeschreibungKategorie_WWSuchKategorieEintrag";"dbo";"ProduktbeschreibungKategorie";"IDWWSuchKategorieEintrag_FS";"dbo";"WWSuchKategorieEintrag";"IDWWSuchKategorieEintrag"
|
|
||||||
"FK_ProduktBeschreibungZusatz_ProduktBeschreibung";"dbo";"ProduktBeschreibungZusatz";"IDProduktBeschreibung_FS";"dbo";"ProduktBeschreibung";"IDProduktBeschreibung"
|
|
||||||
"FK_ProduktDokument_Produkt";"dbo";"ProduktDokument";"IDProdukt_FS";"dbo";"Produkt";"IDProdukt"
|
|
||||||
"FK_ProduktSachbearbeiter_Adresse";"dbo";"ProduktSachbearbeiter";"IDAdresse_FS_Personal";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_ProduktSachbearbeiter_Produkt";"dbo";"ProduktSachbearbeiter";"IDProdukt_FS";"dbo";"Produkt";"IDProdukt"
|
|
||||||
"FK_Produkt_ProduktText";"dbo";"ProduktText";"IDProdukt_FS";"dbo";"Produkt";"IDProdukt"
|
|
||||||
"FK_Qualitaet_ArtFahrplanVersion";"dbo";"Qualitaet";"IDArtFahrplanversion_FS";"dbo";"ArtFahrplanVersion";"IDArtFahrplanVersion"
|
|
||||||
"FK_QualitaetDefinition_ArtBlockArt";"dbo";"QualitaetDefinition";"IDArtBlockArt_FS";"dbo";"ArtBlockArt";"IDArtBlockArt"
|
|
||||||
"FK_QualitaetDefinition_ArtGruppeVerkehrstag";"dbo";"QualitaetDefinition";"IDArtGruppeVerkehrstag_FS";"dbo";"ArtGruppeVerkehrstag";"IDArtGruppeVerkehrstag"
|
|
||||||
"FK_QualitaetDefinition_Qualitaet";"dbo";"QualitaetDefinition";"IDQualitaet_FS";"dbo";"Qualitaet";"IDQualitaet"
|
|
||||||
"FK_QualitaetDefinitionAuftragsart_Auftragsart";"dbo";"QualitaetDefinitionAuftragsart";"IDAuftragsart_FS";"dbo";"Auftragsart";"IDAuftragsart"
|
|
||||||
"FK_QualitaetDefinitionAuftragsart_Qualitaet";"dbo";"QualitaetDefinitionAuftragsart";"IDQualitaet_FS";"dbo";"Qualitaet";"IDQualitaet"
|
|
||||||
"FK_QualitaetDefinitionFahrzeug_Fahrzeug";"dbo";"QualitaetDefinitionFahrzeug";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_QualitaetDefinitionFahrzeug_Qualitaet";"dbo";"QualitaetDefinitionFahrzeug";"IDQualitaet_FS";"dbo";"Qualitaet";"IDQualitaet"
|
|
||||||
"FK_QualitaetDefinitionLinie_Qualitaet";"dbo";"QualitaetDefinitionLinie";"IDQualitaet_FS";"dbo";"Qualitaet";"IDQualitaet"
|
|
||||||
"FK_QualitaetZuordnung_Qualitaet";"dbo";"QualitaetZuordnung";"IDQualitaet_FS";"dbo";"Qualitaet";"IDQualitaet"
|
|
||||||
"FK_QuittierungAusfahrt_AuftragFahrer";"dbo";"QuittierungAusfahrt";"IDAuftragFahrer_FS";"dbo";"AuftragFahrer";"IDAuftragFahrer"
|
|
||||||
"FK_QuittierungAusfahrt_BlockPlanung";"dbo";"QuittierungAusfahrt";"IDBlockPlanung_FS";"dbo";"BlockPlanung";"IDBlockPlanung"
|
|
||||||
"FK_Rechnung_AdresseAnschrift";"dbo";"Rechnung";"IDAdresseAnschrift_FS";"dbo";"AdresseAnschrift";"IDAdresseAnschrift"
|
|
||||||
"FK_Rechnung_ArtZahlungsart";"dbo";"Rechnung";"IDArtZahlungsart_FS";"dbo";"ArtZahlungsart";"IDArtZahlungsart"
|
|
||||||
"FK_Rechnung_RechnungHaupt";"dbo";"Rechnung";"IDRechnungHaupt_FS";"dbo";"RechnungHaupt";"IDRechnungHaupt"
|
|
||||||
"FK_RechnungBetrag_Rechnung";"dbo";"RechnungBetrag";"IDRechnung_FS";"dbo";"Rechnung";"IDRechnung"
|
|
||||||
"FK_RechnungBetragMahnung_RechnungBetrag";"dbo";"RechnungBetragMahnung";"IdRechnungBetrag_FS";"dbo";"RechnungBetrag";"IDRechnungBetrag"
|
|
||||||
"FK_RechnungBuchung_Buchung";"dbo";"RechnungBuchung";"IDBuchung_FS";"dbo";"Buchung";"IDBuchung"
|
|
||||||
"FK_RechnungBuchung_RechnungHaupt";"dbo";"RechnungBuchung";"IDRechnungHaupt_FS";"dbo";"RechnungHaupt";"IDRechnungHaupt"
|
|
||||||
"FK_RechnungHaupt_Adresse";"dbo";"RechnungHaupt";"IDAdresse_FS_Mandant";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_RechnungHaupt_Adresse1";"dbo";"RechnungHaupt";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_RechnungPosten_Rechnung";"dbo";"RechnungPosten";"IDRechnung_FS";"dbo";"Rechnung";"IDRechnung"
|
|
||||||
"FK_RechnungPosten_Teilnehmer_BuchungTNPreis";"dbo";"RechnungPosten_Teilnehmer";"IDBuchungTN_FS";"dbo";"BuchungTNPreis";"IDBuchungTNPreis"
|
|
||||||
"FK_RechnungPosten_Teilnehmer_RechnungPostenBetragTOU";"dbo";"RechnungPosten_Teilnehmer";"IDRechnungPostenBetragTOU_FS";"dbo";"RechnungPostenBetragTOU";"IDRechnungPostenBetragTOU"
|
|
||||||
"FK_RechnungPostenBetragALL_RechnungPosten";"dbo";"RechnungPostenBetragALL";"IDRechnungPosten_FS";"dbo";"RechnungPosten";"IDRechnungPosten"
|
|
||||||
"FK_RechnungPostenBetragTOU_RechnungPosten";"dbo";"RechnungPostenBetragTOU";"IDRechnungPosten_FS";"dbo";"RechnungPosten";"IDRechnungPosten"
|
|
||||||
"FK_RechnungPostenLinieAufteilungTour_RechnungPostenLinieAufteilung";"dbo";"RechnungPostenLinieAufteilungTour";"IDRechnungPostenLinieAufteilung_FS";"dbo";"RechnungPostenLinieAufteilung";"IDRechnungPostenLinieAufteilung"
|
|
||||||
"FK_RechnungPostenMietbusAufteilung_RechnungPostenBetragALL";"dbo";"RechnungPostenMietbusAufteilung";"IDRechnungPostenBetragAll_FS";"dbo";"RechnungPostenBetragALL";"IDRechnungPostenBetragALL"
|
|
||||||
"FK_RechnungSteueraufteilung_Rechnung";"dbo";"RechnungSteueraufteilung";"IDRechnung_FS";"dbo";"Rechnung";"IDRechnung"
|
|
||||||
"FK_ReisabBefoerderung_Reisab";"dbo";"ReisabBefoerderung";"IdReisab_FS";"dbo";"Reisab";"IdReisab"
|
|
||||||
"FK_ReisabLand_Land";"dbo";"ReisabLand";"IdLand_FS";"dbo";"Land";"IDLand"
|
|
||||||
"FK_ReisabLand_Reisab";"dbo";"ReisabLand";"IDReisab_FS";"dbo";"Reisab";"IdReisab"
|
|
||||||
"FK_ReisabLeistung_ArtReiseKosten";"dbo";"ReisabLeistung";"IdArtReiseKosten_FS";"dbo";"ArtReiseKosten";"IDArtReiseKosten"
|
|
||||||
"FK_ReisabLeistung_Reisab";"dbo";"ReisabLeistung";"IdReisab_FS";"dbo";"Reisab";"IdReisab"
|
|
||||||
"FK_ReisabReise_Reisab";"dbo";"ReisabReise";"IDReisab_FS";"dbo";"Reisab";"IdReisab"
|
|
||||||
"FK_ReisabReise_Reise";"dbo";"ReisabReise";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReisabUmsatz_Reisab";"dbo";"ReisabUmsatz";"IdReisab_FS";"dbo";"Reisab";"IdReisab"
|
|
||||||
"FK_Reise_Produkt";"dbo";"Reise";"IDProdukt_FS";"dbo";"Produkt";"IDProdukt"
|
|
||||||
"FK_ReiseBeleg_Reise";"dbo";"ReiseBeleg";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseBelegDokument_ReiseBeleg";"dbo";"ReiseBelegDokument";"IDReiseBeleg_FS";"dbo";"ReiseBeleg";"IDReiseBeleg"
|
|
||||||
"FK_ReiseBelegPosten_ReiseBeleg";"dbo";"ReiseBelegPosten";"IDReiseBeleg_FS";"dbo";"ReiseBeleg";"IDReiseBeleg"
|
|
||||||
"FK_ReiseDevise_ArtWaehrung";"dbo";"ReiseDevise";"IDArtWaehrung_FS";"dbo";"ArtWaehrung";"IDArtWaehrung"
|
|
||||||
"FK_ReiseDevise_Reise";"dbo";"ReiseDevise";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseDokument_Reise";"dbo";"ReiseDokument";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseFlexTarif_KonstantenFlexTarif";"dbo";"ReiseFlexTarif";"IDKonstantenFlexTarif_FS";"dbo";"KonstantenFlexTarif";"IDKonstantenFlexTarif"
|
|
||||||
"FK_ReiseFlexTarif_Reise";"dbo";"ReiseFlexTarif";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseGuides_ArtReiseGuides";"dbo";"ReiseGuides";"IDArtReiseGuides_FS";"dbo";"ArtReiseGuides";"IDArtReiseGuides"
|
|
||||||
"FK_ReiseKosten_Reise";"dbo";"ReiseKosten";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseLand_Land";"dbo";"ReiseLand";"IDLand_FS";"dbo";"Land";"IDLand"
|
|
||||||
"FK_ReiseLand_Reise";"dbo";"ReiseLand";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseLeistung_Leistung";"dbo";"ReiseLeistung";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_ReiseLeistung_Reise";"dbo";"ReiseLeistung";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseLeistungGruppe_Reise";"dbo";"ReiseLeistungGruppe";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseLeistungGruppeZuweisung_Leistung";"dbo";"ReiseLeistungGruppeZuweisung";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_ReiseLeistungGruppeZuweisung_ReiseLeistungGruppe";"dbo";"ReiseLeistungGruppeZuweisung";"IDReiseLeistungGruppe_FS";"dbo";"ReiseLeistungGruppe";"IDReiseLeistungGruppe"
|
|
||||||
"FK_ReiseleiterAbrechnung_Adresse";"dbo";"ReiseleiterAbrechnung";"IDAdresse_FS_Personal";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_ReiseleiterAbrechnung_Reise";"dbo";"ReiseleiterAbrechnung";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseleiterAbrechnung_ReiseleiterZuordnungStamm";"dbo";"ReiseleiterAbrechnung";"IDReiseleiterZuordnungStamm_FS";"dbo";"ReiseleiterZuordnungStamm";"IDReiseleiterZuordnungStamm"
|
|
||||||
"FK_ReiseleiterAbrechnungPosten_ReiseleiterAbrechnung";"dbo";"ReiseleiterAbrechnungPosten";"IDReiseleiterAbrechnung_FS";"dbo";"ReiseleiterAbrechnung";"IDReiseleiterAbrechnung"
|
|
||||||
"FK_ReiseleiterAbrechnungPosten_ReiseleiterPosten";"dbo";"ReiseleiterAbrechnungPosten";"IDReiseleiterPosten_FS";"dbo";"ReiseleiterPosten";"IDReiseleiterPosten"
|
|
||||||
"FK_ReiseleiterAbrechnungSpesen_ArtSpesenVerpflegung";"dbo";"ReiseleiterAbrechnungSpesen";"IDArtSpesenVerpflegung_FS";"dbo";"ArtSpesenVerpflegung";"IDArtSpesenVerpflegung"
|
|
||||||
"FK_ReiseleiterAbrechnungSpesen_ReiseleiterAbrechnung";"dbo";"ReiseleiterAbrechnungSpesen";"IDReiseleiterAbrechnung_FS";"dbo";"ReiseleiterAbrechnung";"IDReiseleiterAbrechnung"
|
|
||||||
"FK_ReiseleiterAbrechnungZuAbschlag_ReiseleiterAbrechnung";"dbo";"ReiseleiterAbrechnungZuAbschlag";"IDReiseleiterAbrechnung_FS";"dbo";"ReiseleiterAbrechnung";"IDReiseleiterAbrechnung"
|
|
||||||
"FK_ReiseleiterAbrVergütungStaffel_ReiseleiterAbrVergütungStamm1";"dbo";"ReiseleiterAbrVergütungStaffel";"IDReiseleiterAbrVergütungStamm_FS";"dbo";"ReiseleiterAbrVergütungStamm";"IDReiseleiterAbrVergütungStamm"
|
|
||||||
"FK_ReiseleiterAbrVergütungStaffelAnAbreise_ReiseleiterAbrVergütungStamm";"dbo";"ReiseleiterAbrVergütungStaffelAnAbreise";"IDReiseleiterAbrVergütungStamm_FS";"dbo";"ReiseleiterAbrVergütungStamm";"IDReiseleiterAbrVergütungStamm"
|
|
||||||
"FK_ReiseleiterArbeitszeit_ReiseleiterZuordnungReiseleiter";"dbo";"ReiseleiterArbeitszeit";"IDReiseleiterZuordnungReiseleiter_FS";"dbo";"ReiseleiterZuordnungReiseleiter";"IDReiseleiterZuordnungReiseleiter"
|
|
||||||
"FK_ReiseleiterHonorarStaffel_Adresse";"dbo";"ReiseleiterHonorarStaffel";"IDAdresse_FS_Personal";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_ReiseleiterHonorarStaffel_ArtReiseleiterHonorar";"dbo";"ReiseleiterHonorarStaffel";"IDArtReiseleiterHonorar_FS";"dbo";"ArtReiseleiterHonorar";"IDArtReiseleiterHonorar"
|
|
||||||
"FK_ReiseleiterSelektionen_Adresse";"dbo";"ReiseleiterSelektionen";"IDAdresse_FS_Personal";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_ReiseleiterSelektionen_Selektionsstamm";"dbo";"ReiseleiterSelektionen";"IDSelektionsstamm_FS";"dbo";"Selektionsstamm";"IDSelektionsstamm"
|
|
||||||
"FK_ReiseleiterSpesenBetrag_ArtSpesenLand";"dbo";"ReiseleiterSpesenBetrag";"IDArtSpesenLand_FS";"dbo";"ArtSpesenLand";"IDArtSpesenLand"
|
|
||||||
"FK_ReiseleiterSpesenBetrag_ReiseleiterSpesenStaffel";"dbo";"ReiseleiterSpesenBetrag";"IDReiseleiterSpesenStaffel_FS";"dbo";"ReiseleiterSpesenStaffel";"IDReiseleiterSpesenStaffel"
|
|
||||||
"FK_ReiseleiterStamm_Adresse";"dbo";"ReiseleiterStamm";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_ReiseleiterWunschReise_Adresse";"dbo";"ReiseleiterWunschReise";"IDAdresse_FS_Personal";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_ReiseleiterWunschReise_Reise";"dbo";"ReiseleiterWunschReise";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseleiterZuordnungReiseleiter_Adresse1";"dbo";"ReiseleiterZuordnungReiseleiter";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_ReiseleiterZuordnungReiseleiter_ReiseleiterZuordnungStamm";"dbo";"ReiseleiterZuordnungReiseleiter";"IDReiseleiterZuordnungStamm_FS";"dbo";"ReiseleiterZuordnungStamm";"IDReiseleiterZuordnungStamm"
|
|
||||||
"FK_ReiseleiterZuordnungZusatzleistung_Leistung";"dbo";"ReiseleiterZuordnungZusatzleistung";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_ReiseleiterZuordnungZusatzleistung_ReiseleiterZuordnungReiseleiter";"dbo";"ReiseleiterZuordnungZusatzleistung";"IDReiseleiterZuordnungReiseleiter_FS";"dbo";"ReiseleiterZuordnungReiseleiter";"IDReiseleiterZuordnungReiseleiter"
|
|
||||||
"FK_ReiseListenVersand_ArtListenart";"dbo";"ReiseListenVersand";"IdArtListenart_FS";"dbo";"ArtListenart";"IDArtListenArt"
|
|
||||||
"FK_ReiseListenVersand_ArtListenart1";"dbo";"ReiseListenVersand";"IdArtListenart_FS";"dbo";"ArtListenart";"IDArtListenArt"
|
|
||||||
"FK_ReiseListenVersand_DruckEinstellungEMail";"dbo";"ReiseListenVersand";"IdDruckeinstellungEmail_FS";"dbo";"DruckEinstellungEMail";"IDDruckEinstellungEMail"
|
|
||||||
"FK_ReiseListenVersand_Reise";"dbo";"ReiseListenVersand";"IdReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReisePartner_Adresse";"dbo";"ReisePartner";"IDAdresse_FS_Partner";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_ReisePartner_ArtVerpflegung";"dbo";"ReisePartner";"IDArtVerpflegung_FS";"dbo";"ArtVerpflegung";"IDArtVerpflegung"
|
|
||||||
"FK_ReisePartner_Reise";"dbo";"ReisePartner";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReisePersonalZimmer_Reise";"dbo";"ReisePersonalZimmer";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReisePersonalZimmer_Zimmerart";"dbo";"ReisePersonalZimmer";"IDZimmerart_FS";"dbo";"Zimmerart";"IDZimmerart"
|
|
||||||
"FK_ReiseSelektionen_Reise";"dbo";"ReiseSelektionen";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseSelektionen_Selektionsstamm";"dbo";"ReiseSelektionen";"IDSelektionsstamm_FS";"dbo";"Selektionsstamm";"IDSelektionsstamm"
|
|
||||||
"FK_ReiseSitzplan_Reise";"dbo";"ReiseSitzplan";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseSitzplan_Sitzplan";"dbo";"ReiseSitzplan";"IDSitzplan_FS";"dbo";"Sitzplan";"IDSitzplan"
|
|
||||||
"FK_ReiseSitzplatzPreis_ArtSitzplatzKategorie";"dbo";"ReiseSitzplatzPreis";"IDArtSitzplatzKategorie_FS";"dbo";"ArtSitzplatzKategorie";"IDArtSitzplatzKategorie"
|
|
||||||
"FK_ReiseSitzplatzPreis_Reise";"dbo";"ReiseSitzplatzPreis";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseStatistik_Reise";"dbo";"ReiseStatistik";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseStatistikVertrieb_ReiseStatistik";"dbo";"ReiseStatistikVertrieb";"IDReiseStatistik_FS";"dbo";"ReiseStatistik";"IDReiseStatistik"
|
|
||||||
"FK_ReiseStatus_Reise";"dbo";"ReiseStatus";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseStatusBuchhaltung_ArtReiseStatusBuchhaltung";"dbo";"ReiseStatusBuchhaltung";"IDArtReiseStatusBuchhaltung_FS";"dbo";"ArtReiseStatusBuchhaltung";"IDArtReiseStatusBuchhaltung"
|
|
||||||
"FK_ReiseStatusBuchhaltung_Reise";"dbo";"ReiseStatusBuchhaltung";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_Reise_ReiseText";"dbo";"ReiseText";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseTextbaustein_Reise";"dbo";"ReiseTextbaustein";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseTextbaustein_Textbaustein";"dbo";"ReiseTextbaustein";"IDTextbaustein_FS";"dbo";"Textbaustein";"IDTextbaustein"
|
|
||||||
"FK_ReiseTextbausteinAbwJeMandant_Mandant";"dbo";"ReiseTextbausteinAbwJeMandant";"IDAdresse_FS_Mandant";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_ReiseTextbausteinAbwJeMandant_Reise";"dbo";"ReiseTextbausteinAbwJeMandant";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseTextbausteinAbwJeMandant_Textbaustein";"dbo";"ReiseTextbausteinAbwJeMandant";"IDTextbaustein_FS";"dbo";"Textbaustein";"IDTextbaustein"
|
|
||||||
"FK_Reiseunterlage_Reise";"dbo";"Reiseunterlage";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_Reiseunterlage_ReiseunterlagenBaustein";"dbo";"Reiseunterlage";"IDReiseunterlagenBaustein_FS";"dbo";"ReiseunterlagenBaustein";"IDReiseunterlagenBaustein"
|
|
||||||
"FK_ReiseunterlageInhalt_Reiseunterlage";"dbo";"ReiseunterlageInhalt";"IDReiseunterlage_FS";"dbo";"Reiseunterlage";"IDReiseunterlage"
|
|
||||||
"FK_ReiseunterlagenBaustein_ReiseunterlagenBausteinTyp";"dbo";"ReiseunterlagenBaustein";"IDReiseunterlagenBausteinTyp_FS";"dbo";"ReiseunterlagenBausteinTyp";"IDReiseunterlagenBausteinTyp"
|
|
||||||
"FK_ReiseunterlagenBausteinBedingung_ReiseunterlagenBaustein";"dbo";"ReiseunterlagenBausteinBedingung";"IDReiseunterlagenBaustein_FS";"dbo";"ReiseunterlagenBaustein";"IDReiseunterlagenBaustein"
|
|
||||||
"FK_ReiseunterlagenBausteinBedingung_ReiseunterlagenBedingung";"dbo";"ReiseunterlagenBausteinBedingung";"IDReiseunterlagenBedingung_FS";"dbo";"ReiseunterlagenBedingung";"IDReiseunterlagenBedingung"
|
|
||||||
"FK_ReiseunterlagenBausteinInhalt_ReiseunterlagenBaustein";"dbo";"ReiseunterlagenBausteinInhalt";"IDReiseunterlagenBaustein_FS";"dbo";"ReiseunterlagenBaustein";"IDReiseunterlagenBaustein"
|
|
||||||
"FK_ReiseunterlagenVorlageBaustein_ReiseunterlagenBaustein";"dbo";"ReiseunterlagenVorlageBaustein";"IDReiseUnterlagenBaustein_FS";"dbo";"ReiseunterlagenBaustein";"IDReiseunterlagenBaustein"
|
|
||||||
"FK_ReiseunterlagenVorlageBaustein_ReiseunterlagenVorlage";"dbo";"ReiseunterlagenVorlageBaustein";"IDReiseUnterlagenVorlage_FS";"dbo";"ReiseunterlagenVorlage";"IDReiseUnterlagenVorlage"
|
|
||||||
"FK_ReiseunterlagenVorlageInhalt_ReiseunterlagenVorlage";"dbo";"ReiseunterlagenVorlageInhalt";"IDReiseunterlagenVorlage_FS";"dbo";"ReiseunterlagenVorlage";"IDReiseUnterlagenVorlage"
|
|
||||||
"FK_ReiseVersicherungsGruppe_ArtVersicherungsGruppe";"dbo";"ReiseVersicherungsGruppe";"IDArtVersicherungsGruppe_FS";"dbo";"ArtVersicherungsGruppe";"IDArtVersicherungsGruppe"
|
|
||||||
"FK_ReiseVersicherungsGruppe_Reise";"dbo";"ReiseVersicherungsGruppe";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseVerwendung_Reise";"dbo";"ReiseVerwendung";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseVplDokument_Dokument";"dbo";"ReiseVplDokument";"IdDokument_FS";"dbo";"Dokument";"IDDokument"
|
|
||||||
"FK_ReiseVplDokument_ReiseVpl";"dbo";"ReiseVplDokument";"IdReiseVpl_FS";"dbo";"ReiseVpl";"IdReiseVpl"
|
|
||||||
"FK_ReiseVplKdVormerkung_ReiseVpl";"dbo";"ReiseVplKdVormerkung";"IdReiseVpl_FS";"dbo";"ReiseVpl";"IdReiseVpl"
|
|
||||||
"FK_ReiseVplPartner_ReiseVpl";"dbo";"ReiseVplPartner";"IdReiseVpl_FS";"dbo";"ReiseVpl";"IdReiseVpl"
|
|
||||||
"FK_ReiseVplPartnerDokument_Dokument";"dbo";"ReiseVplPartnerDokument";"IdDokument_FS";"dbo";"Dokument";"IDDokument"
|
|
||||||
"FK_ReiseVplPartnerDokument_ReiseVplPartner";"dbo";"ReiseVplPartnerDokument";"IdReiseVplPartner_FS";"dbo";"ReiseVplPartner";"IdReiseVplPartner"
|
|
||||||
"FK_ReiseVplPartnerStatus_ArtReiseVplPartnerStatus";"dbo";"ReiseVplPartnerStatus";"IDArtReiseVplPartnerStatus_FS";"dbo";"ArtReiseVplPartnerStatus";"IDArtReiseVplPartnerStatus"
|
|
||||||
"FK_ReiseVplPartnerStatus_ReiseVplPartner";"dbo";"ReiseVplPartnerStatus";"IDReiseVplPartner_FS";"dbo";"ReiseVplPartner";"IdReiseVplPartner"
|
|
||||||
"FK_ReiseVplStatus_ArtReiseVplStatus";"dbo";"ReiseVplStatus";"IDArtReiseVplStatus_FS";"dbo";"ArtReiseVplStatus";"IDArtReiseVplStatus"
|
|
||||||
"FK_ReiseVplStatus_ReiseVpl";"dbo";"ReiseVplStatus";"IDReiseVpl_FS";"dbo";"ReiseVpl";"IdReiseVpl"
|
|
||||||
"FK_ReiseVplVerwendung_ReiseVpl";"dbo";"ReiseVplVerwendung";"IDReiseVpl_FS";"dbo";"ReiseVpl";"IdReiseVpl"
|
|
||||||
"FK_ReiseWerbeplanung_Reise";"dbo";"ReiseWerbeplanung";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReisezielSelektionsstamm_Reiseziel";"dbo";"ReisezielSelektionsstamm";"IDReiseziel_FS";"dbo";"Reiseziel";"IDReiseziel"
|
|
||||||
"FK_ReisezielSelektionsstamm_Selektionsstamm";"dbo";"ReisezielSelektionsstamm";"IDSelektionsstamm_FS";"dbo";"Selektionsstamm";"IDSelektionsstamm"
|
|
||||||
"FK_ReiseZusatz_ArtAgenturPreisgruppe";"dbo";"ReiseZusatz";"IDArtAgenturPreisgruppe_FS";"dbo";"ArtAgenturPreisgruppe";"IDArtAgenturPreisgruppe"
|
|
||||||
"FK_ReiseZusatz_Reise";"dbo";"ReiseZusatz";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReiseZusatzAbwJeMandant_Adresse";"dbo";"ReiseZusatzAbwJeMandant";"IDAdresse_FS_Mandant";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_ReiseZusatzAbwJeMandant_Reise";"dbo";"ReiseZusatzAbwJeMandant";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ReparaturAuftragZusatz_ReparaturAuftrag";"dbo";"ReparaturAuftragZusatz";"IDReparaturAuftrag_FS";"dbo";"Reparaturauftrag";"IDReparaturauftrag"
|
|
||||||
"FK_Rollsystem_ArtFahrplanVersion";"dbo";"Rollsystem";"IDArtFahrplanversion_FS";"dbo";"ArtFahrplanVersion";"IDArtFahrplanVersion"
|
|
||||||
"FK_RollsystemBlock_Block";"dbo";"RollsystemBlock";"IDBlock_FS";"dbo";"Block";"IDBlock"
|
|
||||||
"FK_RollsystemBlock_RollsystemFahrerFahrzeug";"dbo";"RollsystemBlock";"IDRollsystemFahrerFahrzeug_FS";"dbo";"RollsystemFahrerFahrzeug";"IDRollsystemFahrerFahrzeug"
|
|
||||||
"FK_RollsystemFahrerFahrzeug_Rollsystem";"dbo";"RollsystemFahrerFahrzeug";"IDRollsystem_FS";"dbo";"Rollsystem";"IDRollsystem"
|
|
||||||
"FK_RollsystemFerien_Rollsystem";"dbo";"RollsystemFerien";"IDRollsystem_FS";"dbo";"Rollsystem";"IDRollsystem"
|
|
||||||
"FK_RollsystemPerkap_ArtPersonalkapazitaet";"dbo";"RollsystemPerkap";"IDArtPersonalkapazitaet_FS";"dbo";"ArtPersonalkapazitaet";"IDArtPersonalkapazitaet"
|
|
||||||
"FK_RollsystemPerkap_RollsystemFahrerFahrzeug";"dbo";"RollsystemPerkap";"IDRollsystemFahrerFahrzeug_FS";"dbo";"RollsystemFahrerFahrzeug";"IDRollsystemFahrerFahrzeug"
|
|
||||||
"FK_RsbVeransAnzahlung_RsbVerans";"dbo";"RsbVeransAnzahlung";"IDRsbVerans_FS";"dbo";"RsbVerans";"IDRsbVeranstalter"
|
|
||||||
"FK_RsbVeransZusatz_RsbVerans";"dbo";"RsbVeransZusatz";"IDRsbVerans_FS";"dbo";"RsbVerans";"IDRsbVeranstalter"
|
|
||||||
"FK_Rufbus_FPLinieFahrt";"dbo";"Rufbus";"IDFPLinieFahrt_FS";"dbo";"FPLinieFahrt";"IDFPLinieFahrt"
|
|
||||||
"FK_Rufbus_FPLinieHaltestelleZeitartNach";"dbo";"Rufbus";"FPLinieHaltestelleZeitart_FS_Nach";"dbo";"FPLinieHaltestelleZeitart";"IDFPLinieHaltestelleZeitart"
|
|
||||||
"FK_Rufbus_FPLinieHaltestelleZeitartVon";"dbo";"Rufbus";"FPLinieHaltestelleZeitart_FS_Von";"dbo";"FPLinieHaltestelleZeitart";"IDFPLinieHaltestelleZeitart"
|
|
||||||
"FK_Rufbus_Haltepunkt_Nach";"dbo";"Rufbus";"IDHaltepunkt_FS_Nach";"dbo";"Haltepunkt";"IDHaltepunkt"
|
|
||||||
"FK_Rufbus_Haltepunkt_Von";"dbo";"Rufbus";"IDHaltepunkt_FS_Von";"dbo";"Haltepunkt";"IDHaltepunkt"
|
|
||||||
"FK_Rufbus_Haltestelle_Nach";"dbo";"Rufbus";"IDHaltestelle_FS_Nach";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_Rufbus_Haltestelle_Von";"dbo";"Rufbus";"IDHaltestelle_FS_Von";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_Rufbus_Linie";"dbo";"Rufbus";"IDFPLinie_FS";"dbo";"FPLinie";"IDFPLinie"
|
|
||||||
"FK_SaisonZeit_SaisonBezeichnung";"dbo";"SaisonZeit";"IDSaisonBezeichnung_FS";"dbo";"SaisonBezeichnung";"IDSaisonBezeichnung"
|
|
||||||
"FK_SchiffsDeck_Schiff";"dbo";"SchiffsDeck";"IDSchiff_FS";"dbo";"Schiff";"IDSchiff"
|
|
||||||
"FK_SchiffsKabine_SchiffsDeck";"dbo";"SchiffsKabine";"IDSchiffsDeck_FS";"dbo";"SchiffsDeck";"IDSchiffsDeck"
|
|
||||||
"FK_Schule_Schulamt";"dbo";"Schule";"IDSchulamt_FS";"dbo";"Schulamt";"IDSchulamt"
|
|
||||||
"FK_Adresse_Selektion";"dbo";"Selektionen";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Selektionsstamm_Selektion";"dbo";"Selektionen";"IDSelektionsstamm_FS";"dbo";"Selektionsstamm";"IDSelektionsstamm"
|
|
||||||
"FK_Selektionsgruppenstamm_Selektionsgruppe";"dbo";"Selektionsgruppe";"IDSelektionsgruppenstamm_FS";"dbo";"Selektionsgruppenstamm";"IDSelektionsgruppenstamm"
|
|
||||||
"FK_Selektionsstamm_Selektionsgruppe";"dbo";"Selektionsgruppe";"IDSelektionsstamm_FS";"dbo";"Selektionsstamm";"IDSelektionsstamm"
|
|
||||||
"FK_Sitzplan_Sitzplanobjekt";"dbo";"Sitzplanobjekt";"IDSitzplan_FS";"dbo";"Sitzplan";"IDSitzplan"
|
|
||||||
"FK_Sperrung_Adresse";"dbo";"Sperrung";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Sperrung_ArtSperrartFahrer";"dbo";"Sperrung";"IDArtSperrartFahrer_FS";"dbo";"ArtSperrartFahrer";"IDArtSperrartFahrer"
|
|
||||||
"FK_Sperrung_ArtSperrartFahrzeug";"dbo";"Sperrung";"IDArtSperrartFahrzeug_FS";"dbo";"ArtSperrartFahrzeug";"IDArtSperrartFahrzeug"
|
|
||||||
"FK_Sperrung_ArtSperrKategorie";"dbo";"Sperrung";"IDArtSperrKategorie_FS";"dbo";"ArtSperrKategorie";"IDArtSperrKategorie"
|
|
||||||
"FK_Sperrung_Fahrzeug";"dbo";"Sperrung";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_SperrungSperrStatus_ArtSperrStatus";"dbo";"SperrungSperrStatus";"IDArtSperrStatus_FS";"dbo";"ArtSperrStatus";"IDArtSperrStatus"
|
|
||||||
"FK_SperrungSperrStatus_Sperrung";"dbo";"SperrungSperrStatus";"IDSperrung_FS";"dbo";"Sperrung";"IDSperrung"
|
|
||||||
"FK_Spesen_Adresse";"dbo";"Spesen";"IDAdresse_FS_Personal";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Spesen_ArtSpesen";"dbo";"Spesen";"IDArtSpesen_FS";"dbo";"ArtSpesen";"IDArtSpesen"
|
|
||||||
"FK_SpracheMaskeModul_Programmmodul";"dbo";"SpracheMaskeModul";"IDProgrammmodul_FS";"dbo";"Programmmodul";"IDProgrammmodul"
|
|
||||||
"FK_SpracheMaskeText_SpracheMaske";"dbo";"SpracheMaskeText";"IDSpracheMaske_FS";"dbo";"SpracheMaske";"IDSpracheMaske"
|
|
||||||
"FK_SteueraufteilungDetail_Land";"dbo";"SteueraufteilungDetail";"IDLand_FS";"dbo";"Land";"IDLand"
|
|
||||||
"FK_SteueraufteilungDetail_SteueraufteilungArt";"dbo";"SteueraufteilungDetail";"IDSteueraufteilungArt_FS";"dbo";"SteueraufteilungArt";"IDSteueraufteilungArt"
|
|
||||||
"FK_SteueraufteilungDetail_SteueraufteilungUnterart";"dbo";"SteueraufteilungDetail";"IDSteueraufteilungUnterart_FS";"dbo";"SteueraufteilungUnterart";"IDSteueraufteilungUnterart"
|
|
||||||
"FK_SteueraufteilungDetailArtVerpflegung_ArtVerpflegung";"dbo";"SteueraufteilungDetailArtVerpflegung";"IDArtVerpflegung_FS";"dbo";"ArtVerpflegung";"IDArtVerpflegung"
|
|
||||||
"FK_SteueraufteilungDetailArtVerpflegung_SteueraufteilungDetail";"dbo";"SteueraufteilungDetailArtVerpflegung";"IDSteueraufteilungDetail_FS";"dbo";"SteueraufteilungDetail";"IDSteueraufteilungDetail"
|
|
||||||
"FK_SteueraufteilungUnterart_SteueraufteilungArt";"dbo";"SteueraufteilungUnterart";"IDSteueraufteilungArt_FS";"dbo";"SteueraufteilungArt";"IDSteueraufteilungArt"
|
|
||||||
"FK_Tachostand_Fahrzeug";"dbo";"Tachostand";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_Tachostand_TachostandImportEinstellung";"dbo";"Tachostand";"IDTachostandImportEinstellung_FS";"dbo";"TachostandImportEinstellung";"IDTachostandImportEinstellung"
|
|
||||||
"FK_Tankerfassung_ArtBetankung";"dbo";"Tankerfassung";"IDArtBetankung_FS";"dbo";"ArtBetankung";"IDArtBetankung"
|
|
||||||
"FK_Tankerfassung_ArtKarftstoffart";"dbo";"Tankerfassung";"IDArtKraftstoffart_FS";"dbo";"ArtKraftstoffart";"IDArtKraftstoffart"
|
|
||||||
"FK_Tankerfassung_Fahrzeug";"dbo";"Tankerfassung";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_Tankerfassung_TankimportEinstellung";"dbo";"Tankerfassung";"IDTankimportEinstellung_FS";"dbo";"TankimportEinstellung";"IDTankimportEinstellung"
|
|
||||||
"FK_TankimportEinstellung_ArtBetankung";"dbo";"TankimportEinstellung";"IDArtBetankung_FS";"dbo";"ArtBetankung";"IDArtBetankung"
|
|
||||||
"FK_TankimportEinstellungArtBetankung_ArtBetankung";"dbo";"TankimportEinstellungArtBetankung";"IDArtBetankung_FS";"dbo";"ArtBetankung";"IDArtBetankung"
|
|
||||||
"FK_TankimportEinstellungArtBetankung_TankimportEinstellung";"dbo";"TankimportEinstellungArtBetankung";"IDTankimportEinstellung_FS";"dbo";"TankimportEinstellung";"IDTankimportEinstellung"
|
|
||||||
"FK_TankimportEinstellungKraftstoff_ArtKraftstoffart";"dbo";"TankimportEinstellungKraftstoff";"IDArtKraftstoffart_FS";"dbo";"ArtKraftstoffart";"IDArtKraftstoffart"
|
|
||||||
"FK_TankimportEinstellungKraftstoff_TankimportEinstellung";"dbo";"TankimportEinstellungKraftstoff";"IDTankimportEinstellung_FS";"dbo";"TankimportEinstellung";"IDTankimportEinstellung"
|
|
||||||
"FK_TaxiPartner_Adresse";"dbo";"TaxiPartner";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_TaxiPartnerKosten_TaxiPartner";"dbo";"TaxiPartnerKosten";"IDTaxiPartner_FS";"dbo";"TaxiPartner";"IDTaxiPartner"
|
|
||||||
"FK_TaxiPartnerMapGuide_TaxiPartner";"dbo";"TaxiPartnerMapGuide";"IDTaxiPartner_FS";"dbo";"TaxiPartner";"IDTaxiPartner"
|
|
||||||
"FK_TaxiPartnerZustieg_Adresse";"dbo";"TaxiPartnerZustieg";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_TaxiPartnerZustieg_Zustieg";"dbo";"TaxiPartnerZustieg";"IDZustieg_FS";"dbo";"Zustieg";"IDZustieg"
|
|
||||||
"FK_TerminalFreigabe_ArtTerminalFreigabe";"dbo";"TerminalFreigabe";"IDArtTerminalFreigabe_FS";"dbo";"ArtTerminalFreigabe";"IDArtTerminalFreigabe"
|
|
||||||
"FK_TerminalFreigabeArtBetriebshof_ArtBetriebshof";"dbo";"TerminalFreigabeArtBetriebshof";"IDArtBetriebshof_FS";"dbo";"ArtBetriebshof";"IDArtBetriebshof"
|
|
||||||
"FK_TerminalProtokoll_Adresse";"dbo";"TerminalProtokoll";"IDAdresse_FS_Sachbearbeiter";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_TerminArtSachbearbeiter_Adresse";"dbo";"TerminArtSachbearbeiter";"IDAdresse_FS_Personal";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_TerminArtSachbearbeiter_TerminArt";"dbo";"TerminArtSachbearbeiter";"IDTerminArt_FS";"dbo";"TerminArt";"IDTerminArt"
|
|
||||||
"FK_TerminKetteAbfolge_TerminKette";"dbo";"TerminKetteAbfolge";"IDTerminArt_FS";"dbo";"TerminArt";"IDTerminArt"
|
|
||||||
"FK_TerminKetteAbfolge_TerminKette1";"dbo";"TerminKetteAbfolge";"IDTerminKette_FS";"dbo";"TerminKette";"IDTerminKette"
|
|
||||||
"FK_TerminSachbearbeiter_Termin";"dbo";"TerminSachbearbeiter";"IDTermin_FS";"dbo";"Termin";"IDTermin"
|
|
||||||
"FK_Tour_ArtFahrplanVersion";"dbo";"Tour";"IDArtFahrplanVersion_FS";"dbo";"ArtFahrplanVersion";"IDArtFahrplanVersion"
|
|
||||||
"FK_Tour_ArtStatistikGruppe";"dbo";"Tour";"IDArtStatistikgruppe_FS_Haupt";"dbo";"ArtStatistikGruppe";"IDArtStatistikGruppe"
|
|
||||||
"FK_Tour_FPLinieFahrt";"dbo";"Tour";"IDFPLinieFahrt_FS";"dbo";"FPLinieFahrt";"IDFPLinieFahrt"
|
|
||||||
"FK_Tour_Haltepunkt_Anfang";"dbo";"Tour";"IDHaltepunkt_FS_Anfang";"dbo";"Haltepunkt";"IDHaltepunkt"
|
|
||||||
"FK_Tour_Haltepunkt_Ende";"dbo";"Tour";"IDHaltepunkt_FS_Ende";"dbo";"Haltepunkt";"IDHaltepunkt"
|
|
||||||
"FK_Tour_Haltestelle_Anfang";"dbo";"Tour";"IDHaltestelle_FS_Anfang";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_Tour_Haltestelle_Ende";"dbo";"Tour";"IDHaltestelle_FS_Ende";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_Tour_Ort_Anfang";"dbo";"Tour";"IDOrt_FS_Anfang";"dbo";"Ort";"IDOrt"
|
|
||||||
"FK_Tour_Ort_Ende";"dbo";"Tour";"IDOrt_FS_Ende";"dbo";"Ort";"IDOrt"
|
|
||||||
"FK_TourFahrzeugtyp_ArtFahrzeugTypen";"dbo";"TourFahrzeugtyp";"IDArtFahrzeugTypen_FS";"dbo";"ArtFahrzeugTypen";"IDArtFahrzeugTypen"
|
|
||||||
"FK_TourFahrzeugtyp_Tour";"dbo";"TourFahrzeugtyp";"IDTour_FS";"dbo";"Tour";"IDTour"
|
|
||||||
"FK_TourFerienFeier_Tour";"dbo";"TourFerienFeier";"IDTour_FS";"dbo";"Tour";"IDTour"
|
|
||||||
"FK_TourHaltestelleHaltepunkt_Haltepunkt";"dbo";"TourHaltestelleHaltepunkt";"IDHaltepunkt_FS";"dbo";"Haltepunkt";"IDHaltepunkt"
|
|
||||||
"FK_TourHaltestelleHaltepunkt_Haltestelle";"dbo";"TourHaltestelleHaltepunkt";"IDHaltestelle_FS";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_TourHaltestelleHaltepunkt_Tour";"dbo";"TourHaltestelleHaltepunkt";"IDTour_FS";"dbo";"Tour";"IDTour"
|
|
||||||
"FK_TourKreisGebiet_ArtKreisGebiet";"dbo";"TourKreisGebiet";"IDArtKreisGebiet_FS";"dbo";"ArtKreisGebiet";"IDArtKreisGebiet"
|
|
||||||
"FK_TourKreisGebiet_Tour";"dbo";"TourKreisGebiet";"IDTour_FS";"dbo";"Tour";"IDTour"
|
|
||||||
"FK_TourLaufzeit_TourLaufzeit";"dbo";"TourLaufzeit";"IDTour_FS";"dbo";"Tour";"IDTour"
|
|
||||||
"FK_TourPlanung_Adresse";"dbo";"TourPlanung";"IDAdresse_FS_Fahrer";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_TourPlanung_Adresse2";"dbo";"TourPlanung";"LoeschenManuellIDAdresse_FS_SB";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_TourPlanung_ArtFahrplanVersion";"dbo";"TourPlanung";"IDArtFahrplanVersion_FS";"dbo";"ArtFahrplanVersion";"IDArtFahrplanVersion"
|
|
||||||
"FK_TourPlanung_ArtStatistikGruppe";"dbo";"TourPlanung";"IDArtStatistikgruppe_FS_Haupt";"dbo";"ArtStatistikGruppe";"IDArtStatistikGruppe"
|
|
||||||
"FK_TourPlanung_BlockPlanung";"dbo";"TourPlanung";"IDBlockPlanung_FS";"dbo";"BlockPlanung";"IDBlockPlanung"
|
|
||||||
"FK_TourPlanung_Fahrzeug";"dbo";"TourPlanung";"IDFahrzeug_FS";"dbo";"Fahrzeug";"IDFahrzeug"
|
|
||||||
"FK_Tourplanung_Haltepunkt_Anfang";"dbo";"TourPlanung";"IDHaltepunkt_FS_Anfang";"dbo";"Haltepunkt";"IDHaltepunkt"
|
|
||||||
"FK_Tourplanung_Haltepunkt_Ende";"dbo";"TourPlanung";"IDHaltepunkt_FS_Ende";"dbo";"Haltepunkt";"IDHaltepunkt"
|
|
||||||
"FK_Tourplanung_Haltestelle_Anfang";"dbo";"TourPlanung";"IDHaltestelle_FS_Anfang";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_Tourplanung_Haltestelle_Ende";"dbo";"TourPlanung";"IDHaltestelle_FS_Ende";"dbo";"Haltestelle";"IDHaltestelle"
|
|
||||||
"FK_TourPlanung_Ort_Anfang";"dbo";"TourPlanung";"IDOrt_FS_Anfang";"dbo";"Ort";"IDOrt"
|
|
||||||
"FK_TourPlanung_Ort_Ende";"dbo";"TourPlanung";"IDOrt_FS_Ende";"dbo";"Ort";"IDOrt"
|
|
||||||
"FK_TourPlanung_Tour";"dbo";"TourPlanung";"IDTour_FS_Basis";"dbo";"Tour";"IDTour"
|
|
||||||
"FK_TourPlanungFahrzeugtyp_ArtFahrzeugTypen";"dbo";"TourPlanungFahrzeugtyp";"IDArtFahrzeugTypen_FS";"dbo";"ArtFahrzeugTypen";"IDArtFahrzeugTypen"
|
|
||||||
"FK_TourPlanungFahrzeugtyp_TourPlanung";"dbo";"TourPlanungFahrzeugtyp";"IDTourPlanung_FS";"dbo";"TourPlanung";"IDTourPlanung"
|
|
||||||
"FK_TourPlanungKreisGebiet_ArtKreisGebiet";"dbo";"TourPlanungKreisGebiet";"IDArtKreisGebiet_FS";"dbo";"ArtKreisGebiet";"IDArtKreisGebiet"
|
|
||||||
"FK_TourPlanungKreisGebiet_TourPlanung";"dbo";"TourPlanungKreisGebiet";"IDTourPlanung_FS";"dbo";"TourPlanung";"IDTourPlanung"
|
|
||||||
"FK_TourPlanungStatistik_ArtStatistikGruppe";"dbo";"TourPlanungStatistik";"IDArtStatistikGruppe_FS";"dbo";"ArtStatistikGruppe";"IDArtStatistikGruppe"
|
|
||||||
"FK_TourPlanungStatistik_TourPlanung";"dbo";"TourPlanungStatistik";"IDTourPlanung_FS";"dbo";"TourPlanung";"IDTourPlanung"
|
|
||||||
"FK_TourPlanungZusatz_TourPlanung";"dbo";"TourPlanungZusatz";"IDTourPlanung_FS";"dbo";"TourPlanung";"IDTourPlanung"
|
|
||||||
"FK_TourStatistik_ArtStatistikGruppe";"dbo";"TourStatistik";"IDArtStatistikGruppe_FS";"dbo";"ArtStatistikGruppe";"IDArtStatistikGruppe"
|
|
||||||
"FK_TourStatistik_Tour";"dbo";"TourStatistik";"IDTour_FS";"dbo";"Tour";"IDTour"
|
|
||||||
"FK_TourWochentyp_ArtWochentyp";"dbo";"TourWochentyp";"IDArtWochentyp_FS";"dbo";"ArtWochentyp";"IDArtWochentyp"
|
|
||||||
"FK_TourWochentyp_Tour";"dbo";"TourWochentyp";"IDTour_FS";"dbo";"Tour";"IDTour"
|
|
||||||
"FK_TourZeitraum_ArtLaufzeitschema";"dbo";"TourZeitraum";"IDArtLaufzeitschema_FS";"dbo";"ArtLaufzeitschema";"IDArtLaufzeitschema"
|
|
||||||
"FK_TourZeitraum_Tour";"dbo";"TourZeitraum";"IDTour_FS";"dbo";"Tour";"IDTour"
|
|
||||||
"FK_TourZusatz_Tour";"dbo";"TourZusatz";"IDTour_FS";"dbo";"Tour";"IDTour"
|
|
||||||
"FK_UserStartmenue_Adresse";"dbo";"UserStartmenue";"IDAdresse_FS";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_UserStartmenue_Programmmodul";"dbo";"UserStartmenue";"IDProgrammmodul_FS";"dbo";"Programmmodul";"IDProgrammmodul"
|
|
||||||
"FK_VorplanungRasterAblauf_VorplanungRaster";"dbo";"VorplanungRasterAblauf";"IDVorplanungRaster_FS";"dbo";"VorplanungRaster";"IDVorplanungRaster"
|
|
||||||
"FK_VorplanungRasterAblaufAbwPartner_Adresse";"dbo";"VorplanungRasterAblaufAbwPartner";"IDAdresse_FS_Partner";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_VorplanungRasterAblaufAbwPartner_VorplanungRasterAblauf";"dbo";"VorplanungRasterAblaufAbwPartner";"IDVorplanungRasterAblauf_FS";"dbo";"VorplanungRasterAblauf";"IDVorplanungRasterAblauf"
|
|
||||||
"FK_VorplanungRasterAblaufAbwPartner_VorplanungRasterTermin";"dbo";"VorplanungRasterAblaufAbwPartner";"IDVorplanungRasterTermin_FS";"dbo";"VorplanungRasterTermin";"IDVorplanungRasterTermin"
|
|
||||||
"FK_VorplanungRasterAblaufStatus_Adresse";"dbo";"VorplanungRasterAblaufStatus";"IDAdresse_FS_Sachbearbeiter";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_VorplanungRasterAblaufStatus_ArtReiseVplPartnerStatus";"dbo";"VorplanungRasterAblaufStatus";"IDArtReiseVplPartnerStatus_FS";"dbo";"ArtReiseVplPartnerStatus";"IDArtReiseVplPartnerStatus"
|
|
||||||
"FK_VorplanungRasterAblaufStatus_VorplanungRasterAblauf";"dbo";"VorplanungRasterAblaufStatus";"IDVorplanungRasterAblauf_FS";"dbo";"VorplanungRasterAblauf";"IDVorplanungRasterAblauf"
|
|
||||||
"FK_VorplanungRasterAblaufStatus_VorplanungRasterTermin";"dbo";"VorplanungRasterAblaufStatus";"IDVorplanungRasterTermin_FS";"dbo";"VorplanungRasterTermin";"IDVorplanungRasterTermin"
|
|
||||||
"FK_VorplanungRasterAblaufZusatz_VorplanungRasterAblauf";"dbo";"VorplanungRasterAblaufZusatz";"IDVorplanungRasterAblauf_FS";"dbo";"VorplanungRasterAblauf";"IDVorplanungRasterAblauf"
|
|
||||||
"FK_VorplanungRasterBemerkung_VorplanungRaster";"dbo";"VorplanungRasterBemerkung";"IDVorplanungRaster_FS";"dbo";"VorplanungRaster";"IDVorplanungRaster"
|
|
||||||
"FK_VorplanungRasterDokument_Adresse";"dbo";"VorplanungRasterDokument";"IDAdresse_FS_Sachbearbeiter";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_VorplanungRasterDokument_ArtDokument";"dbo";"VorplanungRasterDokument";"IDArtDokument_FS";"dbo";"ArtDokument";"IDArtDokument"
|
|
||||||
"FK_VorplanungRasterDokument_VorplanungRaster";"dbo";"VorplanungRasterDokument";"IDVorplanungRaster_FS";"dbo";"VorplanungRaster";"IDVorplanungRaster"
|
|
||||||
"FK_VorplanungRasterKalkulationÜbernahme_VorplanungRasterKalkulation";"dbo";"VorplanungRasterKalkulationÜbernahme";"IDVorplanungRasterKalkulation_FS";"dbo";"VorplanungRasterKalkulation";"IDVorplanungRasterKalkulation"
|
|
||||||
"FK_VorplanungRasterTermin_VorplanungRaster";"dbo";"VorplanungRasterTermin";"IDVorplanungRaster_FS";"dbo";"VorplanungRaster";"IDVorplanungRaster"
|
|
||||||
"FK_WaehrungsKurse_ArtWaehrung";"dbo";"WaehrungsKurse";"IDArtWaehrung_FS";"dbo";"ArtWaehrung";"IDArtWaehrung"
|
|
||||||
"FK_WerbeplanungPunkte_ArtWerbeplanungReiseart";"dbo";"WerbeplanungPunkte";"IDArtWerbeplanungReiseart_FS";"dbo";"ArtWerbeplanungReiseart";"IDArtWerbeplanungReiseart"
|
|
||||||
"FK_WWKategorieEintrag_WWSuchKategorie";"dbo";"WWSuchKategorieEintrag";"IDWWSuchKategorie_FS";"dbo";"WWSuchKategorie";"IDWWSuchKategorie"
|
|
||||||
"FK_Zahlung_Adresse";"dbo";"Zahlung";"IDAdresse_FS_Sachbearbeiter";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Zahlung_ArtWaehrung";"dbo";"Zahlung";"IDWaehrung_FS";"dbo";"ArtWaehrung";"IDArtWaehrung"
|
|
||||||
"FK_Zahlung_ArtZahlungsart";"dbo";"Zahlung";"IDArtZahlungart_FS";"dbo";"ArtZahlungsart";"IDArtZahlungsart"
|
|
||||||
"FK_Zahlung_ZahlungCode";"dbo";"Zahlung";"IDKontenplan_FS";"dbo";"Kontenplan";"IDKontenplan"
|
|
||||||
"FK_Zahlung_ZahlungStamm";"dbo";"Zahlung";"IDZahlungStamm_FS";"dbo";"ZahlungStamm";"IDZahlungStamm"
|
|
||||||
"FK_ZahlungDokument_Adresse";"dbo";"ZahlungDokument";"IDAdresse_FS_Sachbearbeiter";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_ZahlungDokument_ArtDokument";"dbo";"ZahlungDokument";"IDArtDokument_FS";"dbo";"ArtDokument";"IDArtDokument"
|
|
||||||
"FK_ZahlungDokument_Zahlung";"dbo";"ZahlungDokument";"IDZahlung_FS";"dbo";"Zahlung";"IDZahlung"
|
|
||||||
"FK_ZahlungStamm_Adresse";"dbo";"ZahlungStamm";"IDAdresse_FS_Mandant";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_ZahlungStamm_ArtWaehrung";"dbo";"ZahlungStamm";"IDWaehrung_FS";"dbo";"ArtWaehrung";"IDArtWaehrung"
|
|
||||||
"FK_ZahlungStamm_Nummernkreis";"dbo";"ZahlungStamm";"IDNummernkreis_FS";"dbo";"Nummernkreis";"IDNummernkreis"
|
|
||||||
"FK_ZiellandRoute_Land";"dbo";"ZiellandRoute";"IDLand_FS_Route";"dbo";"Land";"IDLand"
|
|
||||||
"FK_ZiellandRoute_Land1";"dbo";"ZiellandRoute";"IDLand_FS_Zielland";"dbo";"Land";"IDLand"
|
|
||||||
"FK_Zusatzinformation_Adresse";"dbo";"Zusatzinformation";"IDAdresse_FS_Partner";"dbo";"Adresse";"IDAdresse"
|
|
||||||
"FK_Zusatzinformation_ArtZusatzinfoStamm";"dbo";"Zusatzinformation";"IDArtZusatzinfoStamm_FS";"dbo";"ArtZusatzinfoStamm";"IDArtZusatzinfoStamm"
|
|
||||||
"FK_Zusatzinformation_Reise";"dbo";"Zusatzinformation";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ZusatzZimmer_ArtBuchungsStatus";"dbo";"ZusatzZimmer";"IDArtBuchungsstatus_FS";"dbo";"ArtBuchungsStatus";"IDArtBuchungsStatus"
|
|
||||||
"FK_ZusatzZimmer_Reise";"dbo";"ZusatzZimmer";"IDReise_FS";"dbo";"Reise";"IDReise"
|
|
||||||
"FK_ZustiegHinweis_Zustieg";"dbo";"ZustiegHinweis";"IDZustieg_FS";"dbo";"Zustieg";"IDZustieg"
|
|
||||||
"FK_Zustiegkoordinate_Zustieg";"dbo";"Zustiegkoordinate";"IDZustieg_FS";"dbo";"Zustieg";"IDZustieg"
|
|
||||||
"FK_ZustiegKoordinate_ZustiegKoordinatenArt";"dbo";"Zustiegkoordinate";"IDArtKoordinaten_FS";"dbo";"ArtZustiegKoordinaten";"IDArtZustiegKoordinaten"
|
|
||||||
"FK_ZustiegPLZ_Zustieg1";"dbo";"ZustiegPLZ";"IDZustieg_FS";"dbo";"Zustieg";"IDZustieg"
|
|
||||||
"FK_ZustiegPreis_ArtZustiegPreis1";"dbo";"ZustiegPreis";"IDArtZustiegPreis_FS";"dbo";"ArtZustiegPreis";"IDArtZustiegPreis"
|
|
||||||
"FK_ZustiegPreis_Zustieg1";"dbo";"ZustiegPreis";"IDZustieg_FS";"dbo";"Zustieg";"IDZustieg"
|
|
||||||
"FK_ZustiegRoute_ArtZustiegRoutenbeschreibung";"dbo";"ZustiegRoute";"IDArtZustiegRoutenbeschreibung_FS";"dbo";"ArtZustiegRoutenbeschreibung";"IDArtZustiegRoutenbeschreibung"
|
|
||||||
"FK_ZustiegRoute_Zustieg";"dbo";"ZustiegRoute";"IDZustieg_FS";"dbo";"Zustieg";"IDZustieg"
|
|
||||||
"FK_ZustiegsPlanungBesonders_ZustiegsPlanungPerson";"dbo";"ZustiegsPlanungBesonders";"IDZustiegsPlanungPerson_FS";"dbo";"ZustiegsPlanungPerson";"IDZustiegsPlanungPerson"
|
|
||||||
"FK_ZustiegsPlanungBus_ZustiegsPlanung";"dbo";"ZustiegsPlanungBus";"IDZustiegsPlanung_FS";"dbo";"ZustiegsPlanung";"IDZustiegsPlanung"
|
|
||||||
"FK_ZustiegsPlanungBusStelle_ZustiegsPlanungBus";"dbo";"ZustiegsPlanungBusStelle";"IDZustiegsPlanungBus_FS";"dbo";"ZustiegsPlanungBus";"IDZustiegsPlanungBus"
|
|
||||||
"FK_ZustiegsPlanungLeistung_ZustiegsPlanung";"dbo";"ZustiegsPlanungLeistung";"IDZustiegsPlanung_FS";"dbo";"ZustiegsPlanung";"IDZustiegsPlanung"
|
|
||||||
"FK_ZustiegsPlanungPerson_BuchungTN";"dbo";"ZustiegsPlanungPerson";"IDBuchungTN_FS";"dbo";"BuchungTN";"IDBuchungTN"
|
|
||||||
"FK_ZustiegsPlanungPerson_Leistung";"dbo";"ZustiegsPlanungPerson";"IDLeistung_FS";"dbo";"Leistung";"IDLeistung"
|
|
||||||
"FK_ZustiegsPlanungPerson_ZustiegsPlanung";"dbo";"ZustiegsPlanungPerson";"IDZustiegsPlanung_FS";"dbo";"ZustiegsPlanung";"IDZustiegsPlanung"
|
|
||||||
"FK_ZustiegsPlanungPersonStelle_ZustiegsPlanungBusStelle";"dbo";"ZustiegsPlanungPersonStelle";"IDZustiegsPlanungBusStelle_FS";"dbo";"ZustiegsPlanungBusStelle";"IDZustiegsPlanungBusStelle"
|
|
||||||
"FK_ZustiegsPlanungPersonStelle_ZustiegsPlanungPerson";"dbo";"ZustiegsPlanungPersonStelle";"IDZustiegsPlanungPerson_FS";"dbo";"ZustiegsPlanungPerson";"IDZustiegsPlanungPerson"
|
|
||||||
"FK_ZustiegZeitplan_ArtZustiegZeitplan";"dbo";"ZustiegZeitplan";"IDArtZustiegZeitplan_FS";"dbo";"ArtZustiegZeitplan";"IDArtZustiegZeitplan"
|
|
||||||
|
@@ -1,94 +0,0 @@
|
|||||||
# Boarding Plan — Table Relations (BusPro schema)
|
|
||||||
|
|
||||||
> **Authority: [`docs/buspro-database-reference.md`](../buspro-database-reference.md) §8.4.**
|
|
||||||
> Retained for the detailed ER diagram of the `ZustiegsPlanung*` subsystem.
|
|
||||||
|
|
||||||
Read-only reference for building the boarding-plan API endpoint (look up a plan
|
|
||||||
by `IDZustiegsPlanung`, returning names, addresses, and times).
|
|
||||||
|
|
||||||
The query walks:
|
|
||||||
**`ZustiegsPlanung` → persons → (name via `BuchungTN`/`AdressePerson`) →
|
|
||||||
`PersonStelle` junction → `BusStelle` (time) → `Zustieg` (address) → `Bus`.**
|
|
||||||
`ZustiegsPlanungPersonStelle` is the pivot that ties a passenger to a timed stop.
|
|
||||||
|
|
||||||
## ER diagram
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
erDiagram
|
|
||||||
ZustiegsPlanung ||--o{ ZustiegsPlanungBus : "IDZustiegsPlanung_FS"
|
|
||||||
ZustiegsPlanung ||--o{ ZustiegsPlanungPerson : "IDZustiegsPlanung_FS"
|
|
||||||
ZustiegsPlanungBus ||--o{ ZustiegsPlanungBusStelle : "IDZustiegsPlanungBus_FS"
|
|
||||||
ZustiegsPlanungPerson ||--o{ ZustiegsPlanungPersonStelle : "IDZustiegsPlanungPerson_FS"
|
|
||||||
ZustiegsPlanungBusStelle ||--o{ ZustiegsPlanungPersonStelle : "IDZustiegsPlanungBusStelle_FS"
|
|
||||||
|
|
||||||
ZustiegsPlanungPerson }o..|| BuchungTN : "IDBuchungTN_FS"
|
|
||||||
BuchungTN }o..|| AdressePerson : "IDAdressePerson_FS (name)"
|
|
||||||
ZustiegsPlanungBusStelle }o..o| Zustieg : "IDZustieg_FS (soft, address)"
|
|
||||||
|
|
||||||
ZustiegsPlanung {
|
|
||||||
bigint IDZustiegsPlanung PK
|
|
||||||
datetime Datum
|
|
||||||
}
|
|
||||||
ZustiegsPlanungBus {
|
|
||||||
bigint IDZustiegsPlanungBus PK
|
|
||||||
string ReiseleiterName
|
|
||||||
}
|
|
||||||
ZustiegsPlanungBusStelle {
|
|
||||||
bigint IDZustiegsPlanungBusStelle PK
|
|
||||||
datetime Zeit "TIME"
|
|
||||||
smallint Sortierung
|
|
||||||
}
|
|
||||||
ZustiegsPlanungPerson {
|
|
||||||
bigint IDZustiegsPlanungPerson PK
|
|
||||||
}
|
|
||||||
ZustiegsPlanungPersonStelle {
|
|
||||||
bigint IDZustiegsPlanungPersonStelle PK
|
|
||||||
string Art "Einstieg/Ausstieg"
|
|
||||||
}
|
|
||||||
AdressePerson {
|
|
||||||
string Name
|
|
||||||
string Vorname
|
|
||||||
}
|
|
||||||
Zustieg {
|
|
||||||
string Strasse
|
|
||||||
string Plz
|
|
||||||
string Ort
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## ASCII fallback
|
|
||||||
|
|
||||||
```
|
|
||||||
ZustiegsPlanung (the planning id)
|
|
||||||
/ \
|
|
||||||
IDZustiegsPlanung_FS IDZustiegsPlanung_FS
|
|
||||||
/ \
|
|
||||||
ZustiegsPlanungBus ZustiegsPlanungPerson ──IDBuchungTN_FS──▶ BuchungTN
|
|
||||||
│ (Reiseleiter) │ │
|
|
||||||
IDZustiegsPlanungBus_FS IDZustiegsPlanungPerson_FS IDAdressePerson_FS
|
|
||||||
│ │ ▼
|
|
||||||
▼ ▼ AdressePerson
|
|
||||||
ZustiegsPlanungBusStelle ◀──┐ ZustiegsPlanungPersonStelle (NAME)
|
|
||||||
• Zeit (TIME) │ • Art (boarding/alighting)
|
|
||||||
• IDZustieg_FS ··soft··▶ Zustieg (ADDRESS)
|
|
||||||
└──IDZustiegsPlanungBusStelle_FS───┘ (junction: passenger ↔ stop)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Legend:** `||--o{` / solid = enforced FK constraint; `}o..` / `··soft··` =
|
|
||||||
convention-only `_FS` reference (no DB constraint — join defensively, expect
|
|
||||||
`NULL`/`0`/orphaned values).
|
|
||||||
|
|
||||||
## Notes for the query
|
|
||||||
|
|
||||||
- **Name** is not stored in the plan; join out live via
|
|
||||||
`ZustiegsPlanungPerson.IDBuchungTN_FS → BuchungTN.IDAdressePerson_FS → AdressePerson`.
|
|
||||||
- **Time** lives on `ZustiegsPlanungBusStelle.Zeit` (per stop), not on the plan
|
|
||||||
header (`ZustiegsPlanung.Datum` is the plan date).
|
|
||||||
- **Address** comes from `Zustieg` (the boarding point). Prefer reaching it via
|
|
||||||
`ZustiegsPlanungBusStelle.IDZustieg_FS` so it never depends on
|
|
||||||
`ZustiegsPlanungPerson.IDZustieg_FS` being populated.
|
|
||||||
- A passenger can have multiple `ZustiegsPlanungPersonStelle` rows (boarding +
|
|
||||||
alighting), distinguished by `Art`. Filter on `Art` for boarding-only.
|
|
||||||
- To verify against real data: the actual `Art` codes (`nvarchar(6)`), and
|
|
||||||
whether `BusStelle.IDZustieg_FS` always matches the passenger's
|
|
||||||
`Person.IDZustieg_FS` on the boarding leg.
|
|
||||||
@@ -1,469 +0,0 @@
|
|||||||
# `SelektionCRM` — how the XML is assembled, and how to debug a missing selection
|
|
||||||
|
|
||||||
> **Authority: [`docs/buspro-database-reference.md`](../buspro-database-reference.md).**
|
|
||||||
> Schema semantics and the falsified-theory record live there and win on any conflict.
|
|
||||||
> This file is retained as the **full investigation record** for the missing-selection /
|
|
||||||
> `aenderbar` problem, including the queries that closed each theory.
|
|
||||||
|
|
||||||
Companion to `travel-queries.md` (static catalog) and `live-api-queries.md` (live
|
|
||||||
socket endpoints). This one covers the `KUNDENKONTO` / `art=SelektionCRM` response —
|
|
||||||
the document that drives the portal's entire authorization model.
|
|
||||||
|
|
||||||
Request side: `ApiClient::getCrmAttributes()` (`src/BusProNet/ApiClient.php`) sends
|
|
||||||
`satz typ="KUNDENKONTO"` + `art=SelektionCRM` + the customer's email/password.
|
|
||||||
Response side: `CrmAttributesResponseParser`
|
|
||||||
(`src/BusProNet/XmlParser/CrmAttributesResponseParser.php`) turns selection ids into
|
|
||||||
Symfony roles:
|
|
||||||
|
|
||||||
| `IDSelektionsstamm` | Role |
|
|
||||||
| --- | --- |
|
|
||||||
| `1070` | `ROLE_TEAMER` |
|
|
||||||
| `1292` | `ROLE_ADMIN` (+ `ROLE_HOUSE_MANAGER`, + hotel code `SSL`) |
|
|
||||||
| `1293` | `ROLE_MANAGER` |
|
|
||||||
| `1477` | `ROLE_GROUPS_MANAGER` |
|
|
||||||
| `1478` | `ROLE_GROUPS_ADMIN` |
|
|
||||||
| label `Hausleitung {CODE}` | `ROLE_HOUSE_MANAGER` + hotel code `{CODE}` |
|
|
||||||
| nothing matched | `ROLE_CUSTOMER` |
|
|
||||||
|
|
||||||
Only selections with `auswahl="True"` count. Every `Admin/*` controller is gated on
|
|
||||||
one of these roles.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## The four tables
|
|
||||||
|
|
||||||
From `BusPronet_20260601-1517.csv` (columns) and `BusPronet_20260601-1518.csv` (FKs):
|
|
||||||
|
|
||||||
| Table | Columns | Role |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `dbo.Selektionsstamm` | `IDSelektionsstamm`, `Selektion`, `Status` nvarchar(2) null, `System` tinyint | the selection master — that's *all four* columns |
|
|
||||||
| `dbo.Selektionsgruppenstamm` | `IDSelektionsgruppenstamm`, `Selektionsgruppe`, `Status`, `System`, `Ausgrenzung`, `Position` | the group master |
|
|
||||||
| `dbo.Selektionsgruppe` | `IDSelektionsgruppenstamm_FS`, `IDSelektionsstamm_FS`, `Position` | **membership** — which selection sits in which group, and in what order |
|
|
||||||
| `dbo.Selektionen` | `IDAdresse_FS`, `IDSelektionsstamm_FS` | **assignment** — which address holds which selection |
|
|
||||||
|
|
||||||
`<crmaktionen>` comes from a parallel pair: `dbo.CRMAktion` (master: `Code`,
|
|
||||||
`Bezeichnung`, `Aktiv`, `Status`, `AktivDatumVon/Bis`, …) and `dbo.CRMZuweisung`
|
|
||||||
(assignment via `IDAdresse_FS` + `IDCRMAktion_FS`).
|
|
||||||
|
|
||||||
### XML → column mapping
|
|
||||||
|
|
||||||
| XML | Source |
|
|
||||||
| --- | --- |
|
|
||||||
| `<idadresse>` | `Adresse.IDAdresse` (the authenticated account) |
|
|
||||||
| `<idperson>` | `AdressePerson.IDAdressePerson` |
|
|
||||||
| `<selektionsgruppe id>` / `bezeichnung` | `Selektionsgruppenstamm.IDSelektionsgruppenstamm` / `.Selektionsgruppe` |
|
|
||||||
| `<selektion id>` / `bezeichnung` | `Selektionsstamm.IDSelektionsstamm` / `.Selektion` |
|
|
||||||
| `selektion@auswahl` | `EXISTS (Selektionen WHERE IDAdresse_FS = <account> AND IDSelektionsstamm_FS = <id>)` — confirmed |
|
|
||||||
| `selektion@aenderbar` | **computed, not stored** — no such column exists anywhere (see below) |
|
|
||||||
| group order | `Selektionsgruppenstamm.Position`, `<ohne Gruppe>` first |
|
|
||||||
| selection order within a group | `Selektionsgruppe.Position` |
|
|
||||||
| `<crmaktion …>` | `CRMAktion` + `CRMZuweisung` (`auswahl` = assignment exists) |
|
|
||||||
|
|
||||||
### `aenderbar` is not a stored flag
|
|
||||||
|
|
||||||
A case-insensitive search for `aenderbar` / `auswahl` across **every column name in the
|
|
||||||
database** (1023 tables, `ABOBewegungsdaten` … `ZustiegsPlanungPersonStelle`, so the
|
|
||||||
export is complete and untruncated) returns no hit on any `Selektion*` table. The only
|
|
||||||
`*Aenderbar*` column in the entire schema is `AdresseAgenturZusatz.ZahlungsartAenderbar`,
|
|
||||||
which is unrelated. And the three core tables have no spare bit to hide it in:
|
|
||||||
`Selektionsstamm` has exactly four columns, `Selektionsgruppe` three, `Selektionen` two.
|
|
||||||
|
|
||||||
So `aenderbar` is **derived at response time**. Don't go looking for the column again —
|
|
||||||
this is what Query D and Query E are for.
|
|
||||||
|
|
||||||
The `<crmaktionen>` half of the document hints at the intended semantics: `CRMAktion`
|
|
||||||
*does* carry real flags (`Aktiv`, `Anzeige`, `AktivDatumVon`/`Bis`), and in the reference
|
|
||||||
sample the long-expired "Deal Der Woche KW40-2018" is `aenderbar="False"` while the
|
|
||||||
standing "NOMAIL" opt-out is `"True"`. `aenderbar` reads as *"the customer may still
|
|
||||||
toggle this"* — a validity/whitelist question, not a static per-row property.
|
|
||||||
|
|
||||||
Two orderings worth noting, because they rule out the obvious alternatives:
|
|
||||||
|
|
||||||
- Inside `TEAM` the ids run `… 1374, 1375, 1376, 1373, 1377 …` — neither id order nor
|
|
||||||
label order. That is `Selektionsgruppe.Position`.
|
|
||||||
- Within `Interessen` the labels *are* alphabetical, which is consistent with
|
|
||||||
`Position` having been maintained alphabetically there. Don't read it as a
|
|
||||||
different rule.
|
|
||||||
|
|
||||||
### The decisive observation: the catalog is group-driven
|
|
||||||
|
|
||||||
Every `Hausleitung *` row is emitted with `auswahl="False"` for an account that holds
|
|
||||||
none of them. So the endpoint does **not** list "the selections this address has" — it
|
|
||||||
lists **the whole published catalog** and then flags which entries the address holds.
|
|
||||||
|
|
||||||
`<selektionsgruppe id="0" bezeichnung="<ohne Gruppe>">` is the bucket for
|
|
||||||
selections with no (or a `0`) `Selektionsgruppe` membership row.
|
|
||||||
|
|
||||||
The practical consequence: **inserting a row into `dbo.Selektionen` can never make a
|
|
||||||
selection appear.** It can only flip `auswahl` from `False` to `True` on an entry that
|
|
||||||
the catalog already emits. A selection that is absent from the XML is absent from the
|
|
||||||
*catalog*, and the assignment is irrelevant until that is fixed.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Query A — reconstruct the response
|
|
||||||
|
|
||||||
Run this for the account in question and diff its rows against the actual XML. A clean
|
|
||||||
match proves the reconstruction above; any mismatch belongs in the caveats section.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
DECLARE @AddressId bigint = 141747;
|
|
||||||
|
|
||||||
SELECT COALESCE(sg.IDSelektionsgruppenstamm, 0) AS groupId,
|
|
||||||
COALESCE(sg.Selektionsgruppe, '<ohne Gruppe>') AS groupLabel,
|
|
||||||
s.IDSelektionsstamm AS selectionId,
|
|
||||||
s.Selektion AS selectionLabel,
|
|
||||||
CASE WHEN s.System = 0 THEN 'True' ELSE 'False' END AS aenderbar,
|
|
||||||
CASE WHEN sel.IDAdresse_FS IS NULL THEN 'False' ELSE 'True' END AS auswahl
|
|
||||||
FROM dbo.Selektionsstamm s
|
|
||||||
LEFT JOIN dbo.Selektionsgruppe sgm
|
|
||||||
ON sgm.IDSelektionsstamm_FS = s.IDSelektionsstamm
|
|
||||||
LEFT JOIN dbo.Selektionsgruppenstamm sg
|
|
||||||
ON sg.IDSelektionsgruppenstamm = sgm.IDSelektionsgruppenstamm_FS
|
|
||||||
LEFT JOIN dbo.Selektionen sel
|
|
||||||
ON sel.IDSelektionsstamm_FS = s.IDSelektionsstamm
|
|
||||||
AND sel.IDAdresse_FS = @AddressId
|
|
||||||
WHERE COALESCE(s.Status, '') <> 'L'
|
|
||||||
AND COALESCE(sg.Status, '') <> 'L'
|
|
||||||
ORDER BY COALESCE(sg.Position, -1), sgm.Position, s.Selektion;
|
|
||||||
```
|
|
||||||
|
|
||||||
(`Status = 'L'` is this database's soft-delete marker — see
|
|
||||||
`../bpn-connect-query-reference.md`.)
|
|
||||||
|
|
||||||
Note the join fans out: a selection can belong to **several** groups (confirmed —
|
|
||||||
`1292` is in both `10` and `91`), so this query emits one row per membership. The XML
|
|
||||||
shows a selection once per group it is published in.
|
|
||||||
|
|
||||||
`Selektionsgruppenstamm.Ausgrenzung` is *exclusion* semantics (mutually exclusive
|
|
||||||
group), not visibility — don't mistake it for a publish flag.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Query B — diagnose a selection that never appears
|
|
||||||
|
|
||||||
Compares the suspect row against known-good neighbours: `1292`/`1293` (long-standing,
|
|
||||||
emitted, `aenderbar=False`) and `1462` (most recent one that *is* emitted,
|
|
||||||
`aenderbar=True`). Whatever column differs is the answer.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT s.IDSelektionsstamm,
|
|
||||||
s.Selektion,
|
|
||||||
s.Status,
|
|
||||||
s.System,
|
|
||||||
sgm.IDSelektionsgruppenstamm_FS AS groupId,
|
|
||||||
sgm.Position AS positionInGroup,
|
|
||||||
(SELECT COUNT(*) FROM dbo.Selektionen x
|
|
||||||
WHERE x.IDSelektionsstamm_FS = s.IDSelektionsstamm
|
|
||||||
AND x.IDAdresse_FS = 141747) AS assignedToAddress
|
|
||||||
FROM dbo.Selektionsstamm s
|
|
||||||
LEFT JOIN dbo.Selektionsgruppe sgm
|
|
||||||
ON sgm.IDSelektionsstamm_FS = s.IDSelektionsstamm
|
|
||||||
WHERE s.IDSelektionsstamm IN (1477, 1478, 1292, 1293, 1462)
|
|
||||||
ORDER BY s.IDSelektionsstamm;
|
|
||||||
```
|
|
||||||
|
|
||||||
### What Query B ruled out (address `141747`, 2026-08-04)
|
|
||||||
|
|
||||||
| id | Selektion | Status | System | groupId | posInGroup | assigned |
|
|
||||||
| --- | --- | --- | --- | --- | --- | --- |
|
|
||||||
| 1292 | Admin | *(blank)* | 1 | 10 | 33 | 1 |
|
|
||||||
| 1292 | Admin | *(blank)* | 1 | **91** | 1 | 1 |
|
|
||||||
| 1293 | Manager | *(blank)* | 1 | **92** | 1 | 1 |
|
|
||||||
| 1293 | Manager | *(blank)* | 1 | 10 | 34 | 1 |
|
|
||||||
| 1462 | Hausleitung AGR | *(blank)* | 1 | 10 | 74 | 0 |
|
|
||||||
| 1477 | Preisrechner | *(blank)* | 1 | 10 | 75 | 0 |
|
|
||||||
| 1478 | Preisrechner Admin | *(blank)* | 1 | 10 | 76 | 1 |
|
|
||||||
|
|
||||||
Three conclusions:
|
|
||||||
|
|
||||||
1. **The missing-membership theory is dead.** `1477`/`1478` *are* in group `10` (TEAM),
|
|
||||||
at positions `75`/`76`, and `1478` *is* assigned to the address. On every column
|
|
||||||
available in these three tables, `1478` is indistinguishable from `1462` — which the
|
|
||||||
XML does emit. Whatever excludes them lives **outside**
|
|
||||||
`Selektionsstamm` / `Selektionsgruppe` / `Selektionen`.
|
|
||||||
2. **`aenderbar` is not derived from `System`.** `1292` (`aenderbar="False"`) and `1462`
|
|
||||||
(`aenderbar="True"`) both have `System = 1`. `Selektionsstamm` has no other column,
|
|
||||||
so `aenderbar` comes from somewhere else entirely.
|
|
||||||
3. **A selection can belong to several groups, and some groups are not published.**
|
|
||||||
`1292` is in `10` + `91`, `1293` is in `10` + `92`; groups `91`/`92` appear nowhere in
|
|
||||||
the XML, which only emits `0`, `10`, `83`. So there is a **group-level publish
|
|
||||||
filter** — and, suggestively, the two rows with a second (unpublished) group
|
|
||||||
membership are exactly the two that come back `aenderbar="False"`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Query D — the agency whitelist (run this first)
|
|
||||||
|
|
||||||
`dbo.AgenturSelektionenKunde` is a bare two-column link table,
|
|
||||||
`(IDAdresse_FS, IDSelektionsstamm_FS)` — literally *"agency's customer selections"*. The
|
|
||||||
portal authenticates against BusPro as an **agency** account (`bpn_username` in
|
|
||||||
`ApiClient`), so a per-agency whitelist of which customer selections that agency may see
|
|
||||||
and/or edit is exactly the right shape. It is the only candidate found so far that could
|
|
||||||
explain the `1478` omission **and** `aenderbar` through one mechanism.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT ask.IDAdresse_FS AS agencyAddressId,
|
|
||||||
a.SuchName AS agencyName,
|
|
||||||
ask.IDSelektionsstamm_FS AS selectionId,
|
|
||||||
s.Selektion AS selectionLabel
|
|
||||||
FROM dbo.AgenturSelektionenKunde ask
|
|
||||||
JOIN dbo.Selektionsstamm s ON s.IDSelektionsstamm = ask.IDSelektionsstamm_FS
|
|
||||||
LEFT JOIN dbo.Adresse a ON a.IDAdresse = ask.IDAdresse_FS
|
|
||||||
ORDER BY ask.IDAdresse_FS, s.Selektion;
|
|
||||||
|
|
||||||
-- same shape, same question:
|
|
||||||
SELECT * FROM dbo.PartnerSelektionenKunde;
|
|
||||||
```
|
|
||||||
|
|
||||||
Compare the row set for the portal's agency address against a freshly captured response:
|
|
||||||
|
|
||||||
| Result | Meaning |
|
|
||||||
| --- | --- |
|
|
||||||
| set == the 32 emitted entries | **this is the publish gate** — `1477`/`1478` just need rows here |
|
|
||||||
| set == the `aenderbar="True"` subset only | this is the `aenderbar` source |
|
|
||||||
| set is some third thing | note it in this doc and continue to C1 |
|
|
||||||
| table empty | both hypotheses dead — fall through to C1/C2/C3 |
|
|
||||||
|
|
||||||
**Result (2026-08-04): `dbo.AgenturSelektionenKunde` is empty.** The agency-whitelist
|
|
||||||
theory is dead. Do not re-run.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Query E — group-count cross-check (the secondary `aenderbar` theory)
|
|
||||||
|
|
||||||
Query B hinted that the two `aenderbar="False"` rows were the two with a *second*,
|
|
||||||
unpublished group membership (`1292`→{10, 91}, `1293`→{10, 92}) while `aenderbar="True"`
|
|
||||||
`1462` sat in group `10` alone. Two data points is not a rule — this settles it in one run:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT s.IDSelektionsstamm,
|
|
||||||
s.Selektion,
|
|
||||||
COUNT(*) AS groupCount,
|
|
||||||
STRING_AGG(CAST(sgm.IDSelektionsgruppenstamm_FS AS varchar(10)), ',') AS groupIds
|
|
||||||
FROM dbo.Selektionsstamm s
|
|
||||||
JOIN dbo.Selektionsgruppe sgm ON sgm.IDSelektionsstamm_FS = s.IDSelektionsstamm
|
|
||||||
WHERE s.IDSelektionsstamm IN (1064,1065,1066,1067,1068,1069,1070,1156,1292,1293,
|
|
||||||
1299,1304,1377,1459,1461,1462,1477,1478)
|
|
||||||
GROUP BY s.IDSelektionsstamm, s.Selektion
|
|
||||||
ORDER BY s.IDSelektionsstamm;
|
|
||||||
```
|
|
||||||
|
|
||||||
Read against the known `aenderbar` values from the reference response:
|
|
||||||
|
|
||||||
- `aenderbar="False"`: `1070`, `1156`, `1292`, `1293`, `1299`, `1304`, `1377`
|
|
||||||
- `aenderbar="True"`: `1064`–`1069`, `1459`, `1461`, `1462`
|
|
||||||
|
|
||||||
If every `False` row has `groupCount > 1` and every `True` row has `groupCount = 1`, the
|
|
||||||
theory holds. Note it still would not explain `1478` (group `10` only, yet omitted), so
|
|
||||||
Query D remains the priority.
|
|
||||||
|
|
||||||
**Result (2026-08-04): the theory is dead.** Group membership does not correlate with
|
|
||||||
`aenderbar` at all:
|
|
||||||
|
|
||||||
| id | Selektion | groupCount | groupIds | `aenderbar` |
|
|
||||||
| --- | --- | --- | --- | --- |
|
|
||||||
| 1064–1069 | *Interessen* | 1 | 83 | **True** |
|
|
||||||
| 1070 | E&P Teamer - allg. Merkmal | 1 | 10 | **False** |
|
|
||||||
| 1292 | Admin | 2 | 91, 10 | **False** |
|
|
||||||
| 1293 | Manager | 2 | 92, 10 | **False** |
|
|
||||||
| 1299 / 1304 / 1377 | Hausleitung SSL / DKS / KHH | 1 | 10 | **False** |
|
|
||||||
| 1459 / 1461 / 1462 | Hausleitung SVS / SHM / AGR | 1 | 10 | **True** |
|
|
||||||
| 1477 / 1478 | Preisrechner, Preisrechner Admin | 1 | 10 | *(not emitted)* |
|
|
||||||
|
|
||||||
`1299`/`1304`/`1377` and `1459`/`1461`/`1462` have **identical** group membership,
|
|
||||||
identical `Status`, identical `System` — and opposite `aenderbar`. The `1292`/`1293`
|
|
||||||
double membership was a coincidence.
|
|
||||||
|
|
||||||
Confirmed as a side effect: `1156` is absent from this result because the inner join
|
|
||||||
found no `Selektionsgruppe` row for it. `<ohne Gruppe>` really does mean "no membership
|
|
||||||
row", as modelled at the top of this document.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Query F — exhaust every remaining link to `Selektionsstamm`
|
|
||||||
|
|
||||||
After D and E, the schema export lists exactly these tables with an FK into
|
|
||||||
`Selektionsstamm`. This counts all of them at once for the rows whose behaviour differs,
|
|
||||||
which is the last thing the database can tell us:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT s.IDSelektionsstamm, s.Selektion,
|
|
||||||
(SELECT COUNT(*) FROM dbo.Selektionen x WHERE x.IDSelektionsstamm_FS = s.IDSelektionsstamm) AS addressAssignments,
|
|
||||||
(SELECT COUNT(*) FROM dbo.PartnerSelektionenKunde x WHERE x.IDSelektionsstamm_FS = s.IDSelektionsstamm) AS partnerLinks,
|
|
||||||
(SELECT COUNT(*) FROM dbo.LeistungSelektionenKunde x WHERE x.IDSelektionsstamm_FS = s.IDSelektionsstamm) AS serviceLinks,
|
|
||||||
(SELECT COUNT(*) FROM dbo.ReiseleiterSelektionen x WHERE x.IDSelektionsstamm_FS = s.IDSelektionsstamm) AS guideLinks,
|
|
||||||
(SELECT COUNT(*) FROM dbo.ReiseSelektionen x WHERE x.IDSelektionsstamm_FS = s.IDSelektionsstamm) AS travelLinks,
|
|
||||||
(SELECT COUNT(*) FROM dbo.ReisezielSelektionsstamm x WHERE x.IDSelektionsstamm_FS = s.IDSelektionsstamm) AS destinationLinks
|
|
||||||
FROM dbo.Selektionsstamm s
|
|
||||||
WHERE s.IDSelektionsstamm IN (1070,1156,1292,1293,1299,1304,1377,1459,1461,1462,1477,1478,1064)
|
|
||||||
ORDER BY s.IDSelektionsstamm;
|
|
||||||
```
|
|
||||||
|
|
||||||
The decisive comparison is `1377` (`aenderbar="False"`) vs `1462` (`"True"`) vs `1478`
|
|
||||||
(omitted). If all three rows are identical here too, then **no per-selection property in
|
|
||||||
this database distinguishes them**, and the rule is not in the schema at all — go to
|
|
||||||
"Where this leaves the investigation" below.
|
|
||||||
|
|
||||||
**Result (2026-08-04): all link counts are zero** for every row queried, except
|
|
||||||
`travelLinks` on `1070` (11) and `1156` (3) via `ReiseSelektionen` — neither of which
|
|
||||||
correlates with `aenderbar` or with being emitted. Assignment counts don't explain
|
|
||||||
anything either:
|
|
||||||
|
|
||||||
| id | assignments | emitted? | `aenderbar` |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| 1377 Hausleitung KHH | **0** | yes | False |
|
|
||||||
| 1462 Hausleitung AGR | 1 | yes | True |
|
|
||||||
| 1478 Preisrechner Admin | **3** | **no** | — |
|
|
||||||
|
|
||||||
`1377` is published with zero assignments; `1478` has three and is not published. This
|
|
||||||
exhausts every FK path into `Selektionsstamm` in the schema. **Stop querying the
|
|
||||||
database** — the answer is not in it.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Query C — locate the publish gate
|
|
||||||
|
|
||||||
Point (1) means some table or config outside the three core tables decides what gets
|
|
||||||
emitted. Run these in order; the first hit explains everything.
|
|
||||||
|
|
||||||
**C1 — what distinguishes a published group from an unpublished one.** Groups `0`, `10`,
|
|
||||||
`83` are emitted; `91`, `92` are not:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT IDSelektionsgruppenstamm, Selektionsgruppe, Status, System, Ausgrenzung, Position
|
|
||||||
FROM dbo.Selektionsgruppenstamm
|
|
||||||
ORDER BY Position;
|
|
||||||
```
|
|
||||||
|
|
||||||
If exactly the emitted groups share a `Status`/`System`/`Position` value that `91`/`92`
|
|
||||||
lack, that's the group gate. It still won't explain `1478` (same group as `1462`), but
|
|
||||||
it pins down half the rule — and tells you whether `aenderbar` tracks membership in one
|
|
||||||
of these system groups.
|
|
||||||
|
|
||||||
**C2 — hunt for a published-selection whitelist in the config stores.** This is the
|
|
||||||
leading theory for `1478`: a config value enumerating exposed selection ids that was
|
|
||||||
maintained up to `1462` and never extended. This database does store id lists in
|
|
||||||
delimited string columns (`CRMAktion.IDsAgenturEingrenzung`,
|
|
||||||
`ImportAdressen.ZuweisungSelektionIDs`, `AdresseHistorie.SelektionsIDs`), so the pattern
|
|
||||||
exists. Search for a known-published id:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT * FROM dbo.Einstellung
|
|
||||||
WHERE Wert LIKE '%1462%' OR Wert LIKE '%1292%'
|
|
||||||
OR Bezeichnung LIKE '%elektion%' OR Beschreibung LIKE '%elektion%';
|
|
||||||
|
|
||||||
SELECT * FROM dbo.ProgrammEinstellung
|
|
||||||
WHERE Wert LIKE '%1462%' OR Wert LIKE '%1292%' OR Variable LIKE '%elektion%';
|
|
||||||
|
|
||||||
SELECT * FROM dbo.ConnectEinstellung
|
|
||||||
WHERE WertString LIKE '%1462%' OR WertString LIKE '%1292%';
|
|
||||||
```
|
|
||||||
|
|
||||||
**C3 — brute-force the whole schema** if C1/C2 come up empty. Find every wide text
|
|
||||||
column that mentions a published selection id:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
DECLARE @needle nvarchar(20) = '1462';
|
|
||||||
DECLARE @sql nvarchar(max) = '';
|
|
||||||
|
|
||||||
SELECT @sql = @sql + 'SELECT ''' + QUOTENAME(t.name) + ''' AS tbl, ''' + QUOTENAME(c.name)
|
|
||||||
+ ''' AS col, COUNT(*) AS hits FROM dbo.' + QUOTENAME(t.name)
|
|
||||||
+ ' WHERE CAST(' + QUOTENAME(c.name) + ' AS nvarchar(max)) LIKE ''%'
|
|
||||||
+ @needle + '%'' HAVING COUNT(*) > 0;' + CHAR(10)
|
|
||||||
FROM sys.tables t
|
|
||||||
JOIN sys.columns c ON c.object_id = t.object_id
|
|
||||||
JOIN sys.types ty ON ty.user_type_id = c.user_type_id
|
|
||||||
WHERE ty.name IN ('nvarchar', 'varchar', 'nchar', 'char')
|
|
||||||
AND (c.max_length = -1 OR c.max_length >= 100)
|
|
||||||
AND t.name NOT LIKE 'Selektion%';
|
|
||||||
|
|
||||||
EXEC sp_executesql @sql; -- read-only; slow, run off-peak
|
|
||||||
```
|
|
||||||
|
|
||||||
**C4 — rule out staleness/caching first, it's free.** `1478` is structurally identical
|
|
||||||
to an emitted row, which is also what a cached catalog looks like. Re-fetch the XML now
|
|
||||||
and confirm the sample postdates the insert:
|
|
||||||
|
|
||||||
```php
|
|
||||||
// bin/console or a scratch script
|
|
||||||
$client->getCrmAttributes($email, $password);
|
|
||||||
```
|
|
||||||
|
|
||||||
If BusPro caches the catalog in-process, an application-pool/service restart on the
|
|
||||||
BusProNet host is the fix, and no schema change is needed.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Portal side: a correct XML is not enough
|
|
||||||
|
|
||||||
`BpnAuthenticator::createOrUpdateLocalUser()` snapshots the roles onto the local `User`
|
|
||||||
entity at login (`setRoles($crmAttributes->roles)` /
|
|
||||||
`setHotelCodes($crmAttributes->hotelCodes)`). Roles are **not** re-read per request. So
|
|
||||||
after fixing BusPro the affected user must **log out and log in again** before
|
|
||||||
`ROLE_GROUPS_ADMIN` takes effect — an active session keeps the stale role set.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Where this leaves the investigation
|
|
||||||
|
|
||||||
**Conclusion as of 2026-08-04, after Queries A–F: the schema search is exhausted.**
|
|
||||||
|
|
||||||
- `1478` and `1462` are identical on every column and every link the schema exposes —
|
|
||||||
same blank `Status`, same `System = 1`, same single membership in group `10`, adjacent
|
|
||||||
positions (76 vs 74), zero links in all five side tables. `1462` is emitted, `1478` is
|
|
||||||
not.
|
|
||||||
- `1377` (`aenderbar="False"`) and `1462` (`"True"`) are identical by the same measure.
|
|
||||||
Opposite flag.
|
|
||||||
- Assignment count is not it either: `1377` is published with zero assignments, `1478`
|
|
||||||
has three and is not published.
|
|
||||||
|
|
||||||
Rows that are indistinguishable in the database behave differently in the response. Every
|
|
||||||
column of the three core tables, every FK path into `Selektionsstamm`, and a
|
|
||||||
whole-schema column-name search have now been checked. So **the publish set and the
|
|
||||||
`aenderbar` flag are not derived from this database** — they live in the BusProNet
|
|
||||||
service layer: its own configuration, or an in-process cache of the catalog.
|
|
||||||
|
|
||||||
Only C2/C3 (a text search of the config stores, then of every wide text column) remain,
|
|
||||||
and they are long shots. Prefer the escalation path below.
|
|
||||||
|
|
||||||
Escalation path, cheapest first:
|
|
||||||
|
|
||||||
1. **Re-fetch and confirm staleness.** Our side does not cache this call — the
|
|
||||||
`xml-sync` cache tag covers the XML *export* sync, and `ApiClient::getCrmAttributes()`
|
|
||||||
has no cache layer — so a fresh login hits BusPro directly. If `1478` is still absent
|
|
||||||
from a response captured after the insert, the staleness is on the BusProNet host and
|
|
||||||
an application-pool/service restart there is the test.
|
|
||||||
2. **The UI experiment.** Create a throwaway selection in the TEAM group *through the
|
|
||||||
BusPro UI*, then diff its rows against `1478` across `Selektionsstamm` /
|
|
||||||
`Selektionsgruppe` / `Selektionen`. If it appears in the XML, the diff names exactly
|
|
||||||
what a manual `INSERT` misses. If it does not, the gate is provably config/cache and
|
|
||||||
not per-row data.
|
|
||||||
3. **Ask the vendor.** "What determines which `Selektionsstamm` entries `SelektionCRM`
|
|
||||||
publishes, given group membership is not sufficient — and what drives `aenderbar`?"
|
|
||||||
Both are one-sentence answers for them and may not be discoverable from the schema at
|
|
||||||
all. Worth bundling into the open BPN thread.
|
|
||||||
|
|
||||||
## Caveats
|
|
||||||
|
|
||||||
- **`aenderbar` has no known source.** No such column exists in any of the 1023 tables;
|
|
||||||
`Selektionsstamm.System` (Query B), the agency whitelist (Query D) and group membership
|
|
||||||
(Query E) are all falsified. Treat it as unmodellable in a `bpn-connect` endpoint until
|
|
||||||
the vendor explains it — if `bpn-connect` must serve this response, the safe move is to
|
|
||||||
keep `SelektionCRM` on the socket API rather than guess, exactly as
|
|
||||||
`live-api-queries.md` recommends for `getMutableData()`.
|
|
||||||
- Groups `91` and `92` are known to exist and are never emitted; what they are is still
|
|
||||||
unexplained (Query C1). Record the answer here when you have it.
|
|
||||||
- **The publish gate is not identified.** Group membership is necessary but not
|
|
||||||
sufficient: `1478` sits in the published TEAM group with an assignment and is still
|
|
||||||
omitted. Query C is the open investigation.
|
|
||||||
- Consequently Query A **over-reports**: it will return `1477`/`1478` (and anything in
|
|
||||||
groups `91`/`92`) that the real XML omits. It reconstructs the shape of the response,
|
|
||||||
not yet its exact membership.
|
|
||||||
- `dbo.Selektionen` has no status column and no exported primary key, so a duplicate
|
|
||||||
assignment row may well be insertable. Check before inserting.
|
|
||||||
- The schema exports carry no PK/index catalog (noted in
|
|
||||||
`../bpn-connect-query-reference.md`); none of the queries here are
|
|
||||||
performance-sensitive, but a `bpn-connect` endpoint built on them would want indexes
|
|
||||||
on `Selektionen.IDAdresse_FS` and `Selektionsgruppe.IDSelektionsstamm_FS` confirmed.
|
|
||||||
- `Adresse` vs. `AdressePerson`: assignments hang off `IDAdresse`, not `IDPerson`.
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
# Live socket-API endpoints — direct-MSSQL feasibility & queries
|
|
||||||
|
|
||||||
> **Authority: [`docs/buspro-database-reference.md`](../buspro-database-reference.md).**
|
|
||||||
> Schema semantics live there and win on any conflict. This file is retained for the
|
|
||||||
> per-endpoint migration verdicts and the `frei` discovery SQL.
|
|
||||||
|
|
||||||
This complements `travel-queries.md`. That doc replaced the **XML export** (static
|
|
||||||
catalog). This one covers the **live socket API** methods on
|
|
||||||
`src/BusProNet/ApiClient.php` and classifies how replicable each is by direct MSSQL
|
|
||||||
query in the Go proxy.
|
|
||||||
|
|
||||||
**Verdict in one line:** the *structural* parts replicate cleanly; the *live "free"
|
|
||||||
counts* are computed by BusPro's allocation logic (not a flat column) and should either
|
|
||||||
stay on the socket API or be reproduced with an explicitly-approximated query;
|
|
||||||
`getMutableData()` is pure business-rule logic — keep it on the socket API.
|
|
||||||
|
|
||||||
| Endpoint (type) | Tier | Replicable in SQL? |
|
|
||||||
|---|---|---|
|
|
||||||
| `getProducts()` (`PRODUKTE`) | 1 | ✅ trivial |
|
|
||||||
| `getAgencies()` (`AGENTUREN`) | 1 | ✅ structural (confirm agency filter + address joins) |
|
|
||||||
| `getTravelData()` (`PRODUKTDATEN`) | 1 | ✅ = `travel-queries.md` Query B |
|
|
||||||
| `getAvailabilities()` (`VERFUEGBARKEIT`) | 2 | ⚠ structure yes; `frei` count is computed |
|
|
||||||
| `getAvailabilitiesExtended()` (`VERFUEGBARKEIT2`) | 2 | ⚠ structure yes (we have the extra fields); `frei` computed |
|
|
||||||
| `getHotelAvailability()` (`VERFUEGBARKEITHOTEL`) | 2 | ⚠ `frei` = contingent − active bookings |
|
|
||||||
| `getMutableData()` (`MOEGLICHEAENDERUNGEN`) | 3 | ✋ business rules — keep on socket API |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Tier 1 — structural, replicate as-is
|
|
||||||
|
|
||||||
### `getProducts()` → `Product{id, code, name}`
|
|
||||||
Parser keeps only rows with a non-empty `code` (`ProductsParser`).
|
|
||||||
```sql
|
|
||||||
SELECT IDProdukt AS id, Code AS code, Bezeichnung AS name
|
|
||||||
FROM dbo.Produkt
|
|
||||||
WHERE Code IS NOT NULL AND Code <> ''
|
|
||||||
ORDER BY Bezeichnung;
|
|
||||||
```
|
|
||||||
|
|
||||||
### `getTravelData(idprodukt[, hotelId])` → `Travel`
|
|
||||||
This is the same payload as `travel-queries.md` **Query B** (header + services +
|
|
||||||
pickups + hotels/rooms + tags). The *static* structure is fully covered there. Its
|
|
||||||
embedded live `preis`/`frei` values overlap Tier 2 — source those the same way you
|
|
||||||
decide to handle availability below. No new SQL needed; reuse Query B.
|
|
||||||
|
|
||||||
### `getAgencies()` → `Agency{id, name, code, street, postCode, city, phone}`
|
|
||||||
`name/code` map to `Adresse.SuchName` + a code column; `street/postCode/city` live in
|
|
||||||
`AdresseAnschrift`, `phone` in `AdresseKommunikation` (1-to-N → pick the primary row).
|
|
||||||
Two things to pin with the discovery queries below: (1) **which `Adresse` rows are
|
|
||||||
agencies**, and (2) the exact `AdresseAnschrift`/`AdresseKommunikation` column + primary-row flag.
|
|
||||||
```sql
|
|
||||||
-- skeleton (fill the agency filter + verified column names from discovery):
|
|
||||||
SELECT a.IDAdresse AS id,
|
|
||||||
a.SuchName AS name,
|
|
||||||
a.Code AS code, -- ⚠ confirm code column on Adresse
|
|
||||||
an.Strasse AS street, -- ⚠ from AdresseAnschrift
|
|
||||||
an.Plz AS postCode,
|
|
||||||
an.Ort AS city,
|
|
||||||
ak.Wert AS phone -- ⚠ from AdresseKommunikation (primary tel)
|
|
||||||
FROM dbo.Adresse a
|
|
||||||
LEFT JOIN dbo.AdresseAnschrift an ON an.IDAdresse_FS = a.IDAdresse /* AND an.IsPrimary */
|
|
||||||
LEFT JOIN dbo.AdresseKommunikation ak ON ak.IDAdresse_FS = a.IDAdresse /* AND ak.Art = 'TEL' */
|
|
||||||
WHERE /* a is an agency — see discovery */ 1 = 0;
|
|
||||||
```
|
|
||||||
Discovery:
|
|
||||||
```sql
|
|
||||||
-- (1) how agencies are flagged (inspect the org/role tables):
|
|
||||||
SELECT TOP 20 * FROM dbo.AdresseAgenturOrganisation;
|
|
||||||
SELECT IDArtAgenturOrganisation, * FROM dbo.ArtAgenturOrganisation; -- the agency "types"
|
|
||||||
-- (2) the address + comm column names / primary-row flags:
|
|
||||||
SELECT TOP 5 * FROM dbo.AdresseAnschrift;
|
|
||||||
SELECT TOP 5 * FROM dbo.AdresseKommunikation;
|
|
||||||
SELECT TOP 5 * FROM dbo.Adresse; -- find the 'code' column
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Tier 2 — availability: structure is flat, the **`frei` count is computed**
|
|
||||||
|
|
||||||
The response models (`Availability`, `ExtendedAvailability`, `Room`) carry per-item
|
|
||||||
`status`, `preis`, structural attrs (dates/ages/mandatory/board) **and** `frei`. The
|
|
||||||
first set is a flat read; **`frei` is not stored**.
|
|
||||||
|
|
||||||
**Why `frei` is computed (validated):** `Leistung.Kontingent` is a fixed planning
|
|
||||||
allotment, not the remaining free count — e.g. service `155234` has `Kontingent=2500`
|
|
||||||
but `2945` booking rows (bookings *exceed* the allotment). So:
|
|
||||||
```
|
|
||||||
frei = Kontingent − (active, non-cancelled, non-expired bookings) [± pooled contingent]
|
|
||||||
```
|
|
||||||
with three real complications:
|
|
||||||
- **Cancellations**: `BuchungTNLeistung` has no own status; join up via `IDBuchungTN_FS`
|
|
||||||
→ `BuchungTN` → `Buchung` and exclude Storno (the `BuchungStatus*` flags / Storno date).
|
|
||||||
- **Option expiry**: `BuchungTNLeistung.OptionsDatum` + `Leistung.KontingentVerfallTage`
|
|
||||||
— expired options free their slots back.
|
|
||||||
- **Pooled contingents**: `Leistung.IDLeistung_FS_KontingentAUS` / `KontingentAusPartner`
|
|
||||||
— a service may draw from another service's pool (or a `PartnerKontingent`), so its
|
|
||||||
`frei` depends on a sibling, not just its own bookings.
|
|
||||||
|
|
||||||
> **Recommendation:** keep the *authoritative* `frei` on the socket API
|
|
||||||
> (`getAvailabilities`/`getHotelAvailability`). If you want it in SQL, treat it as a
|
|
||||||
> documented **approximation** and validate row-by-row against the socket API before
|
|
||||||
> trusting it for booking decisions. The structural fields below are safe to serve from SQL.
|
|
||||||
|
|
||||||
### `getAvailabilities()` / `getAvailabilitiesExtended()` — structural part
|
|
||||||
```sql
|
|
||||||
SELECT
|
|
||||||
l.IDLeistung AS serviceId,
|
|
||||||
l.Status AS status,
|
|
||||||
lp.Preis AS price,
|
|
||||||
l.Kontingent AS allotment, -- NOT 'frei' (see calc above)
|
|
||||||
-- extended-only fields (VERFUEGBARKEIT2):
|
|
||||||
l.Termin_VON AS dateFrom, l.Termin_BIS AS dateTo,
|
|
||||||
l.Alter_VON AS ageFrom, l.Alter_BIS AS ageTo,
|
|
||||||
l.Pflichtmerkmal AS mandatory
|
|
||||||
FROM dbo.ReiseLeistung rl
|
|
||||||
JOIN dbo.Leistung l ON l.IDLeistung = rl.IDLeistung_FS
|
|
||||||
LEFT JOIN dbo.LeistungPreis lp ON lp.IDLeistung_FS = l.IDLeistung AND lp.IDArtPreisSchema_FS = 1
|
|
||||||
WHERE rl.IDReise_FS = @DateId
|
|
||||||
ORDER BY l.Art, l.Unterart;
|
|
||||||
-- travel-level 'buchungstatusmoeglich' / 'status' come from the Reise + the allowed
|
|
||||||
-- ArtBuchungsStatus set; the extended 'uhrzeit_von'/'hinweis' come from the pickup
|
|
||||||
-- time plan (ZustiegZeitplan) and a Leistung text — see travel-queries.md B2/B2b.
|
|
||||||
```
|
|
||||||
The live `frei` per `serviceId` = the calc above (see recommendation).
|
|
||||||
|
|
||||||
### `getHotelAvailability(idreise, idpartner, terminbis)` → `Room{frei, status, preis, …}`
|
|
||||||
Rooms/price/`status` are the `travel-queries.md` **B3** chain;
|
|
||||||
`status` = `PartnerKontingent.BuchungsStatus` (`''`/`S`/`A`). `frei` = `PartnerKontingent.Anzahl`
|
|
||||||
**minus active hotel bookings** for that `(IDAdresse, IDZimmer, date-span)` — same
|
|
||||||
cancellation/expiry caveats as above.
|
|
||||||
|
|
||||||
### Discovery — quantify the `frei` calc against the socket API
|
|
||||||
```sql
|
|
||||||
-- active (non-storno) booking rows for a service, with option dates:
|
|
||||||
SELECT btl.IDLeistung_FS, COUNT(*) AS rows_total,
|
|
||||||
SUM(CASE WHEN btl.OptionsDatum IS NULL OR btl.OptionsDatum >= GETDATE() THEN 1 ELSE 0 END) AS rows_live
|
|
||||||
FROM dbo.BuchungTNLeistung btl
|
|
||||||
JOIN dbo.BuchungTN bt ON bt.IDBuchungTN = btl.IDBuchungTN_FS
|
|
||||||
JOIN dbo.Buchung b ON b.IDBuchung = bt.IDBuchung_FS -- ⚠ confirm FK name
|
|
||||||
WHERE btl.IDLeistung_FS = @ServiceId
|
|
||||||
/* AND b.<storno flag> = 0 */ -- ⚠ confirm Storno column
|
|
||||||
GROUP BY btl.IDLeistung_FS;
|
|
||||||
-- then compare Leistung.Kontingent − rows_live vs the socket-API 'frei' for the same service.
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Tier 3 — `getMutableData()` — keep on the socket API
|
|
||||||
|
|
||||||
Returns per-aspect `möglich` (bool) + `möglichbiszum` (deadline) for: participant
|
|
||||||
count, transportation, pickup, accommodation, additional services, participant data.
|
|
||||||
These are **derived from booking rules / change-cutoff config / departure timing**
|
|
||||||
(not a flat table). Reproducing them in SQL would mean re-implementing BusPro's
|
|
||||||
change-deadline logic and risks silent divergence. Recommendation: leave this on the
|
|
||||||
socket API; don't migrate it.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary for the Go proxy
|
|
||||||
- **Migrate now (SQL):** `getProducts`, `getAgencies` (after the agency-filter discovery),
|
|
||||||
`getTravelData` (reuse Query B), and the **structural** fields of the two availability
|
|
||||||
endpoints.
|
|
||||||
- **Keep computed:** the live `frei` counts — socket API, or an explicitly-validated
|
|
||||||
approximation.
|
|
||||||
- **Keep on socket:** `getMutableData`.
|
|
||||||
@@ -1,247 +0,0 @@
|
|||||||
# Schema refresh & Zustieg-pricing verification queries
|
|
||||||
|
|
||||||
> **Superseded by [`docs/buspro-database-reference.md`](../buspro-database-reference.md) §12**,
|
|
||||||
> which carries the refresh queries and the verification log. Retained only for the
|
|
||||||
> 2026-08-04 run detail; safe to delete once that is no longer of interest.
|
|
||||||
|
|
||||||
Read-only queries to (a) check whether the June schema dump still matches
|
|
||||||
production after a BusPro update, (b) regenerate it if not, and (c) resolve the
|
|
||||||
open `Abfahrtsortzuschlag (Rückfahrt)` question from the database side.
|
|
||||||
|
|
||||||
Current dump: `BusPronet_20260601-1517.csv` (columns),
|
|
||||||
`BusPronet_20260601-1518.csv` (foreign keys), exported 2026-06-01.
|
|
||||||
|
|
||||||
**All queries are `SELECT`-only.** Run in order — Query 1 decides whether 2–4 are
|
|
||||||
needed at all.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Drift check — run this first
|
|
||||||
|
|
||||||
Cheapest, highest signal. If it returns nothing, the June dump is still accurate
|
|
||||||
and the re-export can be skipped.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT s.name AS schema_name, o.name AS object_name, o.type_desc,
|
|
||||||
o.create_date, o.modify_date
|
|
||||||
FROM sys.objects o
|
|
||||||
JOIN sys.schemas s ON s.schema_id = o.schema_id
|
|
||||||
WHERE o.is_ms_shipped = 0
|
|
||||||
AND (o.create_date > '2026-06-01' OR o.modify_date > '2026-06-01')
|
|
||||||
ORDER BY o.modify_date DESC;
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: `modify_date` moves on metadata changes, so a long list is expected after a
|
|
||||||
version upgrade. What matters is whether anything under `Zustieg*`, `Leistung*`,
|
|
||||||
`Buchung*` appears.
|
|
||||||
|
|
||||||
**Result 2026-08-04.** Every object reports `create_date` **2026-06-03** — the
|
|
||||||
database was restored/rebuilt that day, so `create_date` is a restore artifact and
|
|
||||||
the `> '2026-06-01'` filter matches everything. Use `modify_date` after the restore
|
|
||||||
instead. Only two genuine clusters exist:
|
|
||||||
|
|
||||||
- **2026-06-11** — six new tables, all absent from `…-1517.csv`: `Reichweite`,
|
|
||||||
`ReichweiteZeitraum`, `ReichweiteZeitraumVonBis`, `ReichweiteRestFarbe`,
|
|
||||||
`LadungTankungBetriebshof`, `ConnectViafintechSlip`.
|
|
||||||
- **2026-07-02** — modified: `Tachostand`, `GutscheinAktion`,
|
|
||||||
`AdressePersonalZusatz`, `BlockPlanung`, `Block`, `LeistungZusatz`,
|
|
||||||
`ConnectViafintechSlip`, `TourPlanung`.
|
|
||||||
|
|
||||||
**Nothing under `Zustieg*`, `ZustiegPreis`, `ArtZustiegPreis`,
|
|
||||||
`LeistungBefoerderung` or `BuchungTNZuschlag` changed after the restore.** The June
|
|
||||||
dump is accurate for the booking and boarding areas; the only staleness is the six
|
|
||||||
fleet/payment tables above. For future runs, filter on `o.modify_date > '2026-06-03'`
|
|
||||||
to skip the restore baseline.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Columns — regenerates `…-1517.csv`
|
|
||||||
|
|
||||||
Column order and names match the existing header exactly, so the output is a
|
|
||||||
drop-in replacement. Export semicolon-separated with quoted values, same settings
|
|
||||||
as the June file.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT s.name AS schema_name,
|
|
||||||
t.name AS table_name,
|
|
||||||
c.name AS column_name,
|
|
||||||
ty.name AS data_type,
|
|
||||||
c.max_length,
|
|
||||||
c.precision,
|
|
||||||
c.scale,
|
|
||||||
c.is_nullable,
|
|
||||||
c.is_identity,
|
|
||||||
dc.definition AS default_definition
|
|
||||||
FROM sys.tables t
|
|
||||||
JOIN sys.schemas s ON s.schema_id = t.schema_id
|
|
||||||
JOIN sys.columns c ON c.object_id = t.object_id
|
|
||||||
JOIN sys.types ty ON ty.user_type_id = c.user_type_id
|
|
||||||
LEFT JOIN sys.default_constraints dc ON dc.object_id = c.default_object_id
|
|
||||||
ORDER BY t.name, c.column_id;
|
|
||||||
```
|
|
||||||
|
|
||||||
## 3. Foreign keys — regenerates `…-1518.csv`
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT fk.name AS foreign_key_name,
|
|
||||||
cs.name AS child_schema, ct.name AS child_table, cc.name AS child_column,
|
|
||||||
ps.name AS parent_schema, pt.name AS parent_table, pc.name AS parent_column
|
|
||||||
FROM sys.foreign_keys fk
|
|
||||||
JOIN sys.foreign_key_columns fkc ON fkc.constraint_object_id = fk.object_id
|
|
||||||
JOIN sys.tables ct ON ct.object_id = fkc.parent_object_id
|
|
||||||
JOIN sys.schemas cs ON cs.schema_id = ct.schema_id
|
|
||||||
JOIN sys.columns cc ON cc.object_id = fkc.parent_object_id
|
|
||||||
AND cc.column_id = fkc.parent_column_id
|
|
||||||
JOIN sys.tables pt ON pt.object_id = fkc.referenced_object_id
|
|
||||||
JOIN sys.schemas ps ON ps.schema_id = pt.schema_id
|
|
||||||
JOIN sys.columns pc ON pc.object_id = fkc.referenced_object_id
|
|
||||||
AND pc.column_id = fkc.referenced_column_id
|
|
||||||
ORDER BY ct.name, fk.name, fkc.constraint_column_id;
|
|
||||||
```
|
|
||||||
|
|
||||||
## 4. Primary keys & indexes — the documented gap
|
|
||||||
|
|
||||||
`bpn-connect-query-reference.md` records that PK/index data was expected but
|
|
||||||
missing, and that identity flags are not a reliable PK substitute here. This closes
|
|
||||||
it. Suggested filename: `BusPronet_<date>-pk-index.csv`.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT s.name AS schema_name,
|
|
||||||
t.name AS table_name,
|
|
||||||
i.name AS index_name,
|
|
||||||
i.type_desc,
|
|
||||||
i.is_primary_key,
|
|
||||||
i.is_unique,
|
|
||||||
i.is_unique_constraint,
|
|
||||||
ic.key_ordinal,
|
|
||||||
c.name AS column_name,
|
|
||||||
ic.is_included_column,
|
|
||||||
ic.is_descending_key
|
|
||||||
FROM sys.indexes i
|
|
||||||
JOIN sys.tables t ON t.object_id = i.object_id
|
|
||||||
JOIN sys.schemas s ON s.schema_id = t.schema_id
|
|
||||||
JOIN sys.index_columns ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id
|
|
||||||
JOIN sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
|
|
||||||
WHERE i.type > 0 -- exclude heaps
|
|
||||||
ORDER BY t.name, i.is_primary_key DESC, i.name, ic.key_ordinal;
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Boarding-price types — `ArtZustiegPreis`
|
|
||||||
|
|
||||||
**Answered 2026-08-04:** this is a **tariff-scheme** lookup, *not* a direction
|
|
||||||
lookup. Active schemes are 36 `Woche26/27`, 37 `WE26/27`, 38 `Event26/27`,
|
|
||||||
39 `SER26/27` (plus legacy A–T at `Status = 'L'`). There is no Hinfahrt/Rückfahrt
|
|
||||||
concept in this table.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT IDArtZustiegPreis, Code, Kurztext, Langtext, Status, Hierarchie
|
|
||||||
FROM dbo.ArtZustiegPreis
|
|
||||||
ORDER BY Hierarchie, IDArtZustiegPreis;
|
|
||||||
```
|
|
||||||
|
|
||||||
## 6. Configured prices for boarding point 18
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT zp.IDZustiegPreis,
|
|
||||||
zp.IDZustieg_FS,
|
|
||||||
z.Zustiegcode, z.Ort, z.Plz,
|
|
||||||
zp.IDArtZustiegPreis_FS,
|
|
||||||
ap.Code AS PreisArtCode, ap.Kurztext AS PreisArt,
|
|
||||||
zp.Preis, zp.PreisProvisionPflichtig,
|
|
||||||
zp.Sockelbetrag, zp.SockelbetragProvisionPflichtig
|
|
||||||
FROM dbo.ZustiegPreis zp
|
|
||||||
JOIN dbo.Zustieg z ON z.IDZustieg = zp.IDZustieg_FS
|
|
||||||
LEFT JOIN dbo.ArtZustiegPreis ap ON ap.IDArtZustiegPreis = zp.IDArtZustiegPreis_FS
|
|
||||||
WHERE zp.IDZustieg_FS = 18
|
|
||||||
ORDER BY zp.IDArtZustiegPreis_FS;
|
|
||||||
```
|
|
||||||
|
|
||||||
**Answered 2026-08-04:** three rows for point 18 (`ESS-Route`, Essen), one per
|
|
||||||
active tariff scheme — 36 → **17,90**, 37 → 12,90, 38 → 19,90. Prices are keyed by
|
|
||||||
(point, tariff scheme), **not** by direction. The export's
|
|
||||||
`<zustiege_rueck preis="17,90">` is therefore the same row surfaced through the
|
|
||||||
return service, not a separately configured return price.
|
|
||||||
|
|
||||||
## 7. How the two bus services resolve their boarding price — the mechanism
|
|
||||||
|
|
||||||
`LeistungBefoerderung` carries **`IDArtZustiegPreis_FS`** and
|
|
||||||
**`Richtung_Hin_Rueck`**, so each transport service selects which price type
|
|
||||||
applies. This query shows what outbound service 194779 and return service 194776
|
|
||||||
are configured to use.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT l.IDLeistung, l.Bezeichnung, l.Art, l.Unterart,
|
|
||||||
lb.Richtung_Hin_Rueck,
|
|
||||||
lb.IDArtZustiegPreis_FS,
|
|
||||||
ap.Code AS PreisArtCode, ap.Kurztext AS PreisArt,
|
|
||||||
lb.IDArtZustiegZeitplan_FS,
|
|
||||||
lb.ZustiegeGesperrt,
|
|
||||||
lb.IDZustieg_FS_Basisstelle,
|
|
||||||
lb.ZustiegIDs
|
|
||||||
FROM dbo.LeistungBefoerderung lb
|
|
||||||
JOIN dbo.Leistung l ON l.IDLeistung = lb.IDLeistung_FS
|
|
||||||
LEFT JOIN dbo.ArtZustiegPreis ap ON ap.IDArtZustiegPreis = lb.IDArtZustiegPreis_FS
|
|
||||||
WHERE l.IDLeistung IN (194779, 194776);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Answered 2026-08-04 — configuration is symmetric, config cause excluded:**
|
|
||||||
|
|
||||||
| Leistung | Richtung | `IDArtZustiegPreis_FS` | `ZustiegeGesperrt` | `ZustiegIDs` |
|
|
||||||
|---|---|---|---|---|
|
|
||||||
| 194779 Bus-Hinfahrt | H | 36 (Woche26/27) | 0 | 3,4,18,86,320,470,473 |
|
|
||||||
| 194776 Bus-Rückfahrt | R | 36 (Woche26/27) | 0 | 3,4,18,86,320,470,473 |
|
|
||||||
|
|
||||||
Both legs resolve to the same 17,90 and both have point 18 enabled. The differing
|
|
||||||
BusPro behaviour is therefore not explained by product configuration.
|
|
||||||
|
|
||||||
## 8. Surcharge types — `ArtZuschlag`
|
|
||||||
|
|
||||||
**Answered 2026-08-04:** a discounts/fees lookup (Zwei-Wochen-Rabatt,
|
|
||||||
Kinderermäßigung, Bearbeitungsgebühr, Preiskorrektur, …). No direction concept and
|
|
||||||
**no `Abfahrtsortzuschlag` entry** — so the `SON`/`TRA` positions in the booking
|
|
||||||
response are computed transport surcharges, not `BuchungTNZuschlag` rows. Query 9
|
|
||||||
below is aimed at the wrong table and can be skipped.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT IDArtZuschlag, Code, Kurztext, Langtext, Status, Hierarchie
|
|
||||||
FROM dbo.ArtZuschlag
|
|
||||||
ORDER BY Hierarchie, IDArtZuschlag;
|
|
||||||
```
|
|
||||||
|
|
||||||
## 9. Booked surcharge rows for an affected booking
|
|
||||||
|
|
||||||
The decisive one *if* a completed booking exhibiting the fault is available: does
|
|
||||||
the working booking persist two `BuchungTNZuschlag` rows and the failing one only
|
|
||||||
one?
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT b.Vorgang,
|
|
||||||
tn.IDBuchungTN,
|
|
||||||
z.IDBuchungTNZuschlag,
|
|
||||||
z.BZText, z.Text,
|
|
||||||
z.Art,
|
|
||||||
z.IDZustieg_FS,
|
|
||||||
az.Code AS ZuschlagArtCode, az.Kurztext AS ZuschlagArt,
|
|
||||||
z.BZPreisMitProvision, z.BZPreisOhneProvision
|
|
||||||
FROM dbo.BuchungTNZuschlag z
|
|
||||||
JOIN dbo.BuchungTN tn ON tn.IDBuchungTN = z.IDBuchungTN_FS
|
|
||||||
JOIN dbo.Buchung b ON b.IDBuchung = tn.IDBuchung_FS
|
|
||||||
LEFT JOIN dbo.ArtZuschlag az ON az.IDArtZuschlag = z.IDArtZuschlag_FS
|
|
||||||
WHERE b.Vorgang IN ('<Vorgang-1>', '<Vorgang-2>')
|
|
||||||
ORDER BY b.Vorgang, tn.IDBuchungTN, z.IDBuchungTNZuschlag;
|
|
||||||
```
|
|
||||||
|
|
||||||
**Caveat:** both captured cases were `<buchungsart>Anfrage</buchungsart>` inquiries,
|
|
||||||
and our flow only commits at step 4 — so these rows may not exist for them. Use the
|
|
||||||
`Vorgang` of an actually completed booking that showed the short total. If none
|
|
||||||
exists, skip this query; queries 5–7 carry the argument on their own.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## What to send back
|
|
||||||
|
|
||||||
Query 1's result, plus 5, 6, 7 and 8 as plain result sets — those four are small and
|
|
||||||
answer the pricing question directly. Queries 2–4 only if query 1 shows drift, as
|
|
||||||
CSV files replacing the ones in this directory.
|
|
||||||
@@ -1,305 +0,0 @@
|
|||||||
# Travel catalog — direct MSSQL queries (to replace the XML export)
|
|
||||||
|
|
||||||
> **Authority: [`docs/buspro-database-reference.md`](../buspro-database-reference.md).**
|
|
||||||
> Schema semantics, conventions and confidence marks live there and win on any conflict.
|
|
||||||
> This file is retained as a **query cookbook** — it holds worked SQL (included-services
|
|
||||||
> A2a/A2b, the full room-pricing chain B3) not reproduced in the reference.
|
|
||||||
|
|
||||||
Purpose: reproduce, straight from the BusPro MSSQL tables, the data the Symfony app
|
|
||||||
currently parses from the `Ziel_*.xml` exports, so the Go proxy can serve it without
|
|
||||||
the SFTP/XML round-trip. Two read paths:
|
|
||||||
|
|
||||||
- **Query A — travel list:** every bookable departure + product + dates (+ hotels,
|
|
||||||
+ a minimal bookable-services list).
|
|
||||||
- **Query B — detail:** everything for one selected `(date, hotel)` combination.
|
|
||||||
|
|
||||||
Validated against the live DB using the sample `var/xmlexport/Ziel_1121.xml`
|
|
||||||
(Produkt `1121`, departure `Reise.IDReise=12665`, hotel `Adresse.IDAdresse=224923`,
|
|
||||||
room-type `Zimmerart 1413`, pickup `Zustieg 494`). Columns verified in
|
|
||||||
`docs/buspronet-schema/*.csv`. Header, services, pickups, hotels, room pricing,
|
|
||||||
availability and tags are all confirmed against live data; the "included services"
|
|
||||||
list for Query A (§A2) is defined as price-`0.00` services + the 10 hardcoded
|
|
||||||
"Leistungen" selection groups (ids baked in). Nothing left to resolve.
|
|
||||||
|
|
||||||
## The naming trap (read first)
|
|
||||||
|
|
||||||
The XML export **renames** the core entities — do not map by name:
|
|
||||||
|
|
||||||
| XML term | Real table | Notes |
|
|
||||||
|---|---|---|
|
|
||||||
| `<reise idbuspro=1121>` | **`Produkt`** | the travel *product*: `IDProdukt`, `Code`, `Bezeichnung` |
|
|
||||||
| `<termin idprodukt=1121 idbuspro=12665>` | **`Reise`** | the **dated departure**: `IDReise=12665`, `IDProdukt_FS=1121`, `Code`, `TerminVon/Bis`, `ReiseArt`, `Status`, `StornoDatum` |
|
|
||||||
| DB `Termin` table | — | **ignore**: generic appointments (odometer/repairs) |
|
|
||||||
| `<leistung>` (lei_befoerderung / lei_sonstiges) | `ReiseLeistung → Leistung` | `Leistung.Art` = `BEF` / `SON`; price via `LeistungPreis` |
|
|
||||||
| `<hotel idbuspro=224923>` + rooms | `ReisePartner` (+ `Partner*` subsystem) | **not** `ReiseLeistung`; keyed by the hotel `Adresse` |
|
|
||||||
| `<zustieg>` | `LeistungBefoerderung.ZustiegIDs` → `Zustieg` (+ `ZustiegZeitplan` for the time) | |
|
|
||||||
| `<selektion>` / `<selektiongruppe>` | `ReiseSelektionen → Selektionsstamm` (+ `Selektionsgruppe → Selektionsgruppenstamm`) | |
|
|
||||||
|
|
||||||
`Produkt 1—N Reise`. **`Reise.IDReise` is the "date id"** and the anchor for all
|
|
||||||
per-departure data.
|
|
||||||
|
|
||||||
**Two distinct status axes** — don't conflate them:
|
|
||||||
1. **Departure lifecycle** — `Reise.Status` (`nvarchar(2)`), observed `''` ≈ 9767,
|
|
||||||
`S` ≈ 945, `L` ≈ 845. Confirmed against the live DB:
|
|
||||||
- `''` = active/bookable.
|
|
||||||
- `S` = **Storno** (cancelled): every `S` row has `StornoDatum` set.
|
|
||||||
- `L` = **template / future master** (not a real sellable departure): every `L`
|
|
||||||
row has `StornoDatum NULL`, far-future `TerminVon` (2029–2035) and template-style
|
|
||||||
`Code`s (`MRAGRMP`, no date suffix). `Leistung.Status` mirrors this (`''` = 196 504,
|
|
||||||
`L` = 1 672 — the leistungen attached to those template departures).
|
|
||||||
2. **Availability / booking status** — booking-state codes, **not** `Leistung.Status`
|
|
||||||
(which only holds `''`/`L`). Canonical dictionary = **`dbo.ArtBuchungsStatus`**
|
|
||||||
(`Code`), confirmed values:
|
|
||||||
|
|
||||||
| Code | Kurztext | meaning |
|
|
||||||
|---|---|---|
|
|
||||||
| `F` | Fest | firm/final booking (available) |
|
|
||||||
| `O` | Option | held option |
|
|
||||||
| `A` | Anfrage | on request / inquiry-only (groups-inquiry path) |
|
|
||||||
| `W` | Warteliste | waitlist |
|
|
||||||
| `S` | Storno | cancelled |
|
|
||||||
| `U` | Umbuchung | rebooking |
|
|
||||||
|
|
||||||
The **working column** for hotel availability is **`PartnerKontingent.BuchungsStatus`**
|
|
||||||
(`nvarchar(2)`) — confirmed to carry these: observed `''` ≈ 19 664 (free/Fest),
|
|
||||||
`S` ≈ 12 215 (stop), `A` ≈ 2 060 (on request). So per room/date: `''` = bookable,
|
|
||||||
`S` = not bookable, `A` = inquiry-only. (The same codes also appear via
|
|
||||||
`…IDArtBuchungsstatus_FS` on individual bookings such as `ZusatzZimmer`.) Transport
|
|
||||||
`<status_hin>`/`<status_rueck>` is not in `Leistung.Status`; treat it as derived from
|
|
||||||
the transport `Kontingent` count.
|
|
||||||
|
|
||||||
Bookable filter (departure-level): `Reise.Status = '' AND Reise.StornoDatum IS NULL`
|
|
||||||
(excludes both `S` Storno and `L` templates), then apply `PartnerKontingent.BuchungsStatus`
|
|
||||||
per room/date (exclude `S`, treat `A` as inquiry-only).
|
|
||||||
|
|
||||||
Note the XML repeats `lei_sonstiges` under each hotel; in the DB the services exist
|
|
||||||
**once** on the departure (`ReiseLeistung`).
|
|
||||||
|
|
||||||
Conventions: ids `bigint`→`int64`; prices `decimal(18,2)`; `_FS` = foreign key (many
|
|
||||||
soft/unenforced — join defensively); bracket non-ASCII columns (`[Reisebüro]`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Query A — travel list
|
|
||||||
|
|
||||||
### A1. Departures + product + dates (header)
|
|
||||||
```sql
|
|
||||||
SELECT
|
|
||||||
p.IDProdukt AS ProductId, p.Code AS ProductCode, p.Bezeichnung AS ProductLabel,
|
|
||||||
r.IDReise AS DateId, r.Code AS DateCode,
|
|
||||||
r.ReiseArt AS TravelType, -- XML reiseart, e.g. 'F'
|
|
||||||
r.Bezeichnung AS DateLabel,
|
|
||||||
r.TerminVon AS DateFrom, r.TerminBis AS DateTo
|
|
||||||
FROM dbo.Reise AS r
|
|
||||||
JOIN dbo.Produkt AS p ON p.IDProdukt = r.IDProdukt_FS
|
|
||||||
WHERE r.Status = '' AND r.StornoDatum IS NULL -- bookable (confirm status codes)
|
|
||||||
ORDER BY r.TerminVon, p.Code;
|
|
||||||
```
|
|
||||||
Customer description (XML `<text>`) = `dbo.ReiseText.ExternText` (`IDReise_FS`) — join if wanted.
|
|
||||||
|
|
||||||
### A2. Included-services list per departure
|
|
||||||
"Minimum services" = the **included** services (no surcharge), from two sources that
|
|
||||||
get merged into one plain list per departure.
|
|
||||||
|
|
||||||
**A2a — included `Leistung`s (sales price = 0.00).** Priced services are optional
|
|
||||||
add-ons; the package-included ones have a `0.00` sales price (schema 1).
|
|
||||||
```sql
|
|
||||||
SELECT
|
|
||||||
rl.IDReise_FS AS DateId,
|
|
||||||
l.IDLeistung AS ServiceId,
|
|
||||||
l.Art AS Type, -- 'BEF' (transport) | 'SON' (other)
|
|
||||||
l.Unterart AS SubType, -- BUS/PKW/SPA/VER/SON/LVS/...
|
|
||||||
l.Bezeichnung AS Label
|
|
||||||
FROM dbo.ReiseLeistung rl
|
|
||||||
JOIN dbo.Leistung l ON l.IDLeistung = rl.IDLeistung_FS
|
|
||||||
JOIN dbo.LeistungPreis lp ON lp.IDLeistung_FS = l.IDLeistung
|
|
||||||
AND lp.IDArtPreisSchema_FS = 1 -- standard sales price (confirmed)
|
|
||||||
WHERE rl.IDReise_FS IN (@DateIds)
|
|
||||||
AND lp.Preis = 0.00 -- included = no surcharge
|
|
||||||
ORDER BY rl.IDReise_FS, l.Art, l.Unterart;
|
|
||||||
```
|
|
||||||
> Note: a `LEFT JOIN` would also surface services with **no** price row (NULL). Those
|
|
||||||
> are *not* the same as priced-at-zero — keep the inner join so "included" means an
|
|
||||||
> explicit `0.00`. (If BusPro models some included items as price-less rows, widen to
|
|
||||||
> `lp.Preis = 0.00 OR lp.IDLeistungPreis IS NULL` — confirm with the discovery query.)
|
|
||||||
|
|
||||||
**A2b — "Leistungen…" selection groups (plain list).** The catalog also lists included
|
|
||||||
services as selection tags under the "Leistungen" groups. Filter by a **hardcoded list
|
|
||||||
of `Selektionsgruppenstamm` ids** (configured in the proxy) rather than a label match —
|
|
||||||
the labels can be renamed, the ids are stable. Same join as B4; emit `Selektion` as the
|
|
||||||
plain text item.
|
|
||||||
```sql
|
|
||||||
SELECT rs.IDReise_FS AS DateId,
|
|
||||||
gs.IDSelektionsgruppenstamm AS GroupId,
|
|
||||||
gs.Selektionsgruppe AS GroupLabel, -- e.g. 'Leistungen inklusive'
|
|
||||||
s.Selektion AS Item
|
|
||||||
FROM dbo.ReiseSelektionen rs
|
|
||||||
JOIN dbo.Selektionsstamm s ON s.IDSelektionsstamm = rs.IDSelektionsstamm_FS
|
|
||||||
JOIN dbo.Selektionsgruppe sg ON sg.IDSelektionsstamm_FS = s.IDSelektionsstamm
|
|
||||||
JOIN dbo.Selektionsgruppenstamm gs ON gs.IDSelektionsgruppenstamm = sg.IDSelektionsgruppenstamm_FS
|
|
||||||
WHERE rs.IDReise_FS IN (@DateIds)
|
|
||||||
AND gs.IDSelektionsgruppenstamm IN (70, 89, 87, 88, 86, 7, 8, 90, 85, 71)
|
|
||||||
ORDER BY rs.IDReise_FS, gs.Position, sg.Position;
|
|
||||||
```
|
|
||||||
> The "Leistungen" `Selektionsgruppenstamm` ids (captured from the live DB — hardcode in
|
|
||||||
> the proxy; re-check if groups are added/renamed):
|
|
||||||
>
|
|
||||||
> | id | group | id | group |
|
|
||||||
> |---|---|---|---|
|
|
||||||
> | 70 | Leistungen | 86 | Leistungen Programm |
|
|
||||||
> | 89 | Leistungen Events | 7 | Leistungen Skipass |
|
|
||||||
> | 87 | Leistungen Kurse | 8 | Leistungen Sonstige |
|
|
||||||
> | 88 | Leistungen Personal | 90 | Leistungen Transport |
|
|
||||||
> | 85 | Leistungen Übernachtung | 71 | Leistungen Verpflegung |
|
|
||||||
> `LeistungPreis` has one row per `(Leistung, IDArtPreisSchema_FS)`; schema `1` is the
|
|
||||||
> standard sales price and reproduced the XML service prices exactly (13.90, 50.00, …).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Query B — detail for one `(@DateId [, @HotelId])`
|
|
||||||
|
|
||||||
Run a header query + one query per child collection; assemble in Go.
|
|
||||||
|
|
||||||
### B1. Header — A1 with `WHERE r.IDReise = @DateId` (+ `ReiseText.ExternText`).
|
|
||||||
|
|
||||||
### B2. Services (transport + other, with direction & pickups) — **confirmed**
|
|
||||||
```sql
|
|
||||||
SELECT
|
|
||||||
l.IDLeistung AS ServiceId,
|
|
||||||
l.Art AS Type, -- BEF | SON
|
|
||||||
l.Unterart AS SubType,
|
|
||||||
l.Bezeichnung AS Label,
|
|
||||||
l.Pflichtmerkmal AS Mandatory,
|
|
||||||
l.Kontingent AS Available,
|
|
||||||
l.Alter_VON AS AgeFrom, l.Alter_BIS AS AgeTo,
|
|
||||||
l.Termin_VON AS DateFrom, l.Termin_BIS AS DateTo,
|
|
||||||
lb.Richtung_Hin_Rueck AS Direction, -- 'H' | 'R' (transport only)
|
|
||||||
lb.ZustiegIDs AS PickupIds, -- delimited list of dbo.Zustieg ids
|
|
||||||
lb.IDArtZustiegZeitplan_FS AS PickupTimePlanId,
|
|
||||||
lp.Preis AS Price
|
|
||||||
FROM dbo.ReiseLeistung rl
|
|
||||||
JOIN dbo.Leistung l ON l.IDLeistung = rl.IDLeistung_FS
|
|
||||||
LEFT JOIN dbo.LeistungBefoerderung lb ON lb.IDLeistung_FS = l.IDLeistung
|
|
||||||
LEFT JOIN dbo.LeistungPreis lp ON lp.IDLeistung_FS = l.IDLeistung
|
|
||||||
AND lp.IDArtPreisSchema_FS = 1
|
|
||||||
WHERE rl.IDReise_FS = @DateId
|
|
||||||
ORDER BY l.Art, l.Unterart, l.Bezeichnung;
|
|
||||||
```
|
|
||||||
`LeistungBefoerderung` present ⇒ transport (`lei_befoerderung`), else `lei_sonstiges`.
|
|
||||||
English texts (`<fremdsprache>`) = `dbo.LeistungText.ExternText` (validate).
|
|
||||||
|
|
||||||
### B2b. Pickup detail (resolve the `ZustiegIDs` list + time) — **confirmed**
|
|
||||||
For each id parsed from `lb.ZustiegIDs`:
|
|
||||||
```sql
|
|
||||||
SELECT z.IDZustieg AS PickupId, z.Zustiegcode AS Code,
|
|
||||||
z.Plz AS PostalCode, z.Ort AS City, z.Strasse AS Street,
|
|
||||||
CONVERT(time, zp.Uhrzeit) AS PickupTime
|
|
||||||
FROM dbo.Zustieg z
|
|
||||||
LEFT JOIN dbo.ZustiegZeitplan zp
|
|
||||||
ON zp.IDZustieg_FS = z.IDZustieg
|
|
||||||
AND zp.IDArtZustiegZeitplan_FS = @PickupTimePlanId -- from B2 (0 ⇒ no time)
|
|
||||||
WHERE z.IDZustieg = @PickupId;
|
|
||||||
```
|
|
||||||
Only the **time-of-day** of `Uhrzeit` is meaningful (the date part is a placeholder).
|
|
||||||
Verified: zustieg `494`, plan `2546` → `05:15`.
|
|
||||||
|
|
||||||
### B3. Hotels + rooms + board — **Partner subsystem**
|
|
||||||
The XML `<hotel idbuspro=224923>` is a hotel **`Adresse.IDAdresse`** (224923 =
|
|
||||||
"NEU Sportclub Waldschlössli – Gruppen 2"), **not** a `ReisePartner`. Hotels are
|
|
||||||
assigned at the **product** level via the join table `PartnerProdukt
|
|
||||||
(IDProdukt_FS, IDAdresse_FS)` — that's why a departure (`IDReise`) has no `ReisePartner`
|
|
||||||
row. (`ReisePartner`/`IDArtVerpflegung_FS` is the booking-side hotel; for the *catalog*
|
|
||||||
use `PartnerProdukt`. `ProduktBeschreibung.IDAdresse_FS_Hotel` = the single featured hotel.)
|
|
||||||
```sql
|
|
||||||
-- hotels of the product (each IDAdresse_FS = an XML <hotel idbuspro>)
|
|
||||||
SELECT pp.IDAdresse_FS AS HotelAddressId, a.SuchName AS HotelName
|
|
||||||
FROM dbo.PartnerProdukt pp
|
|
||||||
JOIN dbo.Adresse a ON a.IDAdresse = pp.IDAdresse_FS
|
|
||||||
WHERE pp.IDProdukt_FS = @ProductId -- e.g. 1121
|
|
||||||
-- AND pp.IDAdresse_FS = @HotelAddressId
|
|
||||||
ORDER BY a.SuchName;
|
|
||||||
```
|
|
||||||
Board (`IDArtVerpflegung`) and stay dates aren't on `PartnerProdukt`; board comes from
|
|
||||||
the `PartnerPreisKategorie` rows below, and the stay span = the departure dates
|
|
||||||
(`Reise.TerminVon/TerminBis`, here `11.–13.12.2026` ⇒ `Naechte = 2`).
|
|
||||||
Rooms / price / availability for a hotel address. The pricing fans out over
|
|
||||||
`room → price-category → season tafel → nights`; **validated** against the live DB
|
|
||||||
for hotel `224923`, room-type `Zimmerart 1413` (Code `MBZ`) → `IDArtPartnerPreisCode 38`
|
|
||||||
→ category `9478`, season `15420` (covers `2026-12-11`), `Naechte=2`, `EK_VK='VK'`,
|
|
||||||
`IDArtPreisSchema_FS=1` → **`269,00`** (single clean row; same schema id `1` as the
|
|
||||||
`LeistungPreis` side). Map a room to its price category via `IDArtPartnerPreisCode_FS`.
|
|
||||||
(The XML headline `abpreis` of `289,00` = this room price + mandatory services, i.e.
|
|
||||||
computed — see note below; the flat per-room price is `269,00`.)
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- 1) rooms the hotel offers
|
|
||||||
-- PartnerZimmer ⋈ PartnerPreisKategorie on (IDAdresse_FS, IDArtPartnerPreisCode_FS)
|
|
||||||
SELECT pz.IDPartnerZimmer AS RoomId, za.Code AS RoomCode, za.Bezeichnung AS RoomLabel,
|
|
||||||
za.PaxMin, za.PaxMax, pz.IDArtPartnerPreisCode_FS AS PriceCode
|
|
||||||
FROM dbo.PartnerZimmer pz
|
|
||||||
JOIN dbo.Zimmerart za ON za.IDZimmerart = pz.IDZimmerart_FS
|
|
||||||
WHERE pz.IDAdresse_FS = @HotelAddressId;
|
|
||||||
|
|
||||||
-- 2) which season tafel covers the arrival date
|
|
||||||
SELECT IDPartnerSaisonTafel_FS FROM dbo.PartnerSaisonzeit
|
|
||||||
WHERE IDAdresse_FS = @HotelAddressId
|
|
||||||
AND TerminVon <= @Anreise AND TerminBis >= @Anreise
|
|
||||||
AND EK_VK = 'VK'; -- sale side
|
|
||||||
|
|
||||||
-- 3) price for the room's category × that season × nights
|
|
||||||
SELECT pp.Preis AS RoomPrice, pp.Naechte, pp.Verlaengerung
|
|
||||||
FROM dbo.PartnerPreisKategorie pk
|
|
||||||
JOIN dbo.PartnerPreis pp ON pp.IDPartnerPreisKategorie_FS = pk.IDPartnerPreisKategorie
|
|
||||||
WHERE pk.IDAdresse_FS = @HotelAddressId
|
|
||||||
AND pk.IDArtPartnerPreisCode_FS = @PriceCode -- from step 1 (room → category)
|
|
||||||
AND pk.IDArtVerpflegung_FS = @BoardId -- ReisePartner.IDArtVerpflegung_FS
|
|
||||||
AND pk.EK_VK = 'VK'
|
|
||||||
AND pp.IDPartnerSaisonTafel_FS = @SaisonTafelId -- from step 2
|
|
||||||
AND pp.Naechte = @Nights; -- nights of the stay
|
|
||||||
```
|
|
||||||
> `EK_VK` distinguishes purchase (`EK`) from sale (`VK`) — always filter `VK` for the
|
|
||||||
> customer price. `PartnerZimmer.PreisJeZimmer` flags per-room vs. per-person pricing.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- availability (XML 'verfuegbar') — VALIDATED: = PartnerKontingent.Anzahl for the
|
|
||||||
-- (hotel, room-type, date-span). IDZimmer_FS = the XML idbuspro_zimmer (a Zimmerart).
|
|
||||||
-- Sample: IDAdresse_FS=224923, IDZimmer_FS=1413, 2026-12-11 → Anzahl=25 = XML verfuegbar.
|
|
||||||
SELECT IDZimmer_FS, Anzahl, BuchungsStatus FROM dbo.PartnerKontingent
|
|
||||||
WHERE IDAdresse_FS = @HotelAddressId
|
|
||||||
AND IDZimmer_FS = @ZimmerartId -- the room type (XML idbuspro_zimmer)
|
|
||||||
AND TerminVon <= @Abreise AND TerminBis >= @Anreise;
|
|
||||||
```
|
|
||||||
> `verfuegbar` is a **flat column** (`PartnerKontingent.Anzahl`) — reproducible by SQL.
|
|
||||||
> Gate it with `BuchungsStatus` (`''` bookable, `S` stop, `A` on-request).
|
|
||||||
> `<abpreis>` (the headline "from" price) is still **computed** (cheapest valid room +
|
|
||||||
> mandatory services) — recompute in the proxy from the per-room prices above.
|
|
||||||
|
|
||||||
### B4. Selection tags + groups — **confirmed**
|
|
||||||
```sql
|
|
||||||
SELECT gs.Selektionsgruppe AS GroupLabel, gs.Position AS GroupPos,
|
|
||||||
s.IDSelektionsstamm AS SelectionId, s.Selektion AS SelectionLabel
|
|
||||||
FROM dbo.ReiseSelektionen rs
|
|
||||||
JOIN dbo.Selektionsstamm s ON s.IDSelektionsstamm = rs.IDSelektionsstamm_FS
|
|
||||||
JOIN dbo.Selektionsgruppe sg ON sg.IDSelektionsstamm_FS = s.IDSelektionsstamm
|
|
||||||
JOIN dbo.Selektionsgruppenstamm gs ON gs.IDSelektionsgruppenstamm = sg.IDSelektionsgruppenstamm_FS
|
|
||||||
WHERE rs.IDReise_FS = @DateId
|
|
||||||
ORDER BY gs.Position, sg.Position;
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Status / open decisions
|
|
||||||
1. ~~**Status codes** (A1)~~ — ✅ confirmed: `Reise.Status` `''` = bookable, `S` = Storno
|
|
||||||
(cancelled, `StornoDatum` set), `L` = template/future master (exclude both).
|
|
||||||
2. ~~**F/A/O/S column**~~ — ✅ codes in `dbo.ArtBuchungsStatus` (F/O/A/W/S/U); the
|
|
||||||
working hotel-availability column is `PartnerKontingent.BuchungsStatus` (`''`/`S`/`A`).
|
|
||||||
3. ~~**Hotel link**~~ — ✅ XML `<hotel idbuspro>` = `Adresse.IDAdresse`; departure→hotel
|
|
||||||
via `PartnerProdukt(IDProdukt_FS, IDAdresse_FS)` at product level (not `ReisePartner`).
|
|
||||||
4. ~~**Partner room pricing**~~ — ✅ fully validated end-to-end: room `1413` (MBZ) →
|
|
||||||
code `38` → category `9478` → season `15420` × `Naechte=2` × `VK` × schema `1` =
|
|
||||||
**`269,00`**. (XML `abpreis 289,00` = room + mandatory services = computed.)
|
|
||||||
5. ~~**`verfuegbar`**~~ — ✅ flat: `PartnerKontingent.Anzahl` for `(IDAdresse_FS, IDZimmer_FS,
|
|
||||||
date-span)`; `1413 / 2026-12-11 → 25`. `<abpreis>` remains computed (recompute in proxy).
|
|
||||||
6. ~~**Minimum-services filter**~~ — ✅ defined: "included services" =
|
|
||||||
(A2a) `Leistung`s with sales price `0.00` (schema 1) **+** (A2b) selection items from
|
|
||||||
the hardcoded "Leistungen" `Selektionsgruppenstamm` ids, merged into one plain list.
|
|
||||||
@@ -1,380 +0,0 @@
|
|||||||
# EP Reisen Mobile App — React Native Plan
|
|
||||||
|
|
||||||
**Copy this file into the React Native repository** (suggested: `docs/plan.md`). It is
|
|
||||||
written to be self-contained: you do not need access to the backend codebase to work from
|
|
||||||
it. Where it says "the contract", it means the API guide and fixtures shipped by the
|
|
||||||
backend team — see §3.
|
|
||||||
|
|
||||||
**Companion (backend side, not needed here):** `docs/mobile-app/backend-plan.md` in the
|
|
||||||
`myep` Symfony repository.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. What this app is
|
|
||||||
|
|
||||||
EP Reisen sells group travel — primarily ski and coach trips. A booking is not "one person
|
|
||||||
buys one thing": it is **one applicant booking a set of rooms for a group of participants**,
|
|
||||||
where each participant independently selects ski passes, equipment rental, board, courses,
|
|
||||||
transport, insurance and more, with prices and eligibility varying per person (notably by
|
|
||||||
age).
|
|
||||||
|
|
||||||
The existing web product is a server-rendered Symfony application. This app covers its
|
|
||||||
**public** surface. The admin area is explicitly out of scope and will remain web-only.
|
|
||||||
|
|
||||||
The app is being built for App Store presence and a native-feeling booking flow. Push
|
|
||||||
notifications are **not** currently a requirement — do not build infrastructure for them
|
|
||||||
without asking.
|
|
||||||
|
|
||||||
### The two systems behind the API
|
|
||||||
|
|
||||||
You will see this distinction leak into behaviour, so it is worth knowing:
|
|
||||||
|
|
||||||
- **BusProNet (BPN)** — an external, third-party XML system that is the system of record
|
|
||||||
for individual travel bookings. It is slow, occasionally unavailable, and its rules are
|
|
||||||
not ours to change. Endpoints backed by it can fail in ways local data cannot.
|
|
||||||
- **Local database** — system of record for group/accommodation bookings, user accounts,
|
|
||||||
newsletter consent, and booking drafts. Fast and reliable.
|
|
||||||
|
|
||||||
Increments are deliberately ordered local-data-first so you are not fighting BPN latency
|
|
||||||
while also debugging your own foundations.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. The one architectural rule
|
|
||||||
|
|
||||||
> **Business rules live on the server. The app renders what it is told.**
|
|
||||||
|
|
||||||
This is not a stylistic preference; it is the reason the project is viable. The backend
|
|
||||||
already contains, in one place, 27 condition classes and 25 field handlers that decide —
|
|
||||||
per participant, per field — whether a field is visible, editable, required, or shown as
|
|
||||||
read-only text, and which choices it offers. Reimplementing any of that in TypeScript
|
|
||||||
creates two sources of truth that will silently diverge.
|
|
||||||
|
|
||||||
**Concretely, never write code in this repo that:**
|
|
||||||
|
|
||||||
- decides whether a field is shown, e.g. inferring that a licence-plate input appears
|
|
||||||
because transport is by car;
|
|
||||||
- computes or re-computes a price, total, discount or voucher deduction from line items;
|
|
||||||
- decides whether a participant is eligible for a service, especially by age;
|
|
||||||
- validates a business constraint (as opposed to a formatting constraint like "this looks
|
|
||||||
like an email").
|
|
||||||
|
|
||||||
**If the app appears to need one of those, the schema is missing information. That is a
|
|
||||||
backend bug.** Report it; do not patch around it. This single discipline is what the whole
|
|
||||||
architecture buys, and it is lost the first time it is violated.
|
|
||||||
|
|
||||||
Client-side validation is limited to immediate input affordances — keyboard type, max
|
|
||||||
length, obviously-malformed email — purely for responsiveness. The server's verdict is
|
|
||||||
always authoritative and always re-checked on submit.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. The contract
|
|
||||||
|
|
||||||
The backend ships two artifacts. Together they are your ground truth.
|
|
||||||
|
|
||||||
1. **The API guide** — a self-contained markdown document describing every endpoint:
|
|
||||||
method, path, auth scope, request shape, response shape, error shapes. The backend
|
|
||||||
team updates it in the same commit as the endpoint.
|
|
||||||
2. **Fixtures** — real JSON responses, written out by the backend's own tests. Because
|
|
||||||
they are test *output*, they cannot silently go stale.
|
|
||||||
|
|
||||||
### How to work with them
|
|
||||||
|
|
||||||
- **Build against fixtures first**, then point at a live backend. This means app work
|
|
||||||
never blocks on a deployed endpoint.
|
|
||||||
- **Generate your TypeScript types from the contract** rather than hand-writing them, so
|
|
||||||
a schema change surfaces as a compile error instead of a runtime surprise.
|
|
||||||
- **When the guide is ambiguous, ask — do not guess.** A guess that happens to work
|
|
||||||
becomes an undocumented dependency on an accident.
|
|
||||||
|
|
||||||
### Error shapes to expect
|
|
||||||
|
|
||||||
The existing API does not have one universal error envelope. Handle all of these:
|
|
||||||
|
|
||||||
- `{"message": "..."}` — typically with 404 or 400.
|
|
||||||
- `{"error": "...", "type": "api_error"}` — upstream (BPN) failure. **Some endpoints
|
|
||||||
return this with HTTP 200.** Check for the presence of an `error` key, not only the
|
|
||||||
status code.
|
|
||||||
- `{"success": false, "message": "..."}` — some write endpoints.
|
|
||||||
- **400 with a constraint-violation body** — validation failures. These are the ones you
|
|
||||||
map back onto form fields; see §5.
|
|
||||||
|
|
||||||
`401` means the token is missing or invalid (refresh, then retry once). `403` means
|
|
||||||
insufficient scope — a bug, not a user-recoverable state.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Authentication
|
|
||||||
|
|
||||||
OAuth2 **authorization_code with PKCE**, against the backend's `/token` endpoint. The
|
|
||||||
password and implicit grants are disabled server-side and are not available to you.
|
|
||||||
|
|
||||||
Requirements:
|
|
||||||
|
|
||||||
- Use the system browser / `ASWebAuthenticationSession` / Custom Tabs — **not** a
|
|
||||||
`WebView`. App Store review and OAuth best practice both require this.
|
|
||||||
- Store tokens in the platform secure store (Keychain / Keystore), never in
|
|
||||||
`AsyncStorage`.
|
|
||||||
- **Access tokens are short-lived** — 10 minutes today, likely 1 hour by the time you
|
|
||||||
build. Confirm with the backend team in Increment 0. Either way the refresh-token grant
|
|
||||||
is enabled and you must use it.
|
|
||||||
- **Refresh-token rotation is ON.** Each refresh returns a *new* refresh token and revokes
|
|
||||||
the one you just used. This has two consequences you must design for, not discover:
|
|
||||||
- **Single-flight refresh is mandatory.** If two requests 401 at the same time and both
|
|
||||||
try to refresh, one wins and the other presents an already-revoked token — logging the
|
|
||||||
user out mid-booking. Put a mutex around refresh and queue every other 401 behind it,
|
|
||||||
then replay them with the new token. This is the most common OAuth bug in mobile apps
|
|
||||||
and the booking flow's per-field schema refreshes will find it immediately.
|
|
||||||
- **Persist the rotated token atomically before using it.** A crash between receiving a
|
|
||||||
new refresh token and storing it logs the user out permanently.
|
|
||||||
- The refresh TTL is a sliding window (currently ~1 month, likely 3), so an active user
|
|
||||||
should never be forced to re-authenticate. If yours are, the refresh path is broken —
|
|
||||||
treat it as a bug, not as expected behaviour.
|
|
||||||
- The redirect URI must be registered server-side against the exact host the app calls.
|
|
||||||
This bites during local development — see §8.
|
|
||||||
|
|
||||||
**You will never handle credentials for the upstream BPN system.** The server resolves
|
|
||||||
those internally. If any task seems to require the app to send a BPN password, stop and
|
|
||||||
ask; the answer is no.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Server-driven UI
|
|
||||||
|
|
||||||
The dynamic parts of the booking flow are rendered from a schema the server computes. You
|
|
||||||
build **one generic renderer**, not per-screen field logic.
|
|
||||||
|
|
||||||
### Expected schema shape
|
|
||||||
|
|
||||||
Confirm against the fixtures — they are authoritative — but the agreed shape is:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"name": "skiPass",
|
|
||||||
"type": "choice",
|
|
||||||
"label": "Skipass",
|
|
||||||
"required": true,
|
|
||||||
"value": 881,
|
|
||||||
"choices": [
|
|
||||||
{"id": 881, "label": "6 Tage", "price": 249.0}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{"name": "licensePlate", "type": "text", "hidden": true},
|
|
||||||
{"name": "pickup", "type": "choice", "readonly": true, "value": 12, "choices": []},
|
|
||||||
{"name": "firstName", "type": "text", "staticText": true, "value": "Anna"}
|
|
||||||
],
|
|
||||||
"dependsOn": {
|
|
||||||
"licensePlate": ["transportationOutbound"],
|
|
||||||
"pickup": ["transportationOutbound"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Field states — all four are distinct
|
|
||||||
|
|
||||||
| State | Meaning | Render as |
|
|
||||||
|---|---|---|
|
|
||||||
| `hidden` | Not applicable at all | Nothing |
|
|
||||||
| `staticText` | Value matters, user may not change it | Read-only text, no input affordance |
|
|
||||||
| `readonly` | Input shown but not editable | Disabled-looking input |
|
|
||||||
| `disabled` | Temporarily not interactive | Greyed input |
|
|
||||||
|
|
||||||
`hidden` and `staticText` are genuinely different and the backend treats them
|
|
||||||
differently — `staticText` is used, for example, when personal data is locked because
|
|
||||||
changing it would create a duplicate customer record upstream. Collapsing the two will
|
|
||||||
produce wrong screens.
|
|
||||||
|
|
||||||
### `dependsOn`
|
|
||||||
|
|
||||||
Maps a field to the fields whose changes invalidate it. When the user changes field *X*,
|
|
||||||
any field listing *X* in its `dependsOn` may now have different visibility, state, or
|
|
||||||
choices. **Re-fetch the schema from the server** — do not attempt to recompute locally.
|
|
||||||
This is the mechanism that keeps rule evaluation server-side.
|
|
||||||
|
|
||||||
Debounce these refreshes and keep the UI responsive during them, but never predict the
|
|
||||||
result optimistically for anything that affects price or eligibility.
|
|
||||||
|
|
||||||
### Field types you must support
|
|
||||||
|
|
||||||
`text`, `email`, `tel`, `date`, `choice` (single), `multichoice`, `checkbox`, `address`
|
|
||||||
(composite), plus a `static` presentation mode. Confirm the final list against the
|
|
||||||
contract before building.
|
|
||||||
|
|
||||||
### Mapping validation errors back
|
|
||||||
|
|
||||||
The server returns violations keyed by a property path (e.g.
|
|
||||||
`participants[2].dateOfBirth`). Your renderer must resolve those onto the right field on
|
|
||||||
the right participant. Design for this from the start — retrofitting it is painful.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Domain glossary
|
|
||||||
|
|
||||||
Enough to read the API without guessing.
|
|
||||||
|
|
||||||
- **Travel** — a dated departure of a product, at a specific hotel. Identified by a
|
|
||||||
numeric `dateId` + `hotelId`, or by string codes. Carries the available rooms, services,
|
|
||||||
pickups, insurances.
|
|
||||||
- **Applicant** — participant index 0. The person who books and is billed. Subject to
|
|
||||||
extra rules the others are not; their personal data is frequently locked (`staticText`).
|
|
||||||
- **Participant** — one traveller. Most selections live per participant, not per booking.
|
|
||||||
- **Room selection** — how many of which room type. This determines the participant count,
|
|
||||||
which is why step 1 precedes step 2.
|
|
||||||
- **Service** — anything bookable alongside: ski pass, equipment rental, board, courses,
|
|
||||||
transport, parking, additional services. Some are mandatory, some auto-booked with
|
|
||||||
opt-out, some age-restricted.
|
|
||||||
- **Booking status** — `F` firm booking, `O` option (held), `A` inquiry. Determined by
|
|
||||||
server rules, not user choice.
|
|
||||||
- **Payment method** — `transfer` or `debit`. Debit requires bank account details.
|
|
||||||
- **Voucher** — purchase vouchers (a balance), goodwill vouchers, and promo vouchers
|
|
||||||
(fixed or percentage, per-participant or per-booking). **Always** server-validated;
|
|
||||||
never compute a discount locally.
|
|
||||||
- **Baby** — participants aged 0–2 are a real special case in eligibility and pricing.
|
|
||||||
- **Mutability** — for existing bookings, BPN decides per field whether a change is still
|
|
||||||
permitted. Surfaces as `staticText`/`readonly`. Never override it.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. Increments
|
|
||||||
|
|
||||||
Each increment is a working loop with the backend session; see §9. Do not start an
|
|
||||||
increment before its backend endpoints and fixtures exist.
|
|
||||||
|
|
||||||
### Increment 0 — Foundations
|
|
||||||
|
|
||||||
- RN app skeleton, navigation, theming, error boundary.
|
|
||||||
- OAuth2 authorization_code + PKCE, secure token storage, silent refresh with request
|
|
||||||
queuing.
|
|
||||||
- API client: auth injection, 401-refresh-retry, the error shapes in §3, and a fixture
|
|
||||||
mock mode toggled by env.
|
|
||||||
- **The generic SDUI renderer** — consumes `{fields, dependsOn}`, renders every field type,
|
|
||||||
emits changes, maps server violations onto fields.
|
|
||||||
- Type generation from the contract wired into the build.
|
|
||||||
- **Done when:** Increment 1 renders entirely through the generic renderer.
|
|
||||||
|
|
||||||
### Increment 1 — Accommodation & group offers (pilot)
|
|
||||||
|
|
||||||
Local data, fast, low risk. Deliberately first so the foundations are exercised end-to-end
|
|
||||||
before anything hard.
|
|
||||||
|
|
||||||
- Offer list for the signed-in user, offer detail, accept an offer.
|
|
||||||
- **Validates:** auth, error handling, navigation, the fixture pipeline.
|
|
||||||
|
|
||||||
### Increment 2 — Account, personal data, newsletter, registration
|
|
||||||
|
|
||||||
- Sign-up, password reset, profile view/edit, newsletter preferences.
|
|
||||||
- **First real use of SDUI** on a small form, before betting the booking flow on it. Expect
|
|
||||||
to find gaps in the renderer here — that is the point of doing it now.
|
|
||||||
|
|
||||||
### Increment 3 — Booking read
|
|
||||||
|
|
||||||
- "My travels": booking list, booking detail with participants, services and price
|
|
||||||
breakdown, document/invoice download.
|
|
||||||
- First BPN-backed screens. Expect slower responses and genuine upstream failures — build
|
|
||||||
proper loading and error states here, and reuse them everywhere after.
|
|
||||||
|
|
||||||
### Increment 4 — *(backend only)*
|
|
||||||
|
|
||||||
An internal backend refactor that produces the participant field schema. No app work.
|
|
||||||
Good moment to pay down debt or firm up the renderer.
|
|
||||||
|
|
||||||
### Increment 5 — Booking edit
|
|
||||||
|
|
||||||
- Edit an existing booking through a server-persisted draft: load draft, edit participants
|
|
||||||
via SDUI, review changes, submit.
|
|
||||||
- Honour mutability strictly — many fields will be `staticText`, and that is correct.
|
|
||||||
- Drafts are server-side, so edits survive app restarts. Design the UX around that.
|
|
||||||
|
|
||||||
### Increment 6 — Booking create
|
|
||||||
|
|
||||||
The full four-step flow, last, once everything above is proven.
|
|
||||||
|
|
||||||
1. **Rooms** — select room types and quantities. Determines participant count.
|
|
||||||
2. **Participants** — one card per participant, each opening an SDUI form. Groups can be
|
|
||||||
large (50+); the web version lazy-loads per participant and you should too.
|
|
||||||
3. **Payment** — method, and bank details when debit.
|
|
||||||
4. **Confirmation** — full summary, terms, submit.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
- State lives in a **server-side draft keyed by UUID**, not on the device. The app holds
|
|
||||||
the draft id and renders server state.
|
|
||||||
- Price refreshes are server calls. Never compute totals locally, not even for a
|
|
||||||
"preview".
|
|
||||||
- Submission goes to BPN and can fail or partially succeed. Treat the submit step as
|
|
||||||
genuinely fallible and make retry safe.
|
|
||||||
|
|
||||||
### Increment 7 — Discovery *(not yet scoped)*
|
|
||||||
|
|
||||||
Browsing travels and hotels currently lives in a separate CMS website, not in the backend
|
|
||||||
this app talks to. If the app must be a standalone entry point rather than something users
|
|
||||||
deep-link into, that is a second integration project. Do not assume it is in scope.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. Local development
|
|
||||||
|
|
||||||
- The backend runs in DDEV. A **simulator** can reach it via the host; a **physical
|
|
||||||
device** cannot resolve `*.ddev.site`. The backend team will expose a shareable URL
|
|
||||||
(`ddev share` / ngrok).
|
|
||||||
- OAuth2 redirect URIs are registered per host, so switching between simulator and device
|
|
||||||
URLs needs a backend-side change. Agree the setup in Increment 0 rather than discovering
|
|
||||||
it mid-increment.
|
|
||||||
- Keep fixture-mock mode working permanently, not just as scaffolding — it is how you stay
|
|
||||||
unblocked and how you write deterministic tests.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. Working agreement with the backend session
|
|
||||||
|
|
||||||
The backend lives in a separate repository and a separate sandboxed session. Neither side
|
|
||||||
can see the other's code. The contract is the only coupling point, and keeping it that way
|
|
||||||
is a shared discipline.
|
|
||||||
|
|
||||||
**Per-increment loop:**
|
|
||||||
|
|
||||||
1. Backend ships: endpoints + tests + fixtures + updated guide.
|
|
||||||
2. Handoff brief: what is live, which fixtures to mock, what changed in the guide.
|
|
||||||
3. App builds against fixtures, then against a live backend.
|
|
||||||
4. Joint end-to-end verification.
|
|
||||||
|
|
||||||
The two sessions do not work the same increment concurrently — they overlap by one stage,
|
|
||||||
so the backend starts *N+1* while the app builds *N*.
|
|
||||||
|
|
||||||
**Escalate to the backend rather than solving locally when:**
|
|
||||||
|
|
||||||
- a rule seems missing from the schema;
|
|
||||||
- a response shape is undocumented or contradicts the guide;
|
|
||||||
- you need a field the API does not expose;
|
|
||||||
- you are tempted to hardcode a business constant.
|
|
||||||
|
|
||||||
Each of those is a contract gap. Fixing it in the app is the failure mode this entire
|
|
||||||
architecture exists to prevent.
|
|
||||||
|
|
||||||
**Suggested `AGENTS.md` for this repo**, so future sessions inherit the rule:
|
|
||||||
|
|
||||||
> Business rules live server-side. This app renders a server-provided schema and never
|
|
||||||
> decides field visibility, eligibility, or pricing. If a rule appears to be missing,
|
|
||||||
> that is a backend bug — report it, do not implement it here. Never recompute prices
|
|
||||||
> from line items or infer field visibility from other field values.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. Verification
|
|
||||||
|
|
||||||
- **Unit:** the SDUI renderer against every field type and state combination, including
|
|
||||||
`hidden` vs `staticText`, and violation-path mapping onto nested participant fields.
|
|
||||||
- **Auth:** an explicit test that fires several concurrent requests against an expired
|
|
||||||
access token and asserts *exactly one* refresh call is made, all requests succeed, and
|
|
||||||
the rotated refresh token is persisted. Rotation makes this a correctness test, not a
|
|
||||||
performance one.
|
|
||||||
- **Integration:** screens against committed fixtures — deterministic, no backend needed.
|
|
||||||
- **Contract:** types regenerate cleanly and compile against current fixtures. A failure
|
|
||||||
here is an early warning of drift; treat it as urgent.
|
|
||||||
- **End-to-end:** at the close of Increment 6, complete one booking through the app
|
|
||||||
against a real backend. The backend team verifies the resulting upstream payload is
|
|
||||||
**identical** to the same booking made through the web flow. That equivalence is the
|
|
||||||
project's actual acceptance criterion — everything else is a proxy for it.
|
|
||||||
@@ -1,354 +0,0 @@
|
|||||||
# Mobile App — Backend Plan (myep / Symfony)
|
|
||||||
|
|
||||||
**Audience:** sessions working in *this* repository (`myep`, Symfony 6.4).
|
|
||||||
**Companion:** `docs/mobile-app/app-plan.md` — the React Native side. That document is
|
|
||||||
written to be self-contained and is meant to be **copied into the RN repository**, whose
|
|
||||||
sessions have no access to this codebase.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Context
|
|
||||||
|
|
||||||
`myep` is the booking engine behind the separate CMS marketing site (`ep-reisen.de`;
|
|
||||||
see `BookingSessionManager::DEFAULT_RETURN_URL` and `CmsDataProvider`). It serves:
|
|
||||||
|
|
||||||
- **Individual travel bookings** — system of record is **BusProNet** (BPN), an external
|
|
||||||
XML service reached through `src/BusProNet/ApiClient.php`.
|
|
||||||
- **Groups / accommodation bookings** — system of record is local Doctrine entities under
|
|
||||||
`src/Entity/Groups/`.
|
|
||||||
|
|
||||||
The public UI is Twig + HTMX + Tailwind. The admin UI under `/admin` is **out of scope**
|
|
||||||
for the mobile app and stays web-only.
|
|
||||||
|
|
||||||
### What we are building and why
|
|
||||||
|
|
||||||
A React Native app covering the **full public surface, including the booking create
|
|
||||||
flow**. Drivers: App Store presence/brand and a native-feeling booking experience. A PWA
|
|
||||||
was explicitly ruled out. Push notifications were *not* named as a driver — worth
|
|
||||||
remembering, since push is normally what makes native unarguable; the payoff here rests
|
|
||||||
on brand and UX.
|
|
||||||
|
|
||||||
### The decision that shapes everything
|
|
||||||
|
|
||||||
**The sibling project is the React Native app only. The API stays in this repository.**
|
|
||||||
|
|
||||||
A second backend would recreate exactly the duplication of validation and business rules
|
|
||||||
this plan exists to prevent. The API is a new `src/Controller/Api/App/` namespace reusing
|
|
||||||
the existing services and DTOs.
|
|
||||||
|
|
||||||
**Client strategy: server-driven UI (SDUI).** The server emits a JSON field schema per
|
|
||||||
participant/step; RN renders it generically. Business rules never leave PHP.
|
|
||||||
|
|
||||||
### Why this codebase can actually support that
|
|
||||||
|
|
||||||
The rules are already headless:
|
|
||||||
|
|
||||||
- **Validation lives on DTOs, not form types.** `src/Form/Model/ParticipantDto.php` (21
|
|
||||||
constraints), `BookingDto.php`, `BankAccountDto.php`, `RegistrationDto.php` carry
|
|
||||||
`Assert\*` plus custom `App\Validator\Constraints\Participant`, with validation groups
|
|
||||||
(`booking_create`, `booking_edit`, `strict_required`, `applicant_address`). An API
|
|
||||||
endpoint can denormalize → `validate($dto, groups: [...])` and get identical results.
|
|
||||||
- **Dynamic field logic is a rules engine.** `src/Form/Service/` holds
|
|
||||||
`Contract/FieldStateProviderInterface` (hidden / readonly / disabled / required /
|
|
||||||
static_text), `Contract/FieldOptionsProviderInterface` (labels + choices),
|
|
||||||
`ParticipantFieldHandlerRegistry` (25 handlers) and 27 reusable `Condition/` classes.
|
|
||||||
- **Orchestration is in services, not controllers.** `BookingConfigurator` (652 lines),
|
|
||||||
`RoomAssigner`, `BookingPricingAssembler`, `BookingSummaryAssembler`,
|
|
||||||
`VoucherValidator`, `BookingEditSubmitGuard`, `BookingEditSubmitter`. Controllers are
|
|
||||||
thin — `Step2Controller` is 147 lines, mostly session handling and redirects.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. The four real obstacles
|
|
||||||
|
|
||||||
Everything else is mechanical. These need design work before the increments that depend
|
|
||||||
on them.
|
|
||||||
|
|
||||||
### Obstacle 1 — The create flow is session-bound
|
|
||||||
|
|
||||||
`src/Service/BookingSessionManager.php` keys the entire create flow off
|
|
||||||
`$request->getSession()` (`booking_create`, `booking_create_baseline_snapshot`,
|
|
||||||
`booking_return_url`). Stateless OAuth2 clients have no session.
|
|
||||||
|
|
||||||
**Fix:** a server-persisted draft addressed by UUID. The pattern already exists for edit —
|
|
||||||
`src/Entity/BookingEditDraft.php` plus `BookingEditDraftManager` / `BookingEditDraftMerger`.
|
|
||||||
Generalise into a `BookingDraft` store and put `BookingStateStoreInterface` in front, with
|
|
||||||
two backends: session (web, unchanged) and draft (app). Do **not** fork the flow logic.
|
|
||||||
|
|
||||||
### Obstacle 2 — `FieldStateProviderInterface` leaks Symfony Form
|
|
||||||
|
|
||||||
Two leaks:
|
|
||||||
|
|
||||||
- `getBookingDtoFromForm(FormInterface $form)` is pure Form coupling and belongs in a
|
|
||||||
form-side adapter, not the interface.
|
|
||||||
- `getFieldState()` returns **Symfony form options** (`disabled`, `required`,
|
|
||||||
`attr => ['readonly' => true]`), and `ParticipantFieldOptionsProvider` returns `choices`
|
|
||||||
as domain objects (`Service`, `Pickup`, `Insurance`) plus occasional `choice_loader`.
|
|
||||||
|
|
||||||
**Fix:** a neutral `FieldDescriptor` value object — name, type, label, required, readonly,
|
|
||||||
disabled, hidden, staticText, choices, value. Both `BookingParticipantType` and the new
|
|
||||||
JSON serializer consume it. Conditions and handlers are untouched.
|
|
||||||
|
|
||||||
Note the `hidden` vs `static_text` distinction documented in `AbstractFieldStateProvider`:
|
|
||||||
`hidden` means excluded entirely; `static_text` means "show the value, don't let them edit
|
|
||||||
it". The app must honour both, so `FieldDescriptor` has to carry them separately.
|
|
||||||
|
|
||||||
### Obstacle 3 — Token lifetimes and refresh-token rotation
|
|
||||||
|
|
||||||
`config/packages/league_oauth2_server.yaml` sets `access_token_ttl: PT10M` — deliberately,
|
|
||||||
since the bundle's own default is `PT1H`. Find out why before changing it.
|
|
||||||
|
|
||||||
**TTLs are global.** They live under `authorization_server`; the bundle has no per-client
|
|
||||||
TTL. Any change affects the CMS and M2M clients too. "Raise it just for the app" is not
|
|
||||||
available without a custom grant or response type.
|
|
||||||
|
|
||||||
Two values are unset and defaulted, and both matter:
|
|
||||||
|
|
||||||
- `refresh_token_ttl` → **`P1M`** (1 month)
|
|
||||||
- `revoke_refresh_tokens` → **`true`**, so **refresh-token rotation is on**
|
|
||||||
|
|
||||||
Rotation is correct security, and it is also the main source of mobile auth bugs: two
|
|
||||||
requests 401 concurrently, both refresh, one rotates, the second presents a revoked
|
|
||||||
refresh token and the user is logged out mid-booking. At a 10-minute TTL with step 2
|
|
||||||
firing schema refreshes per field change, this *will* happen. The mitigation is client-side
|
|
||||||
— single-flight refresh — and it is a hard requirement in `app-plan.md` §4.
|
|
||||||
|
|
||||||
A longer access-token TTL does **not** create an unrevocable window here:
|
|
||||||
`persist_access_token` defaults to true and `BearerTokenValidator` checks
|
|
||||||
`isAccessTokenRevoked()` on every request, so tokens can be killed immediately. The usual
|
|
||||||
argument for a very short TTL does not apply.
|
|
||||||
|
|
||||||
**Recommendation:** `access_token_ttl: PT1H`, `refresh_token_ttl: P3M` set explicitly,
|
|
||||||
rotation left on. Rotation issues a fresh refresh token each time, so the refresh TTL is a
|
|
||||||
sliding window — active users never re-auth, only ~3 months of true inactivity forces it.
|
|
||||||
M2M clients get fewer token round-trips. Extra token rows are already handled by
|
|
||||||
`league:oauth2-server:clear-expired-tokens`, scheduled in `config/packages/zenstruck_schedule.yaml`.
|
|
||||||
|
|
||||||
**Settle this before the RN auth layer is written, not after.**
|
|
||||||
|
|
||||||
Otherwise auth is ready: `enable_auth_code_grant: true` with
|
|
||||||
`require_code_challenge_for_public_clients: true` (PKCE), and
|
|
||||||
`enable_password_grant: false` / `enable_implicit_grant: false` — correct for a native app.
|
|
||||||
|
|
||||||
### Obstacle 4 — BPN per-user calls need the user's plaintext password
|
|
||||||
|
|
||||||
`ApiClient::getBookings()`, `getBooking()`, `getPersonalData()`, `updatePersonalData()`,
|
|
||||||
`updateNewsletterRegistration()` all take `(string $email, string $password)`.
|
|
||||||
`src/Controller/Booking/IndexController.php` supplies them via
|
|
||||||
`$this->crypt->decrypt($user->getPassword())` — the local `User` stores the BPN password
|
|
||||||
RSA-encrypted (`src/Security/Crypt.php`).
|
|
||||||
|
|
||||||
Consequences:
|
|
||||||
|
|
||||||
- **The app must never see BPN credentials.** The API layer resolves them server-side from
|
|
||||||
the authenticated `User`, exactly as the web controllers do. This is not a change, but
|
|
||||||
it must be stated so nobody "simplifies" it by proxying credentials.
|
|
||||||
- **BPN-backed user data only works for users with a local `User` record** carrying the
|
|
||||||
encrypted password. Guest flows and app-only accounts cannot read BPN bookings. Confirm
|
|
||||||
how registration-via-app populates this before Increment 3.
|
|
||||||
- The OAuth2 token identifies the user; it does **not** carry BPN authority. Every
|
|
||||||
BPN-backed endpoint needs an explicit ownership check, not just a valid token.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Increments
|
|
||||||
|
|
||||||
Ordered local-data-first, BPN-last, so each stage ships something usable and the riskiest
|
|
||||||
integration lands after the SDUI machinery is proven.
|
|
||||||
|
|
||||||
### Increment 0 — Foundations
|
|
||||||
|
|
||||||
No user-visible feature. Backend side:
|
|
||||||
|
|
||||||
- Register an OAuth2 client for the app (authorization_code + PKCE, native redirect URI).
|
|
||||||
- Resolve Obstacle 3 (TTL).
|
|
||||||
- Resolve device→DDEV access: `.ddev/config.yaml` has `ngrok_args`; `ddev share` is the
|
|
||||||
path. Redirect URIs must be registered against whatever host the app actually calls.
|
|
||||||
- Create `docs/app-api/` with the contract skeleton and the fixtures directory.
|
|
||||||
|
|
||||||
### Increment 1 — Accommodation & group offers (pilot)
|
|
||||||
|
|
||||||
Local Doctrine data, UUID-addressed, and a write endpoint already exists. Zero BPN risk.
|
|
||||||
|
|
||||||
- Reuse `GET /api/accommodation-bookings/{uuid}` and
|
|
||||||
`POST /api/accommodation-bookings/{uuid}/accept`
|
|
||||||
(`src/Controller/Api/AccommodationBookingController.php`).
|
|
||||||
- Add a list endpoint scoped to the authenticated user.
|
|
||||||
- Mirrors `src/Controller/Groups/Offer/IndexController.php`; reuse
|
|
||||||
`AccommodationBookingService`, `AccommodationBookingBreakdownCalculator`,
|
|
||||||
`AccommodationBookingApiResponse`.
|
|
||||||
- **Validates:** auth, serialization conventions, RN navigation, the fixture pipeline.
|
|
||||||
|
|
||||||
### Increment 2 — Account, personal data, newsletter, registration
|
|
||||||
|
|
||||||
- Mirror `src/Controller/Account/`, `RegistrationController`, `ResetPasswordController`,
|
|
||||||
`src/Controller/Newsletter/`.
|
|
||||||
- Reuse `RegistrationDto`, the `PersonalDataType` DTO, `NewsletterManager`,
|
|
||||||
`ProfileCompletenessChecker`, and existing `/api/newsletter-subscriptions`,
|
|
||||||
`/api/userinfo`, `/api/crm-attributes`.
|
|
||||||
- **First real SDUI use**, on a small low-risk form, before betting the booking flow on it.
|
|
||||||
- Registration here is what makes Obstacle 4 tractable — verify the encrypted-password
|
|
||||||
path is populated for app-registered users.
|
|
||||||
|
|
||||||
### Increment 3 — Booking read
|
|
||||||
|
|
||||||
- `GET /api/app/bookings` and `/api/app/bookings/{id}`, mirroring
|
|
||||||
`src/Controller/Booking/IndexController.php` (note its `TravelLoader::patchBookings()`
|
|
||||||
enrichment step).
|
|
||||||
- Documents/invoice, mirroring `src/Controller/Booking/DownloadController.php`.
|
|
||||||
- Reuse `BookingSummaryAssembler`, `BookingPricingAssembler`, `BookingEditDataLoader`.
|
|
||||||
- BPN **reads only, no writes** — the safe way to shake out `BusProNet\Model`
|
|
||||||
normalization (`Travel`, `Booking`, `Service`, `Room`, `Pickup`, `Insurance`), which
|
|
||||||
every later increment depends on.
|
|
||||||
|
|
||||||
### Increment 4 — `FieldDescriptor` refactor
|
|
||||||
|
|
||||||
Pure internal refactor. No new endpoints. Covered by the existing 155 test files.
|
|
||||||
|
|
||||||
- Introduce `FieldDescriptor`; split `getBookingDtoFromForm` out of
|
|
||||||
`FieldStateProviderInterface`.
|
|
||||||
- Adapt `BookingParticipantType::mergeFieldState()` / `addDynamicFields()` to consume it.
|
|
||||||
- Add `ParticipantFieldSchemaSerializer` producing the JSON schema, including the
|
|
||||||
`dependsOn` map derived from `FieldConditionInterface::getDependentFields()` and
|
|
||||||
`ParticipantFieldHandlerInterface::getDependencies()`.
|
|
||||||
- **Ship criterion:** web behaviour byte-identical, Cypress specs green, **no test file
|
|
||||||
changed**. A required test change means a behaviour regression.
|
|
||||||
|
|
||||||
### Increment 5 — Booking edit
|
|
||||||
|
|
||||||
Before create, because drafts already exist here.
|
|
||||||
|
|
||||||
- Endpoints over `BookingEditDraftManager`, `BookingEditPreFlightChecker`,
|
|
||||||
`BookingEditSubmitGuard`, `BookingEditSubmitter`, `BookingChangeTracker`.
|
|
||||||
- Participant schema endpoint from Increment 4.
|
|
||||||
- Honour `BookingMutabilityDto` and BPN's `aenderungmoeglich` mutability rules — these
|
|
||||||
already drive `static_text`, so SDUI inherits them.
|
|
||||||
- **Caution:** the participant[0]/applicant block is on hold pending BPN vendor
|
|
||||||
confirmation about the shared customer id. Do not try to resolve it here.
|
|
||||||
|
|
||||||
### Increment 6 — Booking create
|
|
||||||
|
|
||||||
The full flow, last. Web routes being mirrored:
|
|
||||||
|
|
||||||
| Step | Web route | Controller |
|
|
||||||
|---|---|---|
|
|
||||||
| init | `/bookings/create`, `/bookings/create/init` | `Create/IndexController.php` |
|
|
||||||
| 1 — rooms | `/bookings/create/rooms` (+ `/refresh`) | `Create/Step1Controller.php` |
|
|
||||||
| 2 — participants | `/bookings/create/participants` (+ `/{index}`, `/{index}/refresh`) | `Create/Step2Controller.php`, `Step2ParticipantController.php` |
|
|
||||||
| 3 — payment | `/bookings/create/payment` (+ `/refresh`) | `Create/Step3Controller.php` |
|
|
||||||
| 4 — confirmation | `/bookings/create/confirmation` | `Create/Step4Controller.php` |
|
|
||||||
| success | `/bookings/create/success` | `Create/SuccessController.php` |
|
|
||||||
|
|
||||||
- Generalise draft storage behind `BookingStateStoreInterface` (Obstacle 1).
|
|
||||||
- Reuse `BookingConfigurator` (`startFreshBooking`, `preselectDefaultServices`,
|
|
||||||
`applyCreateBookingStatusRules`, `updateBookingStatusFromRoomSelection`), `RoomAssigner`,
|
|
||||||
`ParticipantDataPrefiller`, `TravelDataProvider`, `VoucherValidator`,
|
|
||||||
`FamilyInsuranceAvailabilityChecker`.
|
|
||||||
- Submit via `BusProNet\ApiClient::createBooking()` / `createBookingInquiry()` with
|
|
||||||
`BookingPayloadBuilder`, exactly as `Step4Controller` does.
|
|
||||||
- Every HTMX refresh route gets a JSON sibling returning recomputed schema + pricing.
|
|
||||||
|
|
||||||
### Increment 7 — Discovery (scoping decision, not yet planned)
|
|
||||||
|
|
||||||
Travel/hotel browsing lives in the **CMS**, not here — this repo only proxies it via
|
|
||||||
`CmsDataProvider` and `/api/hotels`, `/api/products`, `/api/travels`. If the app is to be
|
|
||||||
a standalone entry point rather than a deep-link target, that is a second integration
|
|
||||||
project and should be scoped separately.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Files
|
|
||||||
|
|
||||||
**New (representative):**
|
|
||||||
|
|
||||||
- `src/Controller/Api/App/` — one controller per resource, mirroring the public tree
|
|
||||||
- `src/Form/Service/Model/FieldDescriptor.php`
|
|
||||||
- `src/Form/Service/ParticipantFieldSchemaSerializer.php`
|
|
||||||
- `src/Service/BookingStateStoreInterface.php` + draft-backed implementation
|
|
||||||
- `src/Entity/BookingDraft.php` (generalised from `BookingEditDraft`) + migration
|
|
||||||
- Normalizers for `BusProNet\Model\*` value objects
|
|
||||||
- `tests/Controller/Api/App/`
|
|
||||||
|
|
||||||
**Modified:**
|
|
||||||
|
|
||||||
- `src/Form/Service/Contract/FieldStateProviderInterface.php` — drop the Form-coupled method
|
|
||||||
- `src/Form/Service/Abstract/AbstractFieldStateProvider.php`, `CreateFieldStateProvider.php`,
|
|
||||||
`EditFieldStateProvider.php`
|
|
||||||
- `src/Form/BookingParticipantType.php` — consume `FieldDescriptor`
|
|
||||||
- `src/Service/BookingSessionManager.php` — implement the store interface
|
|
||||||
- `config/packages/league_oauth2_server.yaml` — TTL, app client, scopes
|
|
||||||
- `docs/api-consumer-guide.md` / `docs/app-api/` — the contract
|
|
||||||
|
|
||||||
**Unchanged by design:** every `src/Form/Service/Condition/*`, every
|
|
||||||
`src/Form/Service/Participant*FieldHandler.php`, every `src/Validator/Constraints/*`, and
|
|
||||||
all pricing services. If an increment starts wanting to edit these, the design has drifted
|
|
||||||
— stop and reconsider.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Working across two repos and two sessions
|
|
||||||
|
|
||||||
The RN work happens in a separate repo and a separate sandboxed session. The risk that
|
|
||||||
creates is **contract drift**: the app session guessing at response shapes, or worse,
|
|
||||||
re-deriving business rules in TypeScript because the JSON was ambiguous. That is the
|
|
||||||
original duplication problem arriving through a side door.
|
|
||||||
|
|
||||||
### The contract is an artifact in this repo
|
|
||||||
|
|
||||||
`docs/api-consumer-guide.md` already opens with *"It is self-contained: no access to this
|
|
||||||
codebase is required to use it."* That is the format. Extend it — or split out
|
|
||||||
`docs/app-api/guide.md` once it outgrows one file — **in the same commit as the endpoint**,
|
|
||||||
every time.
|
|
||||||
|
|
||||||
Rule: **the RN session never reads this PHP repo.** Its ground truth is the guide plus
|
|
||||||
fixtures. If it has to ask "what does the server send here?", the guide has a gap — fix
|
|
||||||
the guide, not the app.
|
|
||||||
|
|
||||||
### Fixtures decouple the sessions
|
|
||||||
|
|
||||||
Parity tests in `tests/Controller/Api/App/` write their JSON responses to
|
|
||||||
`docs/app-api/fixtures/*.json` (mirroring the existing `tests/Resources/*.xml`
|
|
||||||
convention). Commit them. The RN session mocks against those files, so app work never
|
|
||||||
waits on a deployed endpoint — and because they are test *output*, they cannot silently
|
|
||||||
go stale.
|
|
||||||
|
|
||||||
### Per-increment rhythm
|
|
||||||
|
|
||||||
1. **Backend session (here):** endpoint + parity test + fixtures + guide update.
|
|
||||||
2. **Handoff:** a short brief — endpoints now live, fixtures to mock, what changed in the
|
|
||||||
guide since last time.
|
|
||||||
3. **App session (RN repo):** build against fixtures first, then against DDEV.
|
|
||||||
4. **Joint verification:** run the increment end-to-end against a real backend.
|
|
||||||
|
|
||||||
Do not run both sessions on the same increment concurrently. Overlap by one stage: while
|
|
||||||
the app session builds increment *N*, start *N+1* here.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Verification
|
|
||||||
|
|
||||||
Per increment:
|
|
||||||
|
|
||||||
1. `vendor/bin/phpunit` — 155 existing test files, notably `tests/Form/`, `tests/Service/`,
|
|
||||||
`tests/BusProNet/`. **Increment 4 must pass with no test changes.**
|
|
||||||
2. `vendor/bin/phpstan analyse` and `vendor/bin/php-cs-fixer fix --dry-run`.
|
|
||||||
3. `npx cypress run` — existing specs cover the web booking flow and guard against SDUI
|
|
||||||
refactors leaking into Twig rendering.
|
|
||||||
4. **Parity tests** under `tests/Controller/Api/App/`, asserting the JSON schema matches
|
|
||||||
what `BookingParticipantType` builds for the same `BookingDto`. This is the single most
|
|
||||||
valuable artifact in the project — it mechanically proves web and app share one rule set.
|
|
||||||
5. Manual: extend the existing `api.http` / `http-client.env.json` collection per increment.
|
|
||||||
6. End-to-end: run one booking through the RN client against a DDEV backend and confirm the
|
|
||||||
BPN payload is identical to a web-flow booking with the same inputs — compare via
|
|
||||||
`src/Controller/Admin/Log/XmlDumpController.php`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. Open questions before Increment 6
|
|
||||||
|
|
||||||
- App-client access token TTL (Obstacle 3).
|
|
||||||
- How app registration populates the encrypted BPN password (Obstacle 4), and whether
|
|
||||||
guest/unauthenticated create is supported in the app as it is on the web via
|
|
||||||
`/bookings/create/init`.
|
|
||||||
- Draft retention and expiry policy.
|
|
||||||
- Whether the app deep-links from the CMS site or must own discovery (Increment 7).
|
|
||||||
File diff suppressed because it is too large
Load Diff
+7
-81
@@ -30,70 +30,9 @@
|
|||||||
"Auth URL": "https://myep.ddev.site/authorize",
|
"Auth URL": "https://myep.ddev.site/authorize",
|
||||||
"Token URL": "https://myep.ddev.site/token",
|
"Token URL": "https://myep.ddev.site/token",
|
||||||
"Scope": "api"
|
"Scope": "api"
|
||||||
},
|
|
||||||
"oauth2_newsletter": {
|
|
||||||
"Type": "OAuth2",
|
|
||||||
"Grant Type": "Client Credentials",
|
|
||||||
"Client ID": "{{oauth2_api_client_id}}",
|
|
||||||
"Client Secret": "{{oauth2_api_client_secret}}",
|
|
||||||
"Auth URL": "https://myep.ddev.site/authorize",
|
|
||||||
"Token URL": "https://myep.ddev.site/token",
|
|
||||||
"Scope": "api"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"clickup_baseurl": "https://api.clickup.com/api/v2",
|
|
||||||
"clickup_api_token": "abcdef123456789",
|
|
||||||
"clickup_list_id": "12345",
|
|
||||||
"clickup_template_id": "t-12345"
|
|
||||||
},
|
|
||||||
"staging": {
|
|
||||||
"Security": {
|
|
||||||
"Auth": {
|
|
||||||
"oauth2_email": {
|
|
||||||
"Type": "OAuth2",
|
|
||||||
"Grant Type": "Authorization Code",
|
|
||||||
"Client ID": "{{oauth2_client_id}}",
|
|
||||||
"Client Secret": "{{oauth2_client_secret}}",
|
|
||||||
"Auth URL": "https://my.ep-reisen.net/authorize",
|
|
||||||
"Token URL": "https://my.ep-reisen.net/token",
|
|
||||||
"Redirect URL": "https://myep-team.ep-reisen.de/auth/check",
|
|
||||||
"Scope": "email"
|
|
||||||
},
|
|
||||||
"oauth2_profile": {
|
|
||||||
"Type": "OAuth2",
|
|
||||||
"Grant Type": "Authorization Code",
|
|
||||||
"Client ID": "{{oauth2_client_id}}",
|
|
||||||
"Client Secret": "{{oauth2_client_secret}}",
|
|
||||||
"Auth URL": "https://my.ep-reisen.net/authorize",
|
|
||||||
"Token URL": "https://my.ep-reisen.net/token",
|
|
||||||
"Redirect URL": "https://myep-team.ep-reisen.de/auth/check",
|
|
||||||
"Scope": "email profile"
|
|
||||||
},
|
|
||||||
"oauth2_api": {
|
|
||||||
"Type": "OAuth2",
|
|
||||||
"Grant Type": "Client Credentials",
|
|
||||||
"Client ID": "{{oauth2_api_client_id}}",
|
|
||||||
"Client Secret": "{{oauth2_api_client_secret}}",
|
|
||||||
"Auth URL": "https://my.ep-reisen.net/authorize",
|
|
||||||
"Token URL": "https://my.ep-reisen.net/token",
|
|
||||||
"Scope": "api"
|
|
||||||
},
|
|
||||||
"oauth2_newsletter": {
|
|
||||||
"Type": "OAuth2",
|
|
||||||
"Grant Type": "Client Credentials",
|
|
||||||
"Client ID": "{{oauth2_api_client_id}}",
|
|
||||||
"Client Secret": "{{oauth2_api_client_secret}}",
|
|
||||||
"Auth URL": "https://my.ep-reisen.net/authorize",
|
|
||||||
"Token URL": "https://my.ep-reisen.net/token",
|
|
||||||
"Scope": "api"
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
"clickup_baseurl": "https://api.clickup.com/api/v2",
|
|
||||||
"clickup_api_token": "abcdef123456789",
|
|
||||||
"clickup_list_id": "12345",
|
|
||||||
"clickup_template_id": "t-12345"
|
|
||||||
},
|
},
|
||||||
"prod": {
|
"prod": {
|
||||||
"Security": {
|
"Security": {
|
||||||
@@ -103,8 +42,8 @@
|
|||||||
"Grant Type": "Authorization Code",
|
"Grant Type": "Authorization Code",
|
||||||
"Client ID": "{{oauth2_client_id}}",
|
"Client ID": "{{oauth2_client_id}}",
|
||||||
"Client Secret": "{{oauth2_client_secret}}",
|
"Client Secret": "{{oauth2_client_secret}}",
|
||||||
"Auth URL": "https://my.ep-reisen.de/authorize",
|
"Auth URL": "https://myep.ep-reisen.de/authorize",
|
||||||
"Token URL": "https://my.ep-reisen.de/token",
|
"Token URL": "https://myep.ep-reisen.de/token",
|
||||||
"Redirect URL": "https://myep-team.ep-reisen.de/auth/check",
|
"Redirect URL": "https://myep-team.ep-reisen.de/auth/check",
|
||||||
"Scope": "email"
|
"Scope": "email"
|
||||||
},
|
},
|
||||||
@@ -113,8 +52,8 @@
|
|||||||
"Grant Type": "Authorization Code",
|
"Grant Type": "Authorization Code",
|
||||||
"Client ID": "{{oauth2_client_id}}",
|
"Client ID": "{{oauth2_client_id}}",
|
||||||
"Client Secret": "{{oauth2_client_secret}}",
|
"Client Secret": "{{oauth2_client_secret}}",
|
||||||
"Auth URL": "https://my.ep-reisen.de/authorize",
|
"Auth URL": "https://myep.ep-reisen.de/authorize",
|
||||||
"Token URL": "https://my.ep-reisen.de/token",
|
"Token URL": "https://myep.ep-reisen.de/token",
|
||||||
"Redirect URL": "https://myep-team.ep-reisen.de/auth/check",
|
"Redirect URL": "https://myep-team.ep-reisen.de/auth/check",
|
||||||
"Scope": "email profile"
|
"Scope": "email profile"
|
||||||
},
|
},
|
||||||
@@ -123,24 +62,11 @@
|
|||||||
"Grant Type": "Client Credentials",
|
"Grant Type": "Client Credentials",
|
||||||
"Client ID": "{{oauth2_api_client_id}}",
|
"Client ID": "{{oauth2_api_client_id}}",
|
||||||
"Client Secret": "{{oauth2_api_client_secret}}",
|
"Client Secret": "{{oauth2_api_client_secret}}",
|
||||||
"Auth URL": "https://my.ep-reisen.de/authorize",
|
"Auth URL": "https://myep.ep-reisen.de/authorize",
|
||||||
"Token URL": "https://my.ep-reisen.de/token",
|
"Token URL": "https://myep.ep-reisen.de/token",
|
||||||
"Scope": "api"
|
|
||||||
},
|
|
||||||
"oauth2_newsletter": {
|
|
||||||
"Type": "OAuth2",
|
|
||||||
"Grant Type": "Client Credentials",
|
|
||||||
"Client ID": "{{oauth2_api_client_id}}",
|
|
||||||
"Client Secret": "{{oauth2_api_client_secret}}",
|
|
||||||
"Auth URL": "https://my.ep-reisen.de/authorize",
|
|
||||||
"Token URL": "https://my.ep-reisen.de/token",
|
|
||||||
"Scope": "api"
|
"Scope": "api"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"clickup_baseurl": "https://api.clickup.com/api/v2",
|
|
||||||
"clickup_api_token": "abcdef123456789",
|
|
||||||
"clickup_list_id": "12345",
|
|
||||||
"clickup_template_id": "t-12345"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-172
@@ -1,172 +0,0 @@
|
|||||||
###
|
|
||||||
# Mailjet API test requests for IntelliJ HTTP Client
|
|
||||||
#
|
|
||||||
# 1) Fill in variables below (or move them to http-client.private.env.json).
|
|
||||||
# 2) Set `mailjet_api_key` and `mailjet_api_secret` in your env file.
|
|
||||||
# IntelliJ HTTP Client will inject them into the Authorization header below.
|
|
||||||
#
|
|
||||||
# Required variables:
|
|
||||||
# - mailjet_base_url (example: https://api.mailjet.com/v3/REST)
|
|
||||||
# - mailjet_api_key
|
|
||||||
# - mailjet_api_secret
|
|
||||||
# - mailjet_contact_email
|
|
||||||
# - mailjet_list_id
|
|
||||||
#
|
|
||||||
# @no-cookie-jar
|
|
||||||
|
|
||||||
### Mailjet health check (authenticated)
|
|
||||||
# @no-cookie-jar
|
|
||||||
GET {{mailjet_base_url}}/apikey
|
|
||||||
Accept: application/json
|
|
||||||
Authorization: Basic {{mailjet_api_key}} {{mailjet_api_secret}}
|
|
||||||
|
|
||||||
|
|
||||||
### Find contact by email
|
|
||||||
# @no-cookie-jar
|
|
||||||
GET {{mailjet_base_url}}/contact?Email={{mailjet_contact_email}}
|
|
||||||
Accept: application/json
|
|
||||||
Authorization: Basic {{mailjet_api_key}} {{mailjet_api_secret}}
|
|
||||||
|
|
||||||
> {%
|
|
||||||
if (response.body && response.body.Data && response.body.Data.length > 0) {
|
|
||||||
client.global.set("contact_id", String(response.body.Data[0].ID));
|
|
||||||
}
|
|
||||||
%}
|
|
||||||
|
|
||||||
|
|
||||||
### Create contact (run if not found)
|
|
||||||
# @no-cookie-jar
|
|
||||||
POST {{mailjet_base_url}}/contact
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
Authorization: Basic {{mailjet_api_key}} {{mailjet_api_secret}}
|
|
||||||
|
|
||||||
{
|
|
||||||
"Email": "{{mailjet_contact_email}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
> {%
|
|
||||||
if (response.body && response.body.Data && response.body.Data.length > 0) {
|
|
||||||
client.global.set("contact_id", String(response.body.Data[0].ID));
|
|
||||||
}
|
|
||||||
%}
|
|
||||||
|
|
||||||
|
|
||||||
### Get contact by ID (after find/create)
|
|
||||||
# @no-cookie-jar
|
|
||||||
GET {{mailjet_base_url}}/contact/{{contact_id}}
|
|
||||||
Accept: application/json
|
|
||||||
Authorization: Basic {{mailjet_api_key}} {{mailjet_api_secret}}
|
|
||||||
|
|
||||||
|
|
||||||
### Get contact metadata by ID (after find/create)
|
|
||||||
# @no-cookie-jar
|
|
||||||
GET {{mailjet_base_url}}/contactdata/{{contact_id}}
|
|
||||||
Accept: application/json
|
|
||||||
Authorization: Basic {{mailjet_api_key}} {{mailjet_api_secret}}
|
|
||||||
|
|
||||||
### Get newsletter list by ID
|
|
||||||
# @no-cookie-jar
|
|
||||||
GET {{mailjet_base_url}}/contactslist/{{mailjet_list_id}}
|
|
||||||
Accept: application/json
|
|
||||||
Authorization: Basic {{mailjet_api_key}} {{mailjet_api_secret}}
|
|
||||||
|
|
||||||
|
|
||||||
### List all available contact lists
|
|
||||||
# @no-cookie-jar
|
|
||||||
GET {{mailjet_base_url}}/contactslist?Limit=1000
|
|
||||||
Accept: application/json
|
|
||||||
Authorization: Basic {{mailjet_api_key}} {{mailjet_api_secret}}
|
|
||||||
|
|
||||||
|
|
||||||
### Check list recipient status (before actions)
|
|
||||||
# @no-cookie-jar
|
|
||||||
GET {{mailjet_base_url}}/listrecipient?ContactsList={{mailjet_list_id}}&Contact={{contact_id}}
|
|
||||||
Accept: application/json
|
|
||||||
Authorization: Basic {{mailjet_api_key}} {{mailjet_api_secret}}
|
|
||||||
|
|
||||||
> {%
|
|
||||||
if (response.body && response.body.Data && response.body.Data.length > 0) {
|
|
||||||
client.global.set("listrecipient_id", String(response.body.Data[0].ID));
|
|
||||||
}
|
|
||||||
%}
|
|
||||||
|
|
||||||
|
|
||||||
### Subscribe to list (addnoforce)
|
|
||||||
# @no-cookie-jar
|
|
||||||
POST {{mailjet_base_url}}/contactslist/{{mailjet_list_id}}/managecontact
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
Authorization: Basic {{mailjet_api_key}} {{mailjet_api_secret}}
|
|
||||||
|
|
||||||
{
|
|
||||||
"Action": "addnoforce",
|
|
||||||
"Email": "{{mailjet_contact_email}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
### Verify list recipient after addnoforce
|
|
||||||
# @no-cookie-jar
|
|
||||||
GET {{mailjet_base_url}}/listrecipient?ContactsList={{mailjet_list_id}}&Contact={{contact_id}}
|
|
||||||
Accept: application/json
|
|
||||||
Authorization: Basic {{mailjet_api_key}} {{mailjet_api_secret}}
|
|
||||||
|
|
||||||
|
|
||||||
### Unsubscribe from list (unsub)
|
|
||||||
# @no-cookie-jar
|
|
||||||
POST {{mailjet_base_url}}/contactslist/{{mailjet_list_id}}/managecontact
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
Authorization: Basic {{mailjet_api_key}} {{mailjet_api_secret}}
|
|
||||||
|
|
||||||
{
|
|
||||||
"Action": "unsub",
|
|
||||||
"Email": "{{mailjet_contact_email}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
### Verify list recipient after unsub
|
|
||||||
# @no-cookie-jar
|
|
||||||
GET {{mailjet_base_url}}/listrecipient?ContactsList={{mailjet_list_id}}&Contact={{contact_id}}
|
|
||||||
Accept: application/json
|
|
||||||
Authorization: Basic {{mailjet_api_key}} {{mailjet_api_secret}}
|
|
||||||
|
|
||||||
|
|
||||||
### Re-subscribe attempt with addnoforce (expected to keep unsubscribed state)
|
|
||||||
# @no-cookie-jar
|
|
||||||
POST {{mailjet_base_url}}/contactslist/{{mailjet_list_id}}/managecontact
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
Authorization: Basic {{mailjet_api_key}} {{mailjet_api_secret}}
|
|
||||||
|
|
||||||
{
|
|
||||||
"Action": "addnoforce",
|
|
||||||
"Email": "{{mailjet_contact_email}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
### Verify list recipient after addnoforce re-subscribe attempt
|
|
||||||
# @no-cookie-jar
|
|
||||||
GET {{mailjet_base_url}}/listrecipient?ContactsList={{mailjet_list_id}}&Contact={{contact_id}}
|
|
||||||
Accept: application/json
|
|
||||||
Authorization: Basic {{mailjet_api_key}} {{mailjet_api_secret}}
|
|
||||||
|
|
||||||
|
|
||||||
### Re-subscribe with addforce (expected to re-subscribe)
|
|
||||||
# @no-cookie-jar
|
|
||||||
POST {{mailjet_base_url}}/contactslist/{{mailjet_list_id}}/managecontact
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
Authorization: Basic {{mailjet_api_key}} {{mailjet_api_secret}}
|
|
||||||
|
|
||||||
{
|
|
||||||
"Action": "addforce",
|
|
||||||
"Email": "{{mailjet_contact_email}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
### Verify list recipient after addforce
|
|
||||||
# @no-cookie-jar
|
|
||||||
GET {{mailjet_base_url}}/listrecipient?ContactsList={{mailjet_list_id}}&Contact={{contact_id}}
|
|
||||||
Accept: application/json
|
|
||||||
Authorization: Basic {{mailjet_api_key}} {{mailjet_api_secret}}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
final class Version20260318113000 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return 'Create newsletter_opt_in_confirmation table for double opt-in confirmations';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql("CREATE TABLE newsletter_opt_in_confirmation (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(255) NOT NULL, token_hash VARCHAR(64) NOT NULL, expires_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', confirmed_at DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime_immutable)', created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', UNIQUE INDEX UNIQ_NEWSLETTER_OPT_IN_TOKEN_HASH (token_hash), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('DROP TABLE newsletter_opt_in_confirmation');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
final class Version20260320071724 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return 'Add error_code column and index to log_entry table';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('ALTER TABLE log_entry ADD error_code VARCHAR(12) DEFAULT NULL');
|
|
||||||
$this->addSql('CREATE INDEX IDX_LOG_ENTRY_ERROR_CODE ON log_entry (error_code)');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('DROP INDEX IDX_LOG_ENTRY_ERROR_CODE ON log_entry');
|
|
||||||
$this->addSql('ALTER TABLE log_entry DROP error_code');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
final class Version20260321120000 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return 'Create travel_snapshot table for DB-primary travel persistence';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('CREATE TABLE travel_snapshot (id INT AUTO_INCREMENT NOT NULL, date_id INT NOT NULL, hotel_id INT NOT NULL, date_code VARCHAR(64) DEFAULT NULL, label VARCHAR(255) DEFAULT NULL, product_id INT DEFAULT NULL, product_code VARCHAR(64) DEFAULT NULL, hotel_code VARCHAR(64) DEFAULT NULL, hotel_label VARCHAR(255) DEFAULT NULL, date_from DATE DEFAULT NULL COMMENT \'(DC2Type:date_immutable)\', date_to DATE DEFAULT NULL COMMENT \'(DC2Type:date_immutable)\', payload LONGTEXT NOT NULL, payload_hash VARCHAR(64) NOT NULL, captured_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', updated_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', extended_refreshed_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', UNIQUE INDEX uniq_snapshot_date_hotel (date_id, hotel_id), INDEX idx_snapshot_date_code (date_code), INDEX idx_snapshot_product_id (product_id), INDEX idx_snapshot_date_to (date_to), INDEX idx_snapshot_extended_refreshed_at (extended_refreshed_at), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('DROP TABLE travel_snapshot');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Auto-generated Migration: Please modify to your needs!
|
|
||||||
*/
|
|
||||||
final class Version20260323182524 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
// this up() migration is auto-generated, please modify it to your needs
|
|
||||||
$this->addSql('ALTER TABLE booking_edit_draft CHANGE form_data form_data JSON NOT NULL');
|
|
||||||
$this->addSql('ALTER TABLE log_entry CHANGE context context JSON DEFAULT NULL, CHANGE extra extra JSON DEFAULT NULL');
|
|
||||||
$this->addSql('ALTER TABLE newsletter_opt_in_confirmation RENAME INDEX uniq_newsletter_opt_in_token_hash TO UNIQ_70505214B3BC57DA');
|
|
||||||
$this->addSql('ALTER TABLE user CHANGE roles roles JSON NOT NULL, CHANGE hotel_codes hotel_codes JSON NOT NULL');
|
|
||||||
$this->addSql('DROP INDEX IDX_75EA56E0FB7336F0 ON messenger_messages');
|
|
||||||
$this->addSql('DROP INDEX IDX_75EA56E0E3BD61CE ON messenger_messages');
|
|
||||||
$this->addSql('DROP INDEX IDX_75EA56E016BA31DB ON messenger_messages');
|
|
||||||
$this->addSql('CREATE INDEX IDX_75EA56E0FB7336F0E3BD61CE16BA31DBBF396750 ON messenger_messages (queue_name, available_at, delivered_at, id)');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
// this down() migration is auto-generated, please modify it to your needs
|
|
||||||
$this->addSql('ALTER TABLE newsletter_opt_in_confirmation RENAME INDEX uniq_70505214b3bc57da TO UNIQ_NEWSLETTER_OPT_IN_TOKEN_HASH');
|
|
||||||
$this->addSql('ALTER TABLE user CHANGE roles roles JSON NOT NULL COLLATE `utf8mb4_bin`, CHANGE hotel_codes hotel_codes JSON NOT NULL COLLATE `utf8mb4_bin`');
|
|
||||||
$this->addSql('DROP INDEX IDX_75EA56E0FB7336F0E3BD61CE16BA31DBBF396750 ON messenger_messages');
|
|
||||||
$this->addSql('CREATE INDEX IDX_75EA56E0FB7336F0 ON messenger_messages (queue_name)');
|
|
||||||
$this->addSql('CREATE INDEX IDX_75EA56E0E3BD61CE ON messenger_messages (available_at)');
|
|
||||||
$this->addSql('CREATE INDEX IDX_75EA56E016BA31DB ON messenger_messages (delivered_at)');
|
|
||||||
$this->addSql('ALTER TABLE log_entry CHANGE context context JSON DEFAULT NULL COLLATE `utf8mb4_bin`, CHANGE extra extra JSON DEFAULT NULL COLLATE `utf8mb4_bin`');
|
|
||||||
$this->addSql('ALTER TABLE booking_edit_draft CHANGE form_data form_data JSON NOT NULL COLLATE `utf8mb4_bin`');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes travel_snapshot.payload from TEXT to LONGTEXT so large
|
|
||||||
* plain JSON snapshot payloads can be stored safely.
|
|
||||||
*/
|
|
||||||
final class Version20260324120000 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return 'Change travel_snapshot.payload from TEXT to LONGTEXT for plain JSON snapshot payloads';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('ALTER TABLE travel_snapshot CHANGE payload payload LONGTEXT NOT NULL');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('ALTER TABLE travel_snapshot CHANGE payload payload TEXT NOT NULL');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
final class Version20260422120000 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return 'Store requested Mailjet list IDs on newsletter opt-in confirmations';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql("ALTER TABLE newsletter_opt_in_confirmation ADD mailjet_list_ids JSON DEFAULT NULL COMMENT '(DC2Type:json)'");
|
|
||||||
$this->addSql('UPDATE newsletter_opt_in_confirmation SET mailjet_list_ids = JSON_ARRAY(10321569) WHERE mailjet_list_ids IS NULL');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('ALTER TABLE newsletter_opt_in_confirmation DROP mailjet_list_ids');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
final class Version20260423120000 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return 'Store optional first and last names on newsletter opt-in confirmations';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql("ALTER TABLE newsletter_opt_in_confirmation ADD first_name VARCHAR(255) DEFAULT NULL, ADD last_name VARCHAR(255) DEFAULT NULL");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('ALTER TABLE newsletter_opt_in_confirmation DROP first_name, DROP last_name');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
final class Version20260424110732 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return 'Track newsletter revocations on opt-in confirmations';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql("ALTER TABLE newsletter_opt_in_confirmation ADD revoked_at DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime_immutable)'");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('ALTER TABLE newsletter_opt_in_confirmation DROP revoked_at');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
final class Version20260429120000 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return 'Create per-list newsletter consent table and backfill confirmed opt-ins';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql("CREATE TABLE newsletter_consent (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(255) NOT NULL, mailjet_list_id INT NOT NULL, confirmed_at DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime_immutable)', revoked_at DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime_immutable)', first_name VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', updated_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', UNIQUE INDEX UNIQ_NEWSLETTER_CONSENT_EMAIL_LIST (email, mailjet_list_id), INDEX IDX_NEWSLETTER_CONSENT_EMAIL (email), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB");
|
|
||||||
$this->addSql("INSERT IGNORE INTO newsletter_consent (email, mailjet_list_id, confirmed_at, revoked_at, first_name, last_name, created_at, updated_at) SELECT email, 10321569, confirmed_at, revoked_at, first_name, last_name, created_at, COALESCE(confirmed_at, created_at) FROM newsletter_opt_in_confirmation WHERE confirmed_at IS NOT NULL AND (mailjet_list_ids IS NULL OR JSON_LENGTH(mailjet_list_ids) = 0)");
|
|
||||||
$this->addSql("INSERT IGNORE INTO newsletter_consent (email, mailjet_list_id, confirmed_at, revoked_at, first_name, last_name, created_at, updated_at) SELECT c.email, CAST(j.list_id AS UNSIGNED), c.confirmed_at, c.revoked_at, c.first_name, c.last_name, c.created_at, COALESCE(c.confirmed_at, c.created_at) FROM newsletter_opt_in_confirmation c JOIN JSON_TABLE(c.mailjet_list_ids, '$[*]' COLUMNS (list_id VARCHAR(32) PATH '$')) j WHERE c.confirmed_at IS NOT NULL");
|
|
||||||
$this->addSql('DELETE FROM newsletter_opt_in_confirmation WHERE confirmed_at IS NOT NULL');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('DROP TABLE newsletter_consent');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
final class Version20260429130000 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return 'Rename newsletter opt-in confirmation table to newsletter opt-in request';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('RENAME TABLE newsletter_opt_in_confirmation TO newsletter_opt_in_request');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('RENAME TABLE newsletter_opt_in_request TO newsletter_opt_in_confirmation');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Auto-generated Migration: Please modify to your needs!
|
|
||||||
*/
|
|
||||||
final class Version20260625092525 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
// this up() migration is auto-generated, please modify it to your needs
|
|
||||||
$this->addSql('CREATE TABLE accommodation (id INT AUTO_INCREMENT NOT NULL, created_by_id INT DEFAULT NULL, updated_by_id INT DEFAULT NULL, name VARCHAR(255) NOT NULL, code VARCHAR(16) NOT NULL, min_pax_summer INT NOT NULL, max_adolescent_age INT NOT NULL, currency VARCHAR(3) NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', updated_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', UNIQUE INDEX UNIQ_2D38541277153098 (code), INDEX IDX_2D385412B03A8386 (created_by_id), INDEX IDX_2D385412896DBBDE (updated_by_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
|
|
||||||
$this->addSql('CREATE TABLE accommodation_price (id INT AUTO_INCREMENT NOT NULL, accommodation_id INT NOT NULL, created_by_id INT DEFAULT NULL, updated_by_id INT DEFAULT NULL, date_from DATE NOT NULL COMMENT \'(DC2Type:date_immutable)\', date_to DATE NOT NULL COMMENT \'(DC2Type:date_immutable)\', included_pax INT NOT NULL, price_per_night INT NOT NULL, price_additional_person INT NOT NULL, min_nights INT NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', updated_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', INDEX IDX_C67701238F3692CD (accommodation_id), INDEX IDX_C6770123B03A8386 (created_by_id), INDEX IDX_C6770123896DBBDE (updated_by_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
|
|
||||||
$this->addSql('CREATE TABLE additional_service (id INT AUTO_INCREMENT NOT NULL, accommodation_id INT NOT NULL, created_by_id INT DEFAULT NULL, updated_by_id INT DEFAULT NULL, label VARCHAR(255) NOT NULL, price INT NOT NULL, type VARCHAR(20) NOT NULL, date_from DATE NOT NULL COMMENT \'(DC2Type:date_immutable)\', date_to DATE NOT NULL COMMENT \'(DC2Type:date_immutable)\', `grouping` VARCHAR(128) NOT NULL, sorting INT NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', updated_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', INDEX IDX_E01749268F3692CD (accommodation_id), INDEX IDX_E0174926B03A8386 (created_by_id), INDEX IDX_E0174926896DBBDE (updated_by_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
|
|
||||||
$this->addSql('CREATE TABLE board_service (id INT AUTO_INCREMENT NOT NULL, accommodation_id INT NOT NULL, created_by_id INT DEFAULT NULL, updated_by_id INT DEFAULT NULL, label VARCHAR(255) NOT NULL, price INT NOT NULL, date_from DATE NOT NULL COMMENT \'(DC2Type:date_immutable)\', date_to DATE NOT NULL COMMENT \'(DC2Type:date_immutable)\', sorting INT NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', updated_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', INDEX IDX_A983B2DD8F3692CD (accommodation_id), INDEX IDX_A983B2DDB03A8386 (created_by_id), INDEX IDX_A983B2DD896DBBDE (updated_by_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
|
|
||||||
$this->addSql('ALTER TABLE accommodation ADD CONSTRAINT FK_2D385412B03A8386 FOREIGN KEY (created_by_id) REFERENCES user (id) ON DELETE SET NULL');
|
|
||||||
$this->addSql('ALTER TABLE accommodation ADD CONSTRAINT FK_2D385412896DBBDE FOREIGN KEY (updated_by_id) REFERENCES user (id) ON DELETE SET NULL');
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price ADD CONSTRAINT FK_C67701238F3692CD FOREIGN KEY (accommodation_id) REFERENCES accommodation (id)');
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price ADD CONSTRAINT FK_C6770123B03A8386 FOREIGN KEY (created_by_id) REFERENCES user (id) ON DELETE SET NULL');
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price ADD CONSTRAINT FK_C6770123896DBBDE FOREIGN KEY (updated_by_id) REFERENCES user (id) ON DELETE SET NULL');
|
|
||||||
$this->addSql('ALTER TABLE additional_service ADD CONSTRAINT FK_E01749268F3692CD FOREIGN KEY (accommodation_id) REFERENCES accommodation (id)');
|
|
||||||
$this->addSql('ALTER TABLE additional_service ADD CONSTRAINT FK_E0174926B03A8386 FOREIGN KEY (created_by_id) REFERENCES user (id) ON DELETE SET NULL');
|
|
||||||
$this->addSql('ALTER TABLE additional_service ADD CONSTRAINT FK_E0174926896DBBDE FOREIGN KEY (updated_by_id) REFERENCES user (id) ON DELETE SET NULL');
|
|
||||||
$this->addSql('ALTER TABLE board_service ADD CONSTRAINT FK_A983B2DD8F3692CD FOREIGN KEY (accommodation_id) REFERENCES accommodation (id)');
|
|
||||||
$this->addSql('ALTER TABLE board_service ADD CONSTRAINT FK_A983B2DDB03A8386 FOREIGN KEY (created_by_id) REFERENCES user (id) ON DELETE SET NULL');
|
|
||||||
$this->addSql('ALTER TABLE board_service ADD CONSTRAINT FK_A983B2DD896DBBDE FOREIGN KEY (updated_by_id) REFERENCES user (id) ON DELETE SET NULL');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
// this down() migration is auto-generated, please modify it to your needs
|
|
||||||
$this->addSql('ALTER TABLE accommodation DROP FOREIGN KEY FK_2D385412B03A8386');
|
|
||||||
$this->addSql('ALTER TABLE accommodation DROP FOREIGN KEY FK_2D385412896DBBDE');
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price DROP FOREIGN KEY FK_C67701238F3692CD');
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price DROP FOREIGN KEY FK_C6770123B03A8386');
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price DROP FOREIGN KEY FK_C6770123896DBBDE');
|
|
||||||
$this->addSql('ALTER TABLE additional_service DROP FOREIGN KEY FK_E01749268F3692CD');
|
|
||||||
$this->addSql('ALTER TABLE additional_service DROP FOREIGN KEY FK_E0174926B03A8386');
|
|
||||||
$this->addSql('ALTER TABLE additional_service DROP FOREIGN KEY FK_E0174926896DBBDE');
|
|
||||||
$this->addSql('ALTER TABLE board_service DROP FOREIGN KEY FK_A983B2DD8F3692CD');
|
|
||||||
$this->addSql('ALTER TABLE board_service DROP FOREIGN KEY FK_A983B2DDB03A8386');
|
|
||||||
$this->addSql('ALTER TABLE board_service DROP FOREIGN KEY FK_A983B2DD896DBBDE');
|
|
||||||
$this->addSql('DROP TABLE accommodation');
|
|
||||||
$this->addSql('DROP TABLE accommodation_price');
|
|
||||||
$this->addSql('DROP TABLE additional_service');
|
|
||||||
$this->addSql('DROP TABLE board_service');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Auto-generated Migration: Please modify to your needs!
|
|
||||||
*/
|
|
||||||
final class Version20260625154838 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
// this up() migration is auto-generated, please modify it to your needs
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price ADD price_per_night_override INT DEFAULT NULL');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
// this down() migration is auto-generated, please modify it to your needs
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price DROP price_per_night_override');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Auto-generated Migration: Please modify to your needs!
|
|
||||||
*/
|
|
||||||
final class Version20260625173941 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
// this up() migration is auto-generated, please modify it to your needs
|
|
||||||
$this->addSql('ALTER TABLE additional_service ADD selection_group VARCHAR(128) DEFAULT NULL, DROP `grouping`');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
// this down() migration is auto-generated, please modify it to your needs
|
|
||||||
$this->addSql('ALTER TABLE additional_service ADD `grouping` VARCHAR(128) NOT NULL, DROP selection_group');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Auto-generated Migration: Please modify to your needs!
|
|
||||||
*/
|
|
||||||
final class Version20260627121823 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
// this up() migration is auto-generated, please modify it to your needs
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price ADD accept_undersubscription TINYINT(1) NOT NULL');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
// this down() migration is auto-generated, please modify it to your needs
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price DROP accept_undersubscription');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Auto-generated Migration: Please modify to your needs!
|
|
||||||
*/
|
|
||||||
final class Version20260627130625 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
// this up() migration is auto-generated, please modify it to your needs
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price ADD price_additional_person_override INT DEFAULT NULL');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
// this down() migration is auto-generated, please modify it to your needs
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price DROP price_additional_person_override');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Auto-generated Migration: Please modify to your needs!
|
|
||||||
*/
|
|
||||||
final class Version20260630080409 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
// this up() migration is auto-generated, please modify it to your needs
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price ADD season VARCHAR(20) NOT NULL');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
// this down() migration is auto-generated, please modify it to your needs
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price DROP season');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Auto-generated Migration: Please modify to your needs!
|
|
||||||
*/
|
|
||||||
final class Version20260630085856 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
// this up() migration is auto-generated, please modify it to your needs
|
|
||||||
$this->addSql('DROP INDEX UNIQ_2D38541277153098 ON accommodation');
|
|
||||||
$this->addSql('ALTER TABLE accommodation ADD cms_code VARCHAR(16) DEFAULT NULL, CHANGE code calendar_code VARCHAR(16) NOT NULL');
|
|
||||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_2D3854122583B3CC ON accommodation (calendar_code)');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
// this down() migration is auto-generated, please modify it to your needs
|
|
||||||
$this->addSql('DROP INDEX UNIQ_2D3854122583B3CC ON accommodation');
|
|
||||||
$this->addSql('ALTER TABLE accommodation DROP cms_code, CHANGE calendar_code code VARCHAR(16) NOT NULL');
|
|
||||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_2D38541277153098 ON accommodation (code)');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Auto-generated Migration: Please modify to your needs!
|
|
||||||
*/
|
|
||||||
final class Version20260702084714 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return 'Replace override price fields with a PriceType column; migrate existing override rows to sibling DISCOUNT records';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price ADD type VARCHAR(20) DEFAULT NULL');
|
|
||||||
|
|
||||||
$this->addSql(<<<'SQL'
|
|
||||||
INSERT INTO accommodation_price
|
|
||||||
(accommodation_id, date_from, date_to, season, included_pax, min_nights,
|
|
||||||
price_per_night, price_additional_person, accept_undersubscription, type,
|
|
||||||
created_at, updated_at, created_by_id, updated_by_id)
|
|
||||||
SELECT accommodation_id, date_from, date_to, season, included_pax, min_nights,
|
|
||||||
price_per_night_override, price_additional_person_override, accept_undersubscription, 'discount',
|
|
||||||
created_at, updated_at, created_by_id, updated_by_id
|
|
||||||
FROM accommodation_price
|
|
||||||
WHERE price_per_night_override IS NOT NULL
|
|
||||||
SQL);
|
|
||||||
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price DROP price_per_night_override, DROP price_additional_person_override');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price ADD price_per_night_override INT DEFAULT NULL, ADD price_additional_person_override INT DEFAULT NULL, DROP type');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DoctrineMigrations;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Auto-generated Migration: Please modify to your needs!
|
|
||||||
*/
|
|
||||||
final class Version20260706120805 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
// this up() migration is auto-generated, please modify it to your needs
|
|
||||||
$this->addSql('ALTER TABLE accommodation DROP min_pax_summer');
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price ADD accept_short_term TINYINT(1) NOT NULL');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
// this down() migration is auto-generated, please modify it to your needs
|
|
||||||
$this->addSql('ALTER TABLE accommodation ADD min_pax_summer INT NOT NULL');
|
|
||||||
$this->addSql('ALTER TABLE accommodation_price DROP accept_short_term');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user