diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index f36f964..5373f35 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -20,7 +20,12 @@
"Skill(run)",
"Bash(ddev describe *)",
"Bash(curl *)",
- "Bash(ddev mysql *)"
+ "Bash(ddev mysql *)",
+ "Bash(git status *)",
+ "Bash(npx playwright *)",
+ "Bash(npm init *)",
+ "Bash(npm install *)",
+ "Bash(NODE_TLS_REJECT_UNAUTHORIZED=0 node test.mjs)"
]
}
}
diff --git a/httpdocs/_backups/spawntree-2026-06-22-backup.xml b/httpdocs/_backups/spawntree-2026-06-22-backup.xml
new file mode 100644
index 0000000..d2796ce
--- /dev/null
+++ b/httpdocs/_backups/spawntree-2026-06-22-backup.xml
@@ -0,0 +1,5800 @@
+
+
+ 2026-06-22T10:34:01+02:00
+ 1.1
+ 2018-03-19T15:03:50+01:00
+ EUR
+ 43709
+ spawntree
+ spawntree GmbH
+ 2025-04-19T03:35:15+02:00
+
+
+
+ 2018-03-19T15:05:22+01:00
+ 7500
+ 487895
+ Dr. Odin
+
+ 2019-01-10T09:57:28+01:00
+ false
+
+
+
+
+ 2018-07-26T10:32:39+02:00
+ 9000
+ 515306
+ Matsen
+
+ 2022-06-03T09:52:58+02:00
+ false
+ hourly_rate
+
+
+
+ 2018-11-27T17:13:25+01:00
+ 0
+ 531184
+ spawntree GmbH
+
+ 2019-09-17T12:44:14+02:00
+ false
+ hourly_rate
+
+
+
+ 2019-01-02T13:02:55+01:00
+ 11250
+ 534086
+ followfood
+ 100 EUR + 12,50 EUR an TankTank
+ 2025-01-29T17:40:30+01:00
+ false
+ hourly_rate
+
+
+ 402469
+ 7500
+
+
+ 402470
+ 7500
+
+
+ 402471
+ 7500
+
+
+ 402472
+ 7500
+
+
+ 402473
+ 7500
+
+
+ 403333
+ 7500
+
+
+ 404635
+ 7500
+
+
+ 425720
+ 7500
+
+
+
+
+ 2019-01-02T13:05:14+01:00
+ 0
+ 534087
+ Northworks GmbH
+
+ 2019-01-10T09:59:10+01:00
+ false
+ hourly_rates_per_service
+
+
+ 402469
+ 6875
+
+
+ 402470
+ 6875
+
+
+ 402471
+ 6875
+
+
+ 402472
+ 6875
+
+
+ 402473
+ 6875
+
+
+ 403333
+ 6875
+
+
+ 404635
+ 6875
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 6875
+
+
+
+
+ 2019-01-07T15:32:43+01:00
+ 0
+ 534671
+ Colt UX
+
+ 2019-01-16T20:13:28+01:00
+ false
+ hourly_rates_per_service
+
+
+ 402469
+ 6500
+
+
+ 402470
+ 6500
+
+
+ 402471
+ 6500
+
+
+ 402472
+ 6500
+
+
+ 402473
+ 6500
+
+
+ 403333
+ 6500
+
+
+ 404635
+ 6500
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 6500
+
+
+
+
+ 2019-01-07T17:01:56+01:00
+ 7500
+ 534716
+ hauni
+
+ 2019-01-10T09:57:35+01:00
+ false
+
+
+
+
+ 2019-01-10T10:03:26+01:00
+ 8000
+ 535258
+ Heike Manthey
+
+ 2023-03-02T16:04:39+01:00
+ false
+ hourly_rates_per_service
+
+
+ 402469
+ 8000
+
+
+ 402470
+ 8000
+
+
+ 402471
+ 8000
+
+
+ 402472
+ 8000
+
+
+ 402473
+ 8000
+
+
+ 403333
+ 8000
+
+
+ 404635
+ 8000
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 8000
+
+
+
+
+ 2019-01-10T12:24:19+01:00
+ 8500
+ 535326
+ Kita Himmelblau
+
+ 2022-06-16T17:05:48+02:00
+ false
+ hourly_rate
+
+
+ 402469
+ 7000
+
+
+ 402470
+ 7000
+
+
+ 402471
+ 7000
+
+
+ 402472
+ 7000
+
+
+ 402473
+ 7000
+
+
+ 403333
+ 7000
+
+
+ 404635
+ 7000
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 7000
+
+
+
+
+ 2019-01-10T12:26:15+01:00
+ 0
+ 535327
+ SMC Consult GmbH
+
+ 2019-01-10T12:26:15+01:00
+ false
+ hourly_rates_per_service
+
+
+ 402469
+ 7000
+
+
+ 402470
+ 7000
+
+
+ 402471
+ 7000
+
+
+ 402472
+ 7000
+
+
+ 402473
+ 7000
+
+
+ 403333
+ 7000
+
+
+ 404635
+ 7000
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 7000
+
+
+
+
+ 2019-01-16T15:12:09+01:00
+ 9000
+ 536229
+ Thielbeer Consulting
+
+ 2022-09-20T13:13:56+02:00
+ false
+ hourly_rate
+
+
+ 402469
+ 7000
+
+
+ 402470
+ 7000
+
+
+ 402471
+ 7000
+
+
+ 402472
+ 7000
+
+
+ 402473
+ 7000
+
+
+ 403333
+ 7000
+
+
+ 404635
+ 7000
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 7000
+
+
+
+
+ 2019-01-17T15:57:45+01:00
+ 0
+ 536420
+ Wood Agency
+
+ 2019-01-17T15:57:45+01:00
+ false
+
+
+
+
+ 2019-01-17T17:25:43+01:00
+ 9000
+ 536439
+ Dogument
+
+ 2022-11-25T09:59:04+01:00
+ false
+ hourly_rate
+
+
+ 402469
+ 7000
+
+
+ 402470
+ 7000
+
+
+ 402471
+ 7000
+
+
+ 402472
+ 7000
+
+
+ 402473
+ 7000
+
+
+ 403333
+ 7000
+
+
+ 404635
+ 7000
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 7000
+
+
+
+
+ 2019-01-29T12:56:25+01:00
+ 0
+ 537958
+ Frauenarztpraxis Bramfeld (Möckel)
+
+ 2020-01-07T14:14:09+01:00
+ false
+ hourly_rates_per_service
+
+
+ 402469
+ 7000
+
+
+ 402470
+ 7000
+
+
+ 402471
+ 7000
+
+
+ 402472
+ 7000
+
+
+ 402473
+ 7000
+
+
+ 403333
+ 7000
+
+
+ 404635
+ 7000
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 7000
+
+
+
+
+ 2019-01-29T16:36:05+01:00
+ 0
+ 537995
+ TankTank
+
+ 2019-01-29T16:36:05+01:00
+ false
+ hourly_rates_per_service
+
+
+ 402469
+ 8000
+
+
+ 402470
+ 8000
+
+
+ 402471
+ 8000
+
+
+ 402472
+ 8000
+
+
+ 402473
+ 8000
+
+
+ 403333
+ 8000
+
+
+ 404635
+ 8000
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 8000
+
+
+
+
+ 2019-02-15T11:43:11+01:00
+ 0
+ 540111
+ Lohmann Pflegesachverständige
+
+ 2019-02-15T11:43:11+01:00
+ false
+ hourly_rates_per_service
+
+
+ 402469
+ 7000
+
+
+ 402470
+ 7000
+
+
+ 402471
+ 7000
+
+
+ 402472
+ 7000
+
+
+ 402473
+ 7000
+
+
+ 403333
+ 7000
+
+
+ 404635
+ 7000
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 7000
+
+
+
+
+ 2019-02-21T20:20:30+01:00
+ 8000
+ 540862
+ PLP GmbH
+
+ 2022-09-30T16:47:22+02:00
+ false
+ hourly_rate
+
+
+ 402469
+ 7000
+
+
+ 402470
+ 7000
+
+
+ 402471
+ 7000
+
+
+ 402472
+ 7000
+
+
+ 402473
+ 7000
+
+
+ 403333
+ 7000
+
+
+ 404635
+ 7000
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 7000
+
+
+
+
+ 2019-03-05T09:15:18+01:00
+ 0
+ 541986
+ Desert Flower Foundation
+
+ 2019-03-05T09:15:18+01:00
+ false
+ hourly_rates_per_service
+
+
+ 402469
+ 7000
+
+
+ 402470
+ 7000
+
+
+ 402471
+ 7000
+
+
+ 402472
+ 7000
+
+
+ 402473
+ 7000
+
+
+ 403333
+ 7000
+
+
+ 404635
+ 7000
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 7000
+
+
+ 430725
+ 0
+
+
+
+
+ 2019-03-08T09:06:38+01:00
+ 0
+ 542508
+ h plus p werbeagentur GmbH
+
+ 2019-03-08T09:06:38+01:00
+ false
+ hourly_rates_per_service
+
+
+ 402469
+ 6500
+
+
+ 402470
+ 6500
+
+
+ 402471
+ 6500
+
+
+ 402472
+ 6500
+
+
+ 402473
+ 6500
+
+
+ 403333
+ 6500
+
+
+ 404635
+ 6500
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 6500
+
+
+ 430725
+ 0
+
+
+
+
+ 2019-03-19T14:54:23+01:00
+ 0
+ 543816
+ Hamburg Dermatologie
+
+ 2019-03-19T14:54:23+01:00
+ false
+ hourly_rates_per_service
+
+
+ 402469
+ 7000
+
+
+ 402470
+ 7000
+
+
+ 402471
+ 7000
+
+
+ 402472
+ 7000
+
+
+ 402473
+ 7000
+
+
+ 403333
+ 7000
+
+
+ 404635
+ 7000
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 7000
+
+
+ 430725
+ 0
+
+
+
+
+ 2019-06-06T14:20:52+02:00
+ 0
+ 551802
+ nordwärts - Simone Meseberg
+
+ 2022-06-03T16:01:04+02:00
+ false
+
+
+
+
+ 2019-07-17T15:39:12+02:00
+ 0
+ 557912
+ DolceRita
+
+ 2019-07-17T15:39:12+02:00
+ false
+
+
+
+
+ 2019-09-16T10:10:20+02:00
+ 0
+ 563666
+ Shaken not Stirred Advertising
+
+ 2019-09-16T10:10:20+02:00
+ false
+
+
+
+
+ 2019-09-28T15:38:51+02:00
+ 0
+ 566133
+ Shaken not Stirred
+
+ 2019-09-28T15:38:51+02:00
+ false
+
+
+
+
+ 2019-11-15T12:57:02+01:00
+ 0
+ 576885
+ Angelika Ballosch
+
+ 2019-11-15T12:57:02+01:00
+ false
+ hourly_rates_per_service
+
+
+ 402469
+ 7000
+
+
+ 402470
+ 7000
+
+
+ 402471
+ 7000
+
+
+ 402472
+ 7000
+
+
+ 402473
+ 7000
+
+
+ 403333
+ 7000
+
+
+ 404635
+ 7000
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 7000
+
+
+ 430725
+ 0
+
+
+
+
+ 2019-12-09T17:51:34+01:00
+ 8000
+ 581212
+ MarxKrontal Partner
+
+ 2022-04-07T14:18:47+02:00
+ false
+ hourly_rate
+
+
+ 402469
+ 7000
+
+
+ 402470
+ 7000
+
+
+ 402471
+ 7000
+
+
+ 402472
+ 7000
+
+
+ 402473
+ 7000
+
+
+ 403333
+ 7000
+
+
+ 404635
+ 7000
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 7000
+
+
+ 430725
+ 0
+
+
+
+
+ 2020-01-07T14:14:53+01:00
+ 0
+ 584073
+ Frauenarztpraxis Sasel (Pelzl)
+
+ 2020-01-07T14:15:40+01:00
+ false
+ hourly_rate
+
+
+
+ 2020-02-12T16:17:18+01:00
+ 0
+ 589306
+ KPS Concertbuero
+
+ 2020-02-12T16:17:18+01:00
+ false
+
+
+
+
+ 2020-02-20T12:29:03+01:00
+ 8000
+ 590244
+ Cabernet & Co.
+
+ 2020-02-20T12:31:19+01:00
+ false
+ hourly_rate
+
+
+
+ 2020-03-22T23:54:45+01:00
+ 8000
+ 593164
+ crazyartists
+
+ 2020-03-22T23:56:02+01:00
+ false
+ hourly_rate
+
+
+
+ 2020-05-29T16:55:27+02:00
+ 8000
+ 600144
+ Irena Celecki
+
+ 2021-05-10T15:57:54+02:00
+ false
+ hourly_rate
+
+
+ 402469
+ 8000
+
+
+ 402470
+ 8000
+
+
+ 402471
+ 8000
+
+
+ 402472
+ 8000
+
+
+ 402473
+ 8000
+
+
+ 403333
+ 8000
+
+
+ 404635
+ 8000
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 8000
+
+
+ 430725
+ 0
+
+
+
+
+ 2020-06-18T10:57:24+02:00
+ 0
+ 601836
+ Laserzentrum Hamburg
+
+ 2020-06-18T10:57:24+02:00
+ false
+
+
+
+
+ 2020-06-26T17:05:07+02:00
+ 0
+ 602745
+ DUBBEL SPÄTH GmbH & Co. KG
+
+ 2020-06-26T17:05:07+02:00
+ false
+
+
+
+
+ 2020-09-04T18:44:53+02:00
+ 0
+ 610604
+ HORST Baumarkt
+
+ 2020-09-04T18:44:53+02:00
+ false
+
+
+
+
+ 2020-09-24T09:52:20+02:00
+ 0
+ 612931
+ Fabian Rädecke
+
+ 2020-09-24T09:52:20+02:00
+ false
+
+
+
+
+ 2020-10-07T19:02:13+02:00
+ 0
+ 614387
+ Lichtblick
+
+ 2020-10-07T19:02:13+02:00
+ false
+
+
+
+
+ 2020-10-08T09:20:08+02:00
+ 8000
+ 614408
+ webschuppen
+
+ 2021-12-07T15:48:19+01:00
+ false
+ hourly_rate
+
+
+
+ 2020-10-30T09:44:45+01:00
+ 7500
+ 616357
+ E-Formel
+
+ 2021-02-10T09:59:31+01:00
+ false
+ hourly_rate
+
+
+
+ 2021-01-18T14:25:12+01:00
+ 8000
+ 623582
+ Champagner Galerie
+
+ 2021-01-18T14:25:12+01:00
+ false
+ hourly_rates_per_service
+
+
+ 402469
+ 8000
+
+
+ 402470
+ 8000
+
+
+ 402471
+ 8000
+
+
+ 402472
+ 8000
+
+
+ 402473
+ 8000
+
+
+ 403333
+ 8000
+
+
+ 404635
+ 8000
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 8000
+
+
+ 430725
+ 0
+
+
+
+
+ 2021-03-09T11:17:07+01:00
+ 7500
+ 628868
+ RealFM
+
+ 2021-03-09T11:17:07+01:00
+ false
+ hourly_rate
+
+
+
+ 2021-05-07T11:12:38+02:00
+ 8000
+ 634746
+ fatchd
+
+ 2021-05-07T11:12:38+02:00
+ false
+ hourly_rate
+
+
+
+ 2021-05-19T10:54:05+02:00
+ 8000
+ 635656
+ Kantar Media GmbH
+
+ 2021-05-19T10:54:05+02:00
+ false
+ hourly_rate
+
+
+
+ 2021-05-28T17:17:50+02:00
+ 8000
+ 636434
+ Elbkapitäne
+
+ 2021-05-28T17:17:50+02:00
+ false
+ hourly_rate
+
+
+
+ 2021-06-29T11:00:29+02:00
+ 8500
+ 639151
+ webförsterei
+
+ 2021-06-29T11:00:29+02:00
+ false
+ hourly_rate
+
+
+
+ 2021-08-09T12:33:23+02:00
+ 8500
+ 642389
+ Mukig.net - Mund-Kiefer-Gesichtschirurgie
+
+ 2021-08-09T12:33:23+02:00
+ false
+ hourly_rate
+
+
+
+ 2021-08-24T11:08:48+02:00
+ 7700
+ 643501
+ CodeControl
+
+ 2021-09-02T13:08:10+02:00
+ false
+ hourly_rate
+
+
+
+ 2021-10-28T15:15:04+02:00
+ 8000
+ 648589
+ FRG Hansa
+
+ 2021-10-28T15:15:04+02:00
+ false
+ hourly_rate
+
+
+
+ 2021-11-11T16:52:57+01:00
+ 8000
+ 649777
+ André Firmenich
+
+ 2021-11-11T16:52:57+01:00
+ false
+ hourly_rate
+
+
+
+ 2021-12-01T17:12:13+01:00
+ 8000
+ 651610
+ Diäko GmbH
+ 112,50 EUR abrechnen
+ 2021-12-01T17:12:24+01:00
+ false
+ hourly_rate
+
+
+
+ 2021-12-09T09:44:58+01:00
+ 9000
+ 652191
+ Bauer Advance
+
+ 2021-12-09T09:44:58+01:00
+ false
+ hourly_rate
+
+
+
+ 2022-01-10T10:22:58+01:00
+ 9000
+ 654013
+ Fuchsbau Timmendorf
+
+ 2022-01-10T10:22:58+01:00
+ false
+ hourly_rate
+
+
+
+ 2022-01-14T14:08:07+01:00
+ 9000
+ 654633
+ PR-Affairs
+
+ 2022-01-14T14:08:07+01:00
+ false
+ hourly_rate
+
+
+
+ 2022-02-08T12:20:51+01:00
+ 9000
+ 657073
+ DHV Prüfungsverband
+
+ 2022-02-08T12:20:51+01:00
+ false
+ hourly_rate
+
+
+
+ 2022-02-17T09:52:27+01:00
+ 9000
+ 658161
+ AKN
+
+ 2022-02-17T09:52:27+01:00
+ false
+ hourly_rate
+
+
+
+ 2022-02-25T18:02:20+01:00
+ 9000
+ 659032
+ signalkontor°
+
+ 2022-02-25T18:02:20+01:00
+ false
+ hourly_rate
+
+
+
+ 2022-03-16T14:53:53+01:00
+ 8000
+ 660872
+ Nadine Patrizia Scheel
+
+ 2022-03-16T14:53:53+01:00
+ false
+ hourly_rate
+
+
+
+ 2022-04-11T12:45:20+02:00
+ 5000
+ 663077
+ Never play alone
+
+ 2022-04-11T12:45:20+02:00
+ false
+ hourly_rate
+
+
+
+ 2022-04-22T09:46:55+02:00
+ 9000
+ 663879
+ Praxis Laage
+
+ 2022-04-22T09:46:55+02:00
+ false
+ hourly_rate
+
+
+
+ 2022-05-12T13:26:08+02:00
+ 8000
+ 668857
+ tBuddy
+
+ 2023-12-05T10:21:23+01:00
+ false
+ hourly_rate
+
+
+
+ 2022-05-12T15:34:19+02:00
+ 9000
+ 668873
+ Rumpel Süsel
+
+ 2022-05-12T15:34:19+02:00
+ false
+ hourly_rate
+
+
+
+ 2022-06-01T16:59:02+02:00
+ 9000
+ 670291
+ Tatjana Meyer
+
+ 2022-06-01T16:59:02+02:00
+ false
+ hourly_rate
+
+
+
+ 2022-06-01T17:02:07+02:00
+ 9000
+ 670292
+ Wash 'n Roll
+
+ 2022-06-01T17:02:07+02:00
+ false
+ hourly_rate
+
+
+
+ 2022-06-08T15:03:49+02:00
+ 9000
+ 670793
+ BSB Rohrreinigung
+
+ 2022-06-08T15:03:49+02:00
+ false
+ hourly_rate
+
+
+
+ 2022-08-16T11:33:00+02:00
+ 9000
+ 675486
+ Stop the water while using me
+
+ 2022-08-16T11:33:00+02:00
+ false
+ hourly_rate
+
+
+
+ 2022-11-07T10:08:02+01:00
+ 9000
+ 681218
+ IB Borowski
+
+ 2022-11-07T10:08:02+01:00
+ false
+ hourly_rate
+
+
+
+ 2022-11-07T11:50:31+01:00
+ 9000
+ 681243
+ Suzlon Energy Ltd.
+
+ 2022-11-07T11:50:31+01:00
+ false
+ hourly_rate
+
+
+
+ 2022-11-16T13:13:07+01:00
+ 9000
+ 682039
+ Britta Edl
+
+ 2022-11-16T13:13:07+01:00
+ false
+ hourly_rate
+
+
+
+ 2022-11-18T15:56:06+01:00
+ 9000
+ 682261
+ Salt & Rocks
+
+ 2022-11-18T15:56:06+01:00
+ false
+ hourly_rate
+
+
+
+ 2023-02-21T18:02:29+01:00
+ 9000
+ 691756
+ Sara Aduse Foundation
+
+ 2023-02-21T18:02:29+01:00
+ false
+ hourly_rate
+
+
+
+ 2023-02-27T17:36:37+01:00
+ 0
+ 693457
+ Polardot Studios
+
+ 2023-02-27T17:36:37+01:00
+ false
+
+
+
+
+ 2023-05-25T16:25:26+02:00
+ 9000
+ 702560
+ FeWo Ruhepol
+
+ 2023-05-25T16:25:26+02:00
+ false
+ hourly_rate
+
+
+
+ 2023-09-07T21:47:27+02:00
+ 9000
+ 711030
+ Lenk Seafood
+
+ 2023-09-07T21:47:27+02:00
+ false
+ hourly_rate
+
+
+
+ 2023-09-19T12:05:25+02:00
+ 9000
+ 711675
+ Matthias Scharf Fotografie
+
+ 2023-09-19T12:05:25+02:00
+ false
+ hourly_rate
+
+
+
+ 2024-02-09T11:09:49+01:00
+ 9000
+ 723675
+ Imaq Pilot
+
+ 2024-02-09T11:09:49+01:00
+ false
+ hourly_rate
+
+
+
+ 2024-04-24T18:00:32+02:00
+ 9000
+ 730679
+ Pönninghaus
+
+ 2024-04-24T18:00:32+02:00
+ false
+ hourly_rate
+
+
+
+ 2024-08-28T09:52:22+02:00
+ 9000
+ 738052
+ Bremer Ortungstechnik Aßmuth
+
+ 2024-08-28T09:52:22+02:00
+ false
+ hourly_rate
+
+
+
+ 2024-09-02T10:59:21+02:00
+ 10000
+ 738291
+ Neurologie Neuer Wall
+
+ 2024-09-02T10:59:21+02:00
+ false
+ hourly_rate
+
+
+
+ 2024-09-05T11:37:42+02:00
+ 9000
+ 738577
+ David Sattler
+
+ 2024-09-05T11:37:42+02:00
+ false
+ hourly_rate
+
+
+
+ 2024-10-21T15:18:42+02:00
+ 10000
+ 741322
+ LaPure
+
+ 2025-05-14T12:33:22+02:00
+ false
+ hourly_rate
+
+
+
+ 2024-10-23T10:31:19+02:00
+ 8000
+ 741450
+ Annika Teerling
+
+ 2024-10-23T10:31:19+02:00
+ false
+ hourly_rate
+
+
+
+ 2024-11-22T12:03:49+01:00
+ 9000
+ 743312
+ Sebastian Salmhofer
+ Bit Pulse Digital PTE Limited
+ 2024-11-22T12:03:49+01:00
+ false
+ hourly_rate
+
+
+
+ 2024-12-11T10:28:12+01:00
+ 9000
+ 744846
+ NorthData
+
+ 2024-12-11T10:28:12+01:00
+ false
+ hourly_rate
+
+
+
+ 2024-12-17T10:45:18+01:00
+ 11250
+ 745164
+ Securvita
+
+ 2024-12-17T10:45:18+01:00
+ false
+ hourly_rate
+
+
+
+ 2025-05-05T15:56:35+02:00
+ 10000
+ 837030
+ Hauke Fuhrmann
+
+ 2025-05-05T15:56:35+02:00
+ false
+ hourly_rate
+
+
+
+ 2025-05-06T13:05:27+02:00
+ 10000
+ 837093
+ Massgebend GmbH
+
+ 2025-05-06T13:05:27+02:00
+ false
+ hourly_rate
+
+
+
+ 2025-06-30T12:05:46+02:00
+ 9000
+ 848981
+ Hospizverein Sylt
+
+ 2025-12-17T13:25:30+01:00
+ false
+ hourly_rate
+
+
+
+ 2025-08-11T18:04:34+02:00
+ 10000
+ 853397
+ Medienkapitän
+
+ 2025-08-11T18:04:34+02:00
+ false
+ hourly_rate
+
+
+
+ 2025-10-10T14:11:09+02:00
+ 10000
+ 863730
+ Car GmbH
+
+ 2025-10-10T14:11:09+02:00
+ false
+ hourly_rate
+
+
+
+ 2025-12-03T13:00:09+01:00
+ 10000
+ 873570
+ Stach Installations
+
+ 2025-12-03T13:00:09+01:00
+ false
+ hourly_rate
+
+
+
+ 2026-04-15T11:18:00+02:00
+ 0
+ 902974
+ Zoomulate
+
+ 2026-04-15T11:18:00+02:00
+ false
+
+
+
+
+ 2026-04-29T10:41:10+02:00
+ 0
+ 905212
+ Altoelankauf.de
+
+ 2026-04-29T10:41:10+02:00
+ false
+
+
+
+
+ 2026-05-20T10:45:28+02:00
+ 0
+ 908443
+ Flashpoint Germany
+
+ 2026-05-20T10:45:28+02:00
+ false
+
+
+
+
+ 2026-06-12T11:00:33+02:00
+ 10000
+ 912127
+ Dressler Elektroanlagenbau
+
+ 2026-06-12T11:00:33+02:00
+ false
+ hourly_rate
+
+
+
+
+
+ 0
+ minutes
+ 2018-03-19T15:05:51+01:00
+ 487895
+ 8000
+ 2411023
+ too1s
+
+ 2021-05-12T16:15:36+02:00
+ true
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2018-11-27T17:14:59+01:00
+ 531184
+ 0
+ 2645017
+ Intern
+
+ 2018-11-27T17:15:10+01:00
+ false
+
+
+
+
+ 6840
+ minutes
+ 2019-01-02T13:04:10+01:00
+ 534086
+ 0
+ 2666337
+ Erstellung Website
+
+ 2021-07-15T10:03:23+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-01-02T13:05:36+01:00
+ 534087
+ 0
+ 2666339
+ Goalunited
+
+ 2021-07-15T10:03:42+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-01-02T13:13:24+01:00
+ 487895
+ 0
+ 2666347
+ Erfassungsblatt
+
+ 2022-04-05T17:52:47+02:00
+ true
+
+
+
+
+ 1440
+ minutes
+ 2019-01-07T15:33:15+01:00
+ 534671
+ 0
+ 2670344
+ Website Update / Refresh
+
+ 2019-01-07T15:33:15+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-01-07T17:02:17+01:00
+ 534716
+ 0
+ 2670539
+ Relaunch 1.0
+
+ 2021-07-15T10:04:40+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-01-10T10:04:31+01:00
+ 535258
+ 0
+ 2674318
+ Leseludi
+
+ 2019-01-10T10:04:31+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-01-10T10:04:37+01:00
+ 535258
+ 0
+ 2674319
+ Lernrudi
+
+ 2019-01-10T10:04:37+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-01-10T10:05:24+01:00
+ 531184
+ 0
+ 2674322
+ Krankheit
+
+ 2019-01-10T10:05:24+01:00
+ false
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2019-01-10T12:24:35+01:00
+ 535326
+ 0
+ 2674622
+ Maintenance
+
+ 2019-01-10T12:24:35+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-01-10T12:26:28+01:00
+ 535327
+ 0
+ 2674625
+ Fuchsbau Timmendorf
+
+ 2022-04-05T17:52:28+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-01-10T12:26:38+01:00
+ 535327
+ 0
+ 2674626
+ AKN
+
+ 2022-04-05T17:52:00+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-01-16T15:12:21+01:00
+ 536229
+ 0
+ 2680371
+ Maintenance
+
+ 2019-01-16T15:12:21+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-01-17T15:57:57+01:00
+ 536420
+ 0
+ 2681725
+ Loeschner LLP
+
+ 2019-01-17T15:57:57+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-01-17T15:58:43+01:00
+ 536420
+ 0
+ 2681729
+ Popp Feinkost
+
+ 2019-01-17T15:58:43+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-01-17T17:25:54+01:00
+ 536439
+ 0
+ 2681882
+ Maintenance
+
+ 2019-01-17T17:25:54+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-01-18T15:00:17+01:00
+ 515306
+ 0
+ 2682677
+ Maintenance
+
+ 2019-01-18T15:00:17+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-01-23T17:08:32+01:00
+ 534716
+ 0
+ 2686638
+ hauni 2.0
+
+ 2021-07-15T10:03:46+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-01-29T12:56:37+01:00
+ 537958
+ 0
+ 2690887
+ Maintenance
+
+ 2019-01-29T12:56:37+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-01-29T16:36:19+01:00
+ 537995
+ 0
+ 2691216
+ Huk24
+
+ 2021-07-15T10:03:49+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-02-01T20:21:41+01:00
+ 535327
+ 0
+ 2694880
+ savvynet
+
+ 2022-04-05T17:52:33+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-02-04T19:20:00+01:00
+ 535327
+ 0
+ 2696468
+ Praxis Laage
+
+ 2022-04-05T17:52:32+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-02-15T11:43:26+01:00
+ 540111
+ 0
+ 2706042
+ Maintenance
+
+ 2019-02-15T11:43:26+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-02-18T09:06:49+01:00
+ 535327
+ 0
+ 2706914
+ Vitiligo Verein
+
+ 2022-04-05T17:52:34+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-02-21T20:18:35+01:00
+ 535327
+ 0
+ 2711494
+ VWAEK Hamburg
+
+ 2022-04-05T17:52:35+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-02-21T20:20:47+01:00
+ 540862
+ 0
+ 2711497
+ Maintenance
+
+ 2019-02-21T20:20:47+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-03-05T09:17:41+01:00
+ 541986
+ 0
+ 2720195
+ Maintenance
+
+ 2019-03-05T09:17:41+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-03-07T10:05:29+01:00
+ 487895
+ 8000
+ 2722465
+ Website
+
+ 2021-02-26T10:11:44+01:00
+ false
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2019-03-08T09:06:50+01:00
+ 542508
+ 0
+ 2723407
+ SAE Dental
+
+ 2021-07-15T10:04:44+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-03-15T18:09:24+01:00
+ 534086
+ 8000
+ 2729350
+ Erweiterungsstufe 01
+
+ 2022-04-05T17:51:31+02:00
+ true
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2019-03-19T14:53:39+01:00
+ 535327
+ 0
+ 2731957
+ Quarree
+
+ 2021-07-15T10:04:35+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-03-19T14:54:37+01:00
+ 543816
+ 0
+ 2731958
+ Maintenance
+
+ 2019-03-19T14:54:37+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-04-01T22:12:48+02:00
+ 534086
+ 8000
+ 2742845
+ followfish - Optimierungen
+
+ 2022-04-05T17:51:40+02:00
+ true
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2019-05-09T17:44:33+02:00
+ 536420
+ 0
+ 2769545
+ IHK Kiel
+
+ 2021-07-15T10:03:52+02:00
+ true
+ hourly_rates_per_service
+
+
+ 402469
+ 7000
+
+
+ 402470
+ 7000
+
+
+ 402471
+ 7000
+
+
+ 402472
+ 7000
+
+
+ 402473
+ 7000
+
+
+ 403333
+ 7000
+
+
+ 404635
+ 7000
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 7000
+
+
+ 430725
+ 0
+
+
+
+
+ 0
+ minutes
+ 2019-06-03T20:36:56+02:00
+ 536420
+ 0
+ 2785029
+ Camping Booknis
+
+ 2019-06-03T20:36:56+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-06-06T14:21:14+02:00
+ 551802
+ 0
+ 2787792
+ Maintenance
+
+ 2019-06-06T14:21:14+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-07-09T14:11:55+02:00
+ 535327
+ 0
+ 2809130
+ DHV Prüfungsverband
+
+ 2022-04-05T17:52:19+02:00
+ true
+
+
+
+
+ 2400
+ minutes
+ 2019-07-17T15:39:35+02:00
+ 557912
+ 0
+ 2814457
+ Erstellung Website
+
+ 2019-07-17T15:39:35+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-07-17T15:42:45+02:00
+ 535327
+ 0
+ 2814461
+ Ehlers & Partner
+
+ 2022-04-05T17:52:22+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-08-06T16:56:38+02:00
+ 535327
+ 0
+ 2826756
+ BSB Bremen
+
+ 2022-04-05T17:52:12+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-08-20T17:12:06+02:00
+ 535327
+ 0
+ 2835339
+ FC St. Pauli Handball
+
+ 2019-08-20T17:12:06+02:00
+ false
+
+
+
+
+ 2400
+ minutes
+ 2019-09-16T10:10:42+02:00
+ 563666
+ 0
+ 2852343
+ Landhaus an de Dün
+
+ 2021-07-15T10:03:57+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-09-17T12:42:41+02:00
+ 540862
+ 6250
+ 2853600
+ PLP internes Tool
+
+ 2022-09-30T16:47:37+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-09-18T12:40:18+02:00
+ 535327
+ 0
+ 2854592
+ Diverse
+
+ 2022-04-05T17:52:20+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-09-28T15:39:03+02:00
+ 566133
+ 0
+ 2865415
+ Caplantic
+
+ 2021-07-15T10:03:09+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-10-09T14:15:15+02:00
+ 534086
+ 0
+ 2875415
+ Maintenance
+
+ 2019-10-09T14:15:15+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-10-21T17:52:19+02:00
+ 535327
+ 0
+ 2887446
+ Hautsache Magazin
+
+ 2022-04-05T17:52:29+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-10-28T20:25:01+01:00
+ 535327
+ 0
+ 2896389
+ FeWo Ruhepol
+
+ 2022-04-05T17:52:26+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-10-31T10:15:02+01:00
+ 563666
+ 0
+ 2899018
+ Dünen Stuuv
+
+ 2021-07-15T10:03:18+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-11-14T14:10:00+01:00
+ 535327
+ 0
+ 2909722
+ DC Casitas
+
+ 2022-04-05T17:52:16+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2019-11-15T12:57:13+01:00
+ 576885
+ 0
+ 2910543
+ Maintenance
+
+ 2019-11-15T12:57:13+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2019-12-09T17:51:54+01:00
+ 581212
+ 0
+ 2938527
+ Maintenance
+
+ 2019-12-09T17:51:54+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2020-01-07T14:16:05+01:00
+ 584073
+ 0
+ 2958076
+ Maintenance
+
+ 2021-07-15T10:04:14+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2020-01-15T11:15:50+01:00
+ 535327
+ 0
+ 2965511
+ Gavin Schilling
+
+ 2021-07-15T10:03:44+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2020-02-12T16:17:32+01:00
+ 589306
+ 0
+ 2992396
+ Maintenance
+
+ 2020-02-12T16:17:32+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2020-02-20T12:31:32+01:00
+ 590244
+ 0
+ 2998252
+ Maintenance
+
+ 2020-02-20T12:31:32+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2020-03-22T23:54:57+01:00
+ 593164
+ 0
+ 3018720
+ Maintenance
+
+ 2020-03-22T23:54:57+01:00
+ false
+
+
+
+
+ 2280
+ minutes
+ 2020-04-28T15:59:09+02:00
+ 515306
+ 0
+ 3044133
+ ICOA
+
+ 2021-07-15T10:03:51+02:00
+ true
+
+
+
+
+ 4800
+ minutes
+ 2020-04-29T16:21:27+02:00
+ 563666
+ 0
+ 3045256
+ Lamy US
+
+ 2022-04-05T17:53:36+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2020-05-29T16:55:46+02:00
+ 600144
+ 0
+ 3062261
+ Karina Ewertz - Innenarchitektin
+
+ 2021-04-29T16:43:23+02:00
+ false
+
+
+
+
+ 2040
+ minutes
+ 2020-06-14T23:24:40+02:00
+ 534086
+ 8000
+ 3069495
+ fish & more
+
+ 2020-07-31T17:04:38+02:00
+ false
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2020-06-16T14:36:39+02:00
+ 535327
+ 0
+ 3071059
+ ITU
+
+ 2022-04-05T17:52:30+02:00
+ true
+
+
+
+
+ 3720
+ minutes
+ 2020-06-18T09:45:04+02:00
+ 563666
+ 0
+ 3072362
+ Lamy DE
+
+ 2022-04-05T17:53:35+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2020-06-18T10:57:36+02:00
+ 601836
+ 0
+ 3072458
+ Maintenance
+
+ 2020-06-18T10:57:36+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2020-06-24T09:35:34+02:00
+ 537995
+ 0
+ 3075541
+ Cover my ass
+
+ 2020-06-24T09:35:34+02:00
+ false
+
+
+
+
+ 2640
+ minutes
+ 2020-06-26T17:05:42+02:00
+ 602745
+ 0
+ 3077516
+ Schule auf der Aue Münster
+
+ 2021-07-15T10:04:48+02:00
+ true
+
+
+
+
+ 1200
+ minutes
+ 2020-07-02T15:21:59+02:00
+ 563666
+ 0
+ 3091383
+ Smart Forward
+
+ 2021-07-15T10:04:55+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2020-07-09T13:36:18+02:00
+ 515306
+ 0
+ 3095483
+ Matsen Stiftung
+
+ 2020-07-09T13:36:18+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2020-09-04T18:45:23+02:00
+ 610604
+ 0
+ 3127165
+ Relaunch
+
+ 2022-04-05T17:54:59+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2020-09-24T09:52:33+02:00
+ 612931
+ 0
+ 3141725
+ Maintenance
+
+ 2020-09-24T09:52:33+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2020-09-25T17:51:52+02:00
+ 566133
+ 0
+ 3142916
+ Diverse
+
+ 2020-09-25T17:51:52+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2020-09-29T16:37:42+02:00
+ 515306
+ 0
+ 3144867
+ Fesago
+
+ 2020-09-29T16:37:42+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2020-10-07T19:03:00+02:00
+ 614387
+ 0
+ 3150700
+ Expertum
+
+ 2021-07-15T10:03:27+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2020-10-07T19:03:09+02:00
+ 614387
+ 0
+ 3150702
+ TimePartner
+
+ 2021-07-15T10:04:50+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2020-10-08T09:20:31+02:00
+ 614408
+ 0
+ 3150821
+ Lilly Pharma
+
+ 2020-10-08T09:21:04+02:00
+ false
+
+
+
+
+ 4200
+ minutes
+ 2020-10-13T11:50:00+02:00
+ 602745
+ 6500
+ 3153702
+ Medintim
+
+ 2021-07-15T10:04:26+02:00
+ true
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2020-10-20T19:49:11+02:00
+ 614408
+ 0
+ 3158769
+ Faktor A
+
+ 2021-07-15T10:03:30+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2020-10-23T14:25:48+02:00
+ 614408
+ 0
+ 3160931
+ Diverse
+
+ 2020-10-23T14:25:48+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2020-10-30T09:45:17+01:00
+ 616357
+ 3250
+ 3164955
+ z-E-Formel (Launch)
+
+ 2022-04-05T17:54:45+02:00
+ true
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2020-11-16T11:43:53+01:00
+ 610604
+ 0
+ 3177395
+ Maintenance
+
+ 2020-11-16T11:43:53+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2021-01-12T15:01:32+01:00
+ 535327
+ 0
+ 3209601
+ Bauer Advance
+
+ 2022-04-05T17:52:06+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2021-01-18T14:25:24+01:00
+ 623582
+ 0
+ 3213871
+ Maintenance
+
+ 2021-01-18T14:25:24+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2021-02-10T10:01:07+01:00
+ 616357
+ 7000
+ 3230616
+ Maintenance (rabattiert)
+
+ 2021-02-22T17:51:15+01:00
+ false
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2021-02-19T10:52:38+01:00
+ 614408
+ 8000
+ 3237023
+ Fester Tag (WAJE)
+
+ 2021-07-15T10:03:36+02:00
+ true
+ hourly_rate
+
+
+
+ 2880
+ minutes
+ 2021-02-26T09:57:57+01:00
+ 487895
+ 8000
+ 3241460
+ too1s - Anlagenkataster funktionsfähig machen
+
+ 2022-04-05T17:52:58+02:00
+ true
+ hourly_rate
+
+
+ 402469
+ 8000
+
+
+ 402470
+ 8000
+
+
+ 402471
+ 8000
+
+
+ 402472
+ 8000
+
+
+ 402473
+ 8000
+
+
+ 403333
+ 8000
+
+
+ 404635
+ 8000
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 8000
+
+
+ 430725
+ 0
+
+
+
+
+ 4800
+ minutes
+ 2021-03-09T11:17:26+01:00
+ 628868
+ 0
+ 3248636
+ BIM Tool
+
+ 2021-04-07T11:28:35+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2021-03-30T23:22:09+02:00
+ 614408
+ 8000
+ 3262287
+ Bürgerumfrage Buxtehude
+
+ 2021-03-30T23:22:09+02:00
+ false
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2021-04-20T10:32:55+02:00
+ 535258
+ 0
+ 3273177
+ Schreibsusi
+
+ 2021-09-28T12:59:06+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2021-05-07T11:13:15+02:00
+ 634746
+ 0
+ 3288686
+ Video-Speed-Dating Landingpage
+
+ 2021-05-07T11:13:15+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2021-05-12T16:17:52+02:00
+ 487895
+ 8000
+ 3291589
+ too1s - Sonstiges
+
+ 2022-04-05T17:52:59+02:00
+ true
+ hourly_rate
+
+
+
+ 1920
+ minutes
+ 2021-05-12T16:18:04+02:00
+ 487895
+ 8000
+ 3291590
+ too1s - Vorbereitung Anlagenkataster
+
+ 2022-04-05T17:53:01+02:00
+ true
+ hourly_rate
+
+
+
+ 2400
+ minutes
+ 2021-05-12T16:18:21+02:00
+ 487895
+ 8000
+ 3291591
+ too1s - Preisblatt und Quellkalkulation
+
+ 2022-04-05T17:52:56+02:00
+ true
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2021-05-19T10:54:17+02:00
+ 635656
+ 0
+ 3294705
+ Newsletter-Erstellung
+
+ 2022-04-05T17:54:28+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2021-05-28T17:18:03+02:00
+ 636434
+ 0
+ 3300284
+ New Work
+
+ 2022-04-05T17:54:23+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2021-06-01T16:08:00+02:00
+ 557912
+ 0
+ 3302255
+ Maintenance
+
+ 2021-06-01T16:08:00+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2021-06-18T10:26:30+02:00
+ 487895
+ 8000
+ 3313296
+ tools - Gesamtimport erweitern
+
+ 2022-04-05T17:53:02+02:00
+ true
+ hourly_rates_per_service
+
+
+ 402469
+ 8000
+
+
+ 402470
+ 8000
+
+
+ 402471
+ 8000
+
+
+ 402472
+ 8000
+
+
+ 402473
+ 8000
+
+
+ 403333
+ 8000
+
+
+ 404635
+ 8000
+
+
+ 425205
+ 0
+
+
+ 425206
+ 0
+
+
+ 425720
+ 8000
+
+
+ 430725
+ 0
+
+
+
+
+ 0
+ minutes
+ 2021-06-29T11:02:34+02:00
+ 639151
+ 0
+ 3318954
+ 24hfahrzeugabgabe
+
+ 2021-06-29T11:47:50+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2021-08-02T09:04:44+02:00
+ 535327
+ 0
+ 3338275
+ Adore & Enjoy
+
+ 2022-04-05T17:51:51+02:00
+ true
+
+
+
+
+ 0
+ minutes
+ 2021-08-09T13:50:42+02:00
+ 642389
+ 0
+ 3342424
+ Maintenance
+
+ 2021-08-09T13:50:42+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2021-08-24T11:09:42+02:00
+ 643501
+ 0
+ 3350074
+ Kollex PHP Entwicklung
+
+ 2021-08-24T11:09:42+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2021-08-26T11:50:05+02:00
+ 536420
+ 8000
+ 3351575
+ Zmile
+
+ 2021-08-26T11:50:05+02:00
+ false
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2021-09-17T15:20:25+02:00
+ 536420
+ 8000
+ 3364339
+ Puraglobe
+
+ 2021-09-17T15:20:45+02:00
+ false
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2021-10-28T15:15:21+02:00
+ 648589
+ 0
+ 3389443
+ Konzeption Webpräsenzen
+
+ 2021-10-28T15:15:21+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2021-11-11T16:53:41+01:00
+ 660872
+ 8000
+ 3398850
+ Maintenance
+
+ 2022-03-16T14:54:16+01:00
+ false
+
+
+
+
+ 10080
+ minutes
+ 2021-12-01T17:13:18+01:00
+ 651610
+ 0
+ 3410990
+ Shopware 6 Implementierung
+
+ 2021-12-01T17:13:18+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2021-12-09T09:45:12+01:00
+ 652191
+ 0
+ 3415777
+ Maintenance
+
+ 2021-12-09T09:45:12+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2021-12-25T10:11:51+01:00
+ 537995
+ 0
+ 3424130
+ Diverse
+
+ 2021-12-25T10:11:51+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-01-10T10:23:10+01:00
+ 654013
+ 0
+ 3430490
+ Maintenance
+
+ 2022-01-10T10:23:10+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-01-10T12:32:34+01:00
+ 542508
+ 0
+ 3430763
+ SAE Dental
+
+ 2022-01-10T12:32:34+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-01-14T14:08:17+01:00
+ 654633
+ 0
+ 3435108
+ Maintenance
+
+ 2022-01-14T14:08:17+01:00
+ false
+
+
+
+
+ 1200
+ minutes
+ 2022-01-17T12:38:00+01:00
+ 487895
+ 8000
+ 3436139
+ Usermenu
+
+ 2022-04-05T17:53:07+02:00
+ true
+ hourly_rate
+
+
+
+ 960
+ minutes
+ 2022-01-17T12:38:25+01:00
+ 487895
+ 8000
+ 3436141
+ Umbau Menu-Struktur
+
+ 2022-04-05T17:53:04+02:00
+ true
+ hourly_rate
+
+
+
+ 5760
+ minutes
+ 2022-01-17T12:38:50+01:00
+ 487895
+ 8000
+ 3436143
+ Delta Norm Tracker
+
+ 2022-02-07T21:05:18+01:00
+ false
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2022-02-07T18:23:20+01:00
+ 616357
+ 0
+ 3451033
+ E-Formel Maintenance ab 2022
+ Maintenance ab 2022 (nicht rabattiert)
+ 2022-02-07T18:23:20+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-02-08T12:21:04+01:00
+ 657073
+ 0
+ 3451551
+ DHV Maintenance
+
+ 2022-02-08T12:21:04+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-02-08T12:21:37+01:00
+ 657073
+ 0
+ 3451553
+ HMT Maintenance
+
+ 2022-02-08T12:21:37+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-02-16T16:59:35+01:00
+ 487895
+ 0
+ 3460193
+ Support / Maintenance
+
+ 2022-02-16T16:59:35+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-02-17T09:52:37+01:00
+ 658161
+ 0
+ 3460509
+ Maintenance - 4600000451
+ Kontraktnummer 4600000451
+ 2022-06-14T12:00:25+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-02-25T18:02:32+01:00
+ 659032
+ 0
+ 3466117
+ Microsoft Newsletter
+
+ 2022-02-25T18:02:32+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-03-14T11:39:28+01:00
+ 649777
+ 0
+ 3475490
+ Webumed
+
+ 2022-03-14T11:39:28+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-04-11T12:45:32+02:00
+ 663077
+ 0
+ 3492885
+ Maintenance
+
+ 2022-04-11T12:45:32+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-04-22T09:47:11+02:00
+ 663879
+ 0
+ 3498043
+ Maintenance
+
+ 2022-04-22T09:47:11+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-05-02T10:24:38+02:00
+ 541986
+ 0
+ 3502877
+ Website 2023
+
+ 2023-10-17T11:42:59+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-05-05T16:04:59+02:00
+ 649777
+ 0
+ 3506292
+ German Health Tech
+
+ 2022-05-05T16:04:59+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-05-10T10:01:19+02:00
+ 648589
+ 0
+ 3508348
+ Erstellung Webseiten
+
+ 2022-05-10T10:01:19+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-05-12T13:26:18+02:00
+ 668857
+ 0
+ 3529775
+ Maintenance
+
+ 2022-05-12T13:26:18+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-05-12T15:34:29+02:00
+ 668873
+ 0
+ 3529923
+ Maintenance
+
+ 2022-05-12T15:34:29+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-06-01T16:59:13+02:00
+ 670291
+ 0
+ 3540448
+ Maintenance
+
+ 2022-06-01T16:59:13+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-06-01T17:02:17+02:00
+ 670292
+ 0
+ 3540455
+ Shop Relaunch
+
+ 2022-06-01T17:02:17+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-06-08T15:03:59+02:00
+ 670793
+ 0
+ 3543767
+ Maintenance
+
+ 2022-06-08T15:03:59+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-07-11T15:37:41+02:00
+ 536420
+ 0
+ 3561703
+ IPDynamics
+
+ 2022-07-11T15:37:41+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-08-02T11:09:09+02:00
+ 675486
+ 9000
+ 3572317
+ Shopify Relaunch
+
+ 2022-08-16T11:33:29+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-08-22T10:15:49+02:00
+ 652191
+ 0
+ 3584216
+ Umfeldplaner
+
+ 2022-08-22T10:15:49+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-11-07T10:08:16+01:00
+ 681218
+ 0
+ 3627497
+ Maintenance
+
+ 2022-11-07T10:08:16+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-11-07T11:50:42+01:00
+ 681243
+ 0
+ 3627656
+ Maintenance
+
+ 2022-11-07T11:50:42+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-11-16T13:13:18+01:00
+ 682039
+ 0
+ 3634709
+ Maintenance
+
+ 2022-11-16T13:13:18+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-11-18T15:56:21+01:00
+ 682261
+ 0
+ 3636811
+ Landhaus an de Dün
+
+ 2022-11-18T15:56:21+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2022-11-18T15:56:31+01:00
+ 682261
+ 0
+ 3636812
+ Dünen Stuuv
+
+ 2022-11-18T15:56:31+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2023-01-10T11:31:25+01:00
+ 682261
+ 0
+ 3661969
+ Das Lornsen
+
+ 2023-01-10T11:31:25+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2023-01-25T12:56:33+01:00
+ 566133
+ 8500
+ 3679525
+ cardentity
+
+ 2023-01-25T12:56:33+01:00
+ false
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2023-02-14T17:02:44+01:00
+ 600144
+ 9000
+ 3692697
+ Besler & Söhne
+
+ 2023-02-14T17:02:44+01:00
+ false
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2023-02-21T18:02:39+01:00
+ 691756
+ 0
+ 3704328
+ Erstellung Website
+
+ 2023-02-21T18:02:47+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2023-02-27T17:36:50+01:00
+ 693457
+ 0
+ 3714684
+ Corner House Altafulla
+
+ 2023-02-27T17:36:50+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2023-03-14T15:25:00+01:00
+ 614408
+ 0
+ 3739809
+ carglass
+
+ 2023-03-14T15:25:00+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2023-04-06T11:27:41+02:00
+ 536420
+ 0
+ 3754111
+ IHK
+
+ 2023-04-12T15:42:37+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2023-04-06T12:29:09+02:00
+ 682261
+ 0
+ 3754186
+ Das Windhuk
+
+ 2023-04-06T12:29:09+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2023-04-06T12:29:17+02:00
+ 682261
+ 0
+ 3754187
+ Maritas Logierhaus
+
+ 2023-04-06T12:29:17+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2023-05-25T16:25:35+02:00
+ 702560
+ 0
+ 3780140
+ Maintenance
+
+ 2023-05-25T16:25:35+02:00
+ false
+
+
+
+
+ 6600
+ minutes
+ 2023-06-07T17:29:28+02:00
+ 628868
+ 0
+ 3794844
+ MPP Tool
+
+ 2023-07-04T17:52:40+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2023-06-28T15:34:16+02:00
+ 691756
+ 0
+ 3805120
+ Maintenance
+
+ 2023-06-28T15:34:16+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2023-08-21T11:46:51+02:00
+ 566133
+ 0
+ 3834270
+ Eucon
+
+ 2023-08-21T11:46:51+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2023-09-07T21:45:17+02:00
+ 515306
+ 0
+ 3843731
+ Intranet-Tool
+
+ 2023-09-07T21:45:17+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2023-09-07T21:49:29+02:00
+ 711030
+ 0
+ 3843733
+ Luna Garnelen
+
+ 2023-09-07T21:49:29+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2023-09-19T12:05:51+02:00
+ 711675
+ 0
+ 3848972
+ Maintenance
+
+ 2023-09-19T12:05:51+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2023-09-20T13:11:06+02:00
+ 614408
+ 0
+ 3849877
+ Spitzner B2B
+
+ 2023-09-20T13:11:06+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2023-10-31T11:39:38+01:00
+ 614408
+ 0
+ 3877107
+ Spitzner Support
+
+ 2023-10-31T11:39:38+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2023-12-04T11:07:47+01:00
+ 535326
+ 9000
+ 3905504
+ Website 2024
+
+ 2023-12-04T11:07:47+01:00
+ false
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2024-02-06T15:31:46+01:00
+ 614408
+ 0
+ 3949946
+ Spitzner SAP Schnittstelle
+
+ 2024-02-06T15:31:46+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2024-02-09T11:10:05+01:00
+ 723675
+ 0
+ 3951946
+ App (intern)
+
+ 2024-02-09T11:10:05+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2024-04-23T11:42:12+02:00
+ 536420
+ 9000
+ 3994020
+ #meermachen
+
+ 2024-04-23T11:42:12+02:00
+ false
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2024-04-24T18:00:43+02:00
+ 730679
+ 0
+ 3995138
+ Shop
+
+ 2024-04-24T18:00:43+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2024-07-09T12:20:21+02:00
+ 536420
+ 0
+ 4029587
+ #meermachen Betreuung
+
+ 2024-07-09T12:20:21+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2024-07-09T17:39:27+02:00
+ 614408
+ 0
+ 4029864
+ Spitzner ReDesign
+
+ 2024-07-09T17:39:27+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2024-08-28T09:52:38+02:00
+ 738052
+ 0
+ 4049805
+ Website 2024
+
+ 2024-08-28T09:52:38+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2024-09-02T10:59:34+02:00
+ 738291
+ 0
+ 4051725
+ Website Relaunch
+
+ 2024-09-02T10:59:34+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2024-09-05T11:37:54+02:00
+ 738577
+ 0
+ 4054610
+ Website
+
+ 2024-09-05T11:37:54+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2024-10-21T15:18:54+02:00
+ 741322
+ 0
+ 4074788
+ Website 2024
+
+ 2024-10-21T15:18:54+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2024-10-23T10:31:28+02:00
+ 741450
+ 0
+ 4075849
+ Website
+
+ 2024-10-23T10:31:28+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2024-11-20T10:34:17+01:00
+ 515306
+ 0
+ 4088839
+ Intranet-Tool Einarbeitung / Nicht verrechenbar
+
+ 2024-11-20T10:34:17+01:00
+ false
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2024-11-22T12:04:00+01:00
+ 743312
+ 0
+ 4090119
+ SBC Solver
+
+ 2024-11-22T12:04:00+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2024-12-11T10:28:23+01:00
+ 744846
+ 0
+ 4099135
+ Maintenance
+
+ 2024-12-11T10:28:23+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2024-12-17T10:45:53+01:00
+ 745164
+ 0
+ 4101605
+ GmbH
+
+ 2024-12-17T10:45:53+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2024-12-17T10:46:00+01:00
+ 745164
+ 0
+ 4101606
+ Website
+
+ 2024-12-17T10:46:00+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2025-01-24T10:01:13+01:00
+ 711030
+ 0
+ 4122888
+ Lenk Frozen
+
+ 2025-01-24T10:01:13+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2025-01-24T10:01:26+01:00
+ 711030
+ 0
+ 4122889
+ Lenk-Seafood
+
+ 2025-01-24T10:01:26+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2025-05-05T15:56:46+02:00
+ 837030
+ 0
+ 4172624
+ Sylt Häuser
+
+ 2025-05-05T15:56:46+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2025-05-06T13:05:51+02:00
+ 837093
+ 0
+ 4173176
+ Einladungs- und Teilnehmermanagement Tool
+
+ 2025-05-06T13:05:51+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2025-05-07T16:46:11+02:00
+ 654013
+ 0
+ 4174087
+ Website 2025
+
+ 2025-05-07T16:46:11+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2025-05-14T12:32:48+02:00
+ 837093
+ 0
+ 4176819
+ Sonstiges
+
+ 2025-05-14T12:32:48+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2025-06-30T12:05:56+02:00
+ 848981
+ 0
+ 4224541
+ Maintenance
+
+ 2025-06-30T12:05:56+02:00
+ false
+
+
+
+
+ 2700
+ minutes
+ 2025-08-11T18:04:44+02:00
+ 853397
+ 0
+ 4256153
+ Lebenshilfe Pinneberg
+
+ 2025-08-11T18:04:44+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2025-10-10T14:11:20+02:00
+ 863730
+ 0
+ 4332363
+ Maintenance
+
+ 2025-10-10T14:11:20+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2025-12-03T13:00:22+01:00
+ 873570
+ 0
+ 4406001
+ Neue Website
+
+ 2025-12-03T13:00:22+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2025-12-11T15:26:03+01:00
+ 660872
+ 9000
+ 4416930
+ be2grow
+
+ 2025-12-11T15:26:03+01:00
+ false
+ hourly_rate
+
+
+
+ 0
+ minutes
+ 2026-03-23T18:02:41+01:00
+ 534086
+ 0
+ 4603894
+ Shopify Anbindung
+
+ 2026-03-23T18:02:41+01:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2026-04-08T12:41:19+02:00
+ 853397
+ 0
+ 4625749
+ Nutzwedel
+
+ 2026-04-08T12:41:19+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2026-04-08T12:41:27+02:00
+ 853397
+ 0
+ 4625752
+ Das Kontaktwerk
+
+ 2026-04-08T12:41:27+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2026-04-15T11:18:22+02:00
+ 902974
+ 0
+ 4635262
+ Erstellung Portal und Applikation
+
+ 2026-04-15T11:18:22+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2026-04-29T10:41:19+02:00
+ 905212
+ 0
+ 4651246
+ Shop
+
+ 2026-04-29T10:41:19+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2026-05-20T10:45:47+02:00
+ 908443
+ 0
+ 4674925
+ Shopware 6 Shop
+
+ 2026-05-20T10:45:47+02:00
+ false
+
+
+
+
+ 0
+ minutes
+ 2026-06-12T11:01:01+02:00
+ 912127
+ 0
+ 4699930
+ Website-Erstellung
+
+ 2026-06-12T11:01:01+02:00
+ false
+
+
+
+
+
+
+ true
+ 2018-03-19T15:06:16+01:00
+ 7500
+ 402469
+ Recherche
+
+ 2019-01-10T09:51:42+01:00
+ false
+
+
+ true
+ 2018-03-19T15:06:46+01:00
+ 7500
+ 402470
+ Software-Entwicklung
+
+ 2019-01-10T09:51:54+01:00
+ false
+
+
+ true
+ 2018-03-19T15:07:01+01:00
+ 7500
+ 402471
+ Frontend-Entwicklung
+
+ 2019-01-10T09:51:22+01:00
+ false
+
+
+ true
+ 2018-03-19T15:07:14+01:00
+ 7500
+ 402472
+ Einarbeitung
+
+ 2019-01-10T09:51:14+01:00
+ false
+
+
+ true
+ 2018-03-19T15:07:40+01:00
+ 7500
+ 402473
+ Meeting
+
+ 2019-01-10T09:51:29+01:00
+ false
+
+
+ true
+ 2018-03-29T17:34:32+02:00
+ 7500
+ 403333
+ Schätzung
+
+ 2019-01-10T09:51:48+01:00
+ false
+
+
+ true
+ 2018-04-13T16:09:27+02:00
+ 7500
+ 404635
+ Dokumentation
+
+ 2019-01-10T09:51:07+01:00
+ false
+
+
+ true
+ 2018-05-07T21:35:20+02:00
+ 7500
+ 406328
+ Erfassungsblatt
+
+ 2019-01-02T13:18:36+01:00
+ true
+
+
+ true
+ 2018-07-26T10:33:36+02:00
+ 6000
+ 413930
+ Frontend Standard
+ Frontend zu 60 EUR / Stunde
+ 2018-08-22T17:59:57+02:00
+ true
+
+
+ false
+ 2019-01-02T13:21:45+01:00
+
+ 425205
+ spawntree Intern
+
+ 2019-01-02T13:21:45+01:00
+ false
+
+
+ false
+ 2019-01-02T13:22:53+01:00
+
+ 425206
+ Diverse Projektarbeit
+
+ 2019-01-02T13:22:53+01:00
+ false
+
+
+ true
+ 2019-01-07T15:33:31+01:00
+ 7500
+ 425720
+ Projektmanagement
+
+ 2019-01-10T09:51:35+01:00
+ false
+
+
+ false
+ 2019-03-01T17:36:21+01:00
+
+ 430725
+ Urlaub
+
+ 2026-05-22T16:36:05+02:00
+ true
+
+
+ false
+ 2023-04-24T10:48:40+02:00
+
+ 515259
+ Nicht monitäre Gegenleistung
+
+ 2023-06-09T15:31:29+02:00
+ false
+
+
+ false
+ 2026-02-26T09:52:51+01:00
+
+ 571381
+ Später verrechnen
+ Stunden, die wir bislang nicht abrechnen konnten.
+ 2026-02-26T09:56:28+01:00
+ false
+
+
+
+
+ true
+ 2018-03-19T15:08:22+01:00
+ 2018-03-12
+ 59420587
+ true
+ 360
+ 2411023
+ 45000.0
+ 402473
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Onboarding
+
+
+ true
+ 2018-03-19T15:08:44+01:00
+ 2018-03-13
+ 59420610
+ true
+ 450
+ 2411023
+ 56250.0
+ 402469
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Recherche und Machbarkeitsstudie
+
+
+ true
+ 2018-03-19T15:08:54+01:00
+ 2018-03-14
+ 59420620
+ true
+ 300
+ 2411023
+ 37500.0
+ 402469
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Recherche und Machbarkeitsstudie
+
+
+ true
+ 2018-03-19T15:09:15+01:00
+ 2018-03-16
+ 59420639
+ true
+ 300
+ 2411023
+ 37500.0
+ 402473
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Absprachen und Machbarkeitsbesprechung
+
+
+ true
+ 2018-03-19T15:09:53+01:00
+ 2018-03-19
+ 59420677
+ true
+ 390
+ 2411023
+ 48750.0
+ 402469
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Recherche (Datenbanken)
+
+
+ true
+ 2018-03-19T15:13:18+01:00
+ 2018-03-12
+ 59420870
+ true
+ 360
+ 2411023
+ 45000.0
+ 402473
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Onboarding
+
+
+ true
+ 2018-03-19T15:15:27+01:00
+ 2018-03-13
+ 59420975
+ true
+ 480
+ 2411023
+ 60000.0
+ 402469
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Recherche und Machbarkeitsstudie
+
+
+ true
+ 2018-03-19T15:15:33+01:00
+ 2018-03-14
+ 59420982
+ true
+ 300
+ 2411023
+ 37500.0
+ 402469
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Recherche und Machbarkeitsstudie
+
+
+ true
+ 2018-03-19T15:16:02+01:00
+ 2018-03-15
+ 59421014
+ true
+ 450
+ 2411023
+ 56250.0
+ 402469
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Recherche und Machbarkeitsstudie
+
+
+ true
+ 2018-03-19T15:16:09+01:00
+ 2018-03-15
+ 59421022
+ true
+ 480
+ 2411023
+ 60000.0
+ 402469
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Recherche und Machbarkeitsstudie
+
+
+ true
+ 2018-03-19T15:16:21+01:00
+ 2018-03-16
+ 59421034
+ true
+ 300
+ 2411023
+ 37500.0
+ 402473
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Absprachen und Machbarkeitsbesprechung
+
+
+ true
+ 2018-03-19T15:16:34+01:00
+ 2018-03-19
+ 59421041
+ true
+ 450
+ 2411023
+ 56250.0
+ 402469
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Recherche (Datenbanken)
+
+
+ true
+ 2018-03-20T17:44:11+01:00
+ 2018-03-20
+ 59470791
+ true
+ 150
+ 2411023
+ 18750.0
+ 402469
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Recherche (Datenbanken)
+
+
+ true
+ 2018-03-20T17:44:20+01:00
+ 2018-03-20
+ 59470799
+ true
+ 150
+ 2411023
+ 18750.0
+ 402469
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Recherche (Datenbanken)
+
+
+ true
+ 2018-03-20T17:44:45+01:00
+ 2018-03-20
+ 59470829
+ true
+ 255
+ 2411023
+ 31875.0
+ 402470
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Git und Electron Setup
+
+
+ true
+ 2018-03-20T17:44:53+01:00
+ 2018-03-20
+ 59470835
+ true
+ 315
+ 2411023
+ 39375.0
+ 402470
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Git und Electron Setup
+
+
+ true
+ 2018-03-21T17:37:38+01:00
+ 2018-03-21
+ 59510306
+ true
+ 240
+ 2411023
+ 30000.0
+ 402470
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Git und Angular Setup
+
+
+ true
+ 2018-03-21T17:37:41+01:00
+ 2018-03-21
+ 59510310
+ true
+ 240
+ 2411023
+ 30000.0
+ 402470
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Git und Angular Setup
+
+
+ true
+ 2018-03-22T16:08:39+01:00
+ 2018-03-22
+ 59548378
+ true
+ 270
+ 2411023
+ 33750.0
+ 402470
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Git und Symfony Setup / Datenbankmodell entwickeln
+
+
+ true
+ 2018-03-22T16:08:44+01:00
+ 2018-03-22
+ 59548383
+ true
+ 270
+ 2411023
+ 33750.0
+ 402470
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Git und Symfony Setup / Datenbankmodell entwickeln
+
+
+ true
+ 2018-03-23T17:26:40+01:00
+ 2018-03-23
+ 59591694
+ true
+ 300
+ 2411023
+ 37500.0
+ 402470
+ 2019-01-10T09:57:29+01:00
+ 115462
+ Datenbank-Design
+
+
+ true
+ 2026-06-12T12:38:58+02:00
+ 2026-06-12
+ 163809280
+ false
+ 30
+ 4674925
+ 3750.0
+ 402473
+ 2026-06-12T12:38:58+02:00
+ 115462
+ Absprachen weiteres Vorgehen
+
+
+ true
+ 2026-06-12T17:24:55+02:00
+ 2026-06-12
+ 163837672
+ false
+ 150
+ 3075541
+ 20000.0
+ 402471
+ 2026-06-12T17:24:55+02:00
+ 115462
+ Language: Spanisch, Sustain Änderungen, Textanpassungen, Foto Seite erstellt
+
+
+ true
+ 2026-06-12T17:34:41+02:00
+ 2026-06-12
+ 163838215
+ false
+ 180
+ 4173176
+ 30000.0
+ 402470
+ 2026-06-12T17:34:41+02:00
+ 123162
+ Excel export, imporvement
+
+
+ true
+ 2026-06-12T17:51:46+02:00
+ 2026-06-12
+ 163839187
+ false
+ 30
+ 4674925
+ 3750.0
+ 402473
+ 2026-06-12T17:51:46+02:00
+ 123163
+ Teams Meeting Shopware 6
+
+
+ true
+ 2026-06-12T17:52:20+02:00
+ 2026-06-12
+ 163839211
+ false
+ 240
+ 4674925
+ 30000.0
+ 402471
+ 2026-06-15T18:12:15+02:00
+ 123163
+ Aufsetzen neuer Shopware 6 Shop. Einrichtung, Anlegen Kategorien und Shop Seiten
+
+
+ true
+ 2026-06-13T13:07:26+02:00
+ 2026-06-03
+ 163848253
+ false
+ 45
+ 3661969
+ 6750.0
+ 402471
+ 2026-06-13T13:07:26+02:00
+ 214511
+ Cookiebar Anpassungen
+
+
+ true
+ 2026-06-13T13:08:47+02:00
+ 2026-06-10
+ 163848310
+ false
+ 15
+ 3661969
+ 2250.0
+ 402471
+ 2026-06-13T13:08:47+02:00
+ 214511
+ Kontakt
+
+
+ true
+ 2026-06-13T13:09:38+02:00
+ 2026-06-11
+ 163848313
+ false
+ 30
+
+ 3750.0
+ 402471
+ 2026-06-13T13:09:38+02:00
+ 214511
+ whizita / Flickenschild
+
+
+ true
+ 2026-06-15T11:29:41+02:00
+ 2026-06-15
+ 163902820
+ false
+ 30
+ 3754111
+ 3750.0
+ 402473
+ 2026-06-15T11:29:41+02:00
+ 115462
+ Weekly
+
+
+ true
+ 2026-06-15T12:25:21+02:00
+ 2026-06-15
+ 163909471
+ false
+ 60
+ 3754111
+ 7500.0
+ 402471
+ 2026-06-15T12:25:21+02:00
+ 115462
+ Farbvarianten 5, 6, 7, 8, 9, 15 angelegt
+
+
+ true
+ 2026-06-15T16:50:09+02:00
+ 2026-06-15
+ 163939247
+ false
+ 150
+ 3754111
+ 18750.0
+ 402471
+ 2026-06-15T16:50:09+02:00
+ 115462
+ Anpassungen / Bugfixing Farbvarianten
+
+
+ true
+ 2026-06-15T17:48:09+02:00
+ 2026-06-15
+ 163946216
+ false
+ 240
+ 4173176
+ 40000.0
+ 402470
+ 2026-06-15T17:48:09+02:00
+ 123162
+ Ticket mit Registrierung, Excel file names, Mailing stats PDF
+
+
+ true
+ 2026-06-15T18:11:38+02:00
+ 2026-06-15
+ 163948091
+ false
+ 240
+ 4674925
+ 30000.0
+ 402471
+ 2026-06-15T18:11:45+02:00
+ 123163
+ Anlegen Zusatzfelder und Import Profil. Programmierung Import Converter
+
+
+ true
+ 2026-06-16T11:45:41+02:00
+ 2026-06-16
+ 163994732
+ false
+ 60
+ 3754111
+ 7500.0
+ 402471
+ 2026-06-16T11:45:41+02:00
+ 115462
+ Farben: Trello Karten
+
+
+ true
+ 2026-06-16T11:45:58+02:00
+ 2026-06-16
+ 163994762
+ false
+ 30
+ 3754111
+ 3750.0
+ 402471
+ 2026-06-16T11:45:58+02:00
+ 115462
+ Bugfixing (IHK Maintenance)
+
+
+ true
+ 2026-06-16T13:13:52+02:00
+ 2026-06-16
+ 164004155
+ false
+ 30
+ 3754111
+ 3750.0
+ 402471
+ 2026-06-16T13:13:52+02:00
+ 115462
+ Trello: Bugfixing
+
+
+ true
+ 2026-06-16T17:18:44+02:00
+ 2026-06-16
+ 164031713
+ false
+ 510
+ 4173176
+ 85000.0
+ 402470
+ 2026-06-16T17:18:44+02:00
+ 123162
+ Mailing stats pdf, register mail resend, etc.
+
+
+ true
+ 2026-06-16T18:14:35+02:00
+ 2026-06-16
+ 164036432
+ false
+ 240
+ 4674925
+ 30000.0
+ 402471
+ 2026-06-16T18:14:35+02:00
+ 123163
+ Programmierung Import Converter und API Schnittstelle
+
+
+ true
+ 2026-06-17T11:04:19+02:00
+ 2026-06-17
+ 164075924
+ false
+ 15
+ 4051725
+ 2500.0
+ 402471
+ 2026-06-17T11:04:19+02:00
+ 115462
+ Anpassungen Bild in News
+
+
+ true
+ 2026-06-17T15:24:21+02:00
+ 2026-06-17
+ 164102336
+ false
+ 360
+ 4173176
+ 60000.0
+ 402470
+ 2026-06-17T15:24:37+02:00
+ 123162
+ Excel Exporte, Support etc.
+
+
+ true
+ 2026-06-17T18:06:35+02:00
+ 2026-06-17
+ 164168690
+ false
+ 60
+ 4674925
+ 7500.0
+ 402471
+ 2026-06-17T18:06:35+02:00
+ 123163
+ Programmierung Import Converter und API Schnittstelle
+
+
+ true
+ 2026-06-17T18:07:17+02:00
+ 2026-06-17
+ 164168726
+ false
+ 60
+ 4603894
+ 11250.0
+ 402471
+ 2026-06-17T18:07:17+02:00
+ 123163
+ Abarbeiten offene Trello Tickets
+
+
+ true
+ 2026-06-18T15:13:31+02:00
+ 2026-06-18
+ 164283350
+ false
+ 15
+ 3754111
+ 1875.0
+ 402471
+ 2026-06-18T15:13:31+02:00
+ 115462
+ IHK Karlsruhe: Kontrolle Signatur "Verkehr"
+
+
+ true
+ 2026-06-18T18:16:59+02:00
+ 2026-06-18
+ 164301914
+ false
+ 60
+ 4603894
+ 11250.0
+ 402471
+ 2026-06-18T18:16:59+02:00
+ 123163
+ Programmierung Storefront Plugin
+
+
+ true
+ 2026-06-19T18:19:49+02:00
+ 2026-06-19
+ 164378054
+ false
+ 180
+ 4603894
+ 33750.0
+ 402471
+ 2026-06-19T18:19:49+02:00
+ 123163
+ Anpassungen Import Script
+
+
+ true
+ 2026-06-21T22:39:17+02:00
+ 2026-06-21
+ 164395181
+ false
+ 30
+ 3754111
+ 3750.0
+ 402471
+ 2026-06-21T22:39:17+02:00
+ 115462
+ Anpassungen laut Asana Tickets
+
+
+ true
+ 2026-06-22T00:27:29+02:00
+ 2026-06-21
+ 164395856
+ false
+ 60
+ 2875415
+ 11250.0
+ 402471
+ 2026-06-22T00:27:29+02:00
+ 115462
+ Cron Jobs anlegen
+
+
+ true
+ 2026-06-22T10:14:19+02:00
+ 2026-06-22
+ 164426144
+ false
+ 15
+ 4224541
+ 2250.0
+ 402471
+ 2026-06-22T10:14:19+02:00
+ 115462
+ Hilfe Einrichtung E-Mail Adresse
+
+
+
+
+ 2018-03-19T15:03:50+01:00
+ f.eisenmenger@spawntree.de
+ 115462
+ Florian Eisenmenger
+
+ 2026-06-22T10:33:51+02:00
+ false
+ de
+ owner
+
+
+ 2019-01-02T13:01:25+01:00
+ d.knudsen@spawntree.de
+ 123162
+ Daniel Knudsen
+
+ 2026-06-17T15:23:59+02:00
+ false
+ de
+ admin
+
+
+ 2019-01-02T13:08:36+01:00
+ dirktietze@nova-sign.de
+ 123163
+ Dirk Tietze
+
+ 2026-06-19T18:19:23+02:00
+ false
+ de
+ time_tracker
+
+
+ 2022-02-18T09:45:06+01:00
+ info@zimmer-lukas.de
+ 214511
+ Lukas Zimmer
+
+ 2026-06-13T13:06:29+02:00
+ false
+ de
+ time_tracker
+
+
+ 2025-01-08T23:08:16+01:00
+ michael.schild@edit.de
+ 232991
+ Michael Schild
+
+ 2025-04-07T10:55:28+02:00
+ true
+ de
+ time_tracker
+
+
+
+
+ 183240
+ 2666337
+ 123162
+
+
+ 209991
+ 2674625
+ 123163
+
+
+ 187147
+ 2674626
+ 123163
+
+
+ 225235
+ 2681729
+ 123163
+
+
+ 202239
+ 2682677
+ 123163
+
+
+ 188341
+ 2742845
+ 123163
+
+
+ 223679
+ 2785029
+ 123163
+
+
+ 229530
+ 2809130
+ 123163
+
+
+ 202240
+ 2854592
+ 123163
+
+
+ 244311
+ 2875415
+ 123163
+
+
+ 228553
+ 2938527
+ 123163
+
+
+ 209764
+ 3095483
+ 123163
+
+
+ 221480
+ 3209601
+ 123163
+
+
+ 228590
+ 3338275
+ 123163
+
+
+ 241387
+ 3398850
+ 123163
+
+
+ 241583
+ 3460509
+ 123163
+
+
+ 250002
+ 3498043
+ 123163
+
+
+ 263810
+ 3502877
+ 123163
+
+
+ 259124
+ 3754111
+ 123163
+
+
+ 262638
+ 3843733
+ 123163
+
+
+ 263447
+ 3905504
+ 123163
+
+
+ 268760
+ 3994020
+ 123163
+
+
+ 270425
+ 3995138
+ 123163
+
+
+ 271142
+ 4029587
+ 123163
+
+
+ 272326
+ 4049805
+ 123163
+
+
+ 281221
+ 4051725
+ 123163
+
+
+ 272575
+ 4054610
+ 123163
+
+
+ 273652
+ 4074788
+ 123163
+
+
+ 273699
+ 4075849
+ 123163
+
+
+ 279938
+ 4172624
+ 123163
+
+
+ 281333
+ 4174087
+ 123163
+
+
+ 282968
+ 4256153
+ 123163
+
+
+ 303303
+ 4416930
+ 123163
+
+
+ 504721
+ 4603894
+ 123163
+
+
+ 514237
+ 4674925
+ 123163
+
+
+ 260498
+ 2682677
+ 214511
+
+
+ 244714
+ 2875415
+ 214511
+
+
+ 250223
+ 3424130
+ 214511
+
+
+ 238715
+ 3451553
+ 214511
+
+
+ 252087
+ 3460509
+ 214511
+
+
+ 253051
+ 3661969
+ 214511
+
+
+ 255049
+ 3714684
+ 214511
+
+
+ 261563
+ 3754111
+ 214511
+
+
+ 263844
+ 3848972
+ 214511
+
+
+ 507340
+ 4625749
+ 214511
+
+
+ 507343
+ 4625752
+ 214511
+
+
+ 516835
+ 4699930
+ 214511
+
+
+ 276081
+ 4101606
+ 232991
+
+
+
diff --git a/httpdocs/assets/scripts/account.js b/httpdocs/assets/scripts/account.js
index 4bb3b60..a93a8bc 100644
--- a/httpdocs/assets/scripts/account.js
+++ b/httpdocs/assets/scripts/account.js
@@ -212,4 +212,220 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
}
+
+ // ── mite-Import ─────────────────────────────────────────────────────────────
+
+ const importFile = document.getElementById('import-file');
+ const importFileInfo = document.getElementById('import-file-info');
+ const importFileName = document.getElementById('import-file-name');
+ const importFileRemove = document.getElementById('import-file-remove');
+ const btnAnalyze = document.getElementById('btn-import-analyze');
+ const btnExecute = document.getElementById('btn-import-execute');
+ const btnReset = document.getElementById('btn-import-reset');
+ const previewCard = document.getElementById('import-preview');
+ const previewContent = document.getElementById('import-preview-content');
+ const resultCard = document.getElementById('import-result');
+ const resultContent = document.getElementById('import-result-content');
+ const dropArea = document.getElementById('import-drop-area');
+
+ if (importFile) {
+ let selectedFile = null;
+
+ function setFile(file) {
+ if (!file || !file.name.toLowerCase().endsWith('.xml')) {
+ showToast(t('importErrorNoFile'), true);
+ return;
+ }
+ selectedFile = file;
+ importFileName.textContent = file.name + ' (' + formatFileSize(file.size) + ')';
+ importFileInfo.hidden = false;
+ btnAnalyze.disabled = false;
+ previewCard.hidden = true;
+ resultCard.hidden = true;
+ }
+
+ function clearFile() {
+ selectedFile = null;
+ importFile.value = '';
+ importFileInfo.hidden = true;
+ btnAnalyze.disabled = true;
+ previewCard.hidden = true;
+ resultCard.hidden = true;
+ }
+
+ function formatFileSize(bytes) {
+ if (bytes < 1024) return bytes + ' B';
+ if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
+ return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
+ }
+
+ importFile.addEventListener('change', () => {
+ if (importFile.files.length > 0) setFile(importFile.files[0]);
+ });
+
+ importFileRemove.addEventListener('click', clearFile);
+
+ // Drag & Drop
+ ['dragenter', 'dragover'].forEach(evt => {
+ dropArea.addEventListener(evt, (e) => {
+ e.preventDefault();
+ dropArea.classList.add('import-upload__area--dragover');
+ });
+ });
+ ['dragleave', 'drop'].forEach(evt => {
+ dropArea.addEventListener(evt, () => {
+ dropArea.classList.remove('import-upload__area--dragover');
+ });
+ });
+ dropArea.addEventListener('drop', (e) => {
+ e.preventDefault();
+ if (e.dataTransfer.files.length > 0) setFile(e.dataTransfer.files[0]);
+ });
+
+ // Analyse
+ btnAnalyze.addEventListener('click', async () => {
+ if (!selectedFile) return;
+
+ btnAnalyze.disabled = true;
+ btnAnalyze.textContent = t('importAnalyzing');
+
+ const form = new FormData();
+ form.append('file', selectedFile);
+
+ try {
+ const res = await fetch('/api/import/mite/preview', { method: 'POST', body: form });
+ const json = await res.json();
+ if (!res.ok) throw new Error(json.error ?? t('errorGeneric'));
+
+ renderPreview(json);
+ previewCard.hidden = false;
+ resultCard.hidden = true;
+ } catch (e) {
+ showToast(e.message, true);
+ } finally {
+ btnAnalyze.textContent = t('importAnalyze');
+ btnAnalyze.disabled = false;
+ }
+ });
+
+ // Import ausführen
+ btnExecute.addEventListener('click', async () => {
+ if (!selectedFile) return;
+ if (!confirm(t('importConfirm'))) return;
+
+ btnExecute.disabled = true;
+ btnExecute.textContent = t('importExecuting');
+
+ const form = new FormData();
+ form.append('file', selectedFile);
+
+ const createUserIds = [];
+ previewContent.querySelectorAll('input[name^="user-mode-"]:checked').forEach(radio => {
+ if (radio.value === 'create') {
+ createUserIds.push(parseInt(radio.name.replace('user-mode-', ''), 10));
+ }
+ });
+ form.append('createUsers', JSON.stringify(createUserIds));
+
+ try {
+ const res = await fetch('/api/import/mite/execute', { method: 'POST', body: form });
+ const json = await res.json();
+ if (!res.ok) throw new Error(json.error ?? t('errorGeneric'));
+
+ renderResult(json);
+ previewCard.hidden = true;
+ resultCard.hidden = false;
+ showToast(t('importSuccess'));
+ } catch (e) {
+ showToast(e.message, true);
+ } finally {
+ btnExecute.textContent = t('importExecute');
+ btnExecute.disabled = false;
+ }
+ });
+
+ // Reset
+ btnReset.addEventListener('click', () => {
+ previewCard.hidden = true;
+ resultCard.hidden = true;
+ });
+
+ function renderPreview(data) {
+ const rows = [
+ { label: t('importLabelClients'), value: data.customers },
+ { label: t('importLabelProjects'), value: data.projects },
+ { label: t('importLabelServices'), value: data.services },
+ { label: t('importLabelEntries'), value: data.timeEntries },
+ ];
+
+ let html = '
';
+ for (const row of rows) {
+ html += `- ${esc(row.label)}
- ${esc(String(row.value))}
`;
+ }
+
+ if (data.dateRange.from && data.dateRange.to) {
+ html += `- ${esc(t('importLabelDateRange'))}
- ${esc(formatDate(data.dateRange.from))} – ${esc(formatDate(data.dateRange.to))}
`;
+ }
+ html += '
';
+
+ // Benutzer-Zuordnung
+ if (data.users.length > 0) {
+ html += `${esc(t('importLabelUsers'))}
`;
+ html += '';
+ }
+
+ // Warnungen
+ if (data.warnings.length > 0) {
+ html += `${esc(t('importLabelWarnings'))}
`;
+ html += '';
+ for (const w of data.warnings) {
+ html += `- ${esc(w)}
`;
+ }
+ html += '
';
+ }
+
+ previewContent.innerHTML = html;
+ }
+
+ function renderResult(data) {
+ function statLine(label, total, created) {
+ const existing = total - created;
+ let detail = `${created} ${esc(t('importNewLabel'))}`;
+ if (existing > 0) {
+ detail += `, ${existing} ${esc(t('importExistingLabel'))}`;
+ }
+ return `${esc(label)}${detail}`;
+ }
+
+ let html = '';
+ html += statLine(t('importLabelClients'), data.clients, data.clientsCreated);
+ html += statLine(t('importLabelProjects'), data.projects, data.projectsCreated);
+ html += statLine(t('importLabelServices'), data.services, data.servicesCreated);
+ html += `- ${esc(t('importResultEntries'))}
- ${data.timeEntries}
`;
+ if (data.usersCreated > 0) {
+ html += `- ${esc(t('importResultUsers'))}
- ${data.usersCreated}
`;
+ }
+ html += '
';
+ resultContent.innerHTML = html;
+ }
+
+ function formatDate(dateStr) {
+ const d = new Date(dateStr + 'T00:00:00');
+ return d.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' });
+ }
+ }
});
diff --git a/httpdocs/assets/scripts/statistics.js b/httpdocs/assets/scripts/statistics.js
index 7b40a5b..9eebb85 100644
--- a/httpdocs/assets/scripts/statistics.js
+++ b/httpdocs/assets/scripts/statistics.js
@@ -298,6 +298,27 @@ function renderDonut(canvasId, legendId, chartKey, items, metric) {
}
}
+function updateTotal(data, metric) {
+ const el = document.getElementById('stats-total');
+ if (!el || !data) return;
+
+ const billable = metric === 'revenue' ? data.billableRevenue : data.billable;
+ const nonBillable = metric === 'revenue' ? data.nonBillableRevenue : data.nonBillable;
+ const total = [...billable, ...nonBillable].reduce((s, v) => s + v, 0);
+
+ if (total === 0) {
+ el.hidden = true;
+ return;
+ }
+
+ if (metric === 'revenue') {
+ el.textContent = t('totalRevenue').replace('__REVENUE__', formatCurrency(total));
+ } else {
+ el.textContent = t('totalHours').replace('__HOURS__', formatHours(total) + ' h');
+ }
+ el.hidden = false;
+}
+
function renderDonuts(data, metric) {
if (!data?.distribution) return;
const d = data.distribution;
@@ -321,6 +342,7 @@ async function loadAndRender(range, metric, userId) {
cachedData = await res.json();
renderChart(cachedData, metric);
renderDonuts(cachedData, metric);
+ updateTotal(cachedData, metric);
} catch {
wrap.innerHTML = '' + t('errorLoad') + '
';
}
@@ -353,6 +375,7 @@ document.addEventListener('DOMContentLoaded', () => {
if (cachedData) {
renderChart(cachedData, metricSelect.value);
renderDonuts(cachedData, metricSelect.value);
+ updateTotal(cachedData, metricSelect.value);
}
});
});
diff --git a/httpdocs/assets/styles/components/_account.scss b/httpdocs/assets/styles/components/_account.scss
index 0b3f6e2..78d8a5b 100644
--- a/httpdocs/assets/styles/components/_account.scss
+++ b/httpdocs/assets/styles/components/_account.scss
@@ -201,6 +201,212 @@
}
}
+// ─── Import-Sektion ──────────────────────────────────────────────────────
+.import-section__title {
+ font-size: $font-size-lg;
+ font-weight: $font-weight-bold;
+ color: $color-text-dark;
+ margin-bottom: $space-2;
+}
+
+.import-section__desc {
+ font-size: $font-size-sm;
+ color: $color-text-muted;
+ line-height: 1.55;
+ margin-bottom: $space-6;
+}
+
+.import-upload__area {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: $space-2;
+ padding: $space-8 $space-6;
+ border: 2px dashed $color-input-border;
+ border-radius: $radius-md;
+ cursor: pointer;
+ transition: border-color $transition-fast, background $transition-fast;
+
+ &:hover,
+ &--dragover {
+ border-color: var(--color-primary, $color-primary);
+ background: rgba(var(--color-primary-rgb, 58, 123, 191), 0.04);
+ }
+}
+
+.import-upload__icon {
+ color: $color-text-muted;
+}
+
+.import-upload__text {
+ font-size: $font-size-sm;
+ font-weight: $font-weight-medium;
+ color: $color-text-dark;
+}
+
+.import-upload__hint {
+ font-size: $font-size-xs;
+ color: $color-text-muted;
+}
+
+.import-upload__file-info {
+ display: flex;
+ align-items: center;
+ gap: $space-3;
+ margin-top: $space-3;
+ padding: $space-3 $space-4;
+ background: $color-bg;
+ border-radius: $radius-sm;
+ font-size: $font-size-sm;
+ color: $color-text-dark;
+}
+
+.import-upload__remove {
+ margin-left: auto;
+ background: none;
+ border: none;
+ font-size: $font-size-lg;
+ color: $color-text-muted;
+ cursor: pointer;
+ padding: 0 $space-2;
+ line-height: 1;
+
+ &:hover { color: $color-error; }
+}
+
+.import-actions {
+ margin-top: $space-5;
+ display: flex;
+ gap: $space-4;
+
+ &--preview {
+ padding-top: $space-5;
+ border-top: 1px solid $color-border;
+ }
+}
+
+// ─── Import-Vorschau ─────────────────────────────────────────────────────
+.import-preview__stats {
+ display: grid;
+ grid-template-columns: auto 1fr;
+ gap: $space-2 $space-6;
+ font-size: $font-size-sm;
+
+ dt {
+ color: $color-text-muted;
+ font-weight: $font-weight-medium;
+ }
+
+ dd {
+ color: $color-text-dark;
+ font-weight: $font-weight-bold;
+ margin: 0;
+ }
+
+ &--result dd {
+ color: $color-text-dark;
+ font-weight: $font-weight-regular;
+
+ strong {
+ color: var(--color-primary, $color-primary);
+ font-weight: $font-weight-bold;
+ }
+ }
+}
+
+.import-preview__subtitle {
+ font-size: $font-size-sm;
+ font-weight: $font-weight-bold;
+ color: $color-text-dark;
+ margin-top: $space-5;
+ margin-bottom: $space-2;
+}
+
+.import-preview__users {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ display: flex;
+ flex-direction: column;
+ gap: $space-2;
+
+ li {
+ font-size: $font-size-sm;
+ color: $color-text-dark;
+ line-height: 1.6;
+ }
+}
+
+.import-preview__email {
+ color: $color-text-muted;
+}
+
+.import-user-options {
+ display: flex;
+ gap: $space-4;
+ margin-top: $space-1;
+}
+
+.import-user-option {
+ display: inline-flex;
+ align-items: center;
+ gap: $space-1;
+ font-size: $font-size-xs;
+ color: $color-text-muted;
+ cursor: pointer;
+
+ input[type="radio"] {
+ margin: 0;
+ accent-color: var(--color-primary, $color-primary);
+ }
+}
+
+.import-badge {
+ display: inline-block;
+ font-size: $font-size-xs;
+ padding: 1px $space-2;
+ border-radius: $radius-pill;
+ font-weight: $font-weight-medium;
+
+ &--matched {
+ background: rgba(34, 197, 94, 0.12);
+ color: #15803d;
+ }
+
+ &--fallback {
+ background: rgba(234, 179, 8, 0.15);
+ color: #a16207;
+ }
+}
+
+.import-preview__warnings {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ display: flex;
+ flex-direction: column;
+ gap: $space-1;
+
+ li {
+ font-size: $font-size-sm;
+ color: $color-text-muted;
+ padding-left: $space-4;
+ position: relative;
+
+ &::before {
+ content: '⚠';
+ position: absolute;
+ left: 0;
+ font-size: $font-size-xs;
+ }
+ }
+}
+
+.import-result {
+ border-left: 3px solid var(--color-primary, $color-primary);
+}
+
// ─── Toast ───────────────────────────────────────────────────────────────────
.account-toast {
position: fixed;
diff --git a/httpdocs/assets/styles/sections/_statistics.scss b/httpdocs/assets/styles/sections/_statistics.scss
index fab7908..3a7046a 100644
--- a/httpdocs/assets/styles/sections/_statistics.scss
+++ b/httpdocs/assets/styles/sections/_statistics.scss
@@ -87,6 +87,15 @@
}
}
+// ─── Gesamtsumme ───────────────────────────────────────────────────────────
+.statistics-total {
+ text-align: right;
+ padding: $space-3 $space-5 0;
+ font-size: $font-size-lg;
+ font-weight: $font-weight-bold;
+ color: $color-text-dark;
+}
+
// ─── Chart ──────────────────────────────────────────────────────────────────
.statistics-chart-wrap {
position: relative;
diff --git a/httpdocs/config/services.yaml b/httpdocs/config/services.yaml
index 0a8b8ff..9da5307 100644
--- a/httpdocs/config/services.yaml
+++ b/httpdocs/config/services.yaml
@@ -69,5 +69,13 @@ services:
tags:
- { name: doctrine.middleware, connection: tenant }
+ App\Service\MiteImportService:
+ arguments:
+ $centralEm: '@doctrine.orm.central_entity_manager'
+
+ App\Controller\ImportController:
+ arguments:
+ $tenantEm: '@doctrine.orm.tenant_entity_manager'
+
App\Controller\InviteController:
arguments: ~
diff --git a/httpdocs/src/Controller/AccountController.php b/httpdocs/src/Controller/AccountController.php
index 8a3e003..2e5f2e8 100644
--- a/httpdocs/src/Controller/AccountController.php
+++ b/httpdocs/src/Controller/AccountController.php
@@ -37,7 +37,7 @@ class AccountController extends AbstractController
$isAdmin = $accountUser?->isAdmin() ?? false;
$tab = $request->query->get('tab', $isAdmin ? 'account' : 'user');
- if (!$isAdmin && $tab === 'account') {
+ if (!$isAdmin && in_array($tab, ['account', 'import'], true)) {
$tab = 'user';
}
diff --git a/httpdocs/src/Controller/ImportController.php b/httpdocs/src/Controller/ImportController.php
new file mode 100644
index 0000000..311bf3b
--- /dev/null
+++ b/httpdocs/src/Controller/ImportController.php
@@ -0,0 +1,84 @@
+roleHelper->isAdmin()) {
+ return $this->json(['error' => $this->translator->trans('app.error.access_denied')], 403);
+ }
+
+ $file = $request->files->get('file');
+ if (!$file) {
+ return $this->json(['error' => $this->translator->trans('app.import.error_no_file')], 400);
+ }
+
+ if ($file->getSize() > self::MAX_FILE_SIZE) {
+ return $this->json(['error' => $this->translator->trans('app.import.error_file_too_large')], 400);
+ }
+
+ $ext = strtolower($file->getClientOriginalExtension());
+ if ($ext !== 'xml') {
+ return $this->json(['error' => $this->translator->trans('app.import.error_invalid_format')], 400);
+ }
+
+ try {
+ $xml = file_get_contents($file->getPathname());
+ $analysis = $this->miteImportService->analyze($xml);
+ return $this->json($analysis);
+ } catch (\RuntimeException $e) {
+ return $this->json(['error' => $e->getMessage()], 422);
+ }
+ }
+
+ #[Route('/api/import/mite/execute', name: 'api_import_mite_execute', methods: ['POST'])]
+ public function execute(Request $request): JsonResponse
+ {
+ if (!$this->roleHelper->isAdmin()) {
+ return $this->json(['error' => $this->translator->trans('app.error.access_denied')], 403);
+ }
+
+ $file = $request->files->get('file');
+ if (!$file) {
+ return $this->json(['error' => $this->translator->trans('app.import.error_no_file')], 400);
+ }
+
+ /** @var User $user */
+ $user = $this->getUser();
+
+ $createUsers = json_decode($request->request->get('createUsers', '[]'), true) ?? [];
+ $createUsers = array_map('intval', $createUsers);
+
+ try {
+ $xml = file_get_contents($file->getPathname());
+ $result = $this->miteImportService->execute($xml, $user->getId(), $this->tenantEm, $createUsers);
+ return $this->json($result);
+ } catch (\RuntimeException $e) {
+ return $this->json(['error' => $e->getMessage()], 422);
+ }
+ }
+}
diff --git a/httpdocs/src/Service/MiteImportService.php b/httpdocs/src/Service/MiteImportService.php
new file mode 100644
index 0000000..68a08df
--- /dev/null
+++ b/httpdocs/src/Service/MiteImportService.php
@@ -0,0 +1,476 @@
+parse($xml);
+
+ $account = $this->tenantContext->getAccount();
+ $userMapping = [];
+ foreach ($data['users'] as $miteUser) {
+ $matched = $this->userRepo->findByEmail($miteUser['email']);
+ $matchedInAccount = null;
+ if ($matched) {
+ $au = $this->accountUserRepo->findOneBy([
+ 'account' => $account,
+ 'user' => $matched,
+ ]);
+ if ($au && !$au->isArchived()) {
+ $matchedInAccount = $matched;
+ }
+ }
+
+ $userMapping[] = [
+ 'miteId' => $miteUser['id'],
+ 'name' => $miteUser['name'],
+ 'email' => $miteUser['email'],
+ 'role' => $miteUser['role'],
+ 'matched' => $matchedInAccount !== null,
+ 'matchedUserId' => $matchedInAccount?->getId(),
+ 'matchedUserName' => $matchedInAccount?->getFullName(),
+ 'entryCount' => $this->countEntriesForUser($data['timeEntries'], $miteUser['id']),
+ ];
+ }
+
+ $warnings = $this->detectWarnings($data);
+
+ $dateRange = $this->getDateRange($data['timeEntries']);
+
+ return [
+ 'customers' => count($data['customers']),
+ 'projects' => count($data['projects']),
+ 'services' => count($data['services']),
+ 'timeEntries' => count($data['timeEntries']),
+ 'users' => $userMapping,
+ 'warnings' => $warnings,
+ 'dateRange' => $dateRange,
+ ];
+ }
+
+ /**
+ * @param int[] $createUserIds mite-User-IDs, für die ein neuer System-User angelegt werden soll
+ */
+ public function execute(string $xml, int $currentUserId, EntityManagerInterface $tenantEm, array $createUserIds = []): array
+ {
+ $data = $this->parse($xml);
+
+ $account = $this->tenantContext->getAccount();
+ $miteUserToSystemUser = [];
+ $usersCreated = 0;
+
+ $miteUsersById = [];
+ foreach ($data['users'] as $mu) {
+ $miteUsersById[$mu['id']] = $mu;
+ }
+
+ foreach ($data['users'] as $miteUser) {
+ $matched = $this->userRepo->findByEmail($miteUser['email']);
+ if ($matched) {
+ $au = $this->accountUserRepo->findOneBy([
+ 'account' => $account,
+ 'user' => $matched,
+ ]);
+ if ($au && !$au->isArchived()) {
+ $miteUserToSystemUser[$miteUser['id']] = $matched->getId();
+ continue;
+ }
+ }
+
+ if (in_array($miteUser['id'], $createUserIds, true)) {
+ $user = $this->createUserFromMite($miteUser, $account);
+ $miteUserToSystemUser[$miteUser['id']] = $user->getId();
+ $usersCreated++;
+ continue;
+ }
+
+ $miteUserToSystemUser[$miteUser['id']] = $currentUserId;
+ }
+
+ $tenantEm->beginTransaction();
+ try {
+ $serviceMap = $this->importServices($data['services'], $tenantEm);
+ $clientMap = $this->importClients($data['customers'], $tenantEm);
+ $projectMap = $this->importProjects($data['projects'], $clientMap, $tenantEm);
+
+ $tenantEm->flush();
+
+ $entryCount = $this->importTimeEntries(
+ $data['timeEntries'],
+ $projectMap,
+ $serviceMap,
+ $miteUserToSystemUser,
+ $currentUserId,
+ $tenantEm,
+ );
+
+ $tenantEm->flush();
+ $tenantEm->commit();
+
+ return [
+ 'services' => count($data['services']),
+ 'servicesCreated' => count($serviceMap['created']),
+ 'clients' => count($data['customers']),
+ 'clientsCreated' => count($clientMap['created']),
+ 'projects' => count($data['projects']),
+ 'projectsCreated' => count($projectMap['created']),
+ 'timeEntries' => $entryCount,
+ 'usersCreated' => $usersCreated,
+ ];
+ } catch (\Throwable $e) {
+ $tenantEm->rollback();
+ throw $e;
+ }
+ }
+
+ private function parse(string $xml): array
+ {
+ libxml_use_internal_errors(true);
+ $doc = simplexml_load_string($xml);
+ if ($doc === false) {
+ $errors = libxml_get_errors();
+ libxml_clear_errors();
+ $msg = !empty($errors) ? $errors[0]->message : 'Unbekannter Fehler';
+ throw new \RuntimeException('XML-Parsing fehlgeschlagen: ' . trim($msg));
+ }
+
+ $customers = [];
+ foreach ($doc->customers->customer ?? [] as $c) {
+ $customers[] = [
+ 'id' => (int) $c->id,
+ 'name' => (string) $c->name,
+ 'hourlyRate' => (int) $c->{'hourly-rate'},
+ 'note' => (string) $c->note,
+ 'archived' => ((string) $c->archived) === 'true',
+ ];
+ }
+
+ $projects = [];
+ foreach ($doc->projects->project ?? [] as $p) {
+ $projects[] = [
+ 'id' => (int) $p->id,
+ 'customerId' => (int) $p->{'customer-id'},
+ 'name' => (string) $p->name,
+ 'hourlyRate' => (int) $p->{'hourly-rate'},
+ 'note' => (string) $p->note,
+ 'archived' => ((string) $p->archived) === 'true',
+ 'budget' => (int) $p->budget,
+ ];
+ }
+
+ $services = [];
+ foreach ($doc->services->service ?? [] as $s) {
+ $services[] = [
+ 'id' => (int) $s->id,
+ 'name' => (string) $s->name,
+ 'billable' => ((string) $s->billable) === 'true',
+ 'hourlyRate' => (int) $s->{'hourly-rate'},
+ 'note' => (string) $s->note,
+ 'archived' => ((string) $s->archived) === 'true',
+ ];
+ }
+
+ $timeEntries = [];
+ foreach ($doc->{'time-entries'}->{'time-entry'} ?? [] as $te) {
+ $timeEntries[] = [
+ 'id' => (int) $te->id,
+ 'dateAt' => (string) $te->{'date-at'},
+ 'minutes' => (int) $te->minutes,
+ 'projectId' => (int) $te->{'project-id'},
+ 'serviceId' => (int) $te->{'service-id'},
+ 'userId' => (int) $te->{'user-id'},
+ 'note' => (string) $te->note,
+ 'billable' => ((string) $te->billable) === 'true',
+ 'locked' => ((string) $te->locked) === 'true',
+ ];
+ }
+
+ $users = [];
+ foreach ($doc->users->user ?? [] as $u) {
+ $users[] = [
+ 'id' => (int) $u->id,
+ 'name' => (string) $u->name,
+ 'email' => (string) $u->email,
+ 'role' => (string) $u->role,
+ 'archived' => ((string) $u->archived) === 'true',
+ ];
+ }
+
+ return compact('customers', 'projects', 'services', 'timeEntries', 'users');
+ }
+
+ /**
+ * @return array{entities: array, created: int[]}
+ */
+ private function importServices(array $miteServices, EntityManagerInterface $em): array
+ {
+ $repo = $em->getRepository(Service::class);
+ $map = ['entities' => [], 'created' => []];
+
+ foreach ($miteServices as $ms) {
+ $existing = $repo->findOneBy(['name' => $ms['name']]);
+ if ($existing) {
+ $map['entities'][$ms['id']] = $existing;
+ continue;
+ }
+
+ $service = new Service();
+ $service->setName($ms['name']);
+ $service->setBillable($ms['billable']);
+ $service->setHourlyRate($this->centsToDecimal($ms['hourlyRate']));
+ if ($ms['note'] !== '') {
+ $service->setNote($ms['note']);
+ }
+ if ($ms['archived']) {
+ $service->setArchivedAt(new \DateTimeImmutable());
+ }
+
+ $em->persist($service);
+ $map['entities'][$ms['id']] = $service;
+ $map['created'][] = $ms['id'];
+ }
+
+ return $map;
+ }
+
+ /**
+ * @return array{entities: array, created: int[]}
+ */
+ private function importClients(array $miteCustomers, EntityManagerInterface $em): array
+ {
+ $repo = $em->getRepository(Client::class);
+ $map = ['entities' => [], 'created' => []];
+
+ foreach ($miteCustomers as $mc) {
+ $existing = $repo->findOneBy(['name' => $mc['name']]);
+ if ($existing) {
+ $map['entities'][$mc['id']] = $existing;
+ continue;
+ }
+
+ $client = new Client();
+ $client->setName($mc['name']);
+ if ($mc['hourlyRate'] > 0) {
+ $client->setHourlyRate($this->centsToDecimal($mc['hourlyRate']));
+ }
+ if ($mc['note'] !== '') {
+ $client->setNote($mc['note']);
+ }
+ if ($mc['archived']) {
+ $client->setArchivedAt(new \DateTimeImmutable());
+ }
+
+ $em->persist($client);
+ $map['entities'][$mc['id']] = $client;
+ $map['created'][] = $mc['id'];
+ }
+
+ return $map;
+ }
+
+ /**
+ * @return array{entities: array, created: int[]}
+ */
+ private function importProjects(array $miteProjects, array $clientMap, EntityManagerInterface $em): array
+ {
+ $repo = $em->getRepository(Project::class);
+ $map = ['entities' => [], 'created' => []];
+
+ foreach ($miteProjects as $mp) {
+ $client = $clientMap['entities'][$mp['customerId']] ?? null;
+ if (!$client) {
+ continue;
+ }
+
+ $existing = $repo->findOneBy(['name' => $mp['name'], 'client' => $client]);
+ if ($existing) {
+ $map['entities'][$mp['id']] = $existing;
+ continue;
+ }
+
+ $project = new Project();
+ $project->setName($mp['name']);
+ $project->setClient($client);
+ if ($mp['hourlyRate'] > 0) {
+ $project->setHourlyRate($this->centsToDecimal($mp['hourlyRate']));
+ }
+ if ($mp['note'] !== '') {
+ $project->setNote($mp['note']);
+ }
+ if ($mp['archived']) {
+ $project->setArchivedAt(new \DateTimeImmutable());
+ }
+
+ $em->persist($project);
+ $map['entities'][$mp['id']] = $project;
+ $map['created'][] = $mp['id'];
+ }
+
+ return $map;
+ }
+
+ private function importTimeEntries(
+ array $miteEntries,
+ array $projectMap,
+ array $serviceMap,
+ array $userMapping,
+ int $fallbackUserId,
+ EntityManagerInterface $em,
+ ): int {
+ $count = 0;
+
+ foreach ($miteEntries as $me) {
+ $project = $projectMap['entities'][$me['projectId']] ?? null;
+ if (!$project) {
+ continue;
+ }
+
+ $service = $serviceMap['entities'][$me['serviceId']] ?? null;
+
+ $entry = new TimeEntry();
+ $entry->setDate(new \DateTimeImmutable($me['dateAt']));
+ $entry->setDuration($me['minutes']);
+ $entry->setProject($project);
+ $entry->setService($service);
+ $entry->setUserId($userMapping[$me['userId']] ?? $fallbackUserId);
+ if ($me['note'] !== '') {
+ $entry->setNote($me['note']);
+ }
+ if ($me['locked']) {
+ $entry->setInvoiced(true);
+ }
+
+ $em->persist($entry);
+ $count++;
+
+ if ($count % 100 === 0) {
+ $em->flush();
+ }
+ }
+
+ return $count;
+ }
+
+ private function createUserFromMite(array $miteUser, \App\Entity\Central\Account $account): User
+ {
+ $existing = $this->userRepo->findByEmail($miteUser['email']);
+ if ($existing) {
+ $user = $existing;
+ } else {
+ [$firstName, $lastName] = $this->splitName($miteUser['name']);
+ $user = new User();
+ $user->setEmail($miteUser['email']);
+ $user->setFirstName($firstName);
+ $user->setLastName($lastName);
+ $this->centralEm->persist($user);
+ }
+
+ $existingAu = $this->accountUserRepo->findOneBy([
+ 'account' => $account,
+ 'user' => $user,
+ ]);
+
+ if (!$existingAu) {
+ $accountUser = new AccountUser();
+ $accountUser->setAccount($account);
+ $accountUser->setUser($user);
+ $accountUser->setRole($this->mapMiteRole($miteUser['role']));
+ $this->centralEm->persist($accountUser);
+ }
+
+ $this->centralEm->flush();
+
+ return $user;
+ }
+
+ private function splitName(string $fullName): array
+ {
+ $parts = preg_split('/\s+/', trim($fullName), 2);
+ return [$parts[0], $parts[1] ?? ''];
+ }
+
+ private function mapMiteRole(string $miteRole): string
+ {
+ return match ($miteRole) {
+ 'owner', 'admin' => AccountUser::ROLE_MEMBER,
+ 'coworker' => AccountUser::ROLE_MEMBER,
+ default => AccountUser::ROLE_TRACKER,
+ };
+ }
+
+ private function centsToDecimal(int $cents): string
+ {
+ return number_format($cents / 100, 2, '.', '');
+ }
+
+ private function countEntriesForUser(array $timeEntries, int $miteUserId): int
+ {
+ return count(array_filter($timeEntries, fn(array $te) => $te['userId'] === $miteUserId));
+ }
+
+ private function detectWarnings(array $data): array
+ {
+ $warnings = [];
+
+ $budgetCount = count(array_filter($data['projects'], fn(array $p) => $p['budget'] > 0));
+ if ($budgetCount > 0) {
+ $warnings[] = sprintf('%d Projekte haben Budgets — werden nicht importiert.', $budgetCount);
+ }
+
+ $lockedCount = count(array_filter($data['timeEntries'], fn(array $te) => $te['locked']));
+ if ($lockedCount > 0) {
+ $warnings[] = sprintf('%d Einträge sind in mite gesperrt — werden als „abgerechnet" importiert.', $lockedCount);
+ }
+
+ $archivedCustomers = count(array_filter($data['customers'], fn(array $c) => $c['archived']));
+ $archivedProjects = count(array_filter($data['projects'], fn(array $p) => $p['archived']));
+ $archivedServices = count(array_filter($data['services'], fn(array $s) => $s['archived']));
+ if ($archivedCustomers + $archivedProjects + $archivedServices > 0) {
+ $parts = [];
+ if ($archivedCustomers > 0) $parts[] = "$archivedCustomers Kunden";
+ if ($archivedProjects > 0) $parts[] = "$archivedProjects Projekte";
+ if ($archivedServices > 0) $parts[] = "$archivedServices Leistungen";
+ $warnings[] = implode(', ', $parts) . ' sind archiviert — werden als archiviert importiert.';
+ }
+
+ $warnings[] = 'Stundensätze werden von Cent (mite) in Euro umgerechnet.';
+
+ return $warnings;
+ }
+
+ private function getDateRange(array $timeEntries): array
+ {
+ if (empty($timeEntries)) {
+ return ['from' => null, 'to' => null];
+ }
+
+ $dates = array_map(fn(array $te) => $te['dateAt'], $timeEntries);
+ sort($dates);
+
+ return [
+ 'from' => $dates[0],
+ 'to' => $dates[count($dates) - 1],
+ ];
+ }
+}
diff --git a/httpdocs/templates/account/index.html.twig b/httpdocs/templates/account/index.html.twig
index 75fcddb..f9b80d8 100644
--- a/httpdocs/templates/account/index.html.twig
+++ b/httpdocs/templates/account/index.html.twig
@@ -2,7 +2,7 @@
{% extends 'base.html.twig' %}
{% block title %}
- {% if tab == 'account' %}{{ 'app.account.page_title_account'|trans }}{% else %}{{ 'app.account.page_title_user'|trans }}{% endif %}
+ {% if tab == 'import' %}{{ 'app.import.tab'|trans }}{% elseif tab == 'account' %}{{ 'app.account.page_title_account'|trans }}{% else %}{{ 'app.account.page_title_user'|trans }}{% endif %}
{% endblock %}
{% block body %}
@@ -23,6 +23,31 @@
changeLabel: {{ 'app.account.change_label'|trans|json_encode|raw }},
cancelLabel: {{ 'app.account.cancel_label'|trans|json_encode|raw }},
errorGeneric: {{ 'app.account.error_generic'|trans|json_encode|raw }},
+ importAnalyze: {{ 'app.import.btn_analyze'|trans|json_encode|raw }},
+ importAnalyzing: {{ 'app.import.btn_analyzing'|trans|json_encode|raw }},
+ importExecute: {{ 'app.import.btn_execute'|trans|json_encode|raw }},
+ importExecuting: {{ 'app.import.btn_executing'|trans|json_encode|raw }},
+ importSuccess: {{ 'app.import.success'|trans|json_encode|raw }},
+ importConfirm: {{ 'app.import.confirm'|trans|json_encode|raw }},
+ importErrorNoFile: {{ 'app.import.error_no_file'|trans|json_encode|raw }},
+ importLabelClients: {{ 'app.import.label_clients'|trans|json_encode|raw }},
+ importLabelProjects: {{ 'app.import.label_projects'|trans|json_encode|raw }},
+ importLabelServices: {{ 'app.import.label_services'|trans|json_encode|raw }},
+ importLabelEntries: {{ 'app.import.label_entries'|trans|json_encode|raw }},
+ importLabelUsers: {{ 'app.import.label_users'|trans|json_encode|raw }},
+ importLabelDateRange: {{ 'app.import.label_date_range'|trans|json_encode|raw }},
+ importLabelWarnings: {{ 'app.import.label_warnings'|trans|json_encode|raw }},
+ importUserMatched: {{ 'app.import.user_matched'|trans|json_encode|raw }},
+ importUserFallback: {{ 'app.import.user_fallback'|trans|json_encode|raw }},
+ importNewLabel: {{ 'app.import.new_label'|trans|json_encode|raw }},
+ importExistingLabel: {{ 'app.import.existing_label'|trans|json_encode|raw }},
+ importResultClients: {{ 'app.import.result_clients'|trans|json_encode|raw }},
+ importResultProjects: {{ 'app.import.result_projects'|trans|json_encode|raw }},
+ importResultServices: {{ 'app.import.result_services'|trans|json_encode|raw }},
+ importResultEntries: {{ 'app.import.result_entries'|trans|json_encode|raw }},
+ importResultUsers: {{ 'app.import.result_users'|trans|json_encode|raw }},
+ importUserAssignMe: {{ 'app.import.user_assign_me'|trans|json_encode|raw }},
+ importUserCreate: {{ 'app.import.user_create'|trans|json_encode|raw }},
},
};
@@ -31,7 +56,7 @@
+
+ {# ── Import-Tab (nur Admin) ────────────────────────────────────────── #}
+ {% if tab == 'import' and isAdmin %}
+
+
+
{{ 'app.import.title_mite'|trans }}
+
{{ 'app.import.desc_mite'|trans }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ 'app.import.title_preview'|trans }}
+
+
+
+
+
+
+
+
+
{{ 'app.import.title_result'|trans }}
+
+
+
+ {% else %}
+
{# ── Account-Tab (nur Admin) ────────────────────────────────────────── #}
@@ -226,6 +309,8 @@
{% endif %}
+ {% endif %}{# Ende Import-else #}
+
diff --git a/httpdocs/templates/report/statistics.html.twig b/httpdocs/templates/report/statistics.html.twig
index 22adea1..3ef62f9 100644
--- a/httpdocs/templates/report/statistics.html.twig
+++ b/httpdocs/templates/report/statistics.html.twig
@@ -27,6 +27,8 @@
services: {{ 'app.statistics.services'|trans|json_encode|raw }},
rest: {{ 'app.statistics.rest'|trans|json_encode|raw }},
noService: {{ 'app.statistics.no_service'|trans|json_encode|raw }},
+ totalHours: {{ 'app.statistics.total_hours'|trans({'%hours%': '__HOURS__'})|json_encode|raw }},
+ totalRevenue: {{ 'app.statistics.total_revenue'|trans({'%revenue%': '__REVENUE__'})|json_encode|raw }},
}
};
@@ -84,6 +86,8 @@
+
+
diff --git a/httpdocs/translations/messages.de.yaml b/httpdocs/translations/messages.de.yaml
index baff9fc..5e0d59f 100644
--- a/httpdocs/translations/messages.de.yaml
+++ b/httpdocs/translations/messages.de.yaml
@@ -327,6 +327,43 @@ app:
interval_half: "Halbe Stunde"
interval_hour: "Stunde"
+ import:
+ tab: "Daten-Import"
+ title_mite: "mite-Import"
+ desc_mite: "Importiere Kunden, Projekte, Leistungen und Zeiteinträge aus einem mite-Backup (XML). Einträge werden zu den bestehenden Daten hinzugefügt — ein doppelter Import erzeugt doppelte Einträge."
+ upload_text: "XML-Datei auswählen oder hierher ziehen"
+ upload_hint: "mite-Backup im XML-Format, max. 20 MB"
+ btn_analyze: "Datei analysieren"
+ btn_analyzing: "Wird analysiert…"
+ btn_execute: "Import starten"
+ btn_executing: "Wird importiert…"
+ btn_reset: "Abbrechen"
+ title_preview: "Vorschau"
+ title_result: "Ergebnis"
+ confirm: "Bist du sicher? Die Einträge werden zu den bestehenden Daten hinzugefügt. Ein erneuter Import erzeugt Duplikate."
+ success: "Import erfolgreich abgeschlossen."
+ error_no_file: "Bitte eine XML-Datei auswählen."
+ error_file_too_large: "Die Datei ist zu groß (max. 20 MB)."
+ error_invalid_format: "Nur XML-Dateien werden unterstützt."
+ label_clients: "Kunden"
+ label_projects: "Projekte"
+ label_services: "Leistungen"
+ label_entries: "Zeiteinträge"
+ label_users: "Benutzer-Zuordnung"
+ label_date_range: "Zeitraum"
+ label_warnings: "Hinweise"
+ user_matched: "zugeordnet zu"
+ user_fallback: "wird dir zugeordnet"
+ user_assign_me: "mir zuordnen"
+ user_create: "User anlegen"
+ new_label: "neu"
+ existing_label: "vorhanden"
+ result_clients: "Kunden angelegt"
+ result_projects: "Projekte angelegt"
+ result_services: "Leistungen angelegt"
+ result_entries: "Zeiteinträge importiert"
+ result_users: "Benutzer angelegt"
+
forgot_password:
page_title: "Passwort vergessen – spawntree"
subtitle: "Gib deine E-Mail ein und wir schicken dir einen Reset-Link."
@@ -469,6 +506,8 @@ app:
services: "Leistungen"
rest: "Rest"
no_service: "Ohne Leistung"
+ total_hours: "Gesamt: %hours%"
+ total_revenue: "Gesamt: %revenue%"
stopwatch:
title: "Stoppuhr"