From 2df5908fcae92754f9a2353b5095932c44989e25 Mon Sep 17 00:00:00 2001 From: 4aiman Date: Sat, 30 May 2026 15:33:59 +0300 Subject: [PATCH] Fix product save: add name_lower, product-only micro fields, strawberry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: addProduct INSERT didn't include name_lower column. Search uses WHERE name_lower LIKE — products with NULL name_lower never found. User entered 20 min of strawberry data for nothing. Fixes: - addProduct: includes name_lower = name.toLowerCase() - Added phosphorus_mg and carotene_mcg to addProduct INSERT - PRODUCT_MICRO_FIELDS: subset of MICRO_FIELDS for product forms (excludes supplement-only fields: omega, EPA/DHA, fish oil etc.) - showAddProductModal and showEditProductModal use PRODUCT_MICRO_FIELDS - Rust: removed stale inserted+=1 line - Seed DB v8: added "Клубника свежая" (alias for "Земляника садовая") --- frontend/data/products_seed.db | Bin 8249344 -> 8249344 bytes frontend/js/db.js | 12 +++--- frontend/js/main.js | 16 ++++++-- src-tauri/src/lib.rs | 72 ++++++++++++++------------------- 4 files changed, 50 insertions(+), 50 deletions(-) diff --git a/frontend/data/products_seed.db b/frontend/data/products_seed.db index f93970bd0f0a7f09fb11e2aeb974a000dc879cfe..c63b99886d53d93cdb68b7a90d157c104979490a 100644 GIT binary patch delta 563 zcmaKk$4*oM07c(pkeQ()U}KbH!Gfq1u>&gT*u{<=#ol{m642yq)rB!(BcCuB1%om1 z4|puy7`J|b3lnvA?mWp|oaE*VeEAt0$n9lK#Y7OqzXkuOdezVpPK@-vtql{;kWlg@ zCiyZ&3M4KGNlKv+l-mk!w|n`ESVGzC~Gh6DH$&Q zRh(bcSvZvZoqSzU-Z92--~0GjGVER%!LyXZqS8f7P6ylHLD)TPG%_vmlVG>!fFnh6X30uQOVBLDyZ delta 461 zcmYMs*)kOX0D$3hoWpTAwoWN2exy>eq_SkGv?;V$(n?9R*q0(^Oc(!kuFQ0kM_|S{ zw;sR?bWzh=bYaHi&Oh_r{>$6{Ld*NU=jZGM0+By~gW@aYHNl;YfuDuJ$TuV?NfMH< zBuk1!Bvqo4Cg~EB49S!%$(9^BByl+`M;4(XH!@=zYhW9gD^>5*P}B7O2yp2>50Aur{X^vi&}mNzmeZ)Hf{$*_#b zd-)(A<&%ucn2gJWOv;o@%Z$v*oP3rqGA|4ARTj5c3a`ggqkDm@=FGl~wb*VfoZg!D cBf46&KKVPr+Tdm+QS~>}8%*r|T1ra!4 { + const microHtml = PRODUCT_MICRO_FIELDS.map(f => { return `
@@ -1403,7 +1403,7 @@ function showAddProductModal(onSaved) { carbs_per_100g: parseFloat(overlay.querySelector("#np-carbs").value) || null, source: "manual", }; - for (const f of MICRO_FIELDS) { + for (const f of PRODUCT_MICRO_FIELDS) { let val = parseFloat(overlay.querySelector(`#np-${f.key}`).value) || null; if (val && f.mult) val *= f.mult; data[f.key] = val; @@ -1414,7 +1414,7 @@ function showAddProductModal(onSaved) { } function showEditProductModal(p, onSaved) { - const microHtml = MICRO_FIELDS.map(f => { + const microHtml = PRODUCT_MICRO_FIELDS.map(f => { let val = p[f.key] ?? ""; if (val && f.mult) val = Math.round(val / f.mult * 1000) / 1000; return `
@@ -1461,7 +1461,7 @@ function showEditProductModal(p, onSaved) { parseFloat(overlay.querySelector("#ep-carbs").value) || null, ]; let idx = 6; - for (const f of MICRO_FIELDS) { + for (const f of PRODUCT_MICRO_FIELDS) { let val = parseFloat(overlay.querySelector(`#ep-${f.key}`).value) || null; if (val && f.mult) val *= f.mult; sets.push(`${f.key}=?${idx}`); @@ -3492,6 +3492,14 @@ const MICRO_FIELDS = [ { key: "lycopene_mg", label: "Ликопин (мг)" }, ]; +// Subset of MICRO_FIELDS that exist as columns in the products table +const PRODUCT_MICRO_FIELDS = MICRO_FIELDS.filter(f => [ + "vit_a_mcg", "vit_c_mg", "vit_d_mcg", "vit_e_mg", "vit_k_mcg", + "vit_b1_mg", "vit_b2_mg", "vit_b3_mg", "vit_b6_mg", "vit_b12_mcg", "folate_mcg", + "calcium_mg", "iron_mg", "magnesium_mg", "zinc_mg", "copper_mg", + "selenium_mcg", "potassium_mg", "sodium_mg", "phosphorus_mg", "carotene_mcg", +].includes(f.key)); + async function renderSupplements(container) { const supps = await DB.getSupplements(); diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 9dcfeba..f96293e 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -105,9 +105,7 @@ fn import_seed_products(app: tauri::AppHandle) -> Result { let seed_db = rusqlite::Connection::open(&seed_path).map_err(|e| e.to_string())?; - let seed_version: i64 = seed_db.query_row( - "SELECT version FROM seed_meta LIMIT 1", [], |r| r.get(0) - ).map_err(|e| e.to_string())?; + let seed_version: i64 = seed_db.query_row("SELECT version FROM seed_meta LIMIT 1", [], |r| r.get(0)).map_err(|e| e.to_string())?; // tauri-plugin-sql may store DB in app_config_dir (desktop: ~/.config/...) // or app_data_dir (desktop: ~/.local/share/...). Check both. @@ -123,17 +121,13 @@ fn import_seed_products(app: tauri::AppHandle) -> Result { }; let main_db = rusqlite::Connection::open(&main_path).map_err(|e| e.to_string())?; - let current_version: i64 = main_db.query_row( - "SELECT COALESCE((SELECT value FROM profile WHERE key='seed_version'), '0')", - [], |r| { + let current_version: i64 = main_db.query_row("SELECT COALESCE((SELECT value FROM profile WHERE key='seed_version'), '0')", [], |r| { let s: String = r.get(0)?; Ok(s.parse::().unwrap_or(0)) } ).unwrap_or(0); - if current_version >= seed_version { - return Ok(format!("up-to-date:v{}", current_version)); - } + if current_version >= seed_version {return Ok(format!("up-to-date:v{}", current_version));} let total: usize = seed_db.query_row("SELECT COUNT(*) FROM products", [], |r| r.get(0)) .map_err(|e| e.to_string())?; @@ -157,8 +151,7 @@ fn import_seed_products(app: tauri::AppHandle) -> Result { // ATTACH seed DB to main DB and do bulk INSERT + UPDATE in SQL (no per-row loop) let seed_str = seed_path.to_str().unwrap_or_default().to_string(); - main_db.execute("ATTACH DATABASE ?1 AS seed", rusqlite::params![seed_str]) - .map_err(|e| e.to_string())?; + main_db.execute("ATTACH DATABASE ?1 AS seed", rusqlite::params![seed_str]).map_err(|e| e.to_string())?; let mut inserted = 0usize; @@ -180,11 +173,8 @@ fn import_seed_products(app: tauri::AppHandle) -> Result { iron_mg, vit_a_mcg, vit_b1_mg, vit_b2_mg, vit_b3_mg, vit_c_mg, vit_e_mg, carotene_mcg, COALESCE(nutrients_guessed, 0) FROM seed.products", - [], - ).map_err(|e| e.to_string())?; + [] ).map_err(|e| e.to_string())?; - inserted += 1; - processed += 1; // Update KBJU + micronutrients for existing store products (seed already attached above) // Sync nutrients_guessed flag @@ -193,32 +183,32 @@ fn import_seed_products(app: tauri::AppHandle) -> Result { WHERE (nutrients_guessed IS NULL OR nutrients_guessed = 0) AND name_lower IN (SELECT s.name_lower FROM seed.products s WHERE s.nutrients_guessed = 1)" ); - // Update KBJU + micronutrients for store products that user hasn't manually edited. - // This ensures seed corrections (e.g. bread 268→243) flow through to app DB. - let _ = main_db.execute_batch( - "UPDATE main.products SET - kcal_per_100g = (SELECT s.kcal_per_100g FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), - protein_per_100g = (SELECT s.protein_per_100g FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), - fat_per_100g = (SELECT s.fat_per_100g FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), - carbs_per_100g = (SELECT s.carbs_per_100g FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), - fiber_per_100g = (SELECT s.fiber_per_100g FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), - nutrients_guessed= COALESCE((SELECT s.nutrients_guessed FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), nutrients_guessed), - sodium_mg = COALESCE((SELECT s.sodium_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), sodium_mg), - potassium_mg = COALESCE((SELECT s.potassium_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), potassium_mg), - calcium_mg = COALESCE((SELECT s.calcium_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), calcium_mg), - magnesium_mg = COALESCE((SELECT s.magnesium_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), magnesium_mg), - phosphorus_mg = COALESCE((SELECT s.phosphorus_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), phosphorus_mg), - iron_mg = COALESCE((SELECT s.iron_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), iron_mg), - vit_a_mcg = COALESCE((SELECT s.vit_a_mcg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), vit_a_mcg), - vit_b1_mg = COALESCE((SELECT s.vit_b1_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), vit_b1_mg), - vit_b2_mg = COALESCE((SELECT s.vit_b2_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), vit_b2_mg), - vit_b3_mg = COALESCE((SELECT s.vit_b3_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), vit_b3_mg), - vit_c_mg = COALESCE((SELECT s.vit_c_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), vit_c_mg), - vit_e_mg = COALESCE((SELECT s.vit_e_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), vit_e_mg), - carotene_mcg = COALESCE((SELECT s.carotene_mcg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), carotene_mcg) - WHERE (main.products.user_edited IS NULL OR main.products.user_edited = 0) - AND main.products.source IN ('magnit', '5ka', 'kdv', 'ion') - AND main.products.name_lower IN (SELECT s2.name_lower FROM seed.products s2)" + // Update KBJU + micronutrients for store products that user hasn't manually edited. + // This ensures seed corrections (e.g. bread 268→243) flow through to app DB. + let _ = main_db.execute_batch( + "UPDATE main.products SET + kcal_per_100g = (SELECT s.kcal_per_100g FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), + protein_per_100g = (SELECT s.protein_per_100g FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), + fat_per_100g = (SELECT s.fat_per_100g FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), + carbs_per_100g = (SELECT s.carbs_per_100g FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), + fiber_per_100g = (SELECT s.fiber_per_100g FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), + nutrients_guessed= COALESCE((SELECT s.nutrients_guessed FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), nutrients_guessed), + sodium_mg = COALESCE((SELECT s.sodium_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), sodium_mg), + potassium_mg = COALESCE((SELECT s.potassium_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), potassium_mg), + calcium_mg = COALESCE((SELECT s.calcium_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), calcium_mg), + magnesium_mg = COALESCE((SELECT s.magnesium_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), magnesium_mg), + phosphorus_mg = COALESCE((SELECT s.phosphorus_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), phosphorus_mg), + iron_mg = COALESCE((SELECT s.iron_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), iron_mg), + vit_a_mcg = COALESCE((SELECT s.vit_a_mcg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), vit_a_mcg), + vit_b1_mg = COALESCE((SELECT s.vit_b1_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), vit_b1_mg), + vit_b2_mg = COALESCE((SELECT s.vit_b2_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), vit_b2_mg), + vit_b3_mg = COALESCE((SELECT s.vit_b3_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), vit_b3_mg), + vit_c_mg = COALESCE((SELECT s.vit_c_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), vit_c_mg), + vit_e_mg = COALESCE((SELECT s.vit_e_mg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), vit_e_mg), + carotene_mcg = COALESCE((SELECT s.carotene_mcg FROM seed.products s WHERE s.name_lower = main.products.name_lower LIMIT 1), carotene_mcg) + WHERE (main.products.user_edited IS NULL OR main.products.user_edited = 0) + AND main.products.source IN ('magnit', '5ka', 'kdv', 'ion') + AND main.products.name_lower IN (SELECT s2.name_lower FROM seed.products s2)" ); // Update seed version AFTER all inserts+updates succeed