PREPARATION PHASE
├─ T1: Create Feature Branch
│
INFRASTRUCTURE PHASE (Can run in parallel after T1)
├─ T2: Create Repository Interface (depends: T1)
├─ T3: Create Service Interface (depends: T1)
├─ T4: Create Service Implementation (depends: T2, T3)
│
MAPPER PHASE (Can run in parallel after T1)
├─ T5: Check/Create DateTimeIntervalDto (depends: T1)
├─ T6: Check/Create DateTimeIntervalMapper (depends: T5)
│
ENTITY PHASE (Can run in parallel after T1)
├─ T7: Update CustomerAgreementEntity (depends: T1)
│ ├─ T7a: Add missing Document fields
│ ├─ T7b: Reorder all fields per XSD
│ ├─ T7c: Fix futureStatus type
│ ├─ T7d: Add @AttributeOverride
│ └─ T7e: Update equals/hashCode
│
DTO PHASE (Can start after T5)
├─ T8: Rewrite CustomerAgreementDto (depends: T5, T7)
│ ├─ T8a: Remove ALL Atom fields
│ ├─ T8b: Remove relationship DTOs
│ ├─ T8c: Add Document fields (9)
│ ├─ T8d: Add Agreement fields (2)
│ └─ T8e: Add CustomerAgreement fields (6)
│
MAPPER UPDATE PHASE (Can start after T6, T8)
├─ T9: Update CustomerAgreementMapper (depends: T6, T8)
│
DATABASE PHASE (Can run in parallel, but sync before testing)
├─ T10: Update Flyway Migration (depends: T7)
│
TESTING PHASE (Can only start after all above complete)
├─ T11: Create CustomerAgreementDtoTest (depends: T8, T9)
├─ T12: Create CustomerAgreementRepositoryTest (depends: T2, T4, T7, T10)
├─ T13: Run All Tests (depends: T11, T12)
│
QUALITY PHASE
├─ T14: Fix SonarQube Violations (depends: T13)
├─ T15: Run Integration Tests (depends: T14)
│
DELIVERY PHASE
├─ T16: Commit and Push (depends: T15)
├─ T17: Create Pull Request (depends: T16)
└─ T18: Update Issue #28 (depends: T17)
Dependencies: None Complexity: Low Estimated Time: 2 minutes
Commands:
git checkout main
git pull origin main
git checkout -b feature/schema-compliance-phase-24-customer-agreementVerification:
git branch --show-current # Should show: feature/schema-compliance-phase-24-customer-agreementDependencies: T1
Complexity: Low
Estimated Time: 5 minutes
File: openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/customer/CustomerAgreementRepository.java
Actions:
- Create new file CustomerAgreementRepository.java
- Extend JpaRepository<CustomerAgreementEntity, UUID>
- Add @Repository annotation
- NO custom query methods (use only inherited methods)
Template:
@Repository
public interface CustomerAgreementRepository extends JpaRepository<CustomerAgreementEntity, UUID> {
// Use only inherited methods to avoid H2 keyword conflicts
}Verification:
- File compiles successfully
- No custom query methods defined
Dependencies: T1
Complexity: Low
Estimated Time: 10 minutes
File: openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/customer/CustomerAgreementService.java
Actions:
- Create new file CustomerAgreementService.java
- Define 6 essential methods:
- save(CustomerAgreementEntity)
- findById(UUID)
- findAll()
- deleteById(UUID)
- existsById(UUID)
- count()
Verification:
- File compiles successfully
- Only essential CRUD methods defined
Dependencies: T2, T3
Complexity: Medium
Estimated Time: 20 minutes
File: openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/customer/impl/CustomerAgreementServiceImpl.java
Actions:
- Create new file CustomerAgreementServiceImpl.java
- Add @Service, @RequiredArgsConstructor, @Slf4j annotations
- Inject CustomerAgreementRepository
- Inject EspiIdGeneratorService
- Implement all 6 methods from interface
- Add UUID v5 generation in save() method
- Namespace: "ESPI-CUSTOMER-AGREEMENT"
- Seed: agreementId or random UUID
Verification:
- File compiles successfully
- All interface methods implemented
- UUID v5 generation working
- Logging statements present
Dependencies: T1
Complexity: Low-Medium
Estimated Time: 15 minutes
File: openespi-common/src/main/java/org/greenbuttonalliance/espi/common/dto/DateTimeIntervalDto.java (if missing)
Actions:
- Check if DateTimeIntervalDto exists in common DTOs
- If missing, create with fields: start (OffsetDateTime), duration (Long)
- Add proper JAXB annotations matching usage.xsd DateTimeInterval
- Use appropriate namespace (likely espi namespace)
Verification:
- DTO compiles successfully
- Fields match usage.xsd DateTimeInterval type
- Proper JAXB annotations present
Dependencies: T5
Complexity: Low
Estimated Time: 10 minutes
File: openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/DateTimeIntervalMapper.java (if missing)
Actions:
- Check if DateTimeIntervalMapper exists
- If missing, create MapStruct mapper
- Map DateTimeInterval (entity) ↔ DateTimeIntervalDto
- Add @Mapper(componentModel = "spring")
Verification:
- Mapper compiles successfully
- MapStruct generates implementation
- Bidirectional mapping works
Dependencies: T1
Complexity: High
Estimated Time: 45 minutes
File: openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/customer/entity/CustomerAgreementEntity.java
Sub-tasks:
Actions:
- Add
typefield (move from line 85 to after class declaration) - Add
authorNamefield (NEW) - Add
electronicAddressembedded field (NEW) - Organisation.ElectronicAddress - Add
docStatusembedded field (NEW) - Status
New Code:
// Document fields - FIRST section
@Column(name = "document_type", length = 256)
private String type; // MOVED from line 85
@Column(name = "author_name", length = 256)
private String authorName; // NEW
// createdDateTime (already exists)
// lastModifiedDateTime (already exists)
// revisionNumber (already exists)
@Embedded
private Organisation.ElectronicAddress electronicAddress; // NEW
// subject (already exists)
// title (already exists)
@Embedded
private Status docStatus; // NEWActions:
-
Reorder to match XSD sequence:
- Document fields (9): type, authorName, createdDateTime, lastModifiedDateTime, revisionNumber, electronicAddress, subject, title, docStatus
- Agreement fields (2): signDate, validityInterval
- CustomerAgreement fields (6): loadMgmt, isPrePay, shutOffDateTime, currency, futureStatus, agreementId
-
Update JavaDoc comments to reflect new order
Actions:
- Change from
List<CustomerEntity.Status>toList<Status> - Apply @AttributeOverride annotations DIRECTLY (no wrapper):
@ElementCollection(fetch = FetchType.LAZY) @CollectionTable(name = "customer_agreement_future_status", joinColumns = @JoinColumn(name = "customer_agreement_id")) @AttributeOverride(name = "value", column = @Column(name = "status_value")) @AttributeOverride(name = "dateTime", column = @Column(name = "status_date_time")) @AttributeOverride(name = "reason", column = @Column(name = "status_reason")) private List<Status> futureStatus;
Actions:
- Add @AttributeOverride annotations directly on class (no wrapper):
@Entity @Table(name = "customer_agreements") @AttributeOverride(name = "upLink.rel", column = @Column(name = "customer_agreement_up_link_rel")) @AttributeOverride(name = "upLink.href", column = @Column(name = "customer_agreement_up_link_href")) @AttributeOverride(name = "upLink.type", column = @Column(name = "customer_agreement_up_link_type")) @AttributeOverride(name = "selfLink.rel", column = @Column(name = "customer_agreement_self_link_rel")) @AttributeOverride(name = "selfLink.href", column = @Column(name = "customer_agreement_self_link_href")) @AttributeOverride(name = "selfLink.type", column = @Column(name = "customer_agreement_self_link_type"))
Actions:
- Use instanceof with pattern variables:
Class<?> oEffectiveClass = o instanceof HibernateProxy hibernateProxy ? hibernateProxy.getHibernateLazyInitializer().getPersistentClass() : o.getClass(); Class<?> thisEffectiveClass = this instanceof HibernateProxy hibernateProxy ? hibernateProxy.getHibernateLazyInitializer().getPersistentClass() : this.getClass();
Verification:
- Entity compiles successfully
- All 17 fields present in correct order
- @AttributeOverride applied directly (no wrapper)
- equals/hashCode use pattern matching
- toString() includes all new fields
Dependencies: T5 (DateTimeIntervalDto), T7 (Entity updates for reference)
Complexity: High
Estimated Time: 60 minutes
File: openespi-common/src/main/java/org/greenbuttonalliance/espi/common/dto/customer/CustomerAgreementDto.java
Sub-tasks:
Actions - DELETE these fields:
❌ private Long id; // Line 53
❌ private OffsetDateTime published; // Line 59
❌ private OffsetDateTime updated; // Line 62
❌ private List<LinkDto> relatedLinks; // Lines 64-66
❌ private LinkDto selfLink; // Lines 68-69
❌ private LinkDto upLink; // Lines 71-72
❌ private String description; // Lines 74-75
❌ public String getSelfHref() {...} // Lines 107-109
❌ public String getUpHref() {...} // Lines 116-118
❌ public String generateSelfHref() {...} // Lines 125-130
❌ public String generateUpHref() {...} // Lines 137-142Actions - DELETE these fields:
❌ private CustomerAccountDto customerAccount; // Lines 83-84
❌ private List<ServiceLocationDto> serviceLocations; // Lines 86-88
❌ private List<StatementDto> statements; // Lines 90-92Actions - ADD these fields in order:
// 1. type
@XmlElement(name = "type", namespace = "http://naesb.org/espi/customer")
private String type;
// 2. authorName
@XmlElement(name = "authorName", namespace = "http://naesb.org/espi/customer")
private String authorName;
// 3. createdDateTime
@XmlElement(name = "createdDateTime", namespace = "http://naesb.org/espi/customer")
private OffsetDateTime createdDateTime;
// 4. lastModifiedDateTime
@XmlElement(name = "lastModifiedDateTime", namespace = "http://naesb.org/espi/customer")
private OffsetDateTime lastModifiedDateTime;
// 5. revisionNumber
@XmlElement(name = "revisionNumber", namespace = "http://naesb.org/espi/customer")
private String revisionNumber;
// 6. electronicAddress
@XmlElement(name = "electronicAddress", namespace = "http://naesb.org/espi/customer")
private CustomerDto.ElectronicAddressDto electronicAddress;
// 7. subject
@XmlElement(name = "subject", namespace = "http://naesb.org/espi/customer")
private String subject;
// 8. title
@XmlElement(name = "title", namespace = "http://naesb.org/espi/customer")
private String title;
// 9. docStatus
@XmlElement(name = "docStatus", namespace = "http://naesb.org/espi/customer")
private CustomerAccountDto.StatusDto docStatus;Actions - ADD these fields:
// 10. signDate (already exists, verify annotation)
@XmlElement(name = "signDate", namespace = "http://naesb.org/espi/customer")
private OffsetDateTime signDate;
// 11. validityInterval - CHANGE TYPE from String to DateTimeIntervalDto
@XmlElement(name = "validityInterval", namespace = "http://naesb.org/espi/customer")
private DateTimeIntervalDto validityInterval; // WAS: StringActions - ADD these fields:
// 12. loadMgmt
@XmlElement(name = "loadMgmt", namespace = "http://naesb.org/espi/customer")
private String loadMgmt;
// 13. isPrePay
@XmlElement(name = "isPrePay", namespace = "http://naesb.org/espi/customer")
private Boolean isPrePay;
// 14. shutOffDateTime
@XmlElement(name = "shutOffDateTime", namespace = "http://naesb.org/espi/customer")
private OffsetDateTime shutOffDateTime;
// 15. currency
@XmlElement(name = "currency", namespace = "http://naesb.org/espi/customer")
private String currency;
// 16. futureStatus
@XmlElement(name = "Status", namespace = "http://naesb.org/espi/customer")
@XmlElementWrapper(name = "futureStatus", namespace = "http://naesb.org/espi/customer")
private List<CustomerAccountDto.StatusDto> futureStatus;
// 17. agreementId
@XmlElement(name = "agreementId", namespace = "http://naesb.org/espi/customer")
private String agreementId;Update @XmlType propOrder:
@XmlType(name = "CustomerAgreement", namespace = "http://naesb.org/espi/customer", propOrder = {
"type", "authorName", "createdDateTime", "lastModifiedDateTime", "revisionNumber",
"electronicAddress", "subject", "title", "docStatus",
"signDate", "validityInterval",
"loadMgmt", "isPrePay", "shutOffDateTime", "currency", "futureStatus", "agreementId"
})Update Constructor(s):
@AllArgsConstructor // Will generate constructor for all 18 fields (uuid + 17 XSD fields)Verification:
- DTO compiles successfully
- NO Atom fields present
- NO relationship DTOs present
- All 17 XSD fields present in correct order
- propOrder matches field order exactly
- All fields use customer namespace
Dependencies: T6 (DateTimeIntervalMapper), T8 (DTO rewrite)
Complexity: Medium
Estimated Time: 30 minutes
File: openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/customer/CustomerAgreementMapper.java
Actions:
-
Update @Mapper uses clause:
@Mapper(componentModel = "spring", uses = { StatusMapper.class, // From Phase 18 ElectronicAddressMapper.class, // From Phase 18 DateTimeIntervalMapper.class // From T6 })
-
Add/Update all 17 field mappings:
@Mapping(target = "uuid", source = "id") // Document fields (9) @Mapping(target = "type", source = "type") @Mapping(target = "authorName", source = "authorName") @Mapping(target = "createdDateTime", source = "createdDateTime") @Mapping(target = "lastModifiedDateTime", source = "lastModifiedDateTime") @Mapping(target = "revisionNumber", source = "revisionNumber") @Mapping(target = "electronicAddress", source = "electronicAddress") @Mapping(target = "subject", source = "subject") @Mapping(target = "title", source = "title") @Mapping(target = "docStatus", source = "docStatus") // Agreement fields (2) @Mapping(target = "signDate", source = "signDate") @Mapping(target = "validityInterval", source = "validityInterval") // CustomerAgreement fields (6) @Mapping(target = "loadMgmt", source = "loadMgmt") @Mapping(target = "isPrePay", source = "isPrePay") @Mapping(target = "shutOffDateTime", source = "shutOffDateTime") @Mapping(target = "currency", source = "currency") @Mapping(target = "futureStatus", source = "futureStatus") @Mapping(target = "agreementId", source = "agreementId") CustomerAgreementDto toDto(CustomerAgreementEntity entity); @InheritInverseConfiguration CustomerAgreementEntity toEntity(CustomerAgreementDto dto);
-
Remove any old mappings for deleted fields (published, updated, links, description, etc.)
Verification:
- Mapper compiles successfully
- MapStruct generates implementation without warnings
- All 17 fields mapped bidirectionally
- No unmapped target warnings for IdentifiedObject fields
Dependencies: T7 (Entity updates)
Complexity: Medium
Estimated Time: 20 minutes
File: openespi-common/src/main/resources/db/migration/V3__Create_additiional_Base_Tables.sql
Actions:
-
Add missing Document field columns to customer_agreements table:
-- Add missing Document fields (3 new columns) ALTER TABLE customer_agreements ADD COLUMN IF NOT EXISTS document_type VARCHAR(256); ALTER TABLE customer_agreements ADD COLUMN IF NOT EXISTS author_name VARCHAR(256); -- Add Document.docStatus embedded fields (3 new columns) ALTER TABLE customer_agreements ADD COLUMN IF NOT EXISTS status_value VARCHAR(256); ALTER TABLE customer_agreements ADD COLUMN IF NOT EXISTS status_date_time TIMESTAMP WITH TIME ZONE; ALTER TABLE customer_agreements ADD COLUMN IF NOT EXISTS status_reason VARCHAR(512); -- Add Document.electronicAddress embedded fields (4 new columns) ALTER TABLE customer_agreements ADD COLUMN IF NOT EXISTS email1 VARCHAR(256); ALTER TABLE customer_agreements ADD COLUMN IF NOT EXISTS email2 VARCHAR(256); ALTER TABLE customer_agreements ADD COLUMN IF NOT EXISTS web VARCHAR(256); ALTER TABLE customer_agreements ADD COLUMN IF NOT EXISTS radio VARCHAR(256);
-
Verify column order comments match new XSD order:
-- Expected column order (matching customer.xsd): -- Document fields: document_type, author_name, created_date_time, last_modified_date_time, -- revision_number, email1, email2, web, radio, subject, title, -- status_value, status_date_time, status_reason -- Agreement fields: sign_date, validity_interval_start, validity_interval_duration -- CustomerAgreement: load_mgmt, is_pre_pay, shut_off_date_time, currency, agreement_id
Verification:
- SQL script has no syntax errors
- All 10 new columns added (3 Document + 3 Status + 4 ElectronicAddress)
- H2, MySQL, PostgreSQL compatible syntax used
Dependencies: T8 (DTO rewrite), T9 (Mapper update)
Complexity: Medium
Estimated Time: 45 minutes
File: openespi-common/src/test/java/org/greenbuttonalliance/espi/common/dto/customer/CustomerAgreementDtoTest.java
Actions:
- Create test class following Phase 18 CustomerAccountDtoTest pattern
- Add @DisplayName("CustomerAgreementDto XML Marshalling Tests")
- Setup DtoExportServiceImpl in @BeforeEach
Test 1: shouldExportCustomerAgreementWithCompleteFields
@Test
@DisplayName("Should export CustomerAgreement with complete Document/Agreement fields")
void shouldExportCustomerAgreementWithCompleteFields() {
// Create DTO with all 17 fields populated
// Wrap in CustomerAtomEntryDto and AtomFeedDto
// Export to XML
// Assert ALL 17 fields present in correct customer namespace
// Assert NO Atom fields inside <cust:CustomerAgreement>
}Test 2: shouldVerifyCustomerAgreementFieldOrder
@Test
@DisplayName("Should verify CustomerAgreement field order matches customer.xsd")
void shouldVerifyCustomerAgreementFieldOrder() {
// Create DTO with all fields
// Export to XML
// Assert Document field order (9 fields)
// Assert Agreement field order (2 fields)
// Assert CustomerAgreement field order (6 fields)
// Use indexOf() to verify sequence matches XSD
}Test 3: shouldUseCorrectCustomerNamespace
@Test
@DisplayName("Should use correct customer namespace")
void shouldUseCorrectCustomerNamespace() {
// Create minimal DTO
// Export to XML
// Assert xmlns:cust="http://naesb.org/espi/customer"
// Assert <cust:CustomerAgreement> present
// Assert NO espi: namespace for CustomerAgreement
}SonarQube Best Practices:
- ✅ NO throws IOException on test methods
- ✅ Chain assertions using fluent API
- ✅ Use meaningful test data
Verification:
- All 3 tests pass
- XML validates against customer.xsd structure
- Field order correct
- Customer namespace used
Dependencies: T2 (Repository), T4 (Service), T7 (Entity), T10 (Migration)
Complexity: High
Estimated Time: 90 minutes
File: openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/customer/CustomerAgreementRepositoryTest.java
Actions:
- Create test class following Phase 18 CustomerAccountRepositoryTest pattern
- Extend BaseRepositoryTest
- Add @Autowired CustomerAgreementRepository
- Create helper methods: createValidCustomerAgreement()
Test Structure (21+ tests in 5 nested classes):
@Nested
@DisplayName("CRUD Operations")
class CrudOperationsTest {
@Test void shouldSaveAndRetrieveCustomerAgreementSuccessfully()
@Test void shouldUpdateCustomerAgreementSuccessfully()
@Test void shouldDeleteCustomerAgreementSuccessfully()
@Test void shouldFindAllCustomerAgreements()
@Test void shouldCheckIfCustomerAgreementExists()
@Test void shouldCountCustomerAgreementsCorrectly()
@Test void shouldHandleNullOptionalFields()
}@Nested
@DisplayName("Document Field Persistence")
class DocumentFieldPersistenceTest {
@Test void shouldPersistAllDocumentBaseFieldsCorrectly()
@Test void shouldPersistDocumentElectronicAddressEmbeddedObject()
@Test void shouldPersistDocumentDocStatusEmbeddedObject()
}@Nested
@DisplayName("Agreement Field Persistence")
class AgreementFieldPersistenceTest {
@Test void shouldPersistSignDateFieldCorrectly()
@Test void shouldPersistValidityIntervalEmbeddedObject()
}@Nested
@DisplayName("CustomerAgreement Field Persistence")
class CustomerAgreementFieldPersistenceTest {
@Test void shouldPersistAllCustomerAgreementSpecificFieldsCorrectly()
@Test void shouldPersistFutureStatusCollectionCorrectly()
@Test void shouldHandleNullOptionalFieldsCorrectly()
@Test void shouldPersistCurrencyFieldCorrectly()
}@Nested
@DisplayName("Base Class Functionality")
class BaseClassTest {
@Test void shouldInheritIdentifiedObjectFunctionality()
@Test void shouldUpdateTimestampsOnModification()
@Test void shouldGenerateUniqueIdsForDifferentEntities()
@Test void shouldHandleEqualsAndHashCodeCorrectly()
@Test void shouldGenerateMeaningfulToStringRepresentation()
}SonarQube Best Practices:
- ✅ NO Thread.sleep() - remove from timestamp tests
- ✅ NO empty catch blocks
- ✅ Chain assertions using fluent API
- ✅ Use hasSameHashCodeAs() for hashCode tests
- ✅ NO unused variables
Verification:
- All 21+ tests pass
- Tests run on H2 in-memory database
- All CRUD operations work
- All fields persist correctly
- Embedded objects work
- Collections persist
Dependencies: T11 (DTO tests), T12 (Repository tests) Complexity: Low Estimated Time: 5 minutes
Actions:
# Run only CustomerAgreement tests
mvn test -pl openespi-common -Dtest=CustomerAgreementDtoTest,CustomerAgreementRepositoryTest
# Run all tests in openespi-common
mvn test -pl openespi-common
# Run all tests in entire project
mvn testVerification:
- All 609+ existing tests pass
- All 24+ new CustomerAgreement tests pass
- No test failures
- No compilation errors
Dependencies: T13 (All tests passing) Complexity: Medium Estimated Time: 30 minutes
Actions:
-
Run SonarQube analysis (if available) or manual code review
-
Check for common violations:
- ❌ Unnecessary IOException throws
- ❌ Multiple separate assertions (should chain)
- ❌ @AttributeOverrides wrapper (should apply directly)
- ❌ Non-serializable embedded objects
- ❌ Old instanceof patterns (should use pattern matching)
- ❌ Unused variables
- ❌ Thread.sleep() usage
- ❌ Empty catch blocks
- ❌ Commented-out code
- ❌ Manual hashCode comparisons (should use hasSameHashCodeAs)
-
Fix any violations found
-
Re-run tests after each fix
Verification:
- Zero SonarQube violations
- All tests still pass
- Code follows Phase 18 quality standards
Dependencies: T14 (SonarQube clean) Complexity: Medium Estimated Time: 10 minutes
Actions:
# Run integration tests with TestContainers
mvn verify -pl openespi-common
# This will test against:
# - H2 (in-memory)
# - MySQL 9.5 (TestContainer)
# - PostgreSQL 18 (TestContainer)Verification:
- All integration tests pass on H2
- All integration tests pass on MySQL 9.5
- All integration tests pass on PostgreSQL 18
- TestContainers start and stop cleanly
- No database-specific errors
Dependencies: T15 (Integration tests pass) Complexity: Low Estimated Time: 10 minutes
Actions:
-
Stage all modified and new files:
git add openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/customer/entity/CustomerAgreementEntity.java git add openespi-common/src/main/java/org/greenbuttonalliance/espi/common/dto/customer/CustomerAgreementDto.java git add openespi-common/src/main/java/org/greenbuttonalliance/espi/common/dto/DateTimeIntervalDto.java # if new git add openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/customer/CustomerAgreementMapper.java git add openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/DateTimeIntervalMapper.java # if new git add openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/customer/CustomerAgreementRepository.java git add openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/customer/CustomerAgreementService.java git add openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/customer/impl/CustomerAgreementServiceImpl.java git add openespi-common/src/main/resources/db/migration/V3__Create_additiional_Base_Tables.sql git add openespi-common/src/test/java/org/greenbuttonalliance/espi/common/dto/customer/CustomerAgreementDtoTest.java git add openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/customer/CustomerAgreementRepositoryTest.java
-
Commit with detailed message:
git commit -m "feat: ESPI 4.0 Schema Compliance - Phase 24: CustomerAgreement Implementation Implement complete CustomerAgreement ESPI 4.0 schema compliance with Document and Agreement base class fields, embedded objects, and comprehensive testing. <Full commit message from plan> "
-
Push to remote:
git push -u origin feature/schema-compliance-phase-24-customer-agreement
Verification:
- All files committed
- Commit message follows convention
- Branch pushed to remote
- No uncommitted changes remain
Dependencies: T16 (Code pushed) Complexity: Low Estimated Time: 15 minutes
Actions:
gh pr create \
--base main \
--head feature/schema-compliance-phase-24-customer-agreement \
--title "feat: ESPI 4.0 Schema Compliance - Phase 24: CustomerAgreement Implementation" \
--body "$(cat <<'EOF'
## Summary
Implement complete CustomerAgreement ESPI 4.0 schema compliance...
<Full PR description with:>
- Key achievements
- Changes overview
- SonarQube fixes
- Test results
- ESPI 4.0 compliance
- Breaking changes (if any)
- Checklist
EOF
)"Verification:
- PR created successfully
- PR description complete
- Base branch is main
- CI/CD checks start automatically
Dependencies: T17 (PR created) Complexity: Low Estimated Time: 5 minutes
Actions:
gh issue comment 28 --body "$(cat <<'EOF'
## Phase 24: CustomerAgreement Implementation - ✅ COMPLETED
**Pull Request:** #XX
**Branch:** feature/schema-compliance-phase-24-customer-agreement
**Status:** Ready for review
### Summary
Completed full ESPI 4.0 schema compliance implementation for CustomerAgreement...
### Implementation Details
<Details from plan>
### Test Results
- ✅ All 633+ tests passing (24 new CustomerAgreement tests)
- ✅ Zero SonarQube violations
- ✅ Integration tests pass (H2, MySQL, PostgreSQL)
EOF
)"Verification:
- Comment posted to Issue #28
- Issue remains OPEN (not closed)
- PR link included
Phase 1: Quick Wins (Parallel - 30 minutes total)
- Start all infrastructure tasks: T2, T3, T4 (can work in parallel)
- Start mapper phase: T5, T6 (can work in parallel)
Phase 2: Core Implementation (Sequential - 90 minutes total)
- T7: Update Entity (must complete before T8)
- T8: Rewrite DTO (needs T5, T7)
- T9: Update Mapper (needs T6, T8)
- T10: Update Migration (needs T7, can parallel with T8/T9)
Phase 3: Testing (Sequential - 140 minutes total)
- T11: Create DTO Test (needs T8, T9)
- T12: Create Repository Test (needs T2, T4, T7, T10)
- T13: Run All Tests (needs T11, T12)
Phase 4: Quality & Delivery (Sequential - 70 minutes total)
- T14: Fix SonarQube (needs T13)
- T15: Integration Tests (needs T14)
- T16: Commit & Push (needs T15)
- T17: Create PR (needs T16)
- T18: Update Issue (needs T17)
- Minimum: 5.5 hours (if everything goes perfectly)
- Expected: 7-8 hours (with normal debugging/fixes)
- Maximum: 10-12 hours (if major issues encountered)
T1 → T7 → T8 → T9 → T11 → T12 → T13 → T14 → T15 → T16 → T17 → T18
- T2, T3, T5 can run in parallel after T1
- T6 can start immediately after T5
- T4 can start after T2 and T3 complete
- T10 can run in parallel with T8/T9
- All tests (T11, T12) need sequential execution
- T7: Entity reordering - complex, many fields, easy to make mistakes
- T8: DTO rewrite - must remove ALL Atom fields correctly
- T12: Repository tests - 21+ tests, lots of edge cases
- T9: Mapper updates - MapStruct unmapped field warnings
- T10: Migration - SQL syntax differences between databases
- T14: SonarQube - may discover unexpected violations
- T2, T3, T4: Infrastructure - straightforward CRUD
- T5, T6: DateTimeInterval - may already exist
- T16-T18: Delivery - mechanical steps
- T1: Feature branch created
- T2: Repository interface created
- T3: Service interface created
- T4: Service implementation created with UUID v5
- T5: DateTimeIntervalDto exists/created
- T6: DateTimeIntervalMapper exists/created
- T7a-e: Entity updated with all fields in correct order
- T8a-e: DTO rewritten with ONLY XSD fields (NO Atom fields)
- T9: Mapper updated with all 17 field mappings
- T10: Migration adds 10 new columns
- T11: 3 DTO tests created and passing
- T12: 21+ repository tests created and passing
- T13: All 633+ tests passing
- T14: Zero SonarQube violations
- T15: Integration tests pass on all databases
- T16: Code committed and pushed
- T17: Pull request created
- T18: Issue #28 updated
CustomerAgreementEntity.java- Add fields, reorder, fix typesCustomerAgreementDto.java- Complete rewrite, remove Atom fieldsV3__Create_additiional_Base_Tables.sql- Add 10 columns
CustomerAgreementRepository.java- NEWCustomerAgreementService.java- NEWCustomerAgreementServiceImpl.java- NEWDateTimeIntervalDto.java- NEW (if not exists)DateTimeIntervalMapper.java- NEW (if not exists)CustomerAgreementDtoTest.java- NEWCustomerAgreementRepositoryTest.java- NEWCustomerAgreementMapper.java- MODIFIED (update mappings)
Plan Created: 2026-01-25 Ready for Execution: Yes Estimated Completion: 7-8 hours of focused work