lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

commit fcdfbe7afa29d55c9974ba9b6576b827cfa36aa4
parent 83c35d8a0fa4c089a578ca1fdddf6d8c864e51cb
Author: triesap <tyson@radroots.org>
Date:   Wed, 29 Apr 2026 19:34:09 +0000

replica: project listing primary bins

Diffstat:
Acrates/replica_db/migrations/0021_trade_product_primary_bin_id.down.sql | 1+
Acrates/replica_db/migrations/0021_trade_product_primary_bin_id.up.sql | 1+
Mcrates/replica_db/src/migrations.rs | 5+++++
Mcrates/replica_db/src/query.rs | 5+++--
Mcrates/replica_db/tests/full_mode.rs | 3+++
Mcrates/replica_db/tests/migration_repairs.rs | 5+++++
Mcrates/replica_db_schema/src/models/trade_product.rs | 7+++++++
Mcrates/replica_sync/src/ingest.rs | 5+++++
8 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/crates/replica_db/migrations/0021_trade_product_primary_bin_id.down.sql b/crates/replica_db/migrations/0021_trade_product_primary_bin_id.down.sql @@ -0,0 +1 @@ +ALTER TABLE trade_product DROP COLUMN primary_bin_id; diff --git a/crates/replica_db/migrations/0021_trade_product_primary_bin_id.up.sql b/crates/replica_db/migrations/0021_trade_product_primary_bin_id.up.sql @@ -0,0 +1 @@ +ALTER TABLE trade_product ADD COLUMN primary_bin_id TEXT; diff --git a/crates/replica_db/src/migrations.rs b/crates/replica_db/src/migrations.rs @@ -108,6 +108,11 @@ pub static MIGRATIONS: &[Migration] = &[ up_sql: include_str!("../migrations/0020_trade_product_listing_addr.up.sql"), down_sql: include_str!("../migrations/0020_trade_product_listing_addr.down.sql"), }, + Migration { + name: "0021_trade_product_primary_bin_id", + up_sql: include_str!("../migrations/0021_trade_product_primary_bin_id.up.sql"), + down_sql: include_str!("../migrations/0021_trade_product_primary_bin_id.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 @@ -20,6 +20,7 @@ pub struct ReplicaTradeProductSummaryRow { pub price_qty_amt: u32, pub price_qty_unit: String, pub listing_addr: Option<String>, + pub primary_bin_id: Option<String>, pub location_primary: Option<String>, } @@ -38,7 +39,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_unit, tp.qty_label, tp.qty_avail, tp.price_amt, tp.price_currency, tp.price_qty_amt, tp.price_qty_unit, tp.listing_addr, loc.location_primary \ + let sql = "SELECT tp.id, tp.key, tp.category, tp.title, tp.summary, tp.qty_amt, tp.qty_unit, tp.qty_label, tp.qty_avail, tp.price_amt, tp.price_currency, tp.price_qty_amt, tp.price_qty_unit, tp.listing_addr, tp.primary_bin_id, 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 \ @@ -78,7 +79,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_unit, tp.qty_label, tp.qty_avail, tp.price_amt, tp.price_currency, tp.price_qty_amt, tp.price_qty_unit, tp.listing_addr, loc.location_primary \ + "SELECT tp.id, tp.key, tp.category, tp.title, tp.summary, tp.qty_amt, tp.qty_unit, tp.qty_label, tp.qty_avail, tp.price_amt, tp.price_currency, tp.price_qty_amt, tp.price_qty_unit, tp.listing_addr, tp.primary_bin_id, 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 @@ -138,6 +138,7 @@ fn full_mode_shaped_query_helpers_cover_cli_reads() { "price_qty_amt": 1, "price_qty_unit": "kg", "listing_addr": listing_addr.clone(), + "primary_bin_id": "bin-a", "notes": "fresh coffee" })); let trade_product_created = db @@ -170,6 +171,7 @@ fn full_mode_shaped_query_helpers_cover_cli_reads() { assert_eq!(rows.len(), 1); 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].location_primary.as_deref(), Some("stockholm")); let lookup_rows = db @@ -181,6 +183,7 @@ fn full_mode_shaped_query_helpers_cover_cli_reads() { lookup_rows[0].listing_addr.as_deref(), Some(listing_addr.as_str()) ); + assert_eq!(lookup_rows[0].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 @@ -91,4 +91,9 @@ fn run_all_up_repairs_missing_indexes_in_legacy_sqlite_dbs() { .and_then(Value::as_str) .is_some_and(|name| name == "listing_addr") })); + assert!(trade_product_columns.iter().any(|row| { + row.get("name") + .and_then(Value::as_str) + .is_some_and(|name| name == "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 @@ -27,6 +27,7 @@ pub struct TradeProduct { pub price_qty_amt: u32, pub price_qty_unit: String, pub listing_addr: Option<String>, + pub primary_bin_id: Option<String>, pub notes: Option<String>, } #[cfg_attr(feature = "ts-rs", derive(TS))] @@ -54,6 +55,8 @@ pub struct ITradeProductFields { #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] pub listing_addr: Option<String>, #[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 notes: Option<String>, } #[cfg_attr(feature = "ts-rs", derive(TS))] @@ -95,6 +98,8 @@ pub struct ITradeProductFieldsPartial { #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] pub listing_addr: Option<serde_json::Value>, #[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 notes: Option<serde_json::Value>, } #[cfg_attr(feature = "ts-rs", derive(TS))] @@ -142,6 +147,8 @@ pub struct ITradeProductFieldsFilter { #[cfg_attr(feature = "ts-rs", ts(optional))] pub listing_addr: Option<String>, #[cfg_attr(feature = "ts-rs", ts(optional))] + pub 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 @@ -604,6 +604,7 @@ fn trade_product_fields_from_listing( price_qty_amt, price_qty_unit, listing_addr: Some(listing_addr.to_string()), + primary_bin_id: Some(listing.primary_bin_id.clone()), notes: None, }) } @@ -671,6 +672,7 @@ fn trade_product_listing_addr_filter(listing_addr: &str) -> ITradeProductFieldsF price_qty_amt: None, price_qty_unit: None, listing_addr: Some(listing_addr.to_string()), + primary_bin_id: None, notes: None, } } @@ -755,6 +757,7 @@ fn trade_product_partial_from_fields(fields: &ITradeProductFields) -> ITradeProd price_qty_amt: Some(Value::from(fields.price_qty_amt)), 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()), notes: to_value_opt(fields.notes.clone()), } } @@ -2311,6 +2314,7 @@ mod tests { Some(listing_addr.as_str()) ); 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].qty_amt, 12); assert_eq!(search_rows[0].qty_avail, Some(5)); assert_eq!(search_rows[0].price_amt, 6.0); @@ -2338,6 +2342,7 @@ mod tests { .results; 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")); let archived = listing_event( 502,