Skip to content

Commit 7be714c

Browse files
committed
BAH-4464 refactored code
1 parent 031e87d commit 7be714c

File tree

8 files changed

+245
-56
lines changed

8 files changed

+245
-56
lines changed

api/src/main/java/org/openmrs/module/appointments/service/AppointmentNumberGenerator.java

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,8 @@
44
import org.springframework.transaction.annotation.Transactional;
55

66
import javax.validation.constraints.NotNull;
7-
import java.util.List;
8-
import java.util.Objects;
97

108
@Transactional
119
public interface AppointmentNumberGenerator {
1210
String generateAppointmentNumber(@NotNull Appointment appointment);
13-
14-
/**
15-
* Apply appointment numbers to a batch of appointments.
16-
* Default behavior: generate one number and apply to all (shared number strategy).*
17-
* @param appointments List of appointments to apply numbers to. Null or empty lists are ignored.
18-
*/
19-
default void applyAppointmentNumbers(@NotNull List<Appointment> appointments) {
20-
if (appointments == null || appointments.isEmpty()) {
21-
return;
22-
}
23-
24-
String sharedNumber = generateAppointmentNumber(appointments.get(0));
25-
appointments.forEach(appointment -> {
26-
if (appointment.getAppointmentNumber() == null) {
27-
appointment.setAppointmentNumber(sharedNumber);
28-
}
29-
});
30-
}
3111
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package org.openmrs.module.appointments.service;
2+
3+
import org.openmrs.module.appointments.model.Appointment;
4+
import org.openmrs.module.appointments.model.AppointmentRecurringPattern;
5+
6+
import java.util.List;
7+
8+
/**
9+
* Strategy interface for applying appointment numbers to recurring appointment series.
10+
*
11+
* Implementations can define different numbering strategies:
12+
* - Shared: All appointments get the same number
13+
* - Suffix: Each appointment gets a suffix (number-1, number-2, etc.)
14+
* - Prefix: Each appointment gets a prefix (APP-001, APP-002, etc.)
15+
* - Custom: Any other custom numbering logic
16+
*/
17+
public interface RecurringAppointmentNumberingStrategy {
18+
19+
/**
20+
* Apply appointment numbers to a batch of recurring appointments.
21+
*
22+
* @param appointments List of appointments in the recurring series
23+
* @param pattern The recurring pattern these appointments belong to
24+
*/
25+
void applyAppointmentNumbers(List<Appointment> appointments,
26+
AppointmentRecurringPattern pattern);
27+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.openmrs.module.appointments.service;
2+
3+
/**
4+
* Locator for retrieving and registering RecurringAppointmentNumberingStrategy implementations.
5+
* Follows the same pattern as AppointmentNumberGeneratorLocator.
6+
*/
7+
public interface RecurringAppointmentNumberingStrategyLocator {
8+
9+
/**
10+
* Retrieve the currently registered RecurringAppointmentNumberingStrategy.
11+
*
12+
* @return the registered strategy, or null if none is registered
13+
*/
14+
RecurringAppointmentNumberingStrategy retrieveRecurringAppointmentNumberingStrategy();
15+
16+
/**
17+
* Register a new RecurringAppointmentNumberingStrategy.
18+
* This allows custom modules to override the default strategy at runtime.
19+
*
20+
* @param strategy the strategy to register (must not be null)
21+
*/
22+
void registerRecurringAppointmentNumberingStrategy(RecurringAppointmentNumberingStrategy strategy);
23+
}

api/src/main/java/org/openmrs/module/appointments/service/impl/AppointmentRecurringPatternServiceImpl.java

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import org.apache.commons.logging.Log;
44
import org.apache.commons.logging.LogFactory;
5+
import org.openmrs.api.APIException;
56
import org.openmrs.module.appointments.dao.AppointmentDao;
67
import org.openmrs.module.appointments.dao.AppointmentRecurringPatternDao;
78
import org.openmrs.module.appointments.helper.AppointmentServiceHelper;
@@ -12,6 +13,8 @@
1213
import org.openmrs.module.appointments.service.AppointmentNumberGenerator;
1314
import org.openmrs.module.appointments.service.AppointmentNumberGeneratorLocator;
1415
import org.openmrs.module.appointments.service.AppointmentRecurringPatternService;
16+
import org.openmrs.module.appointments.service.RecurringAppointmentNumberingStrategy;
17+
import org.openmrs.module.appointments.service.RecurringAppointmentNumberingStrategyLocator;
1518
import org.openmrs.module.appointments.validator.AppointmentStatusChangeValidator;
1619
import org.openmrs.module.appointments.validator.AppointmentValidator;
1720
import org.springframework.transaction.annotation.Transactional;
@@ -44,6 +47,7 @@ public class AppointmentRecurringPatternServiceImpl implements AppointmentRecurr
4447

4548
private List<AppointmentValidator> editAppointmentValidators;
4649
private AppointmentNumberGeneratorLocator appointmentNumberGeneratorLocator;
50+
private RecurringAppointmentNumberingStrategyLocator recurringAppointmentNumberingStrategyLocator;
4751

4852
private AppointmentDao appointmentDao;
4953

@@ -74,6 +78,11 @@ public void setEditAppointmentValidators(List<AppointmentValidator> editAppointm
7478
@Override
7579
public AppointmentRecurringPattern validateAndSave(AppointmentRecurringPattern appointmentRecurringPattern) {
7680
List<Appointment> appointments = new ArrayList<>(appointmentRecurringPattern.getAppointments());
81+
82+
if (appointments.isEmpty()) {
83+
throw new APIException("Cannot save a recurring pattern with no appointments");
84+
}
85+
7786
appointmentServiceHelper.validate(appointments.get(0), appointmentValidators);
7887
updateAppointmentsDetails(appointmentRecurringPattern, appointments);
7988
appointmentRecurringPatternDao.save(appointmentRecurringPattern);
@@ -90,45 +99,32 @@ public AppointmentRecurringPattern update(AppointmentRecurringPattern appointmen
9099
}
91100

92101
private void updateAppointmentsDetails(AppointmentRecurringPattern appointmentRecurringPattern, List<Appointment> appointments) {
93-
assignAppointmentNumbers(appointments);
102+
assignAppointmentNumbers(appointments, appointmentRecurringPattern);
94103

95104
appointments.forEach(appointment -> {
96105
setAppointmentAudit(appointment);
97106
appointment.setAppointmentRecurringPattern(appointmentRecurringPattern);
98107
});
99108
}
100109

101-
private void assignAppointmentNumbers(List<Appointment> appointments) {
110+
private void assignAppointmentNumbers(List<Appointment> appointments,
111+
AppointmentRecurringPattern pattern) {
102112
if (appointments.isEmpty()) {
103113
return;
104114
}
105115

106-
String existingAppointmentNumber = appointments.stream()
107-
.map(Appointment::getAppointmentNumber)
108-
.filter(number -> number != null)
109-
.findFirst()
110-
.orElse(null);
111-
112-
if (existingAppointmentNumber != null) {
113-
// All must use existing number
114-
appointments.forEach(appointment -> {
115-
if (appointment.getAppointmentNumber() == null) {
116-
appointment.setAppointmentNumber(existingAppointmentNumber);
117-
}
118-
});
119-
return;
120-
}
121-
122-
// Delegate to generator's batch method
123-
AppointmentNumberGenerator appointmentNumberGenerator =
124-
appointmentNumberGeneratorLocator.retrieveAppointmentNumberGenerator();
116+
// Delegate all numbering logic to pluggable strategy
117+
// Strategy handles both new appointments and update scenarios
118+
RecurringAppointmentNumberingStrategy strategy =
119+
recurringAppointmentNumberingStrategyLocator
120+
.retrieveRecurringAppointmentNumberingStrategy();
125121

126-
if (appointmentNumberGenerator == null) {
127-
log.warn("Can not generate appointment number. No generator found");
122+
if (strategy == null) {
123+
log.warn("Can not apply appointment numbers. No recurring appointment numbering strategy found");
128124
return;
129125
}
130126

131-
appointmentNumberGenerator.applyAppointmentNumbers(appointments);
127+
strategy.applyAppointmentNumbers(appointments, pattern);
132128
}
133129

134130
@Override
@@ -198,4 +194,9 @@ private void updateAppointmentAudits(Appointment appointment, String notes) {
198194
public void setAppointmentNumberGeneratorLocator(AppointmentNumberGeneratorLocator appointmentNumberGeneratorLocator) {
199195
this.appointmentNumberGeneratorLocator = appointmentNumberGeneratorLocator;
200196
}
197+
198+
public void setRecurringAppointmentNumberingStrategyLocator(
199+
RecurringAppointmentNumberingStrategyLocator recurringAppointmentNumberingStrategyLocator) {
200+
this.recurringAppointmentNumberingStrategyLocator = recurringAppointmentNumberingStrategyLocator;
201+
}
201202
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package org.openmrs.module.appointments.service.impl;
2+
3+
import org.apache.commons.logging.Log;
4+
import org.apache.commons.logging.LogFactory;
5+
import org.openmrs.module.appointments.model.Appointment;
6+
import org.openmrs.module.appointments.model.AppointmentRecurringPattern;
7+
import org.openmrs.module.appointments.service.AppointmentNumberGenerator;
8+
import org.openmrs.module.appointments.service.AppointmentNumberGeneratorLocator;
9+
import org.openmrs.module.appointments.service.RecurringAppointmentNumberingStrategy;
10+
import java.util.List;
11+
12+
public class DefaultRecurringAppointmentNumberingStrategyImpl
13+
implements RecurringAppointmentNumberingStrategy {
14+
15+
private Log log = LogFactory.getLog(this.getClass());
16+
17+
private AppointmentNumberGeneratorLocator appointmentNumberGeneratorLocator;
18+
19+
public DefaultRecurringAppointmentNumberingStrategyImpl(
20+
AppointmentNumberGeneratorLocator appointmentNumberGeneratorLocator) {
21+
this.appointmentNumberGeneratorLocator = appointmentNumberGeneratorLocator;
22+
}
23+
24+
@Override
25+
public void applyAppointmentNumbers(List<Appointment> appointments,
26+
AppointmentRecurringPattern pattern) {
27+
if (appointments == null || appointments.isEmpty()) {
28+
return;
29+
}
30+
31+
// Check if this is an update scenario (appointments already have numbers)
32+
String existingNumber = appointments.stream()
33+
.map(Appointment::getAppointmentNumber)
34+
.filter(number -> number != null)
35+
.findFirst()
36+
.orElse(null);
37+
38+
if (existingNumber != null) {
39+
// Update scenario - reuse existing number for any new appointments
40+
appointments.forEach(appointment -> {
41+
if (appointment.getAppointmentNumber() == null) {
42+
appointment.setAppointmentNumber(existingNumber);
43+
}
44+
});
45+
return;
46+
}
47+
48+
// New series scenario - generate shared number
49+
AppointmentNumberGenerator appointmentNumberGenerator =
50+
appointmentNumberGeneratorLocator.retrieveAppointmentNumberGenerator();
51+
52+
if (appointmentNumberGenerator == null) {
53+
log.warn("Can not generate appointment number. No generator found");
54+
return;
55+
}
56+
57+
String sharedNumber = appointmentNumberGenerator
58+
.generateAppointmentNumber(appointments.get(0));
59+
60+
// Apply the same number to all appointments in the series
61+
appointments.forEach(appointment -> {
62+
if (appointment.getAppointmentNumber() == null) {
63+
appointment.setAppointmentNumber(sharedNumber);
64+
}
65+
});
66+
}
67+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.openmrs.module.appointments.service.impl;
2+
3+
import org.openmrs.module.appointments.service.RecurringAppointmentNumberingStrategy;
4+
import org.openmrs.module.appointments.service.RecurringAppointmentNumberingStrategyLocator;
5+
6+
/**
7+
* Implementation of RecurringAppointmentNumberingStrategyLocator.
8+
* Follows the same pattern as AppointmentNumberGeneratorLocatorImpl.
9+
*
10+
* This allows custom modules to register their own recurring appointment numbering strategies.
11+
*/
12+
public class RecurringAppointmentNumberingStrategyLocatorImpl
13+
implements RecurringAppointmentNumberingStrategyLocator {
14+
15+
private RecurringAppointmentNumberingStrategy recurringAppointmentNumberingStrategy;
16+
17+
/**
18+
* Constructor injection - same pattern as AppointmentNumberGeneratorLocatorImpl.
19+
*
20+
* @param recurringAppointmentNumberingStrategy the default strategy implementation
21+
*/
22+
public RecurringAppointmentNumberingStrategyLocatorImpl(
23+
RecurringAppointmentNumberingStrategy recurringAppointmentNumberingStrategy) {
24+
this.recurringAppointmentNumberingStrategy = recurringAppointmentNumberingStrategy;
25+
}
26+
27+
@Override
28+
public RecurringAppointmentNumberingStrategy retrieveRecurringAppointmentNumberingStrategy() {
29+
return recurringAppointmentNumberingStrategy;
30+
}
31+
32+
@Override
33+
public void registerRecurringAppointmentNumberingStrategy(
34+
RecurringAppointmentNumberingStrategy strategy) {
35+
// Null check - same as AppointmentNumberGeneratorLocatorImpl
36+
if (strategy != null) {
37+
this.recurringAppointmentNumberingStrategy = strategy;
38+
}
39+
}
40+
}

api/src/main/resources/moduleApplicationContext.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@
3030
<constructor-arg name="appointmentNumberGenerator" ref="appointmentNumberGenerator"/>
3131
</bean>
3232

33+
<bean id="defaultRecurringAppointmentNumberingStrategy"
34+
class="org.openmrs.module.appointments.service.impl.DefaultRecurringAppointmentNumberingStrategyImpl">
35+
<constructor-arg name="appointmentNumberGeneratorLocator" ref="appointmentNumberGeneratorLocator"/>
36+
</bean>
37+
38+
<bean id="recurringAppointmentNumberingStrategyLocator"
39+
class="org.openmrs.module.appointments.service.impl.RecurringAppointmentNumberingStrategyLocatorImpl">
40+
<constructor-arg name="recurringAppointmentNumberingStrategy" ref="defaultRecurringAppointmentNumberingStrategy"/>
41+
</bean>
42+
3343
<bean id="appointmentServiceHelper" class="org.openmrs.module.appointments.helper.AppointmentServiceHelper"/>
3444
<bean id="defaultTCApptMailSender" class="org.openmrs.module.appointments.notification.impl.DefaultMailSender">
3545
<constructor-arg ref="adminService"/>
@@ -206,6 +216,7 @@
206216
<ref bean="appointmentDao"/>
207217
</property>
208218
<property name="appointmentNumberGeneratorLocator" ref="appointmentNumberGeneratorLocator"/>
219+
<property name="recurringAppointmentNumberingStrategyLocator" ref="recurringAppointmentNumberingStrategyLocator"/>
209220
</bean>
210221
</property>
211222
<property name="preInterceptors">

0 commit comments

Comments
 (0)