/*************************** RWL July 10,2007 Added setInventoryDefaultValues() RWL Jul 17 2007 Updated setInventoryDefaultValues() to work better with IE 6 RWL Mar 22 2008 Various updates, fixes new functionality RWL Mar 25 2008 Various updates to allow auto-booking of hidden groups, including creating new group and inv_groups objects ***************************/ //hide_inv_items(0); var priceData = new Array(); var groupFlds = new Array(); //these varialbes are set and used by this file and (usually) by /include/subforms/trip_bookings_inventory_requirements.php var num_travellers=null; var inv_items=new Array(); var inv_groups= new invGroups; function validify(value) { if (value == null || value == '' || isNaN(value)) return 0; else return value; } function setValue(fldName, value) { fld = MM_findObj(fldName); if (fld!= null) fld.value = value; } function setText(fldName, value) { fld = MM_findObj(fldName); if (fld != null){ fld.innerHTML = value; } } function getText(fldName) { fld = MM_findObj(fldName); if(fld==null) return ''; return fld.innerHTML; } function getValue(fldName) { fld = MM_findObj(fldName); if(fld==null) return null; return fld.value; } function changeOrder(inv_id, child_id, fld, label) { totalTravelers = (document.frmGetPrice && document.frmGetPrice.totalTravellers && document.frmGetPrice.totalTravellers.value) ? document.frmGetPrice.totalTravellers.value : totalTravelers; var error = false; var num=fld.value; id=(child_id!=null) ? child_id : inv_id; if(inv_items[id]!='undefined'){ if(!inv_items[id].checkQty(true, totalTravelers, false)){ //An error was found //if(inv_items[id].fld.onchange) setTimeout('inv_items[id].fld.onchange()', 500); return false; }else{ return true; } } return true; } //Function to calculate new running total price of trip //function updatePrice(inv_id, child_id, tripPrice){ function updatePrice(inv_id, child_id, qty, includeTaxes, tripPrice, skipCalTotalCostCall){ var subTotal=0; //no tax var total=0; //tax //update the qty of this item if(child_id==null && inv_items[inv_id]){ inv_items[inv_id].qty=parseInt(qty); }else if(child_id!=null && inv_items[inv_id].children[child_id]){ inv_items[inv_id].children[child_id].qty=parseInt(qty); } //alert(inv_items); for(inv_item in inv_items){ // alert(inv_item); subTotal+=inv_items[inv_item].getPrice(false, false); total+=inv_items[inv_item].getPrice(true, false); //alert(subTotal); } if(tripPrice!=null && tripPrice!='' && !isNaN(tripPrice)){ subTotal += tripPrice; total += tripPrice; } if(includeTaxes){ setText('price_so_far', Math.round((total)*100)/100); }else{ setText('price_so_far', Math.round((subTotal)*100)/100); } setValue('inv_subTotal', Math.round((subTotal)*100)/100); setValue('inv_total', Math.round((total)*100)/100); //if(calTotalCost!='undefined') calTotalCost(); if(!skipCalTotalCostCall && window.calTotalCost && calTotalCost!='') calTotalCost(); } function validate_inventory(frm){ //Go through the group fields and check the correct number of items have been selected totalTravelers = parseInt((document.frmGetPrice && document.frmGetPrice.totalTravellers && document.frmGetPrice.totalTravellers.value) ? document.frmGetPrice.totalTravellers.value : totalTravelers); for(group_id in inv_groups.groups){ aGroup = inv_groups.getGroup(group_id); if(typeof(aGroup)=='object' && aGroup.label){ //Get the group's details grpName=aGroup.label; min_req_pt=aGroup.min_req_pt; min_req_pp =aGroup.min_req_pp; max_pt =aGroup.max_allowed_pt; num_booked=0; for(inv_id in aGroup.members){ //Get the field for this member fld = aGroup.members[inv_id].fld; if(fld!=null && fld.value && fld.value>0){ //Check the booked amounts don't exceed the max available for this item num_booked += parseInt(fld.value); } } if(min_req_pp!=null && num_bookedmax_pt){ alert('You can only select up to ' + max_pt + ' items from the "' + grpName + '" for this trip. Please check your inventory bookings for this group before continuing'); return false; } } } //Ensure no qtys exceed the number available for selected items for(inv_item in inv_items){ //qty=inv_items[inv_item].getQty(); if(!inv_items[inv_item].checkQty(true, totalTravelers)){ return false; } } return true; } function setInventoryDefaultValues(frmName, fldName){ /* //Loop through all items and call their initialize function with the passed value for(inv_item in inv_items){ inv_items[inv_item].initialize(parseInt(document.forms[frmName].elements[fldName].value)); } */ autoAdjustReqInvQtys(true); if(window.updatePrice && updatePrice!='') updatePrice(); } function autoAdjustReqInvQtys(force){ //This function can be run at any time. Reads the totalTravellers, and sets qty for all inv_items // that are required, or have imposed min/max requirements if(!inventory_mode || inventory_mode==0 || !inv_items || typeof(num_travellers)=='undefined') return true; //Inventory not being used, bail for(inv_id in inv_items){ //alert(inv_items[inv_id]); if(inv_items[inv_id].isHidden || force) inv_items[inv_id].checkQty(false, parseInt(num_travellers), true); } //Are there any hidden groups? if so, auto-book as needed for(group_id in inv_groups.groups){ aGroup = inv_groups.getGroup(group_id); if(typeof(aGroup)=='object' && aGroup.label && (aGroup.isHidden || force)){ //Auto-book this group of items //First, reset it aGroup.resetGroupBookings(); //Only do a group auto book if num_travellers is above zero if(typeof(num_travellers)=='number' && num_travellers>0 && !aGroup.autoBook(num_travellers, false, force)){ //Not all the required spaces could be auto-booked alert('This exceeds the number of spaces available. Please book no more than ' + aGroup.getQtyAvail() + ' participants'); } } } //For safety sake, update the price updatePrice(); } //Call back function from AJAX fetchInvAvailabilityForTripSeat() call from ajax/inventory_calls.js function fetchInvAvailabilityForTripSeat_CallBack(resp){ //If inv items have been initialized, go through each and set the qtyAvail based on returned values //If the qtyAvil is 0, hide the item from the subform //Also note the total spaces available based on the selected inventory/trip var group_displays=new Array(); //Used to set the display mode of the entire group. if(typeof resp!='object') return resp; //Not using inventory! var is_return_trip = resp['is_return_trip']; var r = ""; if (is_return_trip == true) r = "return_"; //If needed, adjust the available spaces field availSpaceFld= document.getElementById(r+'available_seats'); if(resp['availableSpaces'] && resp['availableSpaces']!=availSpaceFld.value){ availSpaceFld.value=resp['availableSpaces']; } items = (resp['items']) ? resp['items'] : new Array(); for(inv_id in inv_items){ //alert(inv_items[inv_id]); if(items[inv_id] && items[inv_id]['remaining_space'] && parseInt(items[inv_id]['remaining_space'])>0){ inv_items[inv_id].setQtyAvail(parseInt(items[inv_id]['remaining_space'])); //Override the price with price for this trip date if(items[inv_id] && items[inv_id]['price']!='undefined'){ inv_items[inv_id].setBasePrice(items[inv_id]['price']); } if(inv_items[inv_id].row) { inv_items[inv_id].row.style.display=''; //We first have to display the row in case they are changing to a date that it is avail. } //inv_items[inv_id].fld.style.display='block'; //If the qty is zero, hide the field if(items[inv_id]['remaining_space']==0 && inv_items[inv_id].fld) { //inv_items[inv_id].fld.style.display='none'; //if(inv_items(inv_id].row != null){ // inv_items[inv_id].row.style.display='none'; //} if(group_displays[items[inv_id]['group_id']] != 'visible') { //If the group has already bee set to show then we don't try to hide it. group_displays[items[inv_id]['group_id']]='hidden'; //Set the group to be hidden. } //added to hide the item if it is not avail if(inv_items[inv_id].row) { inv_items[inv_id].row.style.display='none'; } //end } else { group_displays[items[inv_id]['group_id']]='visible'; //If the item is avail then we set the group to be shown. } } else{ //Item isn't in array or qty=0 - same thing. Set qty avail to 0 and hide the field inv_items[inv_id].setQtyAvail(0); if(group_displays[items[inv_id]['group_id']] != 'visible') { //If the group has already bee set to show then we don't try to hide it. group_displays[items[inv_id]['group_id']]='hidden'; //Set the group to be hidden. } //Hide the field if(inv_items[inv_id].fld) { //inv_items[inv_id].fld.style.display='none'; if(inv_items[inv_id].row){ inv_items[inv_id].row.style.display='none'; } } //added to hide the item if it is not avail if(inv_items[inv_id].row) { inv_items[inv_id].row.style.display='none'; } //end } } //show/hide groups for(var gid in group_displays) { if(gid != '' && document.getElementById(r+'inv_booking_table_'+gid)) { document.getElementById(r+'inv_booking_table_'+gid).style.visibility=group_displays[gid]; } } //end //Show the initialized inventory table inventory_items=document.getElementById(r+'inv_booking_row'); if(inventory_items!=null && visibleItems > 0){ inventory_items.style.visibility='visible'; // inventory_items.style.display='block'; //inventory_items.style.display='table-row'; } inventory_items=document.getElementById(r+'inv_booking_cell'); if(inventory_items!=null && visibleItems > 0){ //inventory_items.style.visibility='visible'; inventory_items.style.display='block'; //inventory_items.style.display='table-row'; } } //Collection of inventory groups function invGroups(){ var invGroups; this.groups=new Array(); this.addGroup = igs_addGroup; this.getGroup = igs_getGroup; function igs_addGroup(id, label){ this.groups[id]=new inventoryGroup(id, label); return this.groups[id]; } function igs_getGroup(id){ if(typeof(this.groups[id])!='undefined'){ return this.groups[id]; }else{ return null; } } } //inventory group class function inventoryGroup(id, label){ var inventoryGroup; this.id=id; this.label=label; this.min_req_pt=null; this.min_req_pp=null; this.max_allowed_pt=null; this.isHidden=null; this.members=new Array(); this.addItem=ig_addItem; this.autoBook=ig_autoBook; this.getQtyAvail = ig_getQtyAvail ; this.isRequired = ig_isRequired; this.resetGroupBookings = ig_resetGroupBookings; function ig_addItem(item){ if(typeof(item)=='object' && typeof(item.label)!='undefined'){ this.members[item.id]=item; } } function ig_getQtyAvail(){ //return the total available spaces in this group (i.e. across all it's members) qtyAvail=0; for(inv_id in this.members){ qtyAvail += this.members[inv_id].qtyAvail; } //If this group has a min_req_pp, that'll adjust the qtyAvail if(this.min_req_pp!=null && this.min_req_pp>0) qtyAvail=Math.floor(qtyAvail/this.min_req_pp); return qtyAvail; } function ig_isRequired(){ return ((this.min_req_pp!=null && this.min_req_pp>0) | (this.min_req_pt!=null && this.min_req_pt>0)); } function ig_resetGroupBookings(){ for(inv_id in this.members){ this.members[inv_id].setQty(0, true); } } function ig_autoBook(num_to_book, resetFirst, force){ //'Book' the specified number of seats across the items in this group - ONLY if it's a required and hidden group (unless FORCE is true) //If resetFirst is true, first set all qtys of all members to zero (i.e. remove existing bookings) if(typeof(num_to_book)!='number' || num_to_book<=0) return true; //This group may have a min_pp set... if so, that will change the number of bookings we actually need to make if(this.min_req_pp!=null && this.min_req_pp>0) num_to_book=parseInt(num_to_book*this.min_req_pp); //If there is a minimum per trip, make sure we meet it here if(this.min_req_pt!=null && this.min_req_pt>0) num_to_book=Math.max(this.min_req_pt, num_to_book); /* //Items in this group may have a min-pp set as well. if so, up the number of bookings we actually need to make minPerPerson=0; for(inv_id in this.members){ anItem=this.members[inv_id]; if(anItem.isRequired() && anItem.min_req_pp>0){ minPerPerson += anItem.min_req_pp; } } if(minPerPerson>0){ //This group requires this many bookings PER person num_to_book=num_to_book*minPerPerson; } */ //return if the group isn't hidden and we aren't forcing bookings if(!force && (!this.isHidden | !this.isRequired())) return true; //Do a reset loop here as we need to reset ALL items in the group if(resetFirst){ this.resetGroupBookings(); } //Currently we book on a first-come first-served basis. This logic could incorporate more advanced priorities in the future num_booked=0; //num_to_book=qty; optionalItems=new Array(); //Loop through each member of the group and book accordingly for(inv_id in this.members){ anItem=this.members[inv_id]; //We should book required items first. Check if the item is required, if not, save it to process later (if needed) if(anItem.isRequired()){ //Book as many spaces as needed and are available //anItem.setQty(num_to_book-num_booked, true); anItem.setQty(Math.max(anItem.min_req_pp*num_to_book, num_to_book-num_booked), true); //How many were actually booked? num_booked += anItem.getQty(); //How many do we still need to book? //num_to_book -= num_booked; }else{ //Save this item to check later optionalItems[inv_id]=anItem; } //*** RWL. Dec. 4, skip this.. if all items are required (for example sub-times) this logic isn't correct //if(num_booked==num_to_book) return true; //Exit if we've booked everything } //We have processed all required items. If needed, now book the optional ones //Only do this is SOME of the items have been booked, i.e. we need more space. If none were booked //we likely don't need to book anything here if(num_booked>0 && num_booked=num_to_book); } } //Inventory Item class function inventoryItem(label, fld, inv_id, price, canBeShared, canOverlap, qtyAvail, row){ var inventoryItem; this.label=label; this.fld=fld; this.row=row; this.id=inv_id; this.basePrice=(price!=null && price!='' && !isNaN(price)) ? parseFloat(price) : 0; this.canBeShared=canBeShared; this.canOverlap=canOverlap; this.isHidden=false; this.min_req_pp=null; this.max_allowed_pp=null; this.min_req_pt=null; this.isGrouped=false; this.max_allowed_pt=null; this.isChild=false; //true for all children this.parent=null; //for child items, the parent inv item object this.qtyAvail=(qtyAvail=='') ? null : parseInt(qtyAvail); this.taxes = new Array(); this.children = new Array(); this.addChild = ii_addChild; this.getPrice = ii_getPrice; this.setBasePrice = ii_setBasePrice; this.setQtyAvail = ii_setQtyAvail; this.getQty = ii_getQty; this.setQty = ii_setQty; this.checkQty = ii_checkQty; this.addTax = ii_addTax; this.initialize = ii_initialize; this.setQtyRequirements = ii_setQtyRequirements; this.isRequired = ii_isRequired; this.qty=0; //qty ordered return this; } function ii_isRequired(){ return ((this.min_req_pp!=null && this.min_req_pp>0) | (this.min_req_pt!=null && this.min_req_pt>0)); } function ii_setQtyRequirements( min_req_pp, max_allowed_pp, min_req_pt, max_allowed_pt){ //Given string values to easily handle nulls this.min_req_pp = (min_req_pp!='undefined' && min_req_pp!='' && min_req_pp!=null && min_req_pp>0) ? parseInt(min_req_pp) : null; this.max_allowed_pp = (max_allowed_pp!='undefined' && max_allowed_pp!='' && max_allowed_pp!=null && max_allowed_pp>0) ? parseInt(max_allowed_pp) : null; this.min_req_pt = (min_req_pt!='undefined' && min_req_pt!='' && min_req_pt!=null && min_req_pt>0) ? parseInt(min_req_pt) : null; this.max_allowed_pt = (max_allowed_pt!='undefined' && max_allowed_pt!='' && max_allowed_pt!=null && max_allowed_pt>0) ? parseInt(max_allowed_pt) : null; } function ii_getPrice( includeTaxes, includeChildren ){ var price =0; if(includeTaxes){ for(tax in this.taxes){ //alert(this.qty); price += (!isNaN(this.qty)) ? this.qty*this.basePrice*this.taxes[tax]/100 : 0; //alert(price); } } if(includeChildren){ for(child in this.children){ price += this.children[child].getPrice( includeTaxes ); // alert(price); } } price += (!isNaN(this.qty)) ? this.basePrice*this.qty : 0; //alert(price); return price; } function ii_setBasePrice( price ){ this.basePrice=price; //Try to set price strings for subform if(this.isChild==true){ if(this.parent && this.parent.id){ if(price > 0) { //if the item cost nothing, then don't show the price setText('price_str_'+this.parent.id+'_'+this.id, number_format(price,2,'.',',')); } } }else{ if(price > 0) { //if the item cost nothing, then don't show the price setText('price_str_'+this.id, number_format(price,2,'.',',')); } } return true; } function ii_addChild( child ){ this.children[child.id]=child; } function ii_addTax( taxName, taxVal){ this.taxes[taxName]=parseFloat(taxVal); } function ii_initialize(totalTravelers){ totalTravelers=parseInt(totalTravelers); //alert(this.label + ': ' + totalTravelers); //Initialize the field //1. Set the default value as the min_req_pp*totalTravelers if(this.fld && this.min_req_pp!=null && this.min_req_pp!='' && this.min_req_pp!='null' && !isNaN(this.min_req_pp) && this.min_req_pp!=0 && totalTravelers!='' && !isNaN(totalTravelers) && totalTravelers!=0 && this.fld.value==''){ defaultValue = totalTravelers*this.min_req_pp; //this.fld.value= (!isNaN(defaultValue) && defaultValue!='' && defaultValue!=0) ? defaultValue : null; //this.qty=this.fld.value; this.setQty(defaultValue, true); //if(window.updatePrice && updatePrice!='') updatePrice(); //this.fld.onChange(); return true; } return true; } function ii_setQtyAvail(qty){ this.qtyAvail=parseInt(qty); if(this.isChild==true){ if(this.parent && this.parent.id){ setValue('child_remaining_space_'+this.parent.id+'_'+this.id, parseInt(qty)); //Set the visible string too setText('str_child_inv_item_qty_remaining_space_'+this.parent.id+'_'+this.id, parseInt(qty)); } }else{ setValue('inv_remaining_space_'+this.id, parseInt(qty)); //Set the visible string too setText('str_inv_item_qty_remaining_space_'+this.id, parseInt(qty)); } return true; } function ii_getQty(){ //Get the qty (value) of this field if(this.fld && this.fld.value){ //For safety, get the actual value of the field if it's set this.qty=parseInt(this.fld.value); } return this.qty; } function ii_setQty(qty, forceOverwrite){ //Set the qty (value) of this field //Don't allow over booking if(this.qtyAvail!=null){ this.qty=Math.min(qty, this.qtyAvail); }else{ //No max qtyAvail has been set, i.e. unlimited are available this.qty=qty; } if(this.fld && (forceOverwrite || this.fld.value=='' || this.fld.value==null)){ this.fld.value=this.qty; } //Only return true if all the requested qty was booked return (qty==this.qty); } function ii_checkQty(raiseAlert, totTravellers, forceOverwrite){ //alert('Checking QTY for ' + this.label + ' for ' + totTravellers + ' travellers'); /* 10/01/30 Grouped items SHOULD still check to not exceed qtu avail. Modify code below to only check avail qty on grouped items //If this item is grouped we can't check it here - the group settings override if(this.isGrouped) return true; */ //If this is a hidden required field, and forceOverwrite, set the value of the hidden field to the appropriate amount if(!this.isGrouped && this.isHidden && (this.min_req_pp!=null || this.min_req_pt!=null)){ reqQty = this.min_req_pp*totTravellers; //check this is sufficient amount reqQty = Math.max(reqQty, this.min_req_pt); this.setQty(reqQty, true); } //Check the requested qty doesn't exceed the number available. Check children too qty=this.getQty(); error=false; if(qty>this.qtyAvail){ if(raiseAlert) alert('This exceeds the maximum number of ' + this.label + 's available for this trip. Please select a number no greater than ' + this.qtyAvail); this.setQty(this.qtyAvail, forceOverwrite); raiseAlert=false; //Prevent another alert, but do the checking below error=true; } if(!this.isGrouped && totTravellers != 'undefined' && this.min_req_pp!=null && qtythis.max_allowed_pt){ //Check the max_allowed_pt is being met if(raiseAlert) alert('This exceeds the maximum number of ' + this.label + 's allowed on a trip. Please select a number no greater than ' + this.max_allowed_pt); this.setQty(this.max_allowed_pt, forceOverwrite); raiseAlert=false; //Prevent another alert, but do the checking below error=true; } */ //Check the max_allowed_pp is being met if(!this.isGrouped && totTravellers != 'undefined' && this.max_allowed_pp!=null && qty>totTravellers*this.max_allowed_pp){ if(raiseAlert) alert('This exceeds the maximum number of ' + this.label + 's allowed per person on a trip. You can only book ' + this.max_allowed_pp + ' items per person per trip'); this.setQty(totTravellers*this.max_allowed_pp, forceOverwrite); raiseAlert=false; //Prevent another alert, but do the checking below error=true; } //Check the min_req_pt is being met if(!this.isGrouped && this.min_req_pt!=null && qty