Skip to content

Commit 8e519f7

Browse files
dfcoffinclaude
andauthored
refactor: Phase 10 - LineItem ESPI 4.0 schema compliance (#76)
Convert LineItem from UUID to Long ID with IDENTITY generation per ESPI 4.0 XSD (espi.xsd:1449). LineItem extends Object, not IdentifiedObject, requiring Long ID with vendor-specific auto-increment syntax. Changes: - LineItemEntity: Change ID from UUID to Long with GenerationType.IDENTITY - Add missing XSD fields: measurement (SummaryMeasurement), itemKind (required), unitCost, itemPeriod (DateTimeInterval) - LineItemRepository: Change from JpaRepository<LineItemEntity, UUID> to Long - Remove 10 non-indexed queries, keep 2 indexed queries - Create LineItemDto record matching XSD structure (no id/uuid fields) - Create LineItemMapper interface with toDto/toEntity mappings - Create SummaryMeasurementMapper for measurement field mapping - LineItemRepositoryTest: Complete rewrite with comprehensive coverage (CRUD, custom queries, relationships, business logic, persistence) - Move usage_summaries + usage_summary_related_links tables from V3 to V2 vendor-specific migrations to maintain dependency order (line_items FK requires usage_summaries to exist first) - Fix UsageSummaryRepositoryTest to include required itemKind field Database migrations: - Add line_items table to V2 vendor-specific files with auto-increment (BIGINT AUTO_INCREMENT for MySQL/H2, BIGSERIAL for PostgreSQL) - Move usage_summaries from V3 to V2 (before line_items) to resolve Flyway migration order issue All 533 tests pass. Co-authored-by: Claude Sonnet 4.5 <[email protected]>
1 parent cd53b6d commit 8e519f7

File tree

16 files changed

+1278
-420
lines changed

16 files changed

+1278
-420
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
description: Generate and create commit from staged changes
3+
allowed-tools: Bash(git add:*), Bash(git diff:*), Bash(git log:*), Bash(git status:*), Bash(git commit:*)
4+
model: haiku
5+
---
6+
7+
## Context
8+
- Current branch: !`git branch --show-current`
9+
- Commits on this branch: !`git log main..HEAD --oneline`
10+
- Full diff from main: !`git diff main...HEAD --stat`
11+
## Task
12+
Generate a pull request description that includes:
13+
1. **Summary** - One paragraph explaining what this PR does
14+
2. **Changes** - Bullet list of key changes based on commits
15+
3. **Testing** - How to verify these changes work
16+
Use the commit messages to understand intent. Keep the description concise but complete.
17+
After generating, ask if I want to create the PR with `gh pr create`.
18+
19+
## Context
20+
- Current git status: !`git status`
21+
- Staged changes: !`git diff --cached`
22+
- Unstaged changes: !`git diff`
23+
- Recent commits for style: !`git log --oneline -10`
24+
## Task
25+
Based on the staged changes, generate a conventional commit message.
26+
Format: `type(scope): description`
27+
Types: feat, fix, docs, style, refactor, test, chore
28+
Match the commit style used in this repository's recent history.
29+
After generating the message, ask if I want to commit with it.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
description: Analyze failed CI pipeline and suggest fixes
3+
allowed-tools: Bash(gh:*), Read, Write, Edit, Grep
4+
model: sonnet
5+
---
6+
7+
## Context
8+
- Recent workflow runs: !`gh run list --limit 5`
9+
## Task
10+
Analyze the most recent failed CI run and fix the issue.
11+
**Critical: You MUST read the actual error logs before proposing any fix.**
12+
Steps:
13+
1. Get the failed run ID from the list above
14+
2. Run `gh run view <run-id> --log-failed` to see actual errors
15+
3. Analyze the root cause - not just the symptom
16+
4. Search the codebase for relevant files
17+
5. Implement a fix
18+
6. Explain what failed and why your fix addresses it
19+
Do not guess at solutions. The logs contain the answer.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
description: Generate PR description from branch commits
3+
allowed-tools: Bash(git:*), Bash(gh:*)
4+
model: haiku
5+
---
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
description: Prepare a new release with version bump and changelog
3+
allowed-tools: Bash(git:*), Bash(gh:*), Bash(npm:*), Read, Write, Edit
4+
model: haiku
5+
---
6+
7+
## Context
8+
- Current version (package.json): !`cat package.json | grep '"version"' | head -1`
9+
- Commits since last tag: !`git log $(git describe --tags --abbrev=0)..HEAD --oneline`
10+
- Existing tags: !`git tag --sort=-v:refname | head -5`
11+
## Task
12+
Prepare a release. Ask me what version bump type: major, minor, or patch.
13+
Then:
14+
1. Update version in package.json
15+
2. Generate changelog entry from commits since last tag
16+
3. Group changes by type (Features, Fixes, Other)
17+
4. Stage the changes
18+
5. Ask if I want to create the git tag
19+
Do not push or publish - just prepare locally for my review.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
description: Security scan of staged changes before commit
3+
allowed-tools: Bash(git diff:*), Read, Grep
4+
model: sonnet
5+
---
6+
## Context
7+
- Staged changes: !`git diff --cached`
8+
- Changed files: !`git diff --cached --name-only`
9+
## Task
10+
Review the staged changes for security issues.
11+
Check for:
12+
- Hardcoded secrets, API keys, or credentials
13+
- SQL injection vulnerabilities
14+
- XSS attack vectors
15+
- Insecure dependencies being added
16+
- Authentication/authorization bypasses
17+
- Sensitive data exposure
18+
For each issue found:
19+
1. File and line number
20+
2. What the vulnerability is
21+
3. How to fix it
22+
If no issues found, confirm the changes are safe to commit.
23+
Be thorough but avoid false positives on obvious non-issues.

openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/LineItemEntity.java

Lines changed: 112 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,16 @@
2525
import lombok.Getter;
2626
import lombok.NoArgsConstructor;
2727
import lombok.Setter;
28-
import org.hibernate.annotations.JdbcTypeCode;
28+
import org.greenbuttonalliance.espi.common.domain.common.DateTimeInterval;
29+
import org.greenbuttonalliance.espi.common.domain.common.SummaryMeasurement;
2930
import org.hibernate.proxy.HibernateProxy;
30-
import org.hibernate.type.SqlTypes;
3131

3232
import java.math.BigDecimal;
3333
import java.math.RoundingMode;
3434
import java.time.Instant;
3535
import java.time.LocalDateTime;
3636
import java.time.ZoneId;
3737
import java.util.Objects;
38-
import java.util.UUID;
3938

4039
/**
4140
* Pure JPA/Hibernate entity for LineItem without JAXB concerns.
@@ -61,12 +60,12 @@ public class LineItemEntity {
6160

6261
/**
6362
* Primary key identifier.
63+
* LineItem extends Object (not IdentifiedObject) per ESPI 4.0 XSD (espi.xsd:1449).
6464
*/
6565
@Id
66-
@GeneratedValue(strategy = GenerationType.UUID)
67-
@JdbcTypeCode(SqlTypes.CHAR)
68-
@Column(length = 36, columnDefinition = "char(36)", updatable = false, nullable = false)
69-
private UUID id;
66+
@GeneratedValue(strategy = GenerationType.IDENTITY)
67+
@Column(updatable = false, nullable = false)
68+
private Long id;
7069

7170
/**
7271
* Amount for this line item in currency minor units (e.g., cents).
@@ -100,6 +99,47 @@ public class LineItemEntity {
10099
@Size(max = 256, message = "Note cannot exceed 256 characters")
101100
private String note;
102101

102+
/**
103+
* Relevant measurement for line item (optional).
104+
* Per ESPI 4.0 XSD (espi.xsd:1471), extension field.
105+
*/
106+
@Embedded
107+
@AttributeOverrides({
108+
@AttributeOverride(name = "powerOfTenMultiplier", column = @Column(name = "measurement_multiplier")),
109+
@AttributeOverride(name = "timeStamp", column = @Column(name = "measurement_timestamp")),
110+
@AttributeOverride(name = "uom", column = @Column(name = "measurement_uom")),
111+
@AttributeOverride(name = "value", column = @Column(name = "measurement_value")),
112+
@AttributeOverride(name = "readingTypeRef", column = @Column(name = "measurement_reading_type_ref", length = 512))
113+
})
114+
private SummaryMeasurement measurement;
115+
116+
/**
117+
* Classification of line item (required).
118+
* Per ESPI 4.0 XSD (espi.xsd:1476), extension field.
119+
* ItemKind enumeration values (e.g., 1=Energy Generation Fee, 2=Energy Delivery Fee, etc.)
120+
*/
121+
@Column(name = "item_kind", nullable = false)
122+
@NotNull(message = "Item kind cannot be null")
123+
private Integer itemKind;
124+
125+
/**
126+
* Per unit cost (optional).
127+
* Per ESPI 4.0 XSD (espi.xsd:1481), extension field.
128+
*/
129+
@Column(name = "unit_cost")
130+
private Long unitCost;
131+
132+
/**
133+
* Time period covered by the line item (optional).
134+
* Per ESPI 4.0 XSD (espi.xsd:1486), extension field to support pricing changes mid-billing period.
135+
*/
136+
@Embedded
137+
@AttributeOverrides({
138+
@AttributeOverride(name = "start", column = @Column(name = "item_period_start")),
139+
@AttributeOverride(name = "duration", column = @Column(name = "item_period_duration"))
140+
})
141+
private DateTimeInterval itemPeriod;
142+
103143
// ElectricPowerUsageSummary relationship removed - deprecated resource
104144

105145
/**
@@ -111,33 +151,94 @@ public class LineItemEntity {
111151
private UsageSummaryEntity usageSummary;
112152

113153
/**
114-
* Constructor with basic line item information.
115-
*
154+
* Constructor with basic line item information (legacy, pre-ESPI 4.0 compliance).
155+
* @deprecated Use constructor with itemKind parameter for ESPI 4.0 compliance.
156+
*
116157
* @param amount the amount in currency minor units
117158
* @param dateTime the timestamp
118159
* @param note the descriptive note
119160
*/
161+
@Deprecated
120162
public LineItemEntity(Long amount, Long dateTime, String note) {
121163
this.amount = amount;
122164
this.dateTime = dateTime;
123165
this.note = note;
124166
}
125167

126168
/**
127-
* Constructor with full line item information.
128-
*
169+
* Constructor with basic line item information and required itemKind.
170+
*
171+
* @param amount the amount in currency minor units
172+
* @param dateTime the timestamp
173+
* @param note the descriptive note
174+
* @param itemKind the classification of the line item (required)
175+
*/
176+
public LineItemEntity(Long amount, Long dateTime, String note, Integer itemKind) {
177+
this.amount = amount;
178+
this.dateTime = dateTime;
179+
this.note = note;
180+
this.itemKind = itemKind;
181+
}
182+
183+
/**
184+
* Constructor with full line item information (legacy, pre-ESPI 4.0 compliance).
185+
* @deprecated Use constructor with itemKind parameter for ESPI 4.0 compliance.
186+
*
129187
* @param amount the amount in currency minor units
130188
* @param rounding the rounding adjustment
131189
* @param dateTime the timestamp
132190
* @param note the descriptive note
133191
*/
192+
@Deprecated
134193
public LineItemEntity(Long amount, Long rounding, Long dateTime, String note) {
135194
this.amount = amount;
136195
this.rounding = rounding;
137196
this.dateTime = dateTime;
138197
this.note = note;
139198
}
140199

200+
/**
201+
* Constructor with full line item information including required itemKind.
202+
*
203+
* @param amount the amount in currency minor units
204+
* @param rounding the rounding adjustment
205+
* @param dateTime the timestamp
206+
* @param note the descriptive note
207+
* @param itemKind the classification of the line item (required)
208+
*/
209+
public LineItemEntity(Long amount, Long rounding, Long dateTime, String note, Integer itemKind) {
210+
this.amount = amount;
211+
this.rounding = rounding;
212+
this.dateTime = dateTime;
213+
this.note = note;
214+
this.itemKind = itemKind;
215+
}
216+
217+
/**
218+
* Constructor with complete line item information including all optional fields.
219+
*
220+
* @param amount the amount in currency minor units
221+
* @param rounding the rounding adjustment
222+
* @param dateTime the timestamp
223+
* @param note the descriptive note
224+
* @param measurement relevant measurement for line item
225+
* @param itemKind the classification of the line item (required)
226+
* @param unitCost per unit cost
227+
* @param itemPeriod time period covered by the line item
228+
*/
229+
public LineItemEntity(Long amount, Long rounding, Long dateTime, String note,
230+
SummaryMeasurement measurement, Integer itemKind,
231+
Long unitCost, DateTimeInterval itemPeriod) {
232+
this.amount = amount;
233+
this.rounding = rounding;
234+
this.dateTime = dateTime;
235+
this.note = note;
236+
this.measurement = measurement;
237+
this.itemKind = itemKind;
238+
this.unitCost = unitCost;
239+
this.itemPeriod = itemPeriod;
240+
}
241+
141242
// ElectricPowerUsageSummary setter removed - deprecated resource
142243

143244
/**

0 commit comments

Comments
 (0)