commit 0357954d7db7702064c35905509abe8bd00ca090 parent 40877808b5fa742612ace7658611cff6dd7807ee Author: triesap <tyson@radroots.org> Date: Tue, 26 May 2026 09:28:44 +0000 replica: track verified primary bin identity Diffstat:
8 files changed, 39 insertions(+), 2 deletions(-)
diff --git a/crates/replica_db/migrations/0023_trade_product_verified_primary_bin.down.sql b/crates/replica_db/migrations/0023_trade_product_verified_primary_bin.down.sql @@ -0,0 +1 @@ +ALTER TABLE trade_product DROP COLUMN verified_primary_bin_id; diff --git a/crates/replica_db/migrations/0023_trade_product_verified_primary_bin.up.sql b/crates/replica_db/migrations/0023_trade_product_verified_primary_bin.up.sql @@ -0,0 +1 @@ +ALTER TABLE trade_product ADD COLUMN verified_primary_bin_id TEXT; diff --git a/crates/replica_db/src/migrations.rs b/crates/replica_db/src/migrations.rs @@ -118,6 +118,11 @@ pub static MIGRATIONS: &[Migration] = &[ up_sql: include_str!("../migrations/0022_trade_product_exact_economics.up.sql"), down_sql: include_str!("../migrations/0022_trade_product_exact_economics.down.sql"), }, + Migration { + name: "0023_trade_product_verified_primary_bin", + up_sql: include_str!("../migrations/0023_trade_product_verified_primary_bin.up.sql"), + down_sql: include_str!("../migrations/0023_trade_product_verified_primary_bin.down.sql"), + }, ]; pub fn run_all_up<E>(executor: &E) -> Result<(), SqlError> diff --git a/crates/replica_db/src/query.rs b/crates/replica_db/src/query.rs @@ -24,6 +24,7 @@ pub struct ReplicaTradeProductSummaryRow { pub price_qty_unit: String, pub listing_addr: Option<String>, pub primary_bin_id: Option<String>, + pub verified_primary_bin_id: Option<String>, pub notes: Option<String>, pub location_primary: Option<String>, } @@ -43,7 +44,7 @@ impl<E: SqlExecutor> ReplicaSql<E> { &self, lookup: &str, ) -> Result<Vec<ReplicaTradeProductSummaryRow>, SqlError> { - let sql = "SELECT tp.id, tp.key, tp.category, tp.title, tp.summary, tp.qty_amt, tp.qty_amt_exact, tp.qty_unit, tp.qty_label, tp.qty_avail, tp.price_amt, tp.price_amt_exact, tp.price_currency, tp.price_qty_amt, tp.price_qty_amt_exact, tp.price_qty_unit, tp.listing_addr, tp.primary_bin_id, tp.notes, loc.location_primary \ + let sql = "SELECT tp.id, tp.key, tp.category, tp.title, tp.summary, tp.qty_amt, tp.qty_amt_exact, tp.qty_unit, tp.qty_label, tp.qty_avail, tp.price_amt, tp.price_amt_exact, tp.price_currency, tp.price_qty_amt, tp.price_qty_amt_exact, tp.price_qty_unit, tp.listing_addr, tp.primary_bin_id, tp.verified_primary_bin_id, tp.notes, loc.location_primary \ FROM trade_product tp \ LEFT JOIN (\ SELECT tpl.tb_tp AS trade_product_id, MIN(COALESCE(gl.label, gl.gc_name, gl.gc_admin1_name, gl.gc_country_name, gl.d_tag)) AS location_primary \ @@ -83,7 +84,7 @@ impl<E: SqlExecutor> ReplicaSql<E> { } let sql = format!( - "SELECT tp.id, tp.key, tp.category, tp.title, tp.summary, tp.qty_amt, tp.qty_amt_exact, tp.qty_unit, tp.qty_label, tp.qty_avail, tp.price_amt, tp.price_amt_exact, tp.price_currency, tp.price_qty_amt, tp.price_qty_amt_exact, tp.price_qty_unit, tp.listing_addr, tp.primary_bin_id, tp.notes, loc.location_primary \ + "SELECT tp.id, tp.key, tp.category, tp.title, tp.summary, tp.qty_amt, tp.qty_amt_exact, tp.qty_unit, tp.qty_label, tp.qty_avail, tp.price_amt, tp.price_amt_exact, tp.price_currency, tp.price_qty_amt, tp.price_qty_amt_exact, tp.price_qty_unit, tp.listing_addr, tp.primary_bin_id, tp.verified_primary_bin_id, tp.notes, loc.location_primary \ FROM trade_product tp \ LEFT JOIN (\ SELECT tpl.tb_tp AS trade_product_id, MIN(COALESCE(gl.label, gl.gc_name, gl.gc_admin1_name, gl.gc_country_name, gl.d_tag)) AS location_primary \ diff --git a/crates/replica_db/tests/full_mode.rs b/crates/replica_db/tests/full_mode.rs @@ -142,6 +142,7 @@ fn full_mode_shaped_query_helpers_cover_cli_reads() { "price_qty_unit": "kg", "listing_addr": listing_addr.clone(), "primary_bin_id": "bin-a", + "verified_primary_bin_id": "bin-a", "notes": "fresh coffee" })); let trade_product_created = db @@ -175,6 +176,7 @@ fn full_mode_shaped_query_helpers_cover_cli_reads() { assert_eq!(rows[0].key, "product-a"); assert_eq!(rows[0].listing_addr.as_deref(), Some(listing_addr.as_str())); assert_eq!(rows[0].primary_bin_id.as_deref(), Some("bin-a")); + assert_eq!(rows[0].verified_primary_bin_id.as_deref(), Some("bin-a")); assert_eq!(rows[0].location_primary.as_deref(), Some("stockholm")); let lookup_rows = db @@ -187,6 +189,10 @@ fn full_mode_shaped_query_helpers_cover_cli_reads() { Some(listing_addr.as_str()) ); assert_eq!(lookup_rows[0].primary_bin_id.as_deref(), Some("bin-a")); + assert_eq!( + lookup_rows[0].verified_primary_bin_id.as_deref(), + Some("bin-a") + ); assert_eq!( db.trade_product_search(&[]).expect("empty search"), diff --git a/crates/replica_db/tests/migration_repairs.rs b/crates/replica_db/tests/migration_repairs.rs @@ -96,4 +96,9 @@ fn run_all_up_repairs_missing_indexes_in_legacy_sqlite_dbs() { .and_then(Value::as_str) .is_some_and(|name| name == "primary_bin_id") })); + assert!(trade_product_columns.iter().any(|row| { + row.get("name") + .and_then(Value::as_str) + .is_some_and(|name| name == "verified_primary_bin_id") + })); } diff --git a/crates/replica_db_schema/src/models/trade_product.rs b/crates/replica_db_schema/src/models/trade_product.rs @@ -31,6 +31,7 @@ pub struct TradeProduct { pub price_qty_unit: String, pub listing_addr: Option<String>, pub primary_bin_id: Option<String>, + pub verified_primary_bin_id: Option<String>, pub notes: Option<String>, } #[cfg_attr(feature = "ts-rs", derive(TS))] @@ -63,6 +64,8 @@ pub struct ITradeProductFields { #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] pub primary_bin_id: Option<String>, #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub verified_primary_bin_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] pub notes: Option<String>, } #[cfg_attr(feature = "ts-rs", derive(TS))] @@ -112,6 +115,8 @@ pub struct ITradeProductFieldsPartial { #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] pub primary_bin_id: Option<serde_json::Value>, #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub verified_primary_bin_id: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] pub notes: Option<serde_json::Value>, } #[cfg_attr(feature = "ts-rs", derive(TS))] @@ -167,6 +172,8 @@ pub struct ITradeProductFieldsFilter { #[cfg_attr(feature = "ts-rs", ts(optional))] pub primary_bin_id: Option<String>, #[cfg_attr(feature = "ts-rs", ts(optional))] + pub verified_primary_bin_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] pub notes: Option<String>, } #[cfg_attr(feature = "ts-rs", derive(TS))] diff --git a/crates/replica_sync/src/ingest.rs b/crates/replica_sync/src/ingest.rs @@ -607,6 +607,7 @@ fn trade_product_fields_from_listing( price_qty_unit, listing_addr: Some(listing_addr.to_string()), primary_bin_id: Some(listing.primary_bin_id.clone()), + verified_primary_bin_id: Some(listing.primary_bin_id.clone()), notes: trade_product_notes_from_listing(listing)?, }) } @@ -697,6 +698,7 @@ fn trade_product_listing_addr_filter(listing_addr: &str) -> ITradeProductFieldsF price_qty_unit: None, listing_addr: Some(listing_addr.to_string()), primary_bin_id: None, + verified_primary_bin_id: None, notes: None, } } @@ -785,6 +787,7 @@ fn trade_product_partial_from_fields(fields: &ITradeProductFields) -> ITradeProd price_qty_unit: Some(Value::from(fields.price_qty_unit.clone())), listing_addr: to_value_opt(fields.listing_addr.clone()), primary_bin_id: to_value_opt(fields.primary_bin_id.clone()), + verified_primary_bin_id: to_value_opt(fields.verified_primary_bin_id.clone()), notes: to_value_opt(fields.notes.clone()), } } @@ -2357,6 +2360,10 @@ mod tests { ); assert_eq!(search_rows[0].title, "Pasture Eggs"); assert_eq!(search_rows[0].primary_bin_id.as_deref(), Some("bin-a")); + assert_eq!( + search_rows[0].verified_primary_bin_id.as_deref(), + Some("bin-a") + ); assert_eq!(search_rows[0].qty_amt, 12.0); assert_eq!(search_rows[0].qty_amt_exact.as_deref(), Some("12")); assert_eq!(search_rows[0].qty_avail, Some(5)); @@ -2395,6 +2402,10 @@ mod tests { assert_eq!(product_rows.len(), 1); assert_eq!(product_rows[0].title, "Market Eggs"); assert_eq!(product_rows[0].primary_bin_id.as_deref(), Some("bin-a")); + assert_eq!( + product_rows[0].verified_primary_bin_id.as_deref(), + Some("bin-a") + ); let archived = listing_event( 502,