Dependent Dropdowns & Not In List Functionality.
- Click Seed

-
As seed is clicked, the dropdown menu appears and for the first dropdown, API call is made.
-
The crop dropdown starts loading.
function inventory_seed_details() { load_report( "crop_list_inventory", { JWT_token: session("JWT_token").getValue(), nursery_id: session("nursery_id").getValue(), // to be removed later language_id: session("language_id").getValue(), // to be removed later }, inventory_seeds_crop_list_success, inventory_seeds_crop_list_error ); show_menu("menu_seeds", true, false); dropdown("dropdown_inventory_seeds_crop").startLoading();}- On error, the dropdown stops loading and a bottom menu appears with an error message.
function inventory_seeds_crop_list_error(response) { dropdown("dropdown_inventory_seeds_crop").stopLoading(); show_alert( response.error_header, response.error_details, ["fas", "fa-exclamation-triangle"], ["red", "dark"], false, false, { operation: "last_open", reset: true }, [ { operation: "last_open", reset: true }, { operation: "last_open", reset: true }, { operation: "last_open", reset: true }, ], [ { visible: true, caption: translate("Understood"), button_type: 1, button_colour: ["grass", "dark"], }, { visible: false }, { visible: false }, ] );}- On success, the crop dropdown is populated with the list of crops.
- If there is only one crop, that crop is selected automatically.

- And the next variety dropdown which is dependent on crop, an api call is made for that and the variety dropdown starts loading.
function inventory_seeds_crop_list_success(response) { var crop_options = []; var use_english = session("language_id").getValue() == 1; response.forEach(item => { var option_text = item.crop_name; if (use_english && item.name_synonym != null) { option_text = item.name_synonym; } else if (!use_english && item.local_crop_name != null) { option_text = item.local_crop_name; } else { option_text = item.crop_name; }
crop_options.push({ value: String(item.crop_id), text: option_text }); });
let inventory_crops_dropdown = dropdown("dropdown_inventory_seeds_crop"); inventory_crops_dropdown.stopLoading(); inventory_crops_dropdown.addOptionsBulk(crop_options);
// Only auto-select if there's exactly one option if (crop_options.length == 1 && inventory_crops_dropdown.getValue() == "default") { var initial_crop_value = crop_options[0].value; inventory_crops_dropdown.setValue(initial_crop_value); inventory_seeds_selected_crop_batch(); }}function inventory_seeds_selected_crop_batch() { if (dropdown("dropdown_inventory_seeds_crop").getValue() != "default") { dropdown("dropdown_inventory_seeds_variety").reset(); dropdown("dropdown_inventory_seeds_packet_weight").reset();
// Get the selected crop_id from the dropdown var selected_crop_id = dropdown("dropdown_inventory_seeds_crop").getValue();
// Load variety list for the selected crop load_report( "variety_seed_list_inventory", { language_id: session("language_id").getValue(), // to be removed later nursery_id: session("nursery_id").getValue(), // to be removed later crop_id: selected_crop_id, JWT_token: session("JWT_token").getValue(), }, inventory_seeds_variety_list_success, inventory_seeds_variety_list_error ); dropdown("dropdown_inventory_seeds_variety").startLoading(); }}- On error, the dropdown stops loading and a bottom menu appears with an error message.
function inventory_seeds_variety_list_error(response) { dropdown("dropdown_inventory_seeds_variety").stopLoading(); show_alert( response.error_header, response.error_details, ["fas", "fa-exclamation-triangle"], ["red", "dark"], false, false, { operation: "last_open", reset: true }, [ { operation: "last_open", reset: true }, { operation: "last_open", reset: true }, { operation: "last_open", reset: true }, ], [ { visible: true, caption: translate("Understood"), button_type: 1, button_colour: ["grass", "dark"], }, { visible: false }, { visible: false }, ] );}- On success, the variety dropdown is populated with the list of varieties.
- If there is only one variety, that variety is selected automatically.
- In case of variety, A not in list option is added.
function inventory_seeds_variety_list_success(response) { var use_english = session("language_id").getValue() == 1; var variety_options = []; response.forEach(item => { var product_text = item.product_name;
if (!use_english) { if (item.local_product_name != null) { product_text = item.local_product_name; } }
variety_options.push({ value: String(item.product_id), text: product_text, }); });
let inventory_variety_dropdown = dropdown("dropdown_inventory_seeds_variety"); inventory_variety_dropdown.stopLoading(); inventory_variety_dropdown.addOptionsBulk(variety_options); // Add "Not in List" option inventory_variety_dropdown.addOption("nil", "Not in List");
// Only auto-select if there's exactly one option if (variety_options.length == 1 && inventory_variety_dropdown.getValue() == "default") { var initial_variety_value = variety_options[0].value; inventory_variety_dropdown.setValue(initial_variety_value); inventory_seeds_selected_variety_batch(); }}- On clicking not in list a separate flow to be initiated.
- So in the HTML on the
selecttag, we add adata-changeattribute to trigger the flow.
<select id="dropdown_inventory_seeds_variety" data-validate='{"rule_name":"inventory_seeds","rules":["nonDefault"],"required":true,"error_header":"Select Variety","error_text":"Please select a variety from the list before proceeding","show_error":false,"show_ok":true,"language":true,"valid":false}' class="ankur_language" data-change='{"operation" : "function", "function_name" : "inventory_seeds_variety_check_nil"}' style="background-color: white !important"> <option value="default" disabled selected style="display: none"> Select Variety </option></select>function inventory_seeds_variety_check_nil() { const variety_dropdown = dropdown("dropdown_inventory_seeds_variety"); if (!variety_dropdown) return;
const current_value = variety_dropdown.getValue();
// If user selected "Not in List", open the add-variety menu if (current_value == "nil") { show_menu("menu_seeds_add_variety", true, false); } // For any normal selected variety (not default and not "nil"), // load the corresponding packet types. else if (current_value != "default") { inventory_seeds_selected_variety_batch(); }}function inventory_seeds_variety_save() { show_processing("Saving Variety", "Please Wait", true);
let variety_seed_params = { nursery_id: session("nursery_id").getValue(), // to be removed later user_id: session("user_id").getValue(), // to be removed later district_id: 1, // to be removed later crop_id: convertToType(dropdown("dropdown_inventory_seeds_crop").getValue(), "integer"), variety_name: inputbox("input_inventory_variety_name").getValue(), JWT_token: session("JWT_token").getValue(), }; console.log("variety_seed_params:", variety_seed_params); execute_command( "add_variety_seed", variety_seed_params, inventory_seeds_variety_save_success, inventory_seeds_variety_save_error );}function inventory_seeds_variety_save_error(response) { show_alert( response.error_header, response.error_details, ["fas", "fa-exclamation-triangle"], ["red", "dark"], false, false, { operation: "last_open", reset: true }, [ { operation: "last_open", reset: true }, { operation: "last_open", reset: true }, { operation: "last_open", reset: true }, ], [ { visible: true, caption: translate("Understood"), button_type: 1, button_colour: ["grass", "dark"], }, { visible: false }, { visible: false }, ] );}function inventory_seeds_variety_save_success(response) { hide_processing(true);
const variety_dropdown = dropdown("dropdown_inventory_seeds_variety"); const new_variety_name = inputbox("input_inventory_variety_name").getValue();
const product_id = response.product_id; const new_variety_value = convertToType(product_id, "string") || new_variety_name;
variety_dropdown.addOption(new_variety_value, new_variety_name); variety_dropdown.setValue(new_variety_value);
// Ensure "Not in List" stays as the last option variety_dropdown.removeOption("nil"); variety_dropdown.addOption("nil", "Not in List");
show_menu("menu_seeds", false, false); console.log("inventory_variety_save_success response:", new_variety_name);}