Skip to content

Commit 39dc348

Browse files
committed
[28412] fix tardoc limit amount over period verification
1 parent 675f6f9 commit 39dc348

4 files changed

Lines changed: 240 additions & 30 deletions

File tree

bundles/ch.elexis.base.ch.arzttarife.model/src/ch/elexis/base/ch/arzttarife/tardoc/model/TardocLimitation.java

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
import java.time.temporal.ChronoUnit;
66
import java.util.ArrayList;
77
import java.util.Collections;
8+
import java.util.HashSet;
89
import java.util.Iterator;
910
import java.util.List;
1011
import java.util.Map;
12+
import java.util.Set;
1113
import java.util.stream.Collectors;
1214

1315
import org.apache.commons.lang3.StringUtils;
@@ -323,30 +325,49 @@ private Result<IBilled> testDuration(IEncounter kons, IBilled verrechnet) {
323325
}
324326
if (operator.equals("<=")) {
325327
if (tarmedGroup == null) {
326-
List<IBilled> verrechnetByMandant = getVerrechnetByRechnungsstellerAndCodeDuringPeriod(kons,
328+
List<VerrechnetPeriod> verrechnetByMandantPeriods = getVerrechnetByRechnungsstellerAndCodeDuringPeriod(
329+
kons,
327330
verrechnet.getBillable().getCode());
328-
if (!verrechnetByMandant.isEmpty()) {
329-
// replace value from database with current
330-
verrechnetByMandant.remove(verrechnet);
331-
verrechnetByMandant.add(verrechnet);
332-
if (getVerrechnetCount(verrechnetByMandant) > amount) {
333-
ret = new Result<IBilled>(Result.SEVERITY.WARNING, TarmedOptifier.KUMULATION, toString(),
334-
verrechnet, false);
331+
if (!verrechnetByMandantPeriods.isEmpty()) {
332+
for (VerrechnetPeriod verrechnetPeriod : verrechnetByMandantPeriods) {
333+
List<IBilled> verrechnetByMandant = verrechnetPeriod.getVerrechnete();
334+
// replace value from database with current
335+
verrechnetByMandant.remove(verrechnet);
336+
verrechnetByMandant.add(verrechnet);
337+
if (getVerrechnetCount(verrechnetByMandant) > amount) {
338+
ret = new Result<IBilled>(Result.SEVERITY.WARNING, TarmedOptifier.KUMULATION, toString(),
339+
verrechnet, false);
340+
break;
341+
}
335342
}
336343
}
337344
} else {
338-
List<IBilled> allVerrechnetOfGroup = new ArrayList<>();
345+
List<VerrechnetPeriod> allGroupVerrechnetPeriods = new ArrayList<>();
339346
List<String> serviceCodes = tarmedGroup.getServices();
340347
for (String code : serviceCodes) {
341-
allVerrechnetOfGroup.addAll(getVerrechnetByRechnungsstellerAndCodeDuringPeriod(kons, code));
348+
allGroupVerrechnetPeriods.addAll(getVerrechnetByRechnungsstellerAndCodeDuringPeriod(kons, code));
342349
}
343-
// replace loaded with current verrechnet to validate updated amount
344-
allVerrechnetOfGroup.remove(verrechnet);
345-
allVerrechnetOfGroup.add(verrechnet);
346-
if (getVerrechnetCount(allVerrechnetOfGroup) > amount) {
347-
ret = new Result<IBilled>(Result.SEVERITY.WARNING, TarmedOptifier.KUMULATION, toString(),
348-
verrechnet,
349-
false);
350+
// create new groups with all unique verrechnet of groups
351+
Set<IBilled> uniqueSet = new HashSet<>();
352+
for (VerrechnetPeriod groupVerrechnetPeriod : allGroupVerrechnetPeriods) {
353+
uniqueSet.addAll(groupVerrechnetPeriod.getVerrechnete());
354+
}
355+
List<IBilled> allVerrechnet = new ArrayList<IBilled>(uniqueSet);
356+
allGroupVerrechnetPeriods = getGroupedByPeriod(allVerrechnet);
357+
if (!allGroupVerrechnetPeriods.isEmpty()) {
358+
Collections.sort(allGroupVerrechnetPeriods, (l, r) -> Integer.valueOf(r.getVerrechnete().size())
359+
.compareTo(Integer.valueOf(l.getVerrechnete().size())));
360+
for (VerrechnetPeriod verrechnetPeriod : allGroupVerrechnetPeriods) {
361+
List<IBilled> verrechnetByMandant = verrechnetPeriod.getVerrechnete();
362+
// replace value from database with current
363+
verrechnetByMandant.remove(verrechnet);
364+
verrechnetByMandant.add(verrechnet);
365+
if (getVerrechnetCount(verrechnetByMandant) > amount) {
366+
ret = new Result<IBilled>(Result.SEVERITY.WARNING, TarmedOptifier.KUMULATION, toString(),
367+
verrechnet, false);
368+
break;
369+
}
370+
}
350371
}
351372
}
352373
}
@@ -401,7 +422,7 @@ public static List<IBilled> findVerrechnetByPatientCodeDuringPeriod(IPatient pat
401422
* @param code
402423
* @return
403424
*/
404-
private List<IBilled> getVerrechnetByRechnungsstellerAndCodeDuringPeriod(IEncounter kons, String code) {
425+
private List<VerrechnetPeriod> getVerrechnetByRechnungsstellerAndCodeDuringPeriod(IEncounter kons, String code) {
405426
IContact rechnungssteller = kons.getMandator().getBiller();
406427

407428
if (rechnungssteller != null) {
@@ -439,33 +460,29 @@ private List<IBilled> getVerrechnetByRechnungsstellerAndCodeDuringPeriod(IEncoun
439460
// now group in time periods since first verrechnet
440461
LocalDate konsDate = kons.getDate();
441462
List<VerrechnetPeriod> grouped = getGroupedByPeriod(all);
442-
// lookup period matching konsDate
443-
for (VerrechnetPeriod verrechnetPeriod : grouped) {
444-
if (verrechnetPeriod.isDateInPeriod(konsDate)) {
445-
return verrechnetPeriod.getVerrechnete();
446-
}
447-
}
463+
// collect all billed of matching periods
464+
return grouped.stream().filter(g -> g.isDateInPeriod(konsDate)).toList();
448465
}
449466
return Collections.emptyList();
450467
}
451468

452469
private List<VerrechnetPeriod> getGroupedByPeriod(List<IBilled> verrechnete) {
453470
if (!verrechnete.isEmpty()) {
471+
// sort first for deterministic results
472+
Collections.sort(verrechnete, (l, r) -> l.getEncounter().getDate().compareTo(r.getEncounter().getDate()));
454473
List<VerrechnetPeriod> ret = new ArrayList<>();
455474
for (IBilled verrechnet : verrechnete) {
456475
if (ret.isEmpty()) {
457476
ret.add(new VerrechnetPeriod(verrechnet));
458477
} else {
459-
boolean added = false;
460478
for (VerrechnetPeriod verrechnetPeriod : ret) {
461479
if (verrechnetPeriod.isInPeriod(verrechnet)) {
462480
verrechnetPeriod.addVerrechnet(verrechnet);
463-
added = true;
464-
break;
465481
}
466482
}
467-
// start new period
468-
if (!added) {
483+
// always start new period if no period with same start exists
484+
if (ret.stream().filter(p -> p.start.equals(verrechnet.getEncounter().getDate())).findFirst()
485+
.isEmpty()) {
469486
ret.add(new VerrechnetPeriod(verrechnet));
470487
}
471488
}

tests/ch.elexis.base.ch.arzttarife.model.test/src/ch/elexis/base/ch/arzttarife/model/tardoc/test/AbstractTardocTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import ch.elexis.core.model.ICoverage;
1111
import ch.elexis.core.model.IEncounter;
1212
import ch.elexis.core.model.IMandator;
13+
import ch.elexis.core.model.IOrganization;
1314
import ch.elexis.core.model.IPatient;
1415
import ch.elexis.core.model.IPerson;
1516
import ch.elexis.core.model.builder.IContactBuilder;
@@ -30,6 +31,8 @@ public abstract class AbstractTardocTest {
3031
final IModelService coreModelService = AllTestsSuite.getCoreModelService();
3132

3233
IMandator mandator;
34+
IMandator otherMandator;
35+
3336
IPatient patient;
3437
ICoverage coverage;
3538
IEncounter encounter;
@@ -39,9 +42,21 @@ public abstract class AbstractTardocTest {
3942

4043
public void before() {
4144
TimeTool timeTool = new TimeTool();
45+
46+
IOrganization _biller = new IContactBuilder.OrganizationBuilder(coreModelService, "Praxis").buildAndSave();
47+
4248
IPerson _mandator = new IContactBuilder.PersonBuilder(coreModelService, "mandator1 " + timeTool.toString(),
4349
"Anton" + timeTool.toString(), timeTool.toLocalDate(), Gender.MALE).mandator().buildAndSave();
4450
mandator = coreModelService.load(_mandator.getId(), IMandator.class).get();
51+
mandator.setBiller(_biller);
52+
coreModelService.save(mandator);
53+
54+
_mandator = new IContactBuilder.PersonBuilder(coreModelService, "mandator2 " + timeTool.toString(),
55+
"Antonia" + timeTool.toString(), timeTool.toLocalDate(), Gender.FEMALE).mandator().buildAndSave();
56+
otherMandator = coreModelService.load(_mandator.getId(), IMandator.class).get();
57+
otherMandator.setBiller(_biller);
58+
coreModelService.save(otherMandator);
59+
4560
patient = new IContactBuilder.PatientBuilder(coreModelService, "Armer", "Anton" + timeTool.toString(),
4661
timeTool.toLocalDate(), Gender.MALE).buildAndSave();
4762
coverage = new ICoverageBuilder(coreModelService, patient, "Fallbezeichnung", "Fallgrund", "KVG")

tests/ch.elexis.base.ch.arzttarife.model.test/src/ch/elexis/base/ch/arzttarife/model/tardoc/test/AllTardocTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
import ch.elexis.core.utils.OsgiServiceUtil;
1212

1313
@RunWith(Suite.class)
14-
@SuiteClasses({ TardocBillingTest.class, TardocGroupLimitsTest.class, AmbulantePauschalenBillingTest.class })
14+
@SuiteClasses({ TardocBillingTest.class, TardocGroupLimitsTest.class, AmbulantePauschalenBillingTest.class,
15+
TardocLimitsTest.class })
1516
public class AllTardocTests {
1617

1718
public static final String UVG_NAME = Messages.Case_UVG_Short;
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
package ch.elexis.base.ch.arzttarife.model.tardoc.test;
2+
3+
import static org.junit.Assert.assertFalse;
4+
import static org.junit.Assert.assertTrue;
5+
6+
import java.time.LocalDate;
7+
8+
import org.junit.After;
9+
import org.junit.Before;
10+
import org.junit.Test;
11+
12+
import ch.elexis.base.ch.arzttarife.tardoc.model.TardocLeistung;
13+
import ch.elexis.core.model.IBilled;
14+
import ch.elexis.core.model.IEncounter;
15+
import ch.elexis.core.model.builder.IEncounterBuilder;
16+
import ch.elexis.core.rcp.utils.OsgiServiceUtil;
17+
import ch.elexis.core.services.IContextService;
18+
import ch.elexis.core.services.holder.CoreModelServiceHolder;
19+
import ch.elexis.core.services.holder.EncounterServiceHolder;
20+
import ch.elexis.core.test.initializer.TestDatabaseInitializer;
21+
import ch.rgw.tools.Result;
22+
23+
public class TardocLimitsTest extends AbstractTardocTest {
24+
25+
private TardocLeistung code_AA150010;
26+
private TardocLeistung code_AA150060;
27+
28+
@Override
29+
@Before
30+
public void before() {
31+
super.before();
32+
// Für alle Tarifpositionen der Leistungsgruppe LG-002 - Leistungen in
33+
// Abwesenheit (Büroarbeit) gilt: ≤ 30 Mal pro 90 Tage
34+
code_AA150010 = TardocLeistung.getFromCode("AA.15.0010", LocalDate.of(2026, 1, 1), null);
35+
// Für alle Tarifpositionen der Leistungsgruppe LG-002 - Leistungen in
36+
// Abwesenheit (Büroarbeit) gilt: ≤ 30 Mal pro 90 Tage
37+
code_AA150060 = TardocLeistung.getFromCode("AA.15.0060", LocalDate.of(2026, 1, 1), null);
38+
}
39+
40+
@Override
41+
@After
42+
public void after() {
43+
super.after();
44+
}
45+
46+
@Test
47+
public void limitGroup002MultiSessionMultiMandator() {
48+
encounter.setDate(LocalDate.of(2026, 1, 1));
49+
CoreModelServiceHolder.get().save(encounter);
50+
Result<IBilled> status = billingService.bill(code_AA150010, encounter, 5);
51+
assertTrue(status.getMessages().toString(), status.isOK());
52+
status = billingService.bill(code_AA150060, encounter, 5);
53+
assertTrue(status.getMessages().toString(), status.isOK());
54+
55+
IEncounter encounter1 = new IEncounterBuilder(coreModelService, coverage, otherMandator).buildAndSave();
56+
OsgiServiceUtil.getService(IContextService.class).get().setActiveUser(TestDatabaseInitializer.getUser());
57+
OsgiServiceUtil.getService(IContextService.class).get().setActiveMandator(otherMandator);
58+
encounter1.setDate(LocalDate.of(2026, 1, 7));
59+
CoreModelServiceHolder.get().save(encounter1);
60+
status = billingService.bill(code_AA150010, encounter1, 5);
61+
assertTrue(status.getMessages().toString(), status.isOK());
62+
status = billingService.bill(code_AA150060, encounter1, 5);
63+
assertTrue(status.getMessages().toString(), status.isOK());
64+
65+
IEncounter encounter2 = new IEncounterBuilder(coreModelService, coverage, mandator).buildAndSave();
66+
OsgiServiceUtil.getService(IContextService.class).get().setActiveUser(TestDatabaseInitializer.getUser());
67+
OsgiServiceUtil.getService(IContextService.class).get().setActiveMandator(mandator);
68+
encounter2.setDate(LocalDate.of(2026, 1, 14));
69+
CoreModelServiceHolder.get().save(encounter2);
70+
status = billingService.bill(code_AA150010, encounter2, 5);
71+
assertTrue(status.getMessages().toString(), status.isOK());
72+
status = billingService.bill(code_AA150060, encounter2, 5);
73+
assertTrue(status.getMessages().toString(), status.isOK());
74+
75+
IEncounter encounter3 = new IEncounterBuilder(coreModelService, coverage, otherMandator).buildAndSave();
76+
OsgiServiceUtil.getService(IContextService.class).get().setActiveUser(TestDatabaseInitializer.getUser());
77+
OsgiServiceUtil.getService(IContextService.class).get().setActiveMandator(otherMandator);
78+
encounter3.setDate(LocalDate.of(2026, 1, 21));
79+
CoreModelServiceHolder.get().save(encounter3);
80+
status = billingService.bill(code_AA150010, encounter3, 1);
81+
// fail combination of Leistungsgruppe LG-002
82+
assertFalse(status.getMessages().toString(), status.isOK());
83+
status = billingService.bill(code_AA150060, encounter3, 1);
84+
// fail combination of Leistungsgruppe LG-002
85+
assertFalse(status.getMessages().toString(), status.isOK());
86+
87+
// still fail at plus 90 days
88+
IEncounter encounter4 = new IEncounterBuilder(coreModelService, coverage, otherMandator).buildAndSave();
89+
OsgiServiceUtil.getService(IContextService.class).get().setActiveUser(TestDatabaseInitializer.getUser());
90+
OsgiServiceUtil.getService(IContextService.class).get().setActiveMandator(otherMandator);
91+
encounter4.setDate(LocalDate.of(2026, 1, 1).plusDays(9));
92+
CoreModelServiceHolder.get().save(encounter4);
93+
status = billingService.bill(code_AA150010, encounter4, 1);
94+
// fail combination of Leistungsgruppe LG-002 90 days
95+
assertFalse(status.getMessages().toString(), status.isOK());
96+
status = billingService.bill(code_AA150060, encounter4, 1);
97+
// fail combination of Leistungsgruppe LG-002 90 days
98+
assertFalse(status.getMessages().toString(), status.isOK());
99+
100+
// ok after 91 days
101+
IEncounter encounter5 = new IEncounterBuilder(coreModelService, coverage, otherMandator).buildAndSave();
102+
OsgiServiceUtil.getService(IContextService.class).get().setActiveUser(TestDatabaseInitializer.getUser());
103+
OsgiServiceUtil.getService(IContextService.class).get().setActiveMandator(otherMandator);
104+
encounter5.setDate(LocalDate.of(2026, 1, 1).plusDays(91));
105+
CoreModelServiceHolder.get().save(encounter5);
106+
status = billingService.bill(code_AA150010, encounter5, 1);
107+
assertTrue(status.getMessages().toString(), status.isOK());
108+
status = billingService.bill(code_AA150060, encounter5, 1);
109+
assertTrue(status.getMessages().toString(), status.isOK());
110+
}
111+
112+
@Test
113+
public void limitGroup002MultiSessionMultiMandatorMove() {
114+
encounter.setDate(LocalDate.of(2026, 1, 1));
115+
CoreModelServiceHolder.get().save(encounter);
116+
Result<IBilled> status = billingService.bill(code_AA150010, encounter, 5);
117+
assertTrue(status.getMessages().toString(), status.isOK());
118+
status = billingService.bill(code_AA150060, encounter, 5);
119+
assertTrue(status.getMessages().toString(), status.isOK());
120+
121+
IEncounter encounter1 = new IEncounterBuilder(coreModelService, coverage, otherMandator).buildAndSave();
122+
OsgiServiceUtil.getService(IContextService.class).get().setActiveUser(TestDatabaseInitializer.getUser());
123+
OsgiServiceUtil.getService(IContextService.class).get().setActiveMandator(otherMandator);
124+
encounter1.setDate(LocalDate.of(2026, 1, 7));
125+
CoreModelServiceHolder.get().save(encounter1);
126+
status = billingService.bill(code_AA150010, encounter1, 5);
127+
assertTrue(status.getMessages().toString(), status.isOK());
128+
status = billingService.bill(code_AA150060, encounter1, 5);
129+
assertTrue(status.getMessages().toString(), status.isOK());
130+
131+
IEncounter encounter2 = new IEncounterBuilder(coreModelService, coverage, mandator).buildAndSave();
132+
OsgiServiceUtil.getService(IContextService.class).get().setActiveUser(TestDatabaseInitializer.getUser());
133+
OsgiServiceUtil.getService(IContextService.class).get().setActiveMandator(mandator);
134+
encounter2.setDate(LocalDate.of(2026, 1, 14));
135+
CoreModelServiceHolder.get().save(encounter2);
136+
status = billingService.bill(code_AA150010, encounter2, 5);
137+
assertTrue(status.getMessages().toString(), status.isOK());
138+
status = billingService.bill(code_AA150060, encounter2, 5);
139+
assertTrue(status.getMessages().toString(), status.isOK());
140+
141+
IEncounter encounter3 = new IEncounterBuilder(coreModelService, coverage, otherMandator).buildAndSave();
142+
OsgiServiceUtil.getService(IContextService.class).get().setActiveUser(TestDatabaseInitializer.getUser());
143+
OsgiServiceUtil.getService(IContextService.class).get().setActiveMandator(otherMandator);
144+
encounter3.setDate(LocalDate.of(2026, 1, 21));
145+
CoreModelServiceHolder.get().save(encounter3);
146+
status = billingService.bill(code_AA150010, encounter3, 1);
147+
// fail combination of Leistungsgruppe LG-002
148+
assertFalse(status.getMessages().toString(), status.isOK());
149+
status = billingService.bill(code_AA150060, encounter3, 1);
150+
// fail combination of Leistungsgruppe LG-002
151+
assertFalse(status.getMessages().toString(), status.isOK());
152+
153+
IEncounter encounter4 = new IEncounterBuilder(coreModelService, coverage, otherMandator).buildAndSave();
154+
OsgiServiceUtil.getService(IContextService.class).get().setActiveUser(TestDatabaseInitializer.getUser());
155+
OsgiServiceUtil.getService(IContextService.class).get().setActiveMandator(otherMandator);
156+
encounter4.setDate(LocalDate.of(2026, 1, 1).plusDays(96));
157+
CoreModelServiceHolder.get().save(encounter4);
158+
status = billingService.bill(code_AA150010, encounter4, 5);
159+
assertTrue(status.getMessages().toString(), status.isOK());
160+
status = billingService.bill(code_AA150060, encounter4, 5);
161+
assertTrue(status.getMessages().toString(), status.isOK());
162+
163+
// move forward excl. 07.01.26
164+
Result<IEncounter> moveResult = EncounterServiceHolder.get().setEncounterDate(encounter3,
165+
LocalDate.of(2026, 1, 1).plusDays(101));
166+
assertTrue(moveResult.getMessages().toString(), moveResult.isOK());
167+
status = billingService.bill(code_AA150010, encounter3, 5);
168+
assertTrue(status.getMessages().toString(), status.isOK());
169+
status = billingService.bill(code_AA150060, encounter3, 5);
170+
assertTrue(status.getMessages().toString(), status.isOK());
171+
172+
// move backwards incl. 07.01.26
173+
moveResult = EncounterServiceHolder.get().setEncounterDate(encounter3, LocalDate.of(2026, 1, 1).plusDays(91));
174+
assertFalse(moveResult.getMessages().toString(), moveResult.isOK());
175+
assertTrue(encounter3.getBilled().isEmpty());
176+
}
177+
}

0 commit comments

Comments
 (0)