package com.lincfusion.udyamlinc.manualentry; import jakarta.persistence.*; import lombok.*; import java.math.BigDecimal; import java.time.LocalDate; /** * One row of an upload, kept whether or not it was accepted. * *

A rejected row is data too: "we uploaded four hundred rows and three * hundred silently vanished" is the failure this table exists to prevent. Each * rejection carries the reason that produced it. * *

{@code priorModalPrice} records what an accepted row displaced, because a * correction is only auditable if the thing it corrected is still legible. */ @Entity @Table(name = "manual_entry_line", indexes = { @Index(name = "ix_manual_line_batch", columnList = "batchId"), }) @Getter @Setter @NoArgsConstructor @AllArgsConstructor @Builder public class ManualEntryLine { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private Long batchId; /** 1-based row number in the uploaded file, so a reject points somewhere. */ @Column(nullable = false) private Integer rowNumber; // ── as supplied, before resolution ────────────────────── @Column(length = 64) private String rawDistrict; @Column(length = 64) private String rawCommodity; @Column(length = 32) private String rawEffectiveDate; @Column(length = 32) private String rawModalPrice; /** * The whole row as supplied, as JSON. Commit reads its columns from here * rather than from a re-parse of a file nobody kept, and an auditor asking * what the bank actually sent gets the row rather than our reading of it. */ @Column(columnDefinition = "text") private String rawRow; // ── as resolved ───────────────────────────────────────── private Long districtId; private Long commodityId; /** The date the fact describes, as entered. Never the upload time. */ private LocalDate effectiveDate; /** Monday of the ISO week {@link #effectiveDate} falls in. */ private LocalDate weekStart; @Column(precision = 12, scale = 2) private BigDecimal modalPrice; @Column(precision = 12, scale = 2) private BigDecimal minPrice; @Column(precision = 12, scale = 2) private BigDecimal maxPrice; @Column(length = 16) private String unit; // ── verdict ───────────────────────────────────────────── @Column(nullable = false) private boolean accepted; /** * UNKNOWN_DISTRICT | UNKNOWN_COMMODITY | FUTURE_DATE | BAD_DATE | BAD_NUMBER * | UNIT_MISMATCH | PRICE_ORDER | DUPLICATE_IN_FILE | MISSING_REQUIRED */ @Column(length = 32) private String rejectReason; @Column(length = 300) private String rejectDetail; /** Set when the row is accepted but sits outside the sanity band. Not a rejection. */ @Column(length = 300) private String warning; /** True when committing this row replaces an existing manual value. */ private boolean overwriting; @Column(precision = 12, scale = 2) private BigDecimal priorModalPrice; }