diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/common/AmiBillingReadyKind.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/common/AmiBillingReadyKind.java new file mode 100644 index 00000000..dea72809 --- /dev/null +++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/common/AmiBillingReadyKind.java @@ -0,0 +1,94 @@ +/* + * + * Copyright (c) 2025 Green Button Alliance, Inc. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.greenbuttonalliance.espi.common.domain.common; + +/** + * Lifecycle states of the metering installation at a usage point with respect to + * readiness for billing via advanced metering infrastructure reads. + * Per ESPI 4.0 XSD: AmiBillingReadyKind enumeration. + */ +public enum AmiBillingReadyKind { + /** + * Usage point is equipped with an AMI capable meter that is not yet currently + * equipped with a communications module. + */ + AMI_CAPABLE("amiCapable"), + + /** + * Usage point is equipped with an AMI capable meter; however, the AMI functionality + * has been disabled or is not being used. + */ + AMI_DISABLED("amiDisabled"), + + /** + * Usage point is equipped with an operating AMI capable meter and accuracy has been + * certified for billing purposes. + */ + BILLING_APPROVED("billingApproved"), + + /** + * Usage point is equipped with an AMI capable meter having communications capability. + */ + ENABLED("enabled"), + + /** + * Usage point is equipped with a non AMI capable meter. + */ + NON_AMI("nonAmi"), + + /** + * Usage point is not currently equipped with a meter. + */ + NON_METERED("nonMetered"), + + /** + * Usage point is equipped with an AMI capable meter that is functioning and + * communicating with the AMI network. + */ + OPERABLE("operable"); + + private final String value; + + AmiBillingReadyKind(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + /** + * Converts a string value to the corresponding AmiBillingReadyKind enum constant. + * + * @param value the string value from XML/XSD + * @return the matching enum constant, or null if not found + */ + public static AmiBillingReadyKind fromValue(String value) { + if (value == null) { + return null; + } + for (AmiBillingReadyKind kind : AmiBillingReadyKind.values()) { + if (kind.value.equals(value)) { + return kind; + } + } + return null; + } +} diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/common/PhaseCodeKind.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/common/PhaseCodeKind.java new file mode 100644 index 00000000..670fdc98 --- /dev/null +++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/common/PhaseCodeKind.java @@ -0,0 +1,191 @@ +/* + * + * Copyright (c) 2025 Green Button Alliance, Inc. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.greenbuttonalliance.espi.common.domain.common; + +/** + * Enumeration of phase identifiers. Allows designation of phases for both transmission + * and distribution equipment, circuits and loads. Residential and small commercial loads + * are often served from single-phase, or split-phase, secondary circuits. Phases 1 and 2 + * refer to hot wires that are 180 degrees out of phase, while N refers to the neutral wire. + * Through single-phase transformer connections, these secondary circuits may be served from + * one or two of the primary phases A, B, and C. For three-phase loads, use the A, B, C + * phase codes instead of s12N. + *

+ * Per ESPI 4.0 XSD: PhaseCodeKind enumeration (UInt16 values). + */ +public enum PhaseCodeKind { + /** + * ABC to Neutral (three-phase, four-wire). + */ + ABCN(225), + + /** + * Involving all phases (three-phase). + */ + ABC(224), + + /** + * AB to Neutral. + */ + ABN(193), + + /** + * Phases A, C and neutral. + */ + ACN(41), + + /** + * BC to neutral. + */ + BCN(97), + + /** + * Phases A to B. + */ + AB(132), + + /** + * Phases A and C. + */ + AC(96), + + /** + * Phases B to C. + */ + BC(66), + + /** + * Phases A to neutral. + */ + AN(129), + + /** + * Phases B to neutral. + */ + BN(65), + + /** + * Phases C to neutral. + */ + CN(33), + + /** + * Phase A. + */ + A(128), + + /** + * Phase B. + */ + B(64), + + /** + * Phase C. + */ + C(32), + + /** + * Neutral. + */ + N(16), + + /** + * Phase S2 to neutral. + */ + S2N(272), + + /** + * Phase S1, S2 to neutral (split-phase secondary). + */ + S12N(784), + + /** + * Phase S1 to Neutral. + */ + S1N(528), + + /** + * Phase S2. + */ + S2(256), + + /** + * Phase S1 to S2. + */ + S12(768), + + /** + * Phase S1. + */ + S1(512), + + /** + * Not applicable to any phase. + */ + NONE(0), + + /** + * Phase A current relative to Phase A voltage. + */ + A_TO_AV(136), + + /** + * Phase B current or voltage relative to Phase A voltage. + */ + B_AV(72), + + /** + * Phase C current or voltage relative to Phase A voltage. + */ + C_AV(40), + + /** + * Neutral to ground. + */ + NG(17); + + private final Integer value; + + PhaseCodeKind(Integer value) { + this.value = value; + } + + public Integer getValue() { + return value; + } + + /** + * Converts an integer value to the corresponding PhaseCodeKind enum constant. + * + * @param value the integer value from XML/XSD + * @return the matching enum constant, or null if not found + */ + public static PhaseCodeKind fromValue(Integer value) { + if (value == null) { + return null; + } + for (PhaseCodeKind kind : PhaseCodeKind.values()) { + if (kind.value.equals(value)) { + return kind; + } + } + return null; + } +} diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/common/UsagePointConnectedKind.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/common/UsagePointConnectedKind.java new file mode 100644 index 00000000..d61e4f3c --- /dev/null +++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/common/UsagePointConnectedKind.java @@ -0,0 +1,75 @@ +/* + * + * Copyright (c) 2025 Green Button Alliance, Inc. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.greenbuttonalliance.espi.common.domain.common; + +/** + * State of the usage point with respect to connection to the network. + * Per ESPI 4.0 XSD: UsagePointConnectedKind enumeration. + */ +public enum UsagePointConnectedKind { + /** + * The usage point is connected to the network and able to receive or send + * the applicable commodity (electricity, gas, water, etc.). + */ + CONNECTED("connected"), + + /** + * The usage point has been disconnected through operation of a disconnect function + * within the meter present at the usage point. The usage point is unable to receive + * or send the applicable commodity (electricity, gas, water, etc.). A logical + * disconnect can often be achieved without utilising a field crew. + */ + LOGICALLY_DISCONNECTED("logicallyDisconnected"), + + /** + * The usage point has been disconnected from the network at a point upstream of the meter. + * The usage point is unable to receive or send the applicable commodity (electricity, + * gas, water, etc.). A physical disconnect is often achieved by utilising a field crew. + */ + PHYSICALLY_DISCONNECTED("physicallyDisconnected"); + + private final String value; + + UsagePointConnectedKind(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + /** + * Converts a string value to the corresponding UsagePointConnectedKind enum constant. + * + * @param value the string value from XML/XSD + * @return the matching enum constant, or null if not found + */ + public static UsagePointConnectedKind fromValue(String value) { + if (value == null) { + return null; + } + for (UsagePointConnectedKind kind : UsagePointConnectedKind.values()) { + if (kind.value.equals(value)) { + return kind; + } + } + return null; + } +} \ No newline at end of file diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/UsagePointEntity.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/UsagePointEntity.java index 055c0080..c3637df6 100644 --- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/UsagePointEntity.java +++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/UsagePointEntity.java @@ -22,9 +22,12 @@ import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; import lombok.*; +import org.greenbuttonalliance.espi.common.domain.common.AmiBillingReadyKind; import org.greenbuttonalliance.espi.common.domain.common.IdentifiedObject; +import org.greenbuttonalliance.espi.common.domain.common.PhaseCodeKind; import org.greenbuttonalliance.espi.common.domain.common.ServiceCategory; import org.greenbuttonalliance.espi.common.domain.common.SummaryMeasurement; +import org.greenbuttonalliance.espi.common.domain.common.UsagePointConnectedKind; import org.hibernate.annotations.BatchSize; import org.hibernate.proxy.HibernateProxy; @@ -45,8 +48,12 @@ public class UsagePointEntity extends IdentifiedObject { private static final long serialVersionUID = 1L; + // ==================== XSD Sequence Order (per ESPI 4.0 espi.xsd) ==================== + // Fields ordered to match exact XSD element sequence for UsagePoint complexType + /** * Role flags for the usage point (hex binary representation). + * XSD Position 1: roleFlags */ @Column(name = "role_flags") private byte[] roleFlags; @@ -54,6 +61,7 @@ public class UsagePointEntity extends IdentifiedObject { /** * Service category for this usage point. * Required field indicating the type of service. + * XSD Position 2: ServiceCategory */ @NotNull @Enumerated(EnumType.STRING) @@ -62,85 +70,61 @@ public class UsagePointEntity extends IdentifiedObject { /** * Status of the usage point. + * XSD Position 3: status */ @Column(name = "status") private Short status; - /** - * URI for this usage point. - * Used for external references and linking. - */ - @Column(name = "uri") - private String uri; + // XSD Position 4: serviceDeliveryPoint - See relationship section below /** - * Estimated load for this usage point as SummaryMeasurement. - * Contains value, unit of measure, multiplier, and reading type reference. + * Tracks the lifecycle of the metering installation at a usage point with respect to + * readiness for billing via advanced metering infrastructure reads. + * Per ESPI 4.0 XSD: [extension] AmiBillingReadyKind enum. + * XSD Position 5: amiBillingReady */ - @Embedded - @AttributeOverrides({ - @AttributeOverride(name = "powerOfTenMultiplier", column = @Column(name = "estimated_load_multiplier", columnDefinition = "SMALLINT")), - @AttributeOverride(name = "timeStamp", column = @Column(name = "estimated_load_timestamp")), - @AttributeOverride(name = "uom", column = @Column(name = "estimated_load_uom")), - @AttributeOverride(name = "value", column = @Column(name = "estimated_load_value")), - @AttributeOverride(name = "readingTypeRef", column = @Column(name = "estimated_load_reading_type_ref", length = 512)) - }) - private SummaryMeasurement estimatedLoad; + @Enumerated(EnumType.STRING) + @Column(name = "ami_billing_ready", length = 32) + private AmiBillingReadyKind amiBillingReady; /** - * Nominal service voltage for this usage point as SummaryMeasurement. - * Contains value, unit of measure, multiplier, and reading type reference. + * True if as a result of an inspection or otherwise, there is a reason to suspect + * that a previous billing may have been performed with erroneous data. + * Value should be reset once this potential discrepancy has been resolved. + * Per ESPI 4.0 XSD: [extension] boolean field. + * XSD Position 6: checkBilling */ - @Embedded - @AttributeOverrides({ - @AttributeOverride(name = "powerOfTenMultiplier", column = @Column(name = "nominal_voltage_multiplier")), - @AttributeOverride(name = "timeStamp", column = @Column(name = "nominal_voltage_timestamp")), - @AttributeOverride(name = "uom", column = @Column(name = "nominal_voltage_uom")), - @AttributeOverride(name = "value", column = @Column(name = "nominal_voltage_value")), - @AttributeOverride(name = "readingTypeRef", column = @Column(name = "nominal_voltage_reading_type_ref", length = 512)) - }) - private SummaryMeasurement nominalServiceVoltage; + @Column(name = "check_billing") + private Boolean checkBilling; /** - * Rated current for this usage point as SummaryMeasurement. - * Contains value, unit of measure, multiplier, and reading type reference. + * State of the usage point with respect to connection to the network. + * Per ESPI 4.0 XSD: [extension] UsagePointConnectedKind enum. + * XSD Position 7: connectionState */ - @Embedded - @AttributeOverrides({ - @AttributeOverride(name = "powerOfTenMultiplier", column = @Column(name = "rated_current_multiplier")), - @AttributeOverride(name = "timeStamp", column = @Column(name = "rated_current_timestamp")), - @AttributeOverride(name = "uom", column = @Column(name = "rated_current_uom")), - @AttributeOverride(name = "value", column = @Column(name = "rated_current_value")), - @AttributeOverride(name = "readingTypeRef", column = @Column(name = "rated_current_reading_type_ref", length = 512)) - }) - private SummaryMeasurement ratedCurrent; + @Enumerated(EnumType.STRING) + @Column(name = "connection_state", length = 32) + private UsagePointConnectedKind connectionState; /** - * Rated power for this usage point as SummaryMeasurement. + * Estimated load for this usage point as SummaryMeasurement. * Contains value, unit of measure, multiplier, and reading type reference. + * XSD Position 8: estimatedLoad */ @Embedded @AttributeOverrides({ - @AttributeOverride(name = "powerOfTenMultiplier", column = @Column(name = "rated_power_multiplier")), - @AttributeOverride(name = "timeStamp", column = @Column(name = "rated_power_timestamp")), - @AttributeOverride(name = "uom", column = @Column(name = "rated_power_uom")), - @AttributeOverride(name = "value", column = @Column(name = "rated_power_value")), - @AttributeOverride(name = "readingTypeRef", column = @Column(name = "rated_power_reading_type_ref", length = 512)) + @AttributeOverride(name = "powerOfTenMultiplier", column = @Column(name = "estimated_load_multiplier", columnDefinition = "SMALLINT")), + @AttributeOverride(name = "timeStamp", column = @Column(name = "estimated_load_timestamp")), + @AttributeOverride(name = "uom", column = @Column(name = "estimated_load_uom")), + @AttributeOverride(name = "value", column = @Column(name = "estimated_load_value")), + @AttributeOverride(name = "readingTypeRef", column = @Column(name = "estimated_load_reading_type_ref", length = 512)) }) - private SummaryMeasurement ratedPower; - - /** - * True if as a result of an inspection or otherwise, there is a reason to suspect - * that a previous billing may have been performed with erroneous data. - * Value should be reset once this potential discrepancy has been resolved. - * Per ESPI 4.0 XSD: [extension] boolean field. - */ - @Column(name = "check_billing") - private Boolean checkBilling; + private SummaryMeasurement estimatedLoad; /** * True if grounded. * Per ESPI 4.0 XSD: [extension] boolean field. + * XSD Position 9: grounded */ @Column(name = "grounded") private Boolean grounded; @@ -149,6 +133,7 @@ public class UsagePointEntity extends IdentifiedObject { * If true, this usage point is a service delivery point, i.e., a usage point * where the ownership of the service changes hands. * Per ESPI 4.0 XSD: [extension] boolean field. + * XSD Position 10: isSdp */ @Column(name = "is_sdp") private Boolean isSdp; @@ -161,6 +146,7 @@ public class UsagePointEntity extends IdentifiedObject { * Otherwise, the usage point is physical, i.e., there is a logical point in the network * where a meter could be located to collect meter readings. * Per ESPI 4.0 XSD: [extension] boolean field. + * XSD Position 11: isVirtual */ @Column(name = "is_virtual") private Boolean isVirtual; @@ -170,21 +156,81 @@ public class UsagePointEntity extends IdentifiedObject { * premises vacancy, logical or physical disconnect. * It is used for readings validation and estimation. * Per ESPI 4.0 XSD: [extension] boolean field. + * XSD Position 12: minimalUsageExpected */ @Column(name = "minimal_usage_expected") private Boolean minimalUsageExpected; + /** + * Nominal service voltage for this usage point as SummaryMeasurement. + * Contains value, unit of measure, multiplier, and reading type reference. + * XSD Position 13: nominalServiceVoltage + */ + @Embedded + @AttributeOverrides({ + @AttributeOverride(name = "powerOfTenMultiplier", column = @Column(name = "nominal_voltage_multiplier")), + @AttributeOverride(name = "timeStamp", column = @Column(name = "nominal_voltage_timestamp")), + @AttributeOverride(name = "uom", column = @Column(name = "nominal_voltage_uom")), + @AttributeOverride(name = "value", column = @Column(name = "nominal_voltage_value")), + @AttributeOverride(name = "readingTypeRef", column = @Column(name = "nominal_voltage_reading_type_ref", length = 512)) + }) + private SummaryMeasurement nominalServiceVoltage; + /** * Outage region in which this usage point is located. * Per ESPI 4.0 XSD: [extension] String256 field (max length 256). + * XSD Position 14: outageRegion */ @Column(name = "outage_region", length = 256) private String outageRegion; + /** + * Phase code. Number of wires and specific nominal phases can be deduced from + * enumeration literal values. For example, ABCN is three-phase, four-wire, + * s12n (splitSecondary12N) is single-phase, three-wire, and s1n and s2n are + * single-phase, two-wire. + * Per ESPI 4.0 XSD: [extension] PhaseCodeKind enum. + * XSD Position 15: phaseCode + */ + @Enumerated(EnumType.STRING) + @Column(name = "phase_code", length = 32) + private PhaseCodeKind phaseCode; + + /** + * Rated current for this usage point as SummaryMeasurement. + * Contains value, unit of measure, multiplier, and reading type reference. + * XSD Position 16: ratedCurrent + */ + @Embedded + @AttributeOverrides({ + @AttributeOverride(name = "powerOfTenMultiplier", column = @Column(name = "rated_current_multiplier")), + @AttributeOverride(name = "timeStamp", column = @Column(name = "rated_current_timestamp")), + @AttributeOverride(name = "uom", column = @Column(name = "rated_current_uom")), + @AttributeOverride(name = "value", column = @Column(name = "rated_current_value")), + @AttributeOverride(name = "readingTypeRef", column = @Column(name = "rated_current_reading_type_ref", length = 512)) + }) + private SummaryMeasurement ratedCurrent; + + /** + * Rated power for this usage point as SummaryMeasurement. + * Contains value, unit of measure, multiplier, and reading type reference. + * XSD Position 17: ratedPower + */ + @Embedded + @AttributeOverrides({ + @AttributeOverride(name = "powerOfTenMultiplier", column = @Column(name = "rated_power_multiplier")), + @AttributeOverride(name = "timeStamp", column = @Column(name = "rated_power_timestamp")), + @AttributeOverride(name = "uom", column = @Column(name = "rated_power_uom")), + @AttributeOverride(name = "value", column = @Column(name = "rated_power_value")), + @AttributeOverride(name = "readingTypeRef", column = @Column(name = "rated_power_reading_type_ref", length = 512)) + }) + private SummaryMeasurement ratedPower; + /** * Cycle day on which the meter for this usage point will normally be read. * Usually correlated with the billing cycle. * Per ESPI 4.0 XSD: [extension] String256 field (max length 256). + * XSD Position 18: readCycle */ @Column(name = "read_cycle", length = 256) private String readCycle; @@ -193,6 +239,7 @@ public class UsagePointEntity extends IdentifiedObject { * Identifier of the route to which this usage point is assigned for purposes of meter reading. * Typically used to configure hand held meter reading systems prior to collection of reads. * Per ESPI 4.0 XSD: [extension] String256 field (max length 256). + * XSD Position 19: readRoute */ @Column(name = "read_route", length = 256) private String readRoute; @@ -200,6 +247,7 @@ public class UsagePointEntity extends IdentifiedObject { /** * Remarks about this usage point, for example the reason for it being rated with a non-nominal priority. * Per ESPI 4.0 XSD: [extension] String256 field (max length 256). + * XSD Position 20: serviceDeliveryRemark */ @Column(name = "service_delivery_remark", length = 256) private String serviceDeliveryRemark; @@ -208,10 +256,26 @@ public class UsagePointEntity extends IdentifiedObject { * Priority of service for this usage point. * Note that usage points at the same service location can have different priorities. * Per ESPI 4.0 XSD: [extension] String32 field (max length 32). + * XSD Position 21: servicePriority */ @Column(name = "service_priority", length = 32) private String servicePriority; + // XSD Position 22-23: pnodeRefs and aggregateNodeRefs - See relationship sections below + + // ==================== Legacy Fields (NOT in ESPI 4.0 XSD) ==================== + // TODO Phase 16c: Review if these fields should be removed or mapped to XSD elements + + /** + * URI for this usage point. + * Used for external references and linking. + * NOTE: This field is NOT in ESPI 4.0 XSD - legacy field for review. + */ + @Column(name = "uri") + private String uri; + + // ==================== JPA Relationships ==================== + /** * Service delivery point associated with this usage point. * ServiceDeliveryPoint is now a standalone ESPI resource. diff --git a/openespi-common/src/main/resources/db/vendor/h2/V2__H2_Specific_Tables.sql b/openespi-common/src/main/resources/db/vendor/h2/V2__H2_Specific_Tables.sql index 3b391f1c..37afc0f3 100644 --- a/openespi-common/src/main/resources/db/vendor/h2/V2__H2_Specific_Tables.sql +++ b/openespi-common/src/main/resources/db/vendor/h2/V2__H2_Specific_Tables.sql @@ -105,9 +105,9 @@ CREATE TABLE usage_points service_category VARCHAR(50), -- 2. ServiceCategory status SMALLINT, -- 3. status -- 4. serviceDeliveryPoint (FK handled below) - -- 5. amiBillingReady (enum - Phase 16b) + ami_billing_ready VARCHAR(32), -- 5. amiBillingReady (Phase 16b) check_billing BOOLEAN, -- 6. checkBilling (Phase 16a) - -- 7. connectionState (enum - Phase 16b) + connection_state VARCHAR(32), -- 7. connectionState (Phase 16b) -- 8. estimatedLoad (embedded SummaryMeasurement) estimated_load_multiplier VARCHAR(255), @@ -129,7 +129,7 @@ CREATE TABLE usage_points nominal_voltage_reading_type_ref VARCHAR(512), outage_region VARCHAR(256), -- 14. outageRegion (Phase 16a) - -- 15. phaseCode (enum - Phase 16b) + phase_code VARCHAR(32), -- 15. phaseCode (Phase 16b) -- 16. ratedCurrent (embedded SummaryMeasurement) rated_current_multiplier VARCHAR(255), diff --git a/openespi-common/src/main/resources/db/vendor/mysql/V2__MySQL_Specific_Tables.sql b/openespi-common/src/main/resources/db/vendor/mysql/V2__MySQL_Specific_Tables.sql index d2f1d3a2..dd075e5a 100644 --- a/openespi-common/src/main/resources/db/vendor/mysql/V2__MySQL_Specific_Tables.sql +++ b/openespi-common/src/main/resources/db/vendor/mysql/V2__MySQL_Specific_Tables.sql @@ -102,9 +102,9 @@ CREATE TABLE usage_points service_category VARCHAR(50), -- 2. ServiceCategory status SMALLINT, -- 3. status -- 4. serviceDeliveryPoint (FK handled below) - -- 5. amiBillingReady (enum - Phase 16b) + ami_billing_ready VARCHAR(32), -- 5. amiBillingReady (Phase 16b) check_billing BOOLEAN, -- 6. checkBilling (Phase 16a) - -- 7. connectionState (enum - Phase 16b) + connection_state VARCHAR(32), -- 7. connectionState (Phase 16b) -- 8. estimatedLoad (embedded SummaryMeasurement) estimated_load_multiplier VARCHAR(255), @@ -126,7 +126,7 @@ CREATE TABLE usage_points nominal_voltage_reading_type_ref VARCHAR(512), outage_region VARCHAR(256), -- 14. outageRegion (Phase 16a) - -- 15. phaseCode (enum - Phase 16b) + phase_code VARCHAR(32), -- 15. phaseCode (Phase 16b) -- 16. ratedCurrent (embedded SummaryMeasurement) rated_current_multiplier VARCHAR(255), diff --git a/openespi-common/src/main/resources/db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql b/openespi-common/src/main/resources/db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql index 12a84a69..40cf6729 100644 --- a/openespi-common/src/main/resources/db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql +++ b/openespi-common/src/main/resources/db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql @@ -103,9 +103,9 @@ CREATE TABLE usage_points service_category VARCHAR(50), -- 2. ServiceCategory status SMALLINT, -- 3. status -- 4. serviceDeliveryPoint (FK handled below) - -- 5. amiBillingReady (enum - Phase 16b) + ami_billing_ready VARCHAR(32), -- 5. amiBillingReady (Phase 16b) check_billing BOOLEAN, -- 6. checkBilling (Phase 16a) - -- 7. connectionState (enum - Phase 16b) + connection_state VARCHAR(32), -- 7. connectionState (Phase 16b) -- 8. estimatedLoad (embedded SummaryMeasurement) estimated_load_multiplier VARCHAR(255), @@ -127,7 +127,7 @@ CREATE TABLE usage_points nominal_voltage_reading_type_ref VARCHAR(512), outage_region VARCHAR(256), -- 14. outageRegion (Phase 16a) - -- 15. phaseCode (enum - Phase 16b) + phase_code VARCHAR(32), -- 15. phaseCode (Phase 16b) -- 16. ratedCurrent (embedded SummaryMeasurement) rated_current_multiplier VARCHAR(255), diff --git a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/UsagePointRepositoryTest.java b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/UsagePointRepositoryTest.java index 416315aa..2f7f8542 100644 --- a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/UsagePointRepositoryTest.java +++ b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/UsagePointRepositoryTest.java @@ -20,7 +20,10 @@ import org.greenbuttonalliance.espi.common.domain.usage.UsagePointEntity; import org.greenbuttonalliance.espi.common.domain.usage.RetailCustomerEntity; +import org.greenbuttonalliance.espi.common.domain.common.AmiBillingReadyKind; import org.greenbuttonalliance.espi.common.domain.common.LinkType; +import org.greenbuttonalliance.espi.common.domain.common.PhaseCodeKind; +import org.greenbuttonalliance.espi.common.domain.common.UsagePointConnectedKind; import org.greenbuttonalliance.espi.common.test.BaseRepositoryTest; import org.greenbuttonalliance.espi.common.test.TestDataBuilders; import org.junit.jupiter.api.DisplayName; @@ -210,6 +213,33 @@ void shouldPersistAndRetrievePhase16aExtensionFields() { assertThat(entity.getServiceDeliveryRemark()).isEqualTo("High priority customer"); assertThat(entity.getServicePriority()).isEqualTo("P1"); } + + @Test + @DisplayName("Should persist and retrieve Phase 16b enum fields") + void shouldPersistAndRetrievePhase16bEnumFields() { + // Arrange - Create usage point with all Phase 16b enum fields set + UsagePointEntity usagePoint = TestDataBuilders.createValidUsagePoint(); + usagePoint.setDescription("Usage Point with Phase 16b enum fields"); + + // Set Phase 16b enum fields + usagePoint.setAmiBillingReady(AmiBillingReadyKind.BILLING_APPROVED); + usagePoint.setConnectionState(UsagePointConnectedKind.CONNECTED); + usagePoint.setPhaseCode(PhaseCodeKind.ABCN); + + // Act - Save and retrieve + UsagePointEntity saved = usagePointRepository.save(usagePoint); + flushAndClear(); + Optional retrieved = usagePointRepository.findById(saved.getId()); + + // Assert - Verify all Phase 16b enum fields persisted correctly + assertThat(retrieved).isPresent(); + UsagePointEntity entity = retrieved.get(); + + // Verify enum fields + assertThat(entity.getAmiBillingReady()).isEqualTo(AmiBillingReadyKind.BILLING_APPROVED); + assertThat(entity.getConnectionState()).isEqualTo(UsagePointConnectedKind.CONNECTED); + assertThat(entity.getPhaseCode()).isEqualTo(PhaseCodeKind.ABCN); + } } @Nested