1717import org .roda .core .data .v2 .ip .HasId ;
1818import org .roda .core .data .v2 .ip .HasInstanceID ;
1919import org .roda .core .data .v2 .ip .SIPInformation ;
20- import org .roda .core .data .v2 .jpa .ReportListConverter ;
2120import org .roda .core .data .v2 .jpa .StringListConverter ;
2221
2322import com .fasterxml .jackson .annotation .JsonIgnore ;
2423import com .fasterxml .jackson .annotation .JsonInclude ;
2524import com .fasterxml .jackson .annotation .JsonProperty ;
2625
26+ import jakarta .persistence .CascadeType ;
2727import jakarta .persistence .Column ;
2828import jakarta .persistence .Convert ;
2929import jakarta .persistence .Entity ;
3030import jakarta .persistence .EnumType ;
3131import jakarta .persistence .Enumerated ;
32+ import jakarta .persistence .FetchType ;
3233import jakarta .persistence .Id ;
3334import jakarta .persistence .Index ;
35+ import jakarta .persistence .OneToMany ;
36+ import jakarta .persistence .OrderBy ;
3437import jakarta .persistence .Table ;
3538import jakarta .persistence .Temporal ;
3639import jakarta .persistence .TemporalType ;
@@ -111,9 +114,13 @@ public class Report implements IsModelObject, HasId, HasInstanceID {
111114 @ JsonIgnore
112115 private SIPInformation sipInformation = new SIPInformation ();
113116
114- @ Column (name = "reports" , columnDefinition = "TEXT" )
115- @ Convert (converter = ReportListConverter .class )
116- private List <Report > reports = new ArrayList <>();
117+ @ OneToMany (mappedBy = "parentReportId" , cascade = CascadeType .ALL , fetch = FetchType .LAZY , orphanRemoval = true )
118+ @ OrderBy ("stepOrder ASC" )
119+ private List <StepReport > stepReports = new ArrayList <>();
120+
121+ // Transient field for backwards compatibility with existing code that uses List<Report>
122+ @ Transient
123+ private List <Report > reports = null ;
117124
118125 @ Column (name = "line_separator" )
119126 private String lineSeparator = "" ;
@@ -152,7 +159,8 @@ public Report(Report report) {
152159 this .pluginIsMandatory = report .getPluginIsMandatory ();
153160 this .pluginDetails = report .getPluginDetails ();
154161 this .htmlPluginDetails = report .isHtmlPluginDetails ();
155- this .reports = new ArrayList <>();
162+ this .reports = null ;
163+ this .stepReports = new ArrayList <>();
156164 this .instanceId = report .getInstanceId ();
157165 this .transactionId = report .getTransactionId ();
158166 }
@@ -472,12 +480,25 @@ public Report addReport(Report report, boolean updateReportItemDateUpdated) {
472480 ReportUtils .calculatePluginState (getPluginState (), report .getPluginState (), report .getPluginIsMandatory ()));
473481
474482 if (!"" .equals (report .getPluginDetails ()) && !getPluginDetails ().equals (report .getPluginDetails ())) {
483+ // Fix: avoid adding repeated line separators
484+ String separator = (lineSeparator != null && !lineSeparator .isEmpty ()) ? lineSeparator : "\n " ;
475485 setPluginDetails (
476- (!"" .equals (getPluginDetails ()) ? getPluginDetails () + lineSeparator : "" ) + report .getPluginDetails ());
486+ (!"" .equals (getPluginDetails ()) ? getPluginDetails () + separator : "" ) + report .getPluginDetails ());
477487 }
478488 setOutcomeObjectState (report .getOutcomeObjectState ());
479489
490+ // Add to both stepReports (JPA entity) and reports (backwards compatibility)
491+ if (reports == null ) {
492+ reports = new ArrayList <>();
493+ }
480494 reports .add (report );
495+
496+ // Also add as StepReport
497+ if (stepReports == null ) {
498+ stepReports = new ArrayList <>();
499+ }
500+ stepReports .add (new StepReport (report , this .id , stepReports .size ()));
501+
481502 return this ;
482503 }
483504
@@ -497,15 +518,50 @@ public Report addReport(Report report, boolean updateReportItemDateUpdated) {
497518 * return newPluginState; }
498519 */
499520
521+ /**
522+ * Get the step reports as a list of Report objects for backwards compatibility.
523+ * If reports is null, builds it from stepReports.
524+ */
500525 public List <Report > getReports () {
501- return reports ;
526+ if (reports == null && stepReports != null ) {
527+ reports = new ArrayList <>();
528+ for (StepReport stepReport : stepReports ) {
529+ reports .add (stepReport .toReport ());
530+ }
531+ }
532+ return reports != null ? reports : new ArrayList <>();
502533 }
503534
504535 public Report setReports (List <Report > reports ) {
505536 this .reports = reports ;
537+ // Sync to stepReports
538+ if (reports != null ) {
539+ this .stepReports = new ArrayList <>();
540+ int order = 0 ;
541+ for (Report report : reports ) {
542+ this .stepReports .add (new StepReport (report , this .id , order ++));
543+ }
544+ }
506545 return this ;
507546 }
508547
548+ /**
549+ * Get the underlying StepReport entities.
550+ */
551+ @ JsonIgnore
552+ public List <StepReport > getStepReports () {
553+ return stepReports ;
554+ }
555+
556+ /**
557+ * Set the underlying StepReport entities.
558+ */
559+ @ JsonIgnore
560+ public void setStepReports (List <StepReport > stepReports ) {
561+ this .stepReports = stepReports ;
562+ this .reports = null ; // Reset cached reports
563+ }
564+
509565 @ JsonProperty ("lineSeparator" )
510566 public void injectLineSeparator (String lineSeparator ) {
511567 this .lineSeparator = lineSeparator ;
0 commit comments