diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/IntervalReadingEntity.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/IntervalReadingEntity.java index e714512e..02105c10 100644 --- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/IntervalReadingEntity.java +++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/IntervalReadingEntity.java @@ -25,14 +25,11 @@ import lombok.Setter; import org.greenbuttonalliance.espi.common.domain.common.DateTimeInterval; import org.hibernate.annotations.BatchSize; -import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.proxy.HibernateProxy; -import org.hibernate.type.SqlTypes; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.UUID; /** * Pure JPA/Hibernate entity for IntervalReading without JAXB concerns. @@ -52,13 +49,13 @@ public class IntervalReadingEntity { /** - * Primary key identifier. + * Primary key identifier (48+ bits as per ESPI requirement). + * IntervalReading extends Object (not IdentifiedObject) per ESPI 4.0 XSD. */ @Id - @GeneratedValue(strategy = GenerationType.UUID) - @JdbcTypeCode(SqlTypes.CHAR) - @Column(length = 36, columnDefinition = "char(36)", updatable = false, nullable = false) - private UUID id; + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; /** * Cost associated with this interval reading. @@ -67,6 +64,29 @@ public class IntervalReadingEntity { @Column(name = "cost") private Long cost; + /** + * Reading quality indicators for this interval reading. + * One-to-many relationship with cascade and orphan removal. + * -- GETTER -- + * Gets the reading qualities collection. + * Lombok @Data should generate this, but added manually for compilation. + * + * @return the list of reading qualities + + */ + @OneToMany(mappedBy = "intervalReading", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + @BatchSize(size = 10) + private List readingQualities = new ArrayList<>(); + + /** + * Time period for this interval reading. + * Embedded value object containing start time and duration. + */ + @Embedded + @AttributeOverride(name = "start", column = @Column(name = "time_period_start")) + @AttributeOverride(name = "duration", column = @Column(name = "time_period_duration")) + private DateTimeInterval timePeriod; + /** * The actual measured value for this interval. * This is the primary measurement data. @@ -95,15 +115,6 @@ public class IntervalReadingEntity { @Column(name = "cpp") private Long cpp; - /** - * Time period for this interval reading. - * Embedded value object containing start time and duration. - */ - @Embedded - @AttributeOverride(name = "start", column = @Column(name = "time_period_start")) - @AttributeOverride(name = "duration", column = @Column(name = "time_period_duration")) - private DateTimeInterval timePeriod; - /** * Interval block that contains this reading. * Many interval readings belong to one interval block. @@ -112,20 +123,6 @@ public class IntervalReadingEntity { @JoinColumn(name = "interval_block_id") private IntervalBlockEntity intervalBlock; - /** - * Reading quality indicators for this interval reading. - * One-to-many relationship with cascade and orphan removal. - * -- GETTER -- - * Gets the reading qualities collection. - * Lombok @Data should generate this, but added manually for compilation. - * - * @return the list of reading qualities - - */ - @OneToMany(mappedBy = "intervalReading", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) - @BatchSize(size = 10) - private List readingQualities = new ArrayList<>(); - /** * Constructor with core reading data. * @@ -333,10 +330,10 @@ public String toString() { return getClass().getSimpleName() + "(" + "id = " + getId() + ", " + "cost = " + getCost() + ", " + + "timePeriod = " + getTimePeriod() + ", " + "value = " + getValue() + ", " + "consumptionTier = " + getConsumptionTier() + ", " + "tou = " + getTou() + ", " + - "cpp = " + getCpp() + ", " + - "timePeriod = " + getTimePeriod() + ")"; + "cpp = " + getCpp() + ")"; } } \ No newline at end of file diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/dto/usage/IntervalReadingDto.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/dto/usage/IntervalReadingDto.java index 471412c3..14c2aa6c 100644 --- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/dto/usage/IntervalReadingDto.java +++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/dto/usage/IntervalReadingDto.java @@ -35,26 +35,21 @@ @XmlRootElement(name = "IntervalReading", namespace = "http://naesb.org/espi") @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "IntervalReading", namespace = "http://naesb.org/espi", propOrder = { - "cost", "currency", "value", "timePeriod", "readingQualities", - "consumptionTier", "tou", "cpp" + "cost", "readingQualities", "timePeriod", "value", "consumptionTier", "tou", "cpp" }) public record IntervalReadingDto( @XmlElement(name = "cost") Long cost, - @XmlElement(name = "currency") - Integer currency, - - @XmlElement(name = "value") - Long value, + @XmlElement(name = "ReadingQuality") + List readingQualities, @XmlElement(name = "timePeriod") DateTimeIntervalDto timePeriod, - @XmlElement(name = "ReadingQuality") - //@XmlElementWrapper(name = "ReadingQualities") - List readingQualities, + @XmlElement(name = "value") + Long value, @XmlElement(name = "consumptionTier") Integer consumptionTier, @@ -70,20 +65,20 @@ public record IntervalReadingDto( * Default constructor for JAXB. */ public IntervalReadingDto() { - this(null, null, null, null, null, null, null, null); + this(null, null, null, null, null, null, null); } /** * Minimal constructor for basic interval reading data. */ public IntervalReadingDto(Long value, DateTimeIntervalDto timePeriod) { - this(null, null, value, timePeriod, null, null, null, null); + this(null, null, timePeriod, value, null, null, null); } /** * Constructor for interval reading with cost information. */ - public IntervalReadingDto(Long value, Long cost, Integer currency, DateTimeIntervalDto timePeriod) { - this(cost, currency, value, timePeriod, null, null, null, null); + public IntervalReadingDto(Long value, Long cost, DateTimeIntervalDto timePeriod) { + this(cost, null, timePeriod, value, null, null, null); } } \ No newline at end of file diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/usage/IntervalReadingMapper.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/usage/IntervalReadingMapper.java index d5b54f08..f331792a 100644 --- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/usage/IntervalReadingMapper.java +++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/usage/IntervalReadingMapper.java @@ -44,31 +44,17 @@ public interface IntervalReadingMapper { * @param entity the interval reading entity * @return the interval reading DTO */ - @Mapping(target = "cost", source = "cost") - @Mapping(target = "currency", ignore = true) - @Mapping(target = "value", source = "value") - @Mapping(target = "timePeriod", source = "timePeriod") - @Mapping(target = "readingQualities", source = "readingQualities") - @Mapping(target = "consumptionTier", source = "consumptionTier") - @Mapping(target = "tou", source = "tou") - @Mapping(target = "cpp", source = "cpp") IntervalReadingDto toDto(IntervalReadingEntity entity); /** * Converts an IntervalReadingDto to an IntervalReadingEntity. * Maps all related DTOs to their corresponding entities. + * The ID is generated by the database, and intervalBlock is set by the parent. * * @param dto the interval reading DTO * @return the interval reading entity */ @Mapping(target = "id", ignore = true) - @Mapping(target = "cost", source = "cost") - @Mapping(target = "value", source = "value") - @Mapping(target = "timePeriod", source = "timePeriod") - @Mapping(target = "readingQualities", source = "readingQualities") - @Mapping(target = "consumptionTier", source = "consumptionTier") - @Mapping(target = "tou", source = "tou") - @Mapping(target = "cpp", source = "cpp") @Mapping(target = "intervalBlock", ignore = true) IntervalReadingEntity toEntity(IntervalReadingDto dto); diff --git a/openespi-common/src/main/resources/db/migration/V3__Create_additiional_Base_Tables.sql b/openespi-common/src/main/resources/db/migration/V3__Create_additiional_Base_Tables.sql index 05c8cac3..386b256c 100644 --- a/openespi-common/src/main/resources/db/migration/V3__Create_additiional_Base_Tables.sql +++ b/openespi-common/src/main/resources/db/migration/V3__Create_additiional_Base_Tables.sql @@ -1,121 +1,57 @@ --- Meter Reading Table -CREATE TABLE meter_readings -( - id CHAR(36) PRIMARY KEY , - description VARCHAR(255), - created TIMESTAMP NOT NULL, - updated TIMESTAMP NOT NULL, - published TIMESTAMP, - up_link_rel VARCHAR(255), - up_link_href VARCHAR(1024), - up_link_type VARCHAR(255), - self_link_rel VARCHAR(255), - self_link_href VARCHAR(1024), - self_link_type VARCHAR(255), - - -- Foreign key relationships - usage_point_id CHAR(36), - reading_type_id CHAR(36), - - FOREIGN KEY (usage_point_id) REFERENCES usage_points (id) ON DELETE CASCADE, - FOREIGN KEY (reading_type_id) REFERENCES reading_types (id) ON DELETE SET NULL -); - --- Indexes for meter_readings table -CREATE INDEX idx_meter_reading_usage_point_id ON meter_readings (usage_point_id); -CREATE INDEX idx_meter_reading_reading_type_id ON meter_readings (reading_type_id); -CREATE INDEX idx_meter_reading_created ON meter_readings (created); -CREATE INDEX idx_meter_reading_updated ON meter_readings (updated); - --- Related Links Table for Meter Readings -CREATE TABLE meter_reading_related_links -( - meter_reading_id CHAR(36) NOT NULL, - related_links VARCHAR(1024), - FOREIGN KEY (meter_reading_id) REFERENCES meter_readings (id) ON DELETE CASCADE -); - --- Indexes for meter_reading_related_links table -CREATE INDEX idx_meter_reading_related_links ON meter_reading_related_links (meter_reading_id); - --- Interval Block Table -CREATE TABLE interval_blocks -( - id CHAR(36) PRIMARY KEY , - description VARCHAR(255), - created TIMESTAMP NOT NULL, - updated TIMESTAMP NOT NULL, - published TIMESTAMP, - up_link_rel VARCHAR(255), - up_link_href VARCHAR(1024), - up_link_type VARCHAR(255), - self_link_rel VARCHAR(255), - self_link_href VARCHAR(1024), - self_link_type VARCHAR(255), - - -- Interval block specific fields - interval_duration BIGINT, - interval_start BIGINT, - - -- Foreign key relationships - meter_reading_id CHAR(36), - - FOREIGN KEY (meter_reading_id) REFERENCES meter_readings (id) ON DELETE CASCADE -); - --- Indexes for interval_blocks table -CREATE INDEX idx_interval_block_meter_reading_id ON interval_blocks (meter_reading_id); -CREATE INDEX idx_interval_block_start ON interval_blocks (interval_start); -CREATE INDEX idx_interval_block_created ON interval_blocks (created); -CREATE INDEX idx_interval_block_updated ON interval_blocks (updated); - --- Related Links Table for Interval Blocks -CREATE TABLE interval_block_related_links -( - interval_block_id CHAR(36) NOT NULL, - related_links VARCHAR(1024), - FOREIGN KEY (interval_block_id) REFERENCES interval_blocks (id) ON DELETE CASCADE -); - --- Indexes for interval_block_related_links table -CREATE INDEX idx_interval_block_related_links ON interval_block_related_links (interval_block_id); - --- Interval Reading Table -CREATE TABLE interval_readings -( - id CHAR(36) PRIMARY KEY , - description VARCHAR(255), - created TIMESTAMP, - updated TIMESTAMP, - published TIMESTAMP, - up_link_rel VARCHAR(255), - up_link_href VARCHAR(1024), - up_link_type VARCHAR(255), - self_link_rel VARCHAR(255), - self_link_href VARCHAR(1024), - self_link_type VARCHAR(255), - - -- Interval reading specific fields - cost BIGINT, - reading_value BIGINT, - time_period_start BIGINT, - time_period_duration BIGINT, - consumption_tier BIGINT, - tou BIGINT, - cpp BIGINT, - - -- Foreign key relationships - interval_block_id CHAR(36), - - FOREIGN KEY (interval_block_id) REFERENCES interval_blocks (id) ON DELETE CASCADE -); - --- Indexes for interval_readings table -CREATE INDEX idx_interval_reading_interval_block_id ON interval_readings (interval_block_id); -CREATE INDEX idx_interval_reading_time_period_start ON interval_readings (time_period_start); -CREATE INDEX idx_interval_reading_value ON interval_readings (reading_value); -CREATE INDEX idx_interval_reading_created ON interval_readings (created); -CREATE INDEX idx_interval_reading_updated ON interval_readings (updated); +/* + * OpenESPI Additional Base Tables Migration + * + * Copyright (c) 2018-2025 Green Button Alliance, Inc. + * Licensed under the Apache License, Version 2.0 + * + * This migration creates additional vendor-neutral tables that depend on both + * V1 (base tables) and V2 (vendor-specific tables) to already exist. + * + * IMPORTANT: The following tables were moved to V2 vendor-specific files + * because they require vendor-specific syntax OR are part of dependency chains + * that include vendor-specific tables: + * + * Moved to V2: + * - meter_readings (depends on usage_points from V2) + * - meter_reading_related_links (FK dependency) + * - interval_blocks (depends on meter_readings) + * - interval_block_related_links (FK dependency) + * - interval_readings (extends Object, requires vendor-specific auto-increment, depends on interval_blocks) + * + * Reason: IntervalReading extends Object (not IdentifiedObject) per ESPI 4.0 XSD (espi.xsd:1016), + * requiring Long ID with vendor-specific auto-increment syntax (BIGINT AUTO_INCREMENT for MySQL/H2, + * BIGSERIAL for PostgreSQL). To keep the dependency chain together (meter_readings → interval_blocks + * → interval_readings), all three were moved to V2 vendor files. + * + * Tables in this migration: + * - reading_qualities (depends on interval_readings from V2) + * - usage_summaries (depends on usage_points from V2) + * - usage_summary_related_links (FK dependency) + * - subscription_usage_points (join table) + * - aggregated_node_refs (depends on pnode_refs from V2) + * - customer schema tables + * - end_device schema tables + * - service location/supplier tables + * - statement tables + * + * Compatible with: H2, MySQL, PostgreSQL + */ + +-- ================================================================================== +-- TABLES MOVED TO V2 VENDOR-SPECIFIC MIGRATION FILES +-- ================================================================================== +-- The following tables have been moved to V2 vendor-specific files: +-- - db/vendor/mysql/V2__MySQL_Specific_Tables.sql +-- - db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql +-- - db/vendor/h2/V2__H2_Specific_Tables.sql +-- +-- Tables moved: +-- 1. meter_readings + meter_reading_related_links +-- 2. interval_blocks + interval_block_related_links +-- 3. interval_readings (no related_links - extends Object, not IdentifiedObject) +-- +-- See file header for detailed explanation. +-- ================================================================================== -- Reading Quality Table CREATE TABLE reading_qualities @@ -136,7 +72,8 @@ CREATE TABLE reading_qualities quality VARCHAR(50), -- Foreign key relationships - interval_reading_id CHAR(36), + -- IntervalReading uses Long ID (BIGINT/BIGSERIAL) as it extends Object, not IdentifiedObject + interval_reading_id BIGINT, FOREIGN KEY (interval_reading_id) REFERENCES interval_readings (id) ON DELETE CASCADE ); 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 11f1c906..b101b1c8 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 @@ -179,3 +179,115 @@ CREATE TABLE pnode_refs CREATE INDEX idx_pnode_ref_apnode_type ON pnode_refs (apnode_type); CREATE INDEX idx_pnode_ref_ref ON pnode_refs (ref); CREATE INDEX idx_pnode_ref_usage_point_id ON pnode_refs (usage_point_id); + +-- Meter Reading Table +CREATE TABLE meter_readings +( + id CHAR(36) PRIMARY KEY , + description VARCHAR(255), + created TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + updated TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + published TIMESTAMP(6), + up_link_rel VARCHAR(255), + up_link_href VARCHAR(1024), + up_link_type VARCHAR(255), + self_link_rel VARCHAR(255), + self_link_href VARCHAR(1024), + self_link_type VARCHAR(255), + + -- Foreign key relationships + usage_point_id CHAR(36), + reading_type_id CHAR(36), + + FOREIGN KEY (usage_point_id) REFERENCES usage_points (id) ON DELETE CASCADE, + FOREIGN KEY (reading_type_id) REFERENCES reading_types (id) ON DELETE SET NULL +); + +-- Create indexes for meter_readings table +CREATE INDEX idx_meter_reading_usage_point_id ON meter_readings (usage_point_id); +CREATE INDEX idx_meter_reading_reading_type_id ON meter_readings (reading_type_id); +CREATE INDEX idx_meter_reading_created ON meter_readings (created); +CREATE INDEX idx_meter_reading_updated ON meter_readings (updated); + +-- Related Links Table for Meter Readings +CREATE TABLE meter_reading_related_links +( + meter_reading_id CHAR(36) NOT NULL, + related_links VARCHAR(1024), + FOREIGN KEY (meter_reading_id) REFERENCES meter_readings (id) ON DELETE CASCADE +); + +-- Create index for meter_reading_related_links table +CREATE INDEX idx_meter_reading_related_links ON meter_reading_related_links (meter_reading_id); + +-- Interval Block Table +CREATE TABLE interval_blocks +( + id CHAR(36) PRIMARY KEY , + description VARCHAR(255), + created TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + updated TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + published TIMESTAMP(6), + up_link_rel VARCHAR(255), + up_link_href VARCHAR(1024), + up_link_type VARCHAR(255), + self_link_rel VARCHAR(255), + self_link_href VARCHAR(1024), + self_link_type VARCHAR(255), + + -- Interval block specific fields + interval_duration BIGINT, + interval_start BIGINT, + + -- Foreign key relationships + meter_reading_id CHAR(36), + + FOREIGN KEY (meter_reading_id) REFERENCES meter_readings (id) ON DELETE CASCADE +); + +-- Create indexes for interval_blocks table +CREATE INDEX idx_interval_block_meter_reading_id ON interval_blocks (meter_reading_id); +CREATE INDEX idx_interval_block_start ON interval_blocks (interval_start); +CREATE INDEX idx_interval_block_created ON interval_blocks (created); +CREATE INDEX idx_interval_block_updated ON interval_blocks (updated); + +-- Related Links Table for Interval Blocks +CREATE TABLE interval_block_related_links +( + interval_block_id CHAR(36) NOT NULL, + related_links VARCHAR(1024), + FOREIGN KEY (interval_block_id) REFERENCES interval_blocks (id) ON DELETE CASCADE +); + +-- Create index for interval_block_related_links table +CREATE INDEX idx_interval_block_related_links ON interval_block_related_links (interval_block_id); + +-- Interval Reading Table (Object-based entity, no IdentifiedObject) +-- IntervalReading extends Object per ESPI 4.0 XSD (espi.xsd:1016) +-- XSD sequence: cost → ReadingQuality → timePeriod → value → consumptionTier → tou → cpp +CREATE TABLE interval_readings +( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + + -- ESPI 4.0 fields in XSD sequence order + cost BIGINT, + + -- timePeriod (embedded DateTimeInterval) + time_period_start BIGINT, + time_period_duration BIGINT, + + reading_value BIGINT, + consumption_tier BIGINT, + tou BIGINT, + cpp BIGINT, + + -- Foreign key relationship (parent: IntervalBlock) + interval_block_id CHAR(36), + + FOREIGN KEY (interval_block_id) REFERENCES interval_blocks (id) ON DELETE CASCADE +); + +-- Create indexes for interval_readings table +CREATE INDEX idx_interval_reading_block_id ON interval_readings (interval_block_id); +CREATE INDEX idx_interval_reading_time_start ON interval_readings (time_period_start); +CREATE INDEX idx_interval_reading_value ON interval_readings (reading_value); 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 87c0e0ad..648d80a9 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 @@ -190,3 +190,108 @@ CREATE TABLE pnode_refs CREATE INDEX idx_pnode_ref_apnode_type ON pnode_refs (apnode_type); CREATE INDEX idx_pnode_ref_ref ON pnode_refs (ref); CREATE INDEX idx_pnode_ref_usage_point_id ON pnode_refs (usage_point_id); + +-- Meter Reading Table +CREATE TABLE meter_readings +( + id CHAR(36) PRIMARY KEY , + description VARCHAR(255), + created DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + updated DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + published DATETIME(6), + up_link_rel VARCHAR(255), + up_link_href VARCHAR(1024), + up_link_type VARCHAR(255), + self_link_rel VARCHAR(255), + self_link_href VARCHAR(1024), + self_link_type VARCHAR(255), + + -- Foreign key relationships + usage_point_id CHAR(36), + reading_type_id CHAR(36), + + FOREIGN KEY (usage_point_id) REFERENCES usage_points (id) ON DELETE CASCADE, + FOREIGN KEY (reading_type_id) REFERENCES reading_types (id) ON DELETE SET NULL, + + INDEX idx_meter_reading_usage_point_id (usage_point_id), + INDEX idx_meter_reading_reading_type_id (reading_type_id), + INDEX idx_meter_reading_created (created), + INDEX idx_meter_reading_updated (updated) +); + +-- Related Links Table for Meter Readings +CREATE TABLE meter_reading_related_links +( + meter_reading_id CHAR(36) NOT NULL, + related_links VARCHAR(1024), + FOREIGN KEY (meter_reading_id) REFERENCES meter_readings (id) ON DELETE CASCADE, + INDEX idx_meter_reading_related_links (meter_reading_id) +); + +-- Interval Block Table +CREATE TABLE interval_blocks +( + id CHAR(36) PRIMARY KEY , + description VARCHAR(255), + created DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + updated DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + published DATETIME(6), + up_link_rel VARCHAR(255), + up_link_href VARCHAR(1024), + up_link_type VARCHAR(255), + self_link_rel VARCHAR(255), + self_link_href VARCHAR(1024), + self_link_type VARCHAR(255), + + -- Interval block specific fields + interval_duration BIGINT, + interval_start BIGINT, + + -- Foreign key relationships + meter_reading_id CHAR(36), + + FOREIGN KEY (meter_reading_id) REFERENCES meter_readings (id) ON DELETE CASCADE, + + INDEX idx_interval_block_meter_reading_id (meter_reading_id), + INDEX idx_interval_block_start (interval_start), + INDEX idx_interval_block_created (created), + INDEX idx_interval_block_updated (updated) +); + +-- Related Links Table for Interval Blocks +CREATE TABLE interval_block_related_links +( + interval_block_id CHAR(36) NOT NULL, + related_links VARCHAR(1024), + FOREIGN KEY (interval_block_id) REFERENCES interval_blocks (id) ON DELETE CASCADE, + INDEX idx_interval_block_related_links (interval_block_id) +); + +-- Interval Reading Table (Object-based entity, no IdentifiedObject) +-- IntervalReading extends Object per ESPI 4.0 XSD (espi.xsd:1016) +-- XSD sequence: cost → ReadingQuality → timePeriod → value → consumptionTier → tou → cpp +CREATE TABLE interval_readings +( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + + -- ESPI 4.0 fields in XSD sequence order + cost BIGINT, + + -- timePeriod (embedded DateTimeInterval) + time_period_start BIGINT, + time_period_duration BIGINT, + + reading_value BIGINT, + consumption_tier BIGINT, + tou BIGINT, + cpp BIGINT, + + -- Foreign key relationship (parent: IntervalBlock) + interval_block_id CHAR(36), + + FOREIGN KEY (interval_block_id) REFERENCES interval_blocks (id) ON DELETE CASCADE, + + INDEX idx_interval_reading_block_id (interval_block_id), + INDEX idx_interval_reading_time_start (time_period_start), + INDEX idx_interval_reading_value (reading_value) +); 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 63659f92..828a8484 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 @@ -176,3 +176,110 @@ CREATE TABLE pnode_refs CREATE INDEX idx_pnode_ref_apnode_type ON pnode_refs (apnode_type); CREATE INDEX idx_pnode_ref_ref ON pnode_refs (ref); CREATE INDEX idx_pnode_ref_usage_point_id ON pnode_refs (usage_point_id); + +-- Meter Reading Table +CREATE TABLE meter_readings +( + id CHAR(36) PRIMARY KEY , + description VARCHAR(255), + created TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + published TIMESTAMP(6), + up_link_rel VARCHAR(255), + up_link_href VARCHAR(1024), + up_link_type VARCHAR(255), + self_link_rel VARCHAR(255), + self_link_href VARCHAR(1024), + self_link_type VARCHAR(255), + + -- Foreign key relationships + usage_point_id CHAR(36), + reading_type_id CHAR(36), + + FOREIGN KEY (usage_point_id) REFERENCES usage_points (id) ON DELETE CASCADE, + FOREIGN KEY (reading_type_id) REFERENCES reading_types (id) ON DELETE SET NULL +); + +CREATE INDEX idx_meter_reading_usage_point_id ON meter_readings (usage_point_id); +CREATE INDEX idx_meter_reading_reading_type_id ON meter_readings (reading_type_id); +CREATE INDEX idx_meter_reading_created ON meter_readings (created); +CREATE INDEX idx_meter_reading_updated ON meter_readings (updated); + +-- Related Links Table for Meter Readings +CREATE TABLE meter_reading_related_links +( + meter_reading_id CHAR(36) NOT NULL, + related_links VARCHAR(1024), + FOREIGN KEY (meter_reading_id) REFERENCES meter_readings (id) ON DELETE CASCADE +); + +CREATE INDEX idx_meter_reading_related_links ON meter_reading_related_links (meter_reading_id); + +-- Interval Block Table +CREATE TABLE interval_blocks +( + id CHAR(36) PRIMARY KEY , + description VARCHAR(255), + created TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + published TIMESTAMP(6), + up_link_rel VARCHAR(255), + up_link_href VARCHAR(1024), + up_link_type VARCHAR(255), + self_link_rel VARCHAR(255), + self_link_href VARCHAR(1024), + self_link_type VARCHAR(255), + + -- Interval block specific fields + interval_duration BIGINT, + interval_start BIGINT, + + -- Foreign key relationships + meter_reading_id CHAR(36), + + FOREIGN KEY (meter_reading_id) REFERENCES meter_readings (id) ON DELETE CASCADE +); + +CREATE INDEX idx_interval_block_meter_reading_id ON interval_blocks (meter_reading_id); +CREATE INDEX idx_interval_block_start ON interval_blocks (interval_start); +CREATE INDEX idx_interval_block_created ON interval_blocks (created); +CREATE INDEX idx_interval_block_updated ON interval_blocks (updated); + +-- Related Links Table for Interval Blocks +CREATE TABLE interval_block_related_links +( + interval_block_id CHAR(36) NOT NULL, + related_links VARCHAR(1024), + FOREIGN KEY (interval_block_id) REFERENCES interval_blocks (id) ON DELETE CASCADE +); + +CREATE INDEX idx_interval_block_related_links ON interval_block_related_links (interval_block_id); + +-- Interval Reading Table (Object-based entity, no IdentifiedObject) +-- IntervalReading extends Object per ESPI 4.0 XSD (espi.xsd:1016) +-- XSD sequence: cost → ReadingQuality → timePeriod → value → consumptionTier → tou → cpp +CREATE TABLE interval_readings +( + id BIGSERIAL PRIMARY KEY, + + -- ESPI 4.0 fields in XSD sequence order + cost BIGINT, + + -- timePeriod (embedded DateTimeInterval) + time_period_start BIGINT, + time_period_duration BIGINT, + + reading_value BIGINT, + consumption_tier BIGINT, + tou BIGINT, + cpp BIGINT, + + -- Foreign key relationship (parent: IntervalBlock) + interval_block_id CHAR(36), + + FOREIGN KEY (interval_block_id) REFERENCES interval_blocks (id) ON DELETE CASCADE +); + +CREATE INDEX idx_interval_reading_block_id ON interval_readings (interval_block_id); +CREATE INDEX idx_interval_reading_time_start ON interval_readings (time_period_start); +CREATE INDEX idx_interval_reading_value ON interval_readings (reading_value); diff --git a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/service/impl/DtoExportServiceImplTest.java b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/service/impl/DtoExportServiceImplTest.java index 6f9fac2a..0d8c4664 100644 --- a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/service/impl/DtoExportServiceImplTest.java +++ b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/service/impl/DtoExportServiceImplTest.java @@ -270,12 +270,14 @@ void shouldExportEspiIntervalBlockContent() throws IOException { private static @NonNull AtomEntryDto getIntervlBlockEntryDto(OffsetDateTime now) { List intervalReadings = new ArrayList<>(); - intervalReadings.add(new IntervalReadingDto( 974L, null, 282L, new DateTimeIntervalDto(1330578000L, 900L), new ArrayList<>(List.of(new ReadingQualityDto( "8"))), null, null, null)); + // New constructor: cost, readingQualities, timePeriod, value, consumptionTier, tou, cpp + intervalReadings.add(new IntervalReadingDto(974L, new ArrayList<>(List.of(new ReadingQualityDto("8"))), new DateTimeIntervalDto(1330578000L, 900L), 282L, null, null, null)); - intervalReadings.add(new IntervalReadingDto( 965L, null, 323L, new DateTimeIntervalDto(1330578900L, 900L), new ArrayList<>(List.of(new ReadingQualityDto( "7"))), null, null, null)); + intervalReadings.add(new IntervalReadingDto(965L, new ArrayList<>(List.of(new ReadingQualityDto("7"))), new DateTimeIntervalDto(1330578900L, 900L), 323L, null, null, null)); - intervalReadings.add(new IntervalReadingDto(294L, 884L, null, new DateTimeIntervalDto(1330579800L, 900L))); - intervalReadings.add(new IntervalReadingDto(331L, 995L, null, new DateTimeIntervalDto(1330580700L, 900L))); + // Using convenience constructor: value, cost, timePeriod + intervalReadings.add(new IntervalReadingDto(294L, 884L, new DateTimeIntervalDto(1330579800L, 900L))); + intervalReadings.add(new IntervalReadingDto(331L, 995L, new DateTimeIntervalDto(1330580700L, 900L))); IntervalBlockDto intervalBlockDto = new IntervalBlockDto("urn:uuid:FE9A61BB-6913-52D4-88BE-9634A218EF53", new DateTimeIntervalDto(1330578000L, 86400L), intervalReadings);