File: D:/web/coventrydemolition/changes prev/2020-05-12/prev/apputils.asp
<%
' ============
' apputils.asp
' ============
' Version 5.57l (06/04/20)
' ============
' HISTORY
' ============
' (SS,20/06/05) added ShowSubCategoryMenu
' (SS,20/06/05) added SUBSTRING(highlights.ProductCode FROM 1 FOR 8) <> 'WEBTEXT-' to Sub ShowHighlights
' (SS,20/06/05) added Function GetWebText(AWebTextCode) to get text from hightlights page
' (SS,24/06/05) improved Sub ShowDeliveryCostInfo to not show duplicate info if below and above values the same
' (SS,27/06/05) improved Sub ShowHighlights so that nothing shows if no product highlights, and invalid
' products do not show instead of causing error
' (SS,06/07/05) added to Sub ShowDeliveryCostInfo: Iif(Application("UKText") etc to override the default country names
' (SS,06/08/05) Major changes for direct credit card payments
' (SS,08/08/05) Started using UltraEdit instead of TextPad for editing
' (SS,09/08/05) Add () to end of all functions and subs that had no parameters to ensure they appear in function list in UltraEdit
' (SS,03/03/06) Changes to Sub ShowCategoryMenu() to allow alternate colour category menu
' (SS,22/03/06) Added Left(,255) to AddReferrerToDatabase to limit size of strings to 255
' (SS,29/04/06) Added new ComposeTables feature to allow tables in description field of product
' (SS,08/05/06) Added ComposeTables to Sub ShowHighlights()
' (SS,07/07/06) Added Application("ProductsAcrossMax") to ShowProducts
' Added ShowProductMoreImages and ShowProductFiles.
' Modified GetProductLink, added GetProductLinkWithImageNo, GetProductImageLink,
' GetProductFileLink for extra images and files feature
' (SS,07/09/06) Add ePDQ payment feature
' (SS,13/09/06) Adding New Delivery calculation feature,
' including major mod to GetDeliveryCostForCountry
' New tables deliverycosts and deliveryregions
' (SS,15/09/06) Multicurrency feature
' (SS,20/09/06) Extra search engine promotion stuff
' (SS,17/10/06) Added ShowDeliveryMatrix feature
' (SS,18/10/06) Corrected VAT added to delivery in GetDeliveryCostForCountry
' (SS,01/11/06) Correction to ShowProductFiles
' (SS,02/11/06) Made a change to ShowProducts to search for ProductCode ignoring the dashes, or treating dashes as spaces
' (SS,26/03/07) Version 1.30
' Added GetGroupLink and Sub ShowGroupMenu() new 3 level menu
' (SS,27/03/07) Changes to Sub ShowProducts to show new summary list, also added functions CategoryListEnabled, SubcategoryListEnabled, SummaryListEnabled
' Added Hidden Grp to Sub ShowSearch()
' Also added grp to function itp_setsearch() in javascript.js
' Add new function GetGroupForCategory
' (SS,17/04/07) Added ShowShoppingStatusSummary summary version of ShowShoppingStatus, code from ShowShoppingStatus moved to new ShowShoppingStatusMain
' Add DoBasketOperation due to ExpressMusic have shopping status before inc-pages.asp
' DoBasketOperation is called at end of inc-main.asp
' Changed to ShowProducts to pass picture size to inc-template-product-list.asp
' (SS,14/05/07) Modified ShowGroupMenu by adding 'If Application("EnableGroups") Then' to select Main group if groups aren't enabled
' Used by Galleon Supplies' two level menu
' (SS,15/05/07) Made a change in GetProductsPageTitle to fix "Product" showing instead of "Products"
' Made a change ShowProducts to add description to
' ShowProductSummaryListItem and ShowProductListItem
' (SS,17/05/07) Fixed bug in Function ComposeTables which was causing endless loop if ProductDescription was Null
' Added GetProductImgSrcThumbnailSmall
' Added ShowProductRelated
' Added Sub GetSettings (called from Initialise)
' Added Function GetPriceValue, Function GetDiscountedPrice, Function IsDiscounted
' Mods to ShowSingleProduct to get the new price fields
' (SS,18/05/07) Added Function FixLinkForJavascript to fix itp_subcat problem with quote (")
' Mod to Sub ShowSpecialOffers and Sub ShowProducts to use new discount field if enabled
' Added Sub SetAtFirstProduct, Sub SetAtLastProduct, Function AtFirstProduct, Function AtLastProduct
' Eventually replaced SetAtFirstProduct and SetAtLastProduct with SetAtFirstOrLastProduct
' to help inc-template-product-list.asp/inc-template-product-detail.asp determine first or last product
' (SS,21/05/07) Added Function RemoveTable, GetProductDescription, Sub SetProductDescription
' Added SetProductDescription to ShowProducts
' Added Function GetTotalItemsInBasket
' Added <!--#include file="customutils.asp"--> to inc-main.asp
' Also added new customutils.asp file to store customised utilities specific for customers
' Modified Function GetWebText to only open database when required
' New functions FourPricingFieldsEnabled, PricesEnteredIncludeVAT, UseDiscountFieldForSpecialOffers, VATBandsEnabled
' New function GetPricingFieldsForSQL
' (SS,22/05/07) Moved GetPriceValue to customutils.asp and renamed to CustomGetPrice
' Modified GetProductPrice, GetStdPrice, GetSalePrice to remove parameters to use FProductCode, FStdPrice, FSalePrice instead
' also added CustomGetStdPrice and CustomGetSalePrice calls
' (SS,26/05/07) Moved code from GetDeliverySettings to GetSettings, rewrote it
' Removed Sub GetShoppingStatus, doesn't appear to be used anywhere
' Added GetProductDimensions, SetProductDimensions
' Added GetDiscountPercentage (code from ShowSpecialOffers)
' Added Sub Finalise
' Removed all OpenDatabase and CloseDatabase calls, only one kept in Initialise and Finalise
' Finalise added to worldpay-callback.asp, paypal-callback.asp, epqd-callback.asp & epdq-return.asp
' (SS,27/05/07) Added function UseVATFlagFromCountries and global FUseVATFlagFromCountries
' Renamed function IsVATable to IsCountryVATable
' Added GetVATRateForProduct, GetVATForProduct
' Added GetDeliveryCountry
' GetDeliveryCostForCountry renamed to GetDeliveryCost
' Replaced GetCurrencyCountry with GetDeliveryCountry, GetCurrencyCountry with GetInvoiceCountry
' (SS,28/05/07) Added GetVATLabel, SetVATLabel and FVATLabel, Function IsVATRegistered, GetVATAmountToAddNR
' Mods to ShowOrderDetails and EmailOrder to not Show VAT if not registered, and not show "Included" if prices entered don't include VAT
' Mode javascript in SetAlertMessage to new ShowAlertMessage. This fixed problem where stock message was appeared too early and not working
' Added function itp_buyc to javascript.js to allow buying from product list
' (SS,07/06/07) Numerous changes up to this point.
' Scrapped the DeliveryRegion field after making Countries table more flexible with new RegionCode and SortOrder fields
' (SS,14/06/07) Completed moving of settings from global.asa to settings table
' Renamed GetAppSetting in dbfunctions to GetSetting and SetAppSetting to SetSetting
' (SS,22/06/07) Added GetPayPalShippingAddressFlag to Sub SetUpPayPalForm
' Added TotalWeight to html email confirmation
' (SS,29/06/07) Added feature to Sub ShowEmailConfirmationHTML to send an email when cmd=email
' Modified Sub EmailOrder to add two constants to force Dundas to be used or CDO to send without URL
' This was to investigating a hanging bug that occurred for a galleon order which caused worldpay callback failure
' and email not to be sent.
' (SS,06/07/07) Added HTMLEncode to ShowSearch, SetUpWorldPayForm, SetUpPayPalForm, SetUpEPDQForm
' Also to most form fields in inc-template-acount.asp, inc-template-checkout.asp
' This is to allow quotes to be entered in name or address fields.
' Mainly to fix a bug in Galleon, where a customer had useds quotes in the first address line
' and WorldPay payment failed with error "an account holder address is required"
' this is because quotes are part of HTML and when value of a form field is say ""Snowhill", the receive will see ""
' (SS,07/07/07) Added SetProductDimensions(GetFieldValue("Dimensions")) to ShowHighlights
' (SS,11/07/07) Changed FDefaultCountry in GetSettings to "" was United Kingdom
' Added "Please select..." to GetCountryCombo, also added GetImportantCountryMessage (inc-template-checkout.asp)
' next to country combos in inc-template-account.asp and inc-template-checkount
' (SS,20/07/07) Changes to ShowGroupMenu to show non-javascript urls for selected category, javascript for others
' and non-javascript if no menu item selected
' Added GetSubcategoryJavaLink, changed GetSubcategoryLink to use normal normal instead of JavaScript
' (SS,23/07/07) Sub EmailOrder, set CDO_SEND_VIA_URL to False, because there was another callback failure
' i.e. when sending email.
' (SS,01/08/07) Added {bullet} {endbullet} feature to Compose routines
' renamed ComposeTables to ComposeDescription, added ComposeDescWith and GetBulletHTML
' renamed RemoveTables to RemoveComposes, added RemoveCompose
' (SS,24/08/07) Added GetPricingFromProductRecord(0) to Sub ShowHighlights to fix "from" showing highest price in highlights
' (SS,07/09/07) Replaced Switch payment with Maestro, renamed CC_SWITCH to CC_MAESTRO
' Changed ValidateCardNumber to validate Maestro credit card number
' (SS,10/09/07) Added LCase for FDeliveryType in Sub GetSettings (to allow use of leading uppercase letter in Admin settings)
' (SS,11/09/07) Removed SpecialRegionLimitCountries code, now redundant
' (SS,13/09/07) Replaced Application("CompanyName") with GetStoreName, also replaced Application("PayPal..") with Get...
' Application("UseAlternateColorForCategories") moved to settings
' Also FDefaultTitle, FMulticurrencyEnabled, FAllowedCurrencies for Application("DefaultTitle"),
' Application("Multicurrency") and Application("AllowedCurrencies")
' Replaced Application("UseAlternateColorForCategories") with UseAlternateColorForCategories
' Replaced Application("DefaultTitle") with GetDefaultTitle
' changes to functions IsMulticurrency, IsAllowedCurrency to use new settings instead of Application()
' Replaced FCustomDeliveryEnabled with DT_CUSTOM for FDeliveryType
' Added GetDeliveryType
' (SS,14/09/07) Sub GetPricingFromProductRecord added FSalePrice which was missed and causing prices to zero
' Sub UpdateExchangeRate, changed ExecuteQuery to ExecuteQueryCommon
' (SS,05/10/07) Google checkout facility including SetUpGoogleCheckoutPayForm, ProcessGoogleCheckoutCallback
' (SS,06/11/07) Removed FBestSellersCost, setting not used
' (SS,07/11/07) Minor change to DoCreateAccountForm to correct behaviour for toner2desk
' (SS,08/11/07) Defaulted Subscribe to True in SetCheckoutAccountDefaults
' Added GetStoreLogoLink used by inc-template-email-html.asp
' Modified GetEmailBody to added Order Date to ShowEmailOrderHeader
' also changed 'ordering with' to 'order from'.
' (SS,09/11/07) Function GetNextOrderNo, change CInt to CLng to five overflow error when OrderNo > 32767
' (SS,13/11/07) Added Function ConvertCurrencyInternal used by customutils.asp
' (SS,14/11/07) Added FShowRelatedProductsInGroup, changes to ShowProductRelated to show related products in group
' Added Allow Collection feature, and Schema Version checked and restructuring
' (SS,15/11/07) Added Function ProductDiscontinued
' Changes to hold DEFAULT_CURRENCY_CODE etc in settings to allow default currency values to be set per shopping site
' e.g. this allows a different default currency with different exchange rate
' (SS,16/11/07) Added SetStockInfo, GetStockInfo
' Handling of IntraCommunity no VAT etc, and various other changes
' Change to Function GetEmailBody to show collection message
' (SS,22/11/07) Minor fix to SetStockInfo
' (SS,27/11/07) Added Euro html for Euro symbol to GetCurrencyExchangeRate
' (SS,30/11/07) Added IsAllowedCountry and changes to CheckValidPersonalDetails & CheckValidDeliveryAddress
' (SS,11/12/07) Added ShowCheckoutMessage to Sub CheckOut. ShowCheckoutMessage added to inc-templatate-checkout.asp
' GetCheckoutMessage add GetBasketNote added to inc-template-messages.asp
' GetBasketNote added to ShowBasketFooter in inc-template-basket.asp
' (SS,28/01/08) Added FProductNameTokensInBasket and GetProductNameTokensInBasket. Mainly to not charge delivery if pallet item ordered
' Used by customutils.asp
' (SS,29/01/08) Change to GetWorldPayExchangeRates do to WorldPay change to URL
' (SS,30/01/08) Added SendEmailByITPEmailer, also changed Sub EmailOrder to use SendEmailByITPEmailer instead of SendEmailByCDO
' (SS,13/02/08) Removed forcing of Postcode from DoCreateAccountForm, CheckValidPersonalDetails, CheckValidDeliveryAddress
' Also removed the bold from Postcode fields in inc-template-account.asp and inc-template-checkout.asp
' (SS,19/02/08) Changes to Sub ProcessPayPalCallback to handle "Pending" echeque payments
' (SS,01/07/08) Changes to Function GetHTMLTitle to move ProductName to the front and some other minor changes
' (SS,12/08/08) Change Sub ProcessWorldPayCallbackreplaced all Request.Form with Request,
' allowing the query string to be pasted into URL to manually perform a failed callback
' (SS,17/09/08) Added Sub SetDefaultCategory for showing content for doorway domains/pages
' Also added Function HasDefaultCategory, GetCategory and GetDefaultCategory
' GetCategory used in Sub ProductsMain, also change to Sub ShowSearch,
' Also added Function AddStoreURL, and various places where required
' Added Sub CustomInitialise to Sub Initialise
' Also added FSearchIsVisible, Function IsSearchVisible and Sub HideSearch
' (SS,20/10/08) Modified Function CorrectCurrencyNV so that it doesn't deduct VAT if prices not already contain VAT
' To correctly show price in product and status sections (for CovDem, it was incorrectly reducing the price shown, thinking it contained VAT)
' (SS,21/10/08) Added newlines to end of table rows in Sub ShowDeliveryMatrix to make code more readable
' (SS,21/10/08) Amendments to Function GetDeliveryCost for custom delivery to pass on postcode and to work in conjunction with normal by price/weight calculations
' Added Sub UpgradeSchemaTo144
' (SS,22/10/08) Added products.CustomFlag to SQL in Function GetOrderItemsQuery(ASessionID)
' (SS,23/10/08) Added Sub ShowCheckoutAlertMessage to show holiday message, called from Sub Checkout
' (SS,30/10/08) Added Function GetFlaggedItemsInBasket
' Modified Function IsIntraCommunitySupply to use countries table if VATIntraCommunityCountries set to "USE TABLE"
' (SS,10/11/08) Added GoogleAnalytics feature also Ecommerce tracking, new routines DoGoogleAnalytics etc
' Also added ShowCheckoutNextStageButton to inc-template-checkout.asp
' Changes to payment forms to run the javascript tracking function before going to payment site
' (SS,12/11/08) Added GoogleAdWords conversion tracking
' Added forcing of Postcode back in (DoCreateAccountForm, CheckValidPersonalDetails, CheckValidDeliveryAddress)
' This was removed on 13/02/08
' (SS,14/11/08) Renamed FGoogleAnalyticsAccountID back to FGoogleAnalyticsWebPropertyID
' (SS,17/11/08) Change to DoGoogleAdWordsConversionTracking, "&" replaced with "&". Purchase tracking was working.
' Wrote getpictest.asp to test with ct_test table in periodproducts
' (SS,18/11/08) In DoGoogleAdWordsConversionTracking reverted the first "&" back to "amp;" because it's HTML. Only the javascript one wasn't working.
' In Sub DoGoogleAnalyticsEcommerce replaced HTMLEncode with DoubleQuotedJavascriptString
' (SS,20/11/08) GoogleAdWords purchase conversion tracking wasn't working too well via indirect mode
' Implemented via payment redirection, i.e. extra page that does the ecommerce and conversion tracking before redirecting to payment site
' (SS,25/11/08) Corrected bug in GetDeliveryCost which was including delivery by price even when price was 0 (same for weight)
' Also added Sub CheckVAT to change VAT from 17.5% to 15% on 1/12/2008
' (SS,03/12/08) To Sub DoPaymentRedirection added window.setTimeout to add a 2 second delay to hopefully fix the adwords conversion not working issue
' (SS,15/12/08) Added GetTotalAndVATRateForGoogleCheckout for GoogleCheckout to show proper VAT Rate
' (SS,16/12/08) Change to DoGoogleAdWordsConversionTracking to use image method for purchase adword tracking because
' Javascript method not working too well even after adding 2 second delay
' (SS,21/01/09) Correction to Sub SetStockInfo
' (SS,17/03/09) Correction to Sub GetSettings to get the FAllowDifferentDeliveryAddress setting
' which somehow I previously missed. Express Music do not want to allow delivery to different address.
' Also minor change to Sub CheckOut to ensure Session("DeliveryAddressSameAsInvoice") is set
' (SS,18/03/09) In GetEmailBody changed "Invoice.." to "Billing.."
' (SS,19/03/09) Added SmallThumbnailWidth & SmallThumbnailHeight getting settings
' (SS,31/03/09) Change to Sub ShowGroupMenu, added c.GroupName to ORDER BY
' (SS,01/04/09) Changes to ShowShoppingStatusMain and ShowBasket to display prices including VAT (for petfood site)
' (SS,15/04/09) Changes to Sub ShowHighlights and Function GetWebText to use data from sitedetails table instead of highlights
' (SS,17/04/09) Change to Sub DoContactForm to add security code check, also added global FErrorSecurityCode and Function EmailSecurityCodeEnabled
' Added 3 more settings FChooseDeliveryDate, FMaxDeliveryDays, FMobileEnabled
' Added Sub UpgradeSchemaTo145
' Added new Mobile field to GetPersonalDetailsFromForm, GetAccountDetails, TryProcessOrder, GetEmailBody
' Also added to inc-template-account.asp.
' Added DeliveryDate to GetPersonalDetailsFromForm, TryProcessOrder, GetEmailBody
' Added functions GetMobile, GetDeliveryDate
' Changes to Function GetEmailBody to add Mobile, DeliveryDate, VoucherCode and Discount
' (SS,22/04/09) Changes to Sub CopyMainSessionToSecureSession, added new fields
' Added VoucherCode to Sub GetPersonalDetailsFromForm
' (SS,23/04/09) Changes to Sub ShowGroupMenu to show MenuShortName if assigned
' Various changes for new VoucherCode/Discount feature
' (SS,01/05/09) Added FStorePlaceOfRegistration and FStoreRegisteredCompanyNo
' (SS,06/05/09) Bug fix to Function GetWebText
' (SS,08/04/09) Finished adding product review feature
' New routines: GetReviewRatingHTML, GetReviewRatingAndCount, GetShowReviewsLink, GetShowReviewsLink, GetWriteReviewLink
' InProductDescription, InShowReviews, InWriteReview, ShowReviews, WriteReview, CheckReviewDetails, SaveReview
' Also new template inc-template_reviews.asp
' Correction to Sub TryProcessOrder for DeliveryDate (removed quotes to allow Null to be saved
' also change to Function ConvertUKToMySQLDate in dbfunctions.asp
' Added productcategories READ to ProcessOrder
' (SS,11/05/09) Added setting of default of False for FUseVATFlagFromCountries to GetSettings
' Small change to Function IsCountryVATable because ChargeVAT field is now a TinyInt
' (SS,13/05/09) Changes to vouchers, single usage per customer feature, vouchers per product code
' (SS,14/05/09) Improvements to vouchers, also added GetDeliveryInfo token
' (SS,15/05/09) Improvements to reviews, improvements to voucher error message
' (SS,18/05/09) Improvements to reviews and added stock notification feature
' (SS,19/05/09) Added Function IsValidEmailAddress, minor change to Sub DoForgotPassword
' Change to Sub ShowProductRelated
' (SS,22/05/09) Improvements to email/order confirmation, adding of MessageToCustomer and tracking details
' (SS,28/05/09) Optional accounts, New cheque and bank transfer payment features
' (SS,04/06/09) Various improvements over the past week. Also added Recently viewed feature.
' (SS,12/06/09) Minor change to recently viewed feature, added Sub ShowCustomBanners
' (SS,15/06/09) Minor change to AccountsCompulsory, AccountsOptional, AccountsAdmin to only return True if AccountsEnabled
' (SS,16/06/09) Minor improvements mainly to do with payment button logos and entered notes for googlecheckout payment
' (SS,17/06/09) Added validation of VAT numbers when CustomValidVATNumber routine is defined in customutils.asp
' (SS,18/06/09) Added functions to returne site seal settings GetGoogleSiteSeal, also WorldPay and PayPal
' (SS,19/06/09) Minor change to GetWebText to Get Delivery prices excluding VAT
' also slight change to Sub DoCreateAccountForm to not validate delivery address when different address not allowed
' (SS,19/06/09) Added Function GetVoucherCodeForVoucherID used by Sub CopyMainSessionToSecureSession to get VoucherCode for direct payments
' (SS,23/06/09) Added Function GetVATNumberHelpMessage
' Change to Sub DoGoogleSitemap to also show site pages from sitedetails if they exists e.g. CovDem
' Change to Sub CheckOut to get payment on account to work for toner2desk with current apputils
' (SS,16/07/09) Added extra "" parameter to end of GetLookupCombo in GetTitleCombo, GetCountryCombo and ShowSearch
' due to change in dbfunction.asp
' removed Function GetDeliveryRegionCombo, not used anymore
' (SS,31/07/09) Added FCheckoutAlertMessage setting. Change to ShowCheckoutAlertMessage to show FCheckoutAlertMessage if not blank
' (SS,22/09/09) Change to Sub ProductsMain and added Functions CheckEnabledProductExists and GetSearchForPhraseFromReferrer
' Added after Clifton complained about disabled product being found in Google and was linking
' to "product not found" page, e.g. "Swarovski Christmas Star". If no enabled product found it now
' does a search for the search phrase in the referrer URLs query string i.e. q=
' (SS,28/10/09) To fix HTML email not showing USD or EUR currency symbol. Changes to GetEmailBody, SetCurrencyCode, GetCurrencyExchangeRate
' New routines SetCurrencySymbol
' (SS,02/11/09) Change to Function GetEmailBody, replaced "VAT" with LVATLabel, because VAT Deducted/Included not appear on HTML email for clifton
' (SS,10/11/09) Change to CreateAccount, GetSettings and added new subs CheckCustomerRecordChange, CheckCustomerFieldChange
' Also added new globals FNotifyCustomerRecordUpdate, FCustomerFieldChange, FCustomerDetailsOld, FCustomerDetailsNew, FCustomerFieldsChangedList
' This new feature sends notification emails when customer changes billing address details
' (SS,23/11/09) Added Sub UpgradeSchemaTo146 to add new NewCustomer flag to orders table
' Change to Sub UpdateOrderStatusForOrderPlaced to set the NewCustomer flag field if first order, i.e. no previous valid orders
' (SS,22/12/09) Changed to Sub CheckVAT to change VAT to 17.5% on 1/1/2010
' (SS,06/01/10) To functions DoAccountLogin & CreateAccount added calls to new Sub DoLoginLog to help diagnose a problem. Log added to new loginlog table
' also 07/01/10, currently only in petfood may add to others later
' (SS,27/01/10) Added Sub UpgradeSchemaTo147 called from CheckSchema to add new loginlog table
' Changed WorldPay URL in Sub SetUpWorldPayForm to sort "not redirecting" issue
' Change to Function GetDiscountUsingVoucherCode i.e. > to >=
' Added Function GetRegionCodeOrPostalArea, ShowBasket now calls this instead of GetCurrencyPostalArea to show e.g. USA instead of RW
' (SS,02/02/10) Added Sub DisableCache to fix problems some users were having placing orders
' (SS,24/02/10) Change to Function GetProductImageLinkWithImageNo to get around Magic Zoom Plus bug (i.e. moved code2 to the front)
' (SS,17/03/10) Added translation functions
' included "tr" translate given string to different language uses the new translation table
' also SetUpTranslation and Dim FTranslationEnabled, FTranslationLanguage, FTransArrayEnglish(), FTransArrayLanguage()
' New table called "translation" added to common database, two new application settings TranslationEnabled and TranslationLanguage
' Calls to tr, trx, trl in various places.
' (SS,26/04/10) Added Function ReallyInStock (for ExpressMusic 20-04-10)
' (SS,28/04/10) Added UpgradeSchemaTo148 to modify length of Category and Subcategory fields from 50 to 75 for Galleon French/German longer names
' (SS,30/04/10) Added language to WorldPay form i.e. Sub SetUpWorldPayForm, also Function GetWorldPayLanguageCode
' (SS,06/05/10) Added Function GetBuyLLink(AProductCode)
' (SS,07/05/10) Added button functions for translated buttons to replace bitmap
' (SS,08/05/10) Changes to ShowProductListJumpFooterNext and ShowProductListJumpFooterPrevious in Sub ShowProducts
' (SS,12/05/10) Added Function SwapCommasAndDP to show correct decimal separator and point for French/German
' Also added AddCurrencySymbol and GetLanguageDateFormat for correct date format. Added GetReviewCombo
' (SS,13/05/10) Added SPECIAL_OFFERS_SEARCH_TEXT constant
' (SS,15/05/10) Rewrote Function GetCountryISOCode, added Function IsVATNumberValid and Function GetCountryVATNumberFormat
' (SS,17/05/10) Added FButtonType global, setting, including change to GetButton
' (SS,21/05/10) Added IsEnglish, GetPaymentHideCurrency and new FPaymentHideCurrency global setting
' and change to Sub SetUpWorldPayForm to hide currency in payment pages for certain sites, requested by Tim, Galleon
' (SS,25/05/10) Change to Function IsVATNumberValid to save VAT Number Example if VATNumber is invalid,
' added SetVATNumberExample, GetVATNumberExample and global FVATNumberExample, also change to CheckValidPersonalDetails
' (SS,26/05/10) Added GetButtonOutOfStock, and changes to GetStockInfo to use tr() and change to ShowProducts to call SetStockInfo
' (SS,28/05/10) Change to Sub Finalise to call FinaliseDBFunctions and add time taken comments to bottom of page
' Improvements to ShowBestSellers, now much faster, new table called bestsellers is created once per day
' (SS,03/06/10) Category sort order correction to Sub ShowProducts
' (SS,14/06/10) Added EmailConfirmationIsVATReceipt feature, for Galleon, to add VAT receipt to Confirmed received email subject
' added Invoice number to email. Changes made to Sub EmailOrder, Function GetEmailBody, Sub GetSettings,
' added Function EmailConfirmationIsVATReceipt
' (SS,21/06/10) Renamed SwapCommasAndDP to CorrectCommasAndDP
' (SS,22/06/10) Change to GetVATAmountToAddNR to take CountryIsVATable into account
' Also added GetVATAmountToAddNC to prevent issue with GetDeliveryMatrix
' Change to CorrectCurrencyWV to use GetVATAmountToAddNC instead of GetVATAmountToAdd
' Also change to GetWebText to improvement speed by not doing delivery check unless really required
' (SS,23/06/10) Added stock available feature which can show e.g. "Available in 2 to 3 days" on product details page
' Various product and stock routines changed and stock availability functions added
' New suppliers table added, and 3 new fields to products table called SupplierID, AvailabilityDaysFrom and AvailabilityDaysTo
' Also schema version incremented to 1.49, added Sub UpgradeSchemaTo149.
' (SS,24/06/10) Minor change to SetStockInfo for DateDue to override stock availability info
' (SS,25/06/10) Added FShowStockAmount and Function ShowStockAmount and change to SetStockInfo
' to show just "In stock" instead of "x in stock".
' (SS,25/06/10) Change to Sub AddToBasket and Function GetOutOfStockDetails to allow ordering if not in stock but available to order
' Added Function GetStockAvailabilityFieldList, Sub GetStockAvailabilityFieldList to reduce repetitive code
' Added LOCK suppliers READ to Function ProcessOrder, due to suppliers being now used by GetOutOfStockDetails
' Also changed ProcessOrder to add message to a log file using LogErrorToFile in dbfunctions.asp
' (SS,28/06/10) In SetStockAvailablity changed "Available in" to "Delivery"
' Change Sub AddContactToDatabaseAndEmail to prefix email subject with store name
' (SS,29/06/10) Schema updated to 1.50 new Sub UpgradeSchemaTo150, for picture renumbering
' New stored function and procedure added, called from admin app
' RenumberPicturesSingleProduct and RenumberPicturesAllProducts
' (SS,30/06/10) Changed contact form subject "Contact Form" to "Enquiry" in Sub AddContactToDatabaseAndEmail
' (SS,06/07/10) Changed to help with SEO (Clifton) by adding product name to product URL
' New routine GetProductLinkWithName, change to Function ForProductList
' Changes to following templates, renaming GetProductLink to GetProductLinkWithName:
' inc-template-product-list.asp, inc-template-highlights.asp, inc-template-product-details.asp, inc-template-best-sellers.asp
' (SS,19/07/10) In Function GetProductLinkWithName replaced Server.URLEncode(AProductName) with SEOFriendlyStr(AProductName)
' (SS,20/07/10) CustomStockInfo feature and new field called AllowOutOfStockOrdering in products table, various changes to include this feature
' (SS,22/07/10) Bug fix to SetAlertMessage, i.e. added escape for double quote, also to TryProcessOrder, added CleanSQL to LOutOfStockDetails
' Schema updated to 1.51 new Sub UpgradeSchemaTo151, for new field added to products and orderst tables
' (SS,03/08/10) Bug fix to Function IsStockAvailable
' (SS,21/10/10) Added Sub ShowHomePagePictures to show extra pictures on home page
' (SS,26/10/10) Minor change to ShowHomePagePictures to start with random number
' (SS,11/11/10) Added FValidateVATNumber setting and change to Function IsVATNumberValid to return True if validation not enabled
' This was done to fix in in CovDem which uses VATNumber but VATNumberFormat was missing from countries table.
' i.e. Forgot to check other sites use IntraCommunitySupply after adding validation to Galleon sites
' Missing field error was occurring when user entered VAT Number at checkout stage.
' (SS,15/11/10) Changed to Sub GetLatestExchangeRatesIfNeeded, to return straightaway
' Code moved to D:\web\common\exchange-rate-update, scheduled once per day
' We think Clifton's site going done on 13 and 14/11/10 was due to this
' (SS,09/12/10) Changes to ShowHighlights, added ShowHighlightsGrid, ShowHighlightsMain for new product highlights grid
' (SS,17/12/10) Changed Sub AddSearchToLog to only save to log when search string is not blank and not special offers
' (SS,21/12/10) Changed CheckVAT to change VAT rate from 17.5% to 20% from 4/1/2010
' (SS,20/01/11) Change to GetButton to use normal button instead if image if image specified is blank
' Due to change in inc-template-checkout.asp Sub ShowCheckoutOrderDetailsFooter or make printer friendly link into button
' Also added new function IsOption, and target="_blank" options and improvements to option handling
' (SS,07/02/11) Change to GetProductLinkWithName to reverse code and name, as requested by Neil
' (SS,15/02/11) Added order number to end of subject in Sub EmailOrder
' (SS,16/02/11) Added tr to INTRA_COMMUNITY_SUPPLY_MESSAGE in GetEmailBody also added text to status message for English version
' (SS,21/03/11) Added banner routine ShowBanner (calls header, item and footer in inc-template-other)
' and UpgradeSchemaTo152 to add Extra field to files table
' (SS,12/04/11) Add product, category and subcategory redirection (for Clifton)
' New subs ProcessRedirections, GetSubcategory, GetProductCode
' (SS,14/04/11) Added token replacement facility, where tokens in curly brackets, e.g. {catalogue} are replaced with content from site details table
' Changes to ComposeDescription and added new functions ReplaceTokens, GetToken
' (SS,12/05/11) Added Sub ShowProductPriceV2 new version for clifton
' Added ProductsPerPageFixed setting to force so many products per page, i.e. from AcrossMax, DownMax, and change to ShowProducts to make use of this
' (SS,13/05/11) Renamed ShowProductRelated to ShowRelatedProducts, added code to prevent it from being called twice
' Moved code to ShowViewOtherProduct, and also prevent it from being called twice
' (SS,16/05/11) Recently viewed i.e. MAX_RECENTLY_VIEWED was 5, adjusted for clifton to allow more, via new setting MaxRecentlyUsed
' Added Sub SetNumInStock, Function GetNumInStock and global FNumInStock, for use my template functions to which NumInStock isn't passed
' (SS,16/05/11) Added GetBuyLinkPreviousOrders and GetReplacementProductCode, for galleon to allow customers to order from viewing order orders replacement products
' Also made change to inc-template-checkout.asp to called GetBuyLinkPreviousOrders instead of GetBuyLink
' (SS,17/05/11) Added Function GetGroup
' Also added routines GetProductsPageTitleV2, GetBreadcrumb, GetBreadcrumbLink, GetBreadcrumbSeparator
' Changed some calls to ConvertNewlinesToHTML to ConvertNewlinesEtcToHTML for quotes and pounds to show correctly
' (SS,19/05/11) Added CurrencyCharToHTML to a line in GetSettings
' (SS,23/05/11) Added call to ComposeDescription in ReplaceTokens so that bullets, tables and further tokens can be used in tokens themselves.
' This will result in recursion due to ComposeDescription called ReplaceTokens. Added code to prevent too many recursions and stack overflow.
' Also changed to ReplaceTokens to handle comma separated parameters.
' (SS,25/05/11) Changes to ComposeDescription so that it also converts newlines and special chars.
' ComposeDescription also added to category and subcategory to process tokens.
' (SS,03/06/11) Change to ComposeDescription to fix too many newlines issue when ReplaceTokens used, also made "@@@" a global constant
' (SS,06/06/11) Change to ComposeDescWith, GetTableHTML, GetBulletHTML to work correctly when {endbullet} {endtable} don't start on a new line
' ComposeDescription added to GetWebText, to allow tables, bullets, tokens to work for PageContent, new lines handled differently
' Change to ComposeDescription, added a new parameter AConvertNewLines, to stop new lines from being converted to <br>s when called from GetWebText
' (SS,07/06/11) Added SetConfirmDialog to show confirm dialog for account log on or proceed without logon, also added itp_ConfirmMessage to javascript.js
' Change to ShowCategoryMenu to only show category when it contains at least one enabled product
' (SS,08/06/11) Fix to Function GetTableHTML, previous amendment had resulted in extra blank row after header line
' (SS,16/06/11) Added Function RemoveHashCommentLines, called by ComposeDescription to ignore lines beginning with hash #, used for comments
' (SS,16/06/11) Various changes to checkout, accounts, payment, to incorporate new div templates, changes made over the past few days
' (SS,17/06/11) Added new Products / DescriptionTabs setting i.e. FProductDescriptionTabs
' (SS,30/06/11) New product attributes and product options
' (SS,30/06/11) Added ProductID field to shoppingbaskets table, changes to AddToBasket routine
' (SS,01/07/11) Changes to Sub ShowProducts to search for attributes, using attributeid, min and max
' New function GetBuyOLink to buy with options
' Added Sub DoRedirect, which handles all redirects, ProcessRedirections calls this
' (SS,06/07/11) Numerous changes to handle options
' Changes to GetPricingFromProductRecord, ABasketQty unused parameter, replaced with calculate options prices flag
' GetPricingFromProductRecord now calculates the Options Price, FOptionsPrice added to GetProductPrice
' (SS,14/07/11) Schema changes to 1.53, first version to go live (covdem)
' (SS,15/07/11) Changes for new split combo product option input type
' (SS,18/07/11) Added functions GetPageName and IsPageName
' (SS,19/07/11) Change to Function GetProductImgSrc, add AOptions, with "t" to allow title to be set
' (SS,20/07/11) Changes to ProcessAddToBasketForOptions to allow it to be used directly from code as well as from form
' (SS,22/07/11) Minor fix to SQL in ShowCategoryMenu
' (SS,24/07/11) Added Function GetProductDescriptionForID to get just description for shopping admin
' (SS,26/07/11) Change to SetStockInfo to shorten stock info text, added HTMLEncode to references to ProductName, due to it containing pound sign etc for clifton
' Also had to add HTMLDecode to GetProductLinkWithName, also assed HTMLEncode to GetBreadcrumb
' Added functions GetShortNameForCategory, GetShortNameForSubcategory, changes to ShowMenuSubcategory to use short name and HTMLEncode
' (SS,27/07/11) Added HTMLEncode to GetHTMLTitle, GetHTMLDescription, GetHTMLKeyWords
' Sorted product list navigation, moved code from ShowProducts to new routines ShowProductListNavigation, GetProductListNavItem
' (SS,28/07/11) Added FDefaultGroup and GetDefaultGroup to set default group for ShowCategoryMenu when groups enabled
' Added GroupOnlySpecified and change to ShowCategoryMenu to clear previously saved category/subcategory when group specified
' (SS,01/08/11) Added FAlwaysDefaultGroupForMenu, AlwaysDefaultGroupForMenu to allow menu to be shown for same group always
' Improved breadcrumb accuracy, added routines GetBreadcrumbGroupCatSubcatForProduct, IsValidCatSubcatForProduct,
' SetBreadcrumbCategory, SetBreadcrumbSubcategory, GetBreadcrumbCategory, GetBreadcrumbSubcategory, also added GetDefaultGroupCatSubcatForProduct
' In ShowMainMenu changed GetFirstCatSubcatForProduct to GetBreadcrumbGroupCatSubcatForProduct
' (SS,02/08/11) Added IncludeGroupInHTMLTitleTag and FIncludeGroupInHTMLTitleTag to exclude group name from title tag for clifton, i.e. we don't want "Brands" to be in the title tag
' Changes to GetHTMLTitle to exclude group and also use short names for categories and subcategories when groups enabled
' (SS,04/08/11) Removed GetProductDescriptionForID, added GetProductDescriptionScratch for use by shopping admin product description browser preview
' (SS,05/08/11) Changed bank transfer constant from "BANKTRANSFER" to "BANK TRANSFER", and google from "GOOGLECHECKOUT" to "GOOGLE CHECKOUT"
' (SS,10/08/11) Added ShowFacebookLikeImageMeta and GetFullProductImageLink, to help facebook like pick the correct image
' (SS,11/08/11) Changed WorldPay URL to latest in SetUpWorldPayForm
' (SS,06/09/11) Removed Sub DisableCache, which had been moved to dbfunctions.asp on 27/5/10 but accidentally left in apputils.asp
' (SS,19/09/11) Added ComposeDescription to Function GetProductDescription because it was missing tokens feature
' (SS,20/09/11) Changes to SetUpPaymentButton and GetButtonPayment, to allow cheque and bank transfer payment options to be buttons instead of gifs
' (SS,22/09/11) Added HTMLEncodeSpecialChars to GetWebText
' (SS,27/09/11) Added functions PageExists, GetPageContent
' (SS,04/10/11) Added Function GetAttributeInBasketCount, to count how many of an item in basket, i.e. for custom delivery purposes, e.g. how many doors in basket (covdem)
' (SS,05/10/11) Added product_attributes and attributes to LOCK TABLE in ProcessOrder, missed from change made yesterday, caused error in checkout
' Also modified RemoveHTMLTags to replace breaks with a space
' Added GetButtonSubmitCreateAccountVAT, for VAT number validation via javascript, change also made to javascript.js
' (SS,06/10/11) Improved VAT Validation, ajax, javascript also used.
' New Sub NoteVATValidation and AddVATNumberValidationHTML, change to TryProcessOrder and CreateAccount
' Changes to GetCheckoutSubmitLink, GetButtonSubmitCreateAccount, GetButtonProceedToNextStage
' Changes GetButton and new function GetOptionEquals
' (SS,01/11/11) Change to SetUpGoogleCheckoutPayForm to fix VAT not being charged for countries outside the UK
' (SS,24/11/11) Change to ProcessGoogleCheckoutCallback to send an alert email when payment received doesn't match the order total,
' Also added new Sub SendPaymentMismatchEmail, new Function CleanNum added to dbfunction.asp,
' GetSQLValueAsString in dbfunction.asp now always returns a string, added CDbl to GetPriceForProductOptionValue
' (SS,25/11/11) Change to AddContactToDatabaseAndEmail, added GetSQLLastInsertID to subject, to keep enquiry threads separate in Outlook, requested by Neil, I have the same issue in my Outlook
' (SS,05/12/11) Added Function GetStockAmountInfo, new setting StockAmountInfoSpec, change to SetStockInfo
' (SS,12/12/11) Change to Sub ShowHighlightsMain to fix disabled products showing
' (SS,12/01/12) Change to subs SetStockInfo and SetStockAvailability to show "Preorder" if availability from days is negative
' Change to Sub ShowRelatedProducts to remove SortOrder from ORDER BY
' (SS,13/01/12) Fixed & being encoded as & in stock notifications emails for ProductName, added Function GetProductNameNoHTMLEncode, change in Sub SendStockNotificationEmails to call it, also Sub SendStockNotificationReceivedEmail
' (SS,02/02/12) Change to GetProductOptionComboBox to allow all options to be shown when none in stock, avoiding empty combo box, so a preorder can be done
' (SS,02/02/12) Added auto href feature to automatically try to add ahref links to comma separated list of products
' Change to function GetTableHTML, new functions AddAutoProductHRefs and GetProductCodeForAutoHRef
' Change to GetHTMLTitle to fix product name being HTML encoded twice
' (SS,07/02/12) Change to Sub CheckOut to no longer default delivery address to invoice address when different delivery address chosen
' Requested by Neil, customer had changed the delivery address but forgot to change the postcode, decided best to leave blank
' (SS,14/02/12) New feature - allow out of stocks to be list and stock notification.
' New field "ExtraSettings" added to table product_options and "ProductOption" to table stocknotifications, schema version 1.54
' Changes to Sub ShowProductOptions and Sub ShowProductOption to use ExtraSettings, when set to LO it'll stick the options
' Changes also made to Function GetProductOptionComboBox to pass back array of option values out of stock
' New function GetStockNotificationWithOptionLink, change to Sub StockNotify, Sub SaveStockNotification, Sub SendStockNotificationReceivedEmail
' and Sub SendStockNotificationEmails.
' (SS,15/02/12) Fixed issue/bug when searching for a product and it automatically goes to the single found product the stock notification link is missing the productcode
' Fixing involved new Sub SetProductCode and changes to ShowSingleProduct, Sub Initialise, Function GetProductCode
' (SS,15/02/12) Added new reviews page and recent review on the left. New template inc-template-reviews.asp, new reviews-page styles to styles.css
' New subs DoReviewsPage and ShowRecentReviews, change to inc-pages.asp to called DoReviewsPage and inc-template-main.asp to call ShowRecentReviews
' (SS,16/02/12) Change to Sub AddReferrerToDatabase to only add if referrer isn't blank, most records were blank due to search engine hits
' New function GetPageLink
' Added DeliveryCountry combobox to basket page to allow customer to see delivery cost before checking out.
' Changes made to inc-template-basket.asp. Change to Sub RecalculateBasket to store the value from new delivery country combo.
' Change to Sub ShowBasket to correctly default the delivery country used by basket page new delivery country combo.
' Change to sub CheckOut to default to same country for different delivery address, this was previously removed.
' Fixed bug in function GetCountryCombo added new function SetDeliveryCountry. Change to function GetDeliveryCountry to use default country if currency country is also blank
' Added new function ParagraphiseText to dbfunctions.asp and change to GetLookupCombo to add ID to allow onchange event via jquery.
' (SS,17/02/12) PayPal bug fix for multi-currency, changes to SetUpPayPalForm to fix issue which was resulting on PayPal purchases from another currency to be charged in Euro or Dollar amount in pounds, i.e. overcharged.
' New function IsCurrencyGBP. Changed to function AllowCheques to allow cheques only for GBP.
' (SS,20/02/12) Changes to SetUpPayPalForm to fix issue which was resulting on PayPal purchases from another currency to be charged in Euro or Dollar amount in pounds, i.e. overcharged.
' Improvements to multi-currency VAT. New function GetCountryChargeVATFlag. Sub GetCurrencyExchangeRate, now sets FCurrencyShowVAT using settings from the countries table rather than the common exchangerates.
' This overcomes issue in hyperflight using flat rate VAT, which was showing prices ex VAT for USA.
' Correction to function GetDeliveryCost, VAT wasn't being added to delivery cost when country was not vatable and prices include VAT.
' Fixed by changing GetVATAmountToAdd to GetVATAmountToAddNC.
' Added new cookie routines SetCookie and GetCookie to dbfunctions.asp.
' Changes to GetBankTransferDetails to allow separate setting details for EUR and USD currencies.
' Delivery County now also held in a cookie, change to Function GetDeliveryCountry. New sub SetDeliveryCountryCookie called fron Initialise.
' (SS,21/02/12) Change to GetDeliveryCost to make sure delivery cost is rounded to 2dp, had noticed some values in orders table for clifton with more than 2dp.
' (SS,14/03/12) Improvements to Sub SendStockNotificationEmails to add a staggered (delay) facility, amount of emails sent per product are equal to the number in stock. Rest are delayed by 23 (x) hours.
' Routine was split into EmailSendStockNotificationEmailsResult, StockNotificationHeader, StockNotificationFooter, StockNotificationRow, SendStockNotificationEmail.
' Result email also sent. New field called DelayUntil added to stocknotifications table via Sub UpgradeSchemaTo155.
' (SS,14/03/12) Change to Sub ShowProducts, adding new AVal parameter to search for exact attribute value instead of min/max
' (SS,14/03/12) Added code Sub ShowViewOtherProducts to show related category if set in product attribute, allows miscellaneous link to be added to categories without listing related products
' (SS,15/03/12) Removed DoLoginLog, dropping the table, commented out the sub
' Minor change to ShowViewOtherProducts to use \ or � as separator instead of /
' (SS,16/03/12) Modified GetHTMLTitle, GetHTMLDescription, GetHTMLKeyWords to get the keywords meta tag if available from categories, subcategories, groups, product_attributes and sitedetails tables
' New functions GetTitleTagForPage, GetTitleTagForProduct, GetTitleTagForCategory, GetTitleTagForSubcategory, GetTitleTagForGroup, GetTagNameForType, IsGroupPage
' Schema changes to tables categories, subcategories, groups, sitedetails, attributes, product_attributes via UpgradeSchemaTo156, also dropped loginlog, add adding of title/meta tag records to attributes table
' Removed references to ProductListCategorySuffix field in groups table, field has been dropped, also commented out Sub GetProductListCategorySuffixForGroup
' (SS,21/03/12) Slight change to function GetHTMLDescription, improvement for product page, to not prefix with product name if description is longer than name
' (SS,23/03/12) Improved writing reviews security. Spam reviews were bypassing existing security. Improved by adding forcing email to be entered and checking if email address has been used to place an order.
' Changes to CheckReviewDetails, new function Function EmailAddressHasOrdered. Also changes to Sub ShowReviewEntryForm (inc-template-other.asp)
' (SS,11/04/12) Modified UpdateOrderStatusForOrderPlaced to set new PaymentReceived field in orders table
' New PaymentReceived field added to orders table, manually, not via schema update
' ALTER TABLE orders ADD PaymentReceived TINYINT(1) NOT NULL DEFAULT 0 AFTER PaymentReference
' Also ran the following SQL to populate the new field
' UPDATE orders SET PaymentReceived = TRUE
' WHERE Status = "COMPLETED" OR Status = "PAYMENT RECEIVED" OR Status = "PAYMENT ON ACCOUNT" OR (Status = "CANCELLED" AND PaymentMethod <> "" AND RefundAmount <> 0)
' (SS,17/04/12) Added Special Offers to ShowCategoryMenu
' (SS,18/04/12) Add CollectionOnlyOrder feature, including new function HasCollectionOnlyInBasket, shows message and disables DeliveryWillCollect checkbox
' when a product in basket has an attribute of CollectionOnly = "Yes". New Session("CollectionOnlyOrder") used.
' (SS,19/04/12) Changes to CheckOut to bypass confirm page for Bank Transfer and Cheque payments
' Changes to functions RemoveHashCommentLines, ReplaceTokens and added new function HasScriptOrStyle, to allow script and stylesheet in page content held in sitedetails
' Because {} and # are used by stylesheets and scripts, this also means tokens can't be used if page content / description contains a style or script html tag
' (SS,27/04/12) OptionValue resized from 100 to 150 via UpgradeSchemaTo157
' Improvements to options radio, Function GetProductOptionComboBox, better alignment, semicolons converted to breaks. If only one option when it is checked.
' Price difference shown at the end.
' Bug fix to Function GetProductAttributeByName, to handle the case where ID might be blank.
' Changes to stock notifications, Sub SendStockNotificationEmails, to remove duplicates (new Sub StockNotificationsRemoveDuplicates).
' Also added BackInStockNotificationsStockMultiplier to send x many emails per number in stock.
' Bug fix to query in SendStockNotificationEmails for options, it wasn't delaying when Option was blank when options enabled.
' (SS,10/05/12) Modifications to ePDQ facility to get it working for CovDem
' Under Offer attribute feature for products, to show "Currently under offer" if product has 0 stock and "Under Offer" attribute
' New function ProductAttributeExistsm change to sub SetStockInfo, new function GetSQLRecordExists added to dbfunctions.asp
' (SS,15/05/12) Change to ProcessPayPalCallback and ProcessOrderThirdPartyPayment to set status to either AWAITING PAYMENT or PAYMENT RECEIVED
' (SS,18/05/12) New "Main" field added to productcategories table, populated using special query which looks at priorty field.
' Change to Function GetCategoryListSQL to use this new Main instead of Priority from categories and subcategories, which may be dropped in future
' Change to related products to show related products in category/subcategory order, also to move out of stock products to the end or hide them.
' To implement this, changes were made to ShowRelatedProducts, new setting RelatedProductsOutOfStock was added.
' Implemented per category/subcategory best sellers, changes to Sub ShowBestSellers, new table create by ShowBestSellers called bestsellers_per_category
' (SS,22/05/12) Fixed bug on best sellers for category where product is in more than one subcategory for same category and it showed duplicates.
' Added best totals when querystring has bstotals=y
' Change to GetAccountDetails to allow login with special password, for internal use
' (SS,25/05/12) Changed to Sub UpdateOrderStatusForOrderPlaced to allow status to be updated when it's currently awaiting payment, for PayPal echecks that have cleared
' (SS,29/05/12) Change to ShowBestSellers orderdetails.ProductName with products.ProductName
' (SS,30/05/12) New NumInStock field to orderdetails table to hold number in stock just before the product was purchased, i.e. a historical value for stocking checking purposes.
' Change to insert query in TryProcessOrder to set this new field. Also change to Function GetOrderItemsQuery to include this new field
' Added new sub StockSnapshot, to log the stock levels in new stock_snapshots table. Schedule once a day via stock-snapshot.asp called via d:\bin\scripts\StockSnapshot.vbs
' (SS,01/06/12) Renamed field NumInStock added to orderdetails (30/5/12) to PrevStock to fix ambigious error in Stock Required report and other potient issues
' (SS,12/06/12) Added CleanSQLStr to prevent SQL injection, most queries already had this, made sure all parameters/values passed to SQL used CleanSQLStr and also start and end in "'"
' Hyperflight was hacked, on 1/6/12 by somebody using a tool called Havij from http://itsecteam.com/en/projects/project1.htm
' Using a FireFox addon, determined that page and code were vulnerable form/query string variables.
' These changes should prevent future attacks.
' (SS,13/06/12) Change to Sub TryProcessOrder, insert query with PrevInStock, changed NB to new NNULL routine to handle null
' (SS,13/06/12) Change to Sub ShowProducts to use ParseInt instead of CInt to prevent error if SQL Injection used for PageNo
' (SS,18/06/12) Various changes to improve password security, password is now stored hashed and encrypted. Password is no longer email, password reset URL emailed instead.
' (SS,19/06/12) Continued with various changes to improve password security including UpgradeSchemaTo159.
' Also removed old redundant login using Account ID which was only used by Toner2Desk.
' Added new Sub DoResetPassword. Changes were also made to inc-template-account.asp, including new reset password routines.
' (SS,05/07/12) Added new function GetAttributeInBasketCountNumValCompare used by hyperflight to add extra delivery via customutils.asp if certain item in basket
' (SS,01/08/12) Change to Sub UpgradeSchemaTo153 to make sure OptionValue is 150 long, and OptionName is 50 long.
' (SS,21/08/12) Added Function GetMaxMessageLength to restrict customer message entered in check to 600.
' This is called by Sub CheckOut and ShowCheckoutPersonalDetailsEntryForm in inc-template-checkout.asp.
' (SS,29/08/12) Added function GetFeefoLogon and function IsDiscountedOver2PC (for Galleon)
' (SS,31/08/12) Added function function GetProductCodeForName
' (SS,12/09/12) Changed to Sub DoBasketOperation for shopping status deduct, now can also use xcode
' Added Sub SingleProductRedirect, now called when search finds single product
' Improvements to ShowRecentlyViewed to not show current product (originally done 5/9/12), new functions GetProductCodeQS and GetRecentlyViewedCount
' New function GetProductFileLinkCached used by banner to cache, calls existing GetProductFileLink, change to ShowBanner to call GetProductFileLinkCached instead
' New sub GetButtonYourAccount (replaces GetButtonMyAccount)
' New routines ShowFavourites, GetCustomerNameUsingCookie, GetSetCookieCustomerID
' (SS,20/09/12) Change to ProcessWorldPayCallback to trap test payments made in live mode
' Added Auto log in feature, which replaces previous GetSetCookieCustomerID which was being used to retrieve just the customer name and favourites.
' Now the customer stays logged in until they log out.
' New setting FAutoLoginEnabled and ACustomerID parameter added to GetAccountDetails (and "" to most calls to this routine)
' New routines AutoLoginEnabled, SetDateTimeOfLastLogin, DoAutoLogin, SaveAutoLoginToCookie, ClearAutoLoginFromCookie.
' Routine GetCustomerNameUsingCookie renamed to GetCustomerNameForWelcome and modified.
' (SS,28/09/12) Added review requests facility, both Feefo and internal
' New routines: SendReviewRequests, EmailReviewRequestsResult, ReviewRequestResultHeader, ReviewRequestResultFooter, ReviewRequestResultRow,
' SendReviewRequestEmail, SetFeefoFileName, AddFeefoField, AddFeefoRecord, GetFeefoFTPHost, GetFeefoFTPUser, GetFeefoFTPPassword, ReviewTypeIsFeefo
' Change to SendEmailByCDO to allow image to be embedded in email, new parameter added.
' Added FTPFile to dbfunctions.asp
' Also added UpgradeSchemaTo160 to add new field orders table and some extra indexes
' (SS,04/10/12) Replaced GetAttributeInBasketCountNumValCompare with new function GetAttributeInBasketMaxOptionValue, used by hyperflight in customutils.asp
' (SS,02/11/12) Changes to functions GetEmailBody, GetInvoiceCountry, CorrectCurrencyNV, CorrectCurrencyWV, SignedCurrencyPrice to correctly show prices exc VAT
' for stores that have prices including VAT, i.e. HyperFlight, also change to ShowBasketFooter in inc-template-basket.asp
' and change to ShowEmailOrderFooter in inc-template-email-html.asp.
' Change to Sub SendReviewRequestEmail to show Order No in email subject.
' (SS,03/11/12) Change to function GetEmailBody to SetInvoiceCountry for correct VAT deducted figures for confirmation emails due to change made yesterday
' which resulting in emails without VAT deducted from figures although GrandTotal itself was fine.
' (SS,08/11/12) Added new min USD and EUR price feature to allow a minimum price to be set in USD or EUR.
' New functions IsCurrencyUSD, IsCurrencyEUR, MinCurrencyPricesEnabled, changes to GetPricingFromProductRecord, GetProductPrice, GetStdPrice, GetSalePrice, GetPricingFieldsForSQL.
' 3 new fields added to products table including DateAdded, schema update to 161 via UpgradeSchemaTo161.
' (SS,09/11/12) Made a correction to min currency price feature to work correct with negative credit prices by only adjusting price if it's greater than 0
' (SS,14/11/12) Change to SetUpWorldPayForm to add MD5 Secret for more security i.e. to prevent sabotage of form fields sent to WorldPay.
' Also new routine GetWorldPayMD5SecretKey and payment setting value WorldPayMD5SecretKey.
' (SS,16/11/12) Removed products2.asp from DoGoogleSitemap, changed to Function GetPostalArea to not use OpenQuery (oRs), created issue in Clifton when applying latest apputils
' (SS,22/11/12) Added Function GetMaxPicturesPerProduct
' Change to Sub ShowProductMoreImages and added Sub ShowProductMoreImages(AProductCode)
' (SS,27/11/12) Added alert email when order eventually paid for becomes cancelled due to inactivity delay which cna cause stock to go wrong (i.e. high)
' Added Sub SendAlertEmail and change to Sub UpdateOrderStatusForOrderPlaced to check for cancelled status and send alert email
' (SS,06/12/12) New order reminder facility, two new fields added to orders table (UpgradeSchemaTo162)
' New routines SendOrderReminders, EmailOrderRemindersResult, OrderReminderResultHeader, OrderReminderResultFooter, OrderReminderResultFooter, OrderReminderResultRow, SendOrderReminderEmail, OrderReminderOptOut
' New asp file order-reminders.asp. Change to inc-pages.asp to handle the opt-out and added Sub ShowPageContentGiven to inc-template-page-content.asp.
' (SS,11/12/12) Change to Function GetProductImageLinkWithImageNo to fix Magic Zoom / Magic 360 issue.
' (SS,12/12/12) Added Intra-Community alert email if enabled. Also corrected VAT not being deducted for Intra-Community when prices include VAT.
' (SS,13/12/12) Change to Sub RecalculateBasket to fix issue where it was taking into account the reserved items via Order Placed, if customer changes his/her mind after proceeding to second stage
' and goes to basket to make adjustment.
' (SS,19/12/12) Change to Sub SendOrderReminders to also assume PAYMENT RECEIVED is an order, to prevent customers who ordered after closing time today and hadn't ordered for a month from receiving a reminder
' (SS,10/01/13) Changes to allow a heading in category/subcategory other than category/subcategory name.
' Also to allow description text in category/subcategory page header and footer.
' These were part of the SEO changes for CovDem. New fields Footer and Heading were added to the category and subcategory tables.
' Also added Sub UpgradeSchemaTo163 to add the new fields.
' (SS,11/01/13) Further change to ShowProducts to pass the heading to ShowProductListHeaderForCategory in inc-template-product-list.asp.
' Also corrected GetShortNameForSubcategory by added the missing ACategory parameter.
' Correction in GetProductsPageTitleV2 for breadcrumb sometimes showing in category or subcategory page.
' (SS,15/01/13) Added settings ReviewRequestsEnabled and ReviewRequestsDays and change to SendReviewRequests to make use of these settings.
' (SS,16/01/13) "inc VAT" was incorrectly showing for clifton when RW country chosen. Fixed by replacing IsCurrencyVATable with IsCountryVATable in
' routines ShowProductPrice, ShowProductPriceExcVAT and ShowProductPriceV2.
' Added facility to initially not search the description (for galleon because it was returning too many products when search product code).
' Change made to Sub ShowProducts, including moved code to new Function GetProductSearchSQL and IsNormalSearch, also new setting ProductSearchWithoutDescFirst.
' (SS,17/01/13) Added Function GetBasketLink which is now used by inc-template-shopping-status.asp instead of GetCheckoutLink.
' Added ShowNewProducts to show newly added products via new template inc-template-new-products.asp.
' Added custom pages facility, including changes to GetTitleTagForPage and PageExists, new CustomPagesEnabled
' Also corrected HTML errors in Sub ShowSearch and Sub ShowSearchForStreamline.
' Made other HTML corrections, including change & to & in links.
' (SS,18/01/13) Added new products search facility to list newest products. New contant NEW_PRODUCTS_SEARCH_TEXT, change to Function GetProductSearchSQL.
' New template inc-template-new-products.asp.
' (SS,24/01/13) Change to Sub ShowProducts to not show number of products found if AttributeID used.
' (SS,01/02/13) Changes Sub SendOrderReminders etc. to improve the look of order reminders on iPhone, Android and Gmail
' (SS,05/02/13) Change to Function PageExists to return True when page name is blank i.e. home page
' (SS,05/02/13) In Function SignedCurrencyPrice(APrice) replaced "-" with "‑" to prevent wordwrap after "-" in product option prices.
' (SS,05/02/13) Change to Function GetProductOptionComboBox to default first item in radio group, also added NumInStock to sort order
' (SS,14/02/13) Added chasing of cancelled orders facility via Sub SendChaseCancelledOrderEmails
' New field CancelledOrderChasedDate added to orders table via UpgradeSchemaTo164
' New script chase-cancelled-orders.asp added which calls SendChaseCancelledOrderEmails.
' This can be called via Shopping Admin or via schedule on server.
' (SS,15/02/13) Changed to Sub SendChaseCancelledOrderEmails to allow order no to be specified for testing purposes
' Also added FirstName and Surname tokens, requested by Neil.
' (SS,19/02/13) Change to ProductsMain to remove call sitemap. Rewrote ShowFullList, now called GetHTMLSiteMap.
' Menu SQL code moved to new function GetCategoryMenuSQL.
' Change to Function GetWebText to also handle SITEMAP and CUSTOM-SITEMAP tokens from page content.
' Change to DoGoogleSitemap to also GetCustomXMLSitemap if such function exists.
' Template inc-template-main-streamline.asp replaced with inc-template-sitemap.asp.
' Added function GetURLForPage to create page URLs.
' (SS,20/02/13) Change to Sub AddGoogleSitemapLevel, replacing "&" with "&"
' Modified GetWebText to bypass ComposeDescription when {SITEMAP} or {CUSTOM-SITEMAP} tokens used, for much faster large sitemaps.
' (SS,21/02/13) Removed functions GetProductsForFullListSQL, OpenProductsForFullListQuery, OpenProductsForFullListQuery2, Sub OpenCategoriesAndSubcategoriesQuery
' ShowProductsOnFullListCat setting is now redundant.
' Changes to Function GetHTMLSiteMap to add Group.
' (SS,22/02/13) Email Marketing facility
' Following changes were originally made on 19/12/12:
' New Sub UpdateCustomerRecord, call added to subs ProcessOrderFinalise, ProcessOrderThirdPartyPayment
' Change to Function AccountExists
' Changes to Function CreateAccount and Sub UpdateCustomerRecord.
' These ensure than the customer record is updated or new one created where buyer hasn't created an account.
' (SS,26/02/13) Completed the Marketing Emails facility.
' (SS,28/02/13) Added UpgradeSchemaTo165 to add the new MarketingEmailSentDate field.
' This also runs queries for shopping sites that have optional accounts to create a customer record for every customer who
' has an order but no CustomerID assigned.
' (SS,28/02/13) Manually added new table "google_adwords_groups" and field "AdWordsGroupID" to subcategories table.
' Also attribute "Exclude from Google Shopping". These are used by Shopping Admin for Google Shopping.
' (SS,01/03/13) Change to Sub ProcessRedirections to allow semicolon for separation.
' Added Session.Abandon to Sub CheckOut, mainly to empty session variables from previous order, requested by Neil.
' Improvement to Sub SendReviewRequests to allow OrderNo to be specified for testing purposes.
' (SS,05/03/13) Change to UpgradeSchemaTo165 to only had customer records if customer has a valid order. New Function GetWebTextDef.
' Improvements to Sub DoContactForm and Sub DoCreateAccountForm.
' (SS,06/03/13) Added Sub UpgradeSchemaTo166 to change CustomerID in orders from varchar to int. Improvements to AddContactToDatabaseAndEmail and Sub DoContactForm.
' (SS,07/03/13) Bug fix to Sub TryProcessOrder, due to '' now being assigned to CustomerID which had changed from VARCHAR to INT, '' to NULL fixes it.
' Removed references to contracts including FContractsEnabled, functions LookupPriceForContract, OnContract, ContractsEnabled removed.
' Removed contract code from functions GetStdPrice and GetSalePrice
' (SS,08/03/13) Improve the order history page, including change "COMPLETED" to "DESPATCHED" status and show extra details, also tracking URL.
' Changes made to routines GetStoreAddress, ShowPreviousOrders, GetEmailBody, ShowOrderDetailsMaster.
' New functions StatusForCustomer, GetTrackingURLForCourier added.
' Also removed Session.Abandon (added 1/3/13) which was cause alert payment to be sent when PayPal callback was delayed.
' (SS,21/03/13) Change to function RemoveHTMLTags, to fix issue when tags within HTML comment tags.
' (SS,22/03/13) Changes to add Sage Pay (search for 3/13 to find all the changes made). Changes were applied to expressmusic old apputils, later added here as well.
' Changes included new sub CheckPaymentProviderTestMode and functions IsPaymentProviderTestModeActivated, IsSagePayTestMode and GetUniquePaymentProviderSuffix
' Also made a change to dbfunctions to include Sage Pay's encryption functions in common/asp/rijndael.asp
' Added new table called sagepaycallbacks.
' (SS,23/03/13) Change to Sub SetUpSagePayForm, added URLEncode to ptSagePay because it now contains a space
' (SS,23/04/13) Added rounding to 2dp to FMinCurrencyPrice to Sub GetPricingFromProductRecord
' (SS,30/04/13) Added rounding to 0dp to Sub ShowBestSellers, for special total values visible via ?bstotals=y, also made bold
' (SS,10/05/13) Added Sub ShowCanonicalLink for CovDem version, also added to latest AppUtils.asp.
' Also added NL to Sub ShowFacebookLikeImageMeta
' (SS,16/05/13) Change to function GetTrackingURLForCourier and calls to this function to add tracking for APC, had to add postcode parameter
' (SS,20/06/13) Change to Sub SetUpWorldPayForm to fix issue where amounts over 1000 were failing due to comma separator
' (SS,02/07/13) Change to Sub SetUpPayPalForm to not send the delivery address to PayPal, this change was made on 2007, but no longer necessary
' PayPal customers with different delivery address were having problems.
' (SS,16/07/13) Change to use function ComposeDescription for to convert tokens and suffix with "..." if longer than max length
' Also added Function ProductDescriptionListForGoogleShopping to send product description list
' (SS,17/07/13) Improvements to Best Sellers to exclude products that have the attribute "Exclude from Best Sellers".
' Change to sub Sub ShowBestSellers and added new function GetAttributeIDForName
' (SS,04/10/13) Added SortOrder to Sub ShowRelatedProducts
' (SS,14/10/13) Added Function IsSalesPostalAreaInvoice and change to function IsCountryVATable
' This sorts issue where invoice country is RW and delivery country is UK (or EU) and VAT wasn't being charged. It is now.
' (SS,11/11/13) Change to Sub CheckSchema to only upgrade schema if cmd=upgrade is in query string
' Change to Sub UpgradeSchemaTo165 to set the DateTimeCreated to today's date, noticed it was being set to null.
' Also added DebugLog -1, ... just before each ExecSQL to help debug why it was taking a very long time for clifton
' (SS,13/11/13) Added Address and Postcode back to Sub DoContactForm, required by some sites for sample requests.
' (SS,13/11/13) Change to Sub ShowPreviousOrders to only show despatch details if such details exist for customer
' (SS,18/11/13) Change to Sub Finalise to only call DoGoogleAnalytics if page is product.asp. This removes the Google Analytics javascript from the order confirmation emails.
' Also added function called ScriptIsProducts. Also made change in Sub Finalise to only show timer if in products.asp
' (SS,22/11/13) Added Function GetProductCodePrefixesInBasket
' (SS,28/11/13) Changed Google Analytics from synchronous to asynchronous version. Add dynamic remarketing tag. Various changes to incorporate this.
' Head tag in inc-template-main.asp should contain (just before the end head tag) a call to DoGoogleAnalytics(True) for the asynchronous javascript.
' (SS,29/11/13) Change to Function ProductDescriptionListForGoogleShopping to replaced tabs with a space for google shopping (was causing problems in products containing a tab in description)
' (SS,29/11/13) Change to Sub DoGoogleAdWordsRemarketingTag to show remarketing tag without a conversion label.
' (SS,06/12/13) Change to Sub DoGoogleAdWordsRemarketingTag to fix issue with "purchase" ecomm_pagetype not showing ("other" was being shown")
' (SS,13/12/13) Added picture to product option value. New Picture flag field added to product_option_values_table. New function GetProductOptionValueImageLink and
' change to Function GetProductOptionComboBox.
' (SS,17/12/13) Change to Function GetProductOptionComboBox to underline the option value picture link
' (SS,18/12/13) Change to Function GetProductOptionComboBox, added class=""MagicThumb"" for Magic Zoom popup, also a title for caption
' (SS,19/12/13) Change to GetProductOptionComboBox to optimise the Magic Zoom popup
' Fixed VAT No check issue for Greece, which uses code "EL" not ISO "GR". Change made to function IsVATNumberValid and added new function GetEUCountryCode
' (SS,19/12/13) Change to GetProductOptionComboBox to improve radio alignment in IE using new div with class option-radio-div
' (SS,20/12/13) Change to Function IsVATNumberValid to only validate VAT if intra-community supply
' (SS,23/12/13) Change to Sub SendMarketingEmails, commented out date limit, to allow Christmas emails to be sent
' (SS,18/02/14) Change to add to basket to redirect to shopping basket page. Experimented with adding a popup after adding to basket. Decided going to basket was the better option.
' (SS,19/02/14) Added a redirect for checkout when filling in the details so that the page URL becomes unique i.e. products.asp?cmd=checkout&xcmd=filled (for AdWords requested by Clifton)
' (SS,20/02/14) Change to Sub CheckOut to fix stuck in "account already exists" alert loop due to change made yesterday
' (SS,20/02/14) Modified the add to basket alert to also do popup via new Sub ShowPopUpMessage, routine called SetAddToBasketAlert, does either redirect or popup or neither depending on setting
' (SS,21/02/14) Update to Sub CheckOut and Sub SetUpChequeOrBankTransferPayForm to allow Cheque/BankTransfer payment to have the same URL as return from third party payment (i.e. stage=co at the end)
' (SS,24/02/14) Change to Sub CheckOut to improve message shown when returning direct from payment provider e.g. WorldPay or PayPal
' (SS,03/03/14) Modified Sub ProcessWorldPayCallback to call ProcessOrderThirdPartyPayment. It wasn't automatically creating customer records when paying by WorldPay
' Added Function IsReturnFromPaymentProviderPage and change to Sub DoGoogleAdWordsRemarketingTag to show purchase remarketing tag when returning from payment provider.
' (SS,14/04/14) Added UKMail and UPS to Function GetTrackingURLForCourier
' (SS,05/06/14) New function GetGeoIPCountry added
' (SS,10/06/14) New multicurrency method added which uses local currencies table. Used by HyperFlight only currently.
' New routines added: GetCurrencyCombo, GetCurrencyCodeForCountry, SetCurrencyAndDeliveryCountryCookieFromGeoIP
' Changes made to GetCurrencyExchangeRate, SetCurrencySymbol, IsAllowedCurrency.
' New settings FMulticurrencyMethod, FExchangeRateAdjustmentRatio and FGeoIPEnabled
' Change to SaveReview to show reviews immediately if new setting FProductsShowReviewsImmediately is true, default is false.
' Two new fields Currency and SortOrderISOCode added to countries table (added to all sites).
' New fields SymbolHTML, CurrencyName and SortOrder added to currencies table.
' (SS,11/06/14) Change to Sub ShowPopUpMessage to position popup just below the shopping status div
' (SS,23/06/14) Added new Function GetPriceIncVAT, used to calculate VAT inclusive price, required by Galleon because Google Shopping now insists that inc VAT prices are show on site.
' (SS,14/08/14) Added Sub ShowMenuForCategories, ShowMenuForCategory to use for bootstrap (popup) type menu
' (SS,15/08/14) Added new setting FResponsiveWebDesign and Function IsRWD, used to allow different HTML to be sent where applicable and held in apputils.asp
' (SS,15/08/14) Added functions GetBreadcrumbHeader and GetBreadcrumbFooter in addition to existing GetBreadcrumbLink and GetBreadcrumbSeparator
' These four functions can also exist in inc-template-products.asp to override these default ones
' (SS,19/08/14) Added new function ProductOptionExists. Used to find if Sections option exists in Cast Iron Radiator Centre.
' Added function Sub ShowProductPriceSimple.
' (SS,20/08/14) Added Sub ShowProductPriceIncVAT
' (SS,21/08/14) Correction to Function GetTrackingURLForCourier for UPS. Tracking No had been hardcoded by mistake.
' (SS,22/08/14) Change to Function RemoveHashCommentLines to allow special #SECTION used to separate description text into sections
' New Function GetDescriptionSection, Sub ShowProductDescriptionSections
' New Function GetProductID
' (SS,26/08/14) Change to Function GetButton to use difference class names for Bootstrap and remain compatible with previous versions
' Change to Sub ShowRecentReviews, added new Sub ShowRecentReviewsV2, both call new ShowRecentReviewsMain (with contains code from ShowRecentReviews)
' Change to Sub ShowProductOption to allow default option HTML to be overridden via custom routine
' (SS,28/08/14) Change to GetPricingFromProductRecord to call CustomGetOptionsPriceAndWeight routine if CUSTOM extra setting in settings
' Added PostalWeight field to Function GetPricingFieldsForSQL to allow it to be passed to CustomGetOptionsPriceAndWeight
' (SS,11/09/14) Added two new parameters to Sub ShowProductMoreImagesV2, removed old ShowProductMoreImages and ShowProductMoreImagesExclude, renamed ShowProductMoreImagesV2 to ShowProductMoreImages
' Change to Function GetProductImageLink for compatibility
' (SS,12/09/14) Added ParcelForce to Function GetTrackingURLForCourier
' (SS,13/09/14) Added generic counter routines Sub ClearCounter, IncCounter, Function GetCounter and Dim FCounter, can be used by templates
' (SS,01/10/14) Added Facebook conversion tracking code as requested by Clifton, new setting FFacebookConversionTrackingID, new Sub DoFacebookConversionTracking
' (SS,02/10/14) Change to Sub SetUpEPDQForm to add PARAMVAR to allow more than one site to use same ePDQ account, also added FEPDQPARAMVAR setting
' (SS,09/10/14) Added getting and setting of DeliveryInfo via CustomGetOrderDeliveryInfo to determine the likely delivery date of order, new field DeliveryInfo added to orders table
' (SS,10/10/14) Added call to CustomFinalise in Finalise. Change to ShowBasket to call CustomProductInBasket
' (SS,16/10/14) Change to Function IsVATNumberValid to bypass VAT number validation if it ends in [NV], on rare occasions the checking site might say it's not valid when it actually is
' (SS,20/10/14) New review and Feefo settings FReviewsType, FFeefoLogon, FFeefoFTPHost, FFeefoFTPUser, FFeefoFTPPassword.
' (SS,23/10/14) Bugfix to Function GetDiscountPercentage to prevent overflow error when standard price is 0
' (SS,09/12/14) Change to Function GetDeliveryCost, added LByPriceDeliveryCost, LByWeightDeliveryCost parameters to CustomGetDelivery (for Clifton, UK ignores by weight delivery)
' (SS,09/12/14) Changes to incorporate PayPal Express (various changes made 18/11/14 to 24/11/14)
' (SS,18/12/14) & 19/12/14 Various changes made to implement default payment currency and improved bank transfer details Changes also made to inc-template-checkout.asp (ShowOrderThankYou)
' Schema change made to orders tableadd new DefaultPaymentCurrencyCode and Rate and PaymentLog fields, need to apply to all shopping sites due to shopping admin:
' ALTER TABLE orders
' ADD DefaultPaymentCurrencyCode CHAR(3) DEFAULT 'GBP' COMMENT 'Added 19/12/2012' AFTER ExchangeRate,
' ADD DefaultPaymentCurrencyRate DOUBLE DEFAULT '1' COMMENT 'Added 19/12/2012' AFTER DefaultPaymentCurrencyCode,
' ADD PaymentLog MEDIUMTEXT DEFAULT NULL COMMENT 'Added 19/12/2012' AFTER CancelledOrderChasedDate;
'
' New currencies table, kept empty if not used:
'
' CREATE TABLE currencies (
' Currency CHAR(3) NOT NULL DEFAULT '',
' ExchangeRate DECIMAL(11,4) DEFAULT NULL,
' SymbolHTML VARCHAR(50) DEFAULT NULL,
' CurrencyName VARCHAR(50) DEFAULT NULL,
' DefaultPaymentCurrency CHAR(3) DEFAULT NULL,
' SortOrder INTEGER(11) DEFAULT NULL,
' PRIMARY KEY (Currency)
' )ENGINE=MyISAM
' AVG_ROW_LENGTH=116 ROW_FORMAT=FIXED CHARACTER SET 'latin1' COLLATE 'latin1_swedish_ci';
' (SS,28/12/14) Change to Sub EmptyBasket to delete from shopping_basket_options, missing from when options first added in July 2011
' (SS,30/12/14) New version of Sub ShowRelatedProducts to use union instead of creating temporary table to prevent adding to binary log for replication
' (SS,15/01/15) Added Function GetButtonBasketApplyVoucherCode
' (SS,21/01/15) New APaymentInfo parameter added to Sub UpdateOrderStatusForOrderPlaced and to new PaymentInfo field to orders
' ALTER TABLE orders
' ADD PaymentInfo VARCHAR(255) DEFAULT '' AFTER PaymentReference;
' (SS,03/02/15) Minor change to Sub DoPayPalExpress(ALaterStage) to improve error display
' (SS,04/02/15) Change to Sub SendReviewRequests, to fix feature not working when CustomerID is null, added NB so line becomes LCustomerID = NB(GetQueryField("CustomerID"))
' (SS,04/02/15) Change to Function GetButtonPayment to improve layout of payment logs for non RWD
' (SS,06/02/15) Bug fixes to GeoIP Locate, delivery country on cookie not valid then clear it. Also GBP value not being passed to final stage of PayPal Express
' (SS,06/02/15) Added ShowCheckoutMakePayPalPaymentText to Sub SetUpPaymentButton, also to inc-template-checkout.asp
' (SS,11/02/15) Added GroupName parameter to Function GetDeliveryRegionFromPostcode, new field "GroupName" added to delivery_postcodes table
' (SS,12/02/15) Modified Sub DoGoogleAnalytics to allow different DoubleClick.Net URL depending on new setting FGoogleAnalyticsUseDoubleClickDotNet, requested by Miromedia for CovDem
' Change to Sub AppUtilsAddToPaymentLog, now calls new Sub AddToPaymentLog
' Change to Sub ProcessOrderChequeOrBankTransferPayment to log bank transfer details in PaymentLog
' Change to Sub DoPayPalExpress to add Description
' (SS,16/02/15) Added Auto voucher code feature, Voucher Type "A"
' New routines Function IsVoucherCodeAuto, Function GetDiscountUsingAutoVoucherCode
' Changes made to Sub GetOrderTotalsForBasket, Sub SetVoucherCode
' Change to Function GetDetailsFromPayPalExpress to replace telephone prefix "+44 " with 0, requested by Deborah
' (SS,17/02/15) Improvements to PayPal Express checkout. Including handling of error "10486" by redirecting back to PayPal.
' Change to Function IsVoucherCodeValid, added voucher type "MC" multiple user per customer
' (SS,18/02/15) Improvement to Function GetDeliveryRegionFromPostcode to handle, which now calls CorrectPostcodeForUK, and handles special London postcodes like SW1A 2AA, EC2M 2RH, E1W 2AB
' (SS,20/02/15) Added Save Basket and Restore Basket feature
' (SS,21/02/15) Correct to GetVoucherIDForVoucherCode. GetValueFromQuery was returning Null when voucher code not found, modified to convert this to NULL as a string
' this was occurring when invalid voucher code entered e.g. 'RAD-5' containing actual quotes, when PayPal Express Checkout Used
' which allows an invalid voucher code get past the order creation stage
' (SS,25/02/15) Basket saving facility added. New routines SaveBasket, RestoreBasket, SendSavedBasketEmail.
' New tables saved_baskets, saved_basket_items and saved_basket_options.
' (SS,26/02/15) Fix to basket page issue when same product ID added more than once. Caused by yesterday's Email basket update. Overcome by adding new OriginalBasketItemID field in
' saved_basket_items table. OriginalBasketItemID now used as a key instead of ProductID in the session dictionary object.
' Also added title option to Function GetButton for add tooltip.
' (SS,26/02/15) Change to Function CorrectCurrencyWV, to log to TempDebugLog to help diagnose issue with inc VAT prices sometimes showing the same as exc VAT price for CIRC.
' Changes to functions GetDeliveryCountry, GetInvoiceCountry, GetCountryChargeVATFlag to fix the VAT bug, occurred when DeliveryCountry cookie invalid, and
' because " = Null" check not behaving as it should, now using IsNull function. Delivery cookie is also now deleted if invalid.
' (SS,01/03/15) Replaced replaced & with & Functions GetButtonLogOut, GetButtonCreateAccount, GetButtonEditAccount
' (SS,02/03/15) Change to ShowCanonicalLink to include link to product details page if code only, canonical is with name=
' Change to Sub DoGoogleSitemap, Sub AddGoogleSitemapLevel, to add product name to product page URL
' (SS,04/03/15) Fixed issue where delivery price not calculated as on basket page, due to missing call to CustomProductInBasket.
' This was resulting in oversize pallet charge not being added in checkout stage out for CIRC.
' (SS,18/03/15) Added Google Shopping data feed routines e.g. Sub DoGoogleShoppingDataFeed and others
' google-shopping.asp is the page used to generate the feed and the URL supplied to Google.
' Had to adjust GetAttributeInBasketCount, GetFlaggedItemsInBasket.
' (SS,19/03/15) Modified Function GetDeliveryRegionFromPostcode to allow all postcodes, default to DEFAULT_DELIVERY_REGION_UK if not found, also send alert email if an invalid postcode is entered
' Added Function GetDefaultCurrencyCode
' (SS,10/04/15) Added FUseDeliveryPostcodes setting
' (SS,14/05/15) Menu changes, including new multi menu for Bootstrap
' Following new routines added StartMultiMenu, SetMultiMenuType, IsMultiMenuTypeGroup, AddMultiMenu, AddMultiMenuLevel, MultiMenuAdditions, EndMultiMenu
' (SS,18/06/15) Change to Function GetTableHTML work with RWD (Bootstrap) which has it's own special table classes
' (SS,01/07/15) Changed Sub AddMultiMenuLevel to not show menu item if SortOrder field is negative, allows hiding of categories or subcategories.
' (SS,23/07/15) Changes to SagePay to migrate from 2.23 to 3.00. 5 new fields added to sagepaycallbacks table and changes made to SetUpSagePayForm, ProcessSagePayCallback and GetSagePayToken
' (SS,12/08/15) Removed FSearchIsVisible, Function IsSearchVisible and Sub HideSearch
' ... various changes to be documented
' (SS,11/09/15) Change to Sub Initialise to no longer call AddReferrerToDatabase, referrers table now removed, Sub AddReferrerToDatabase routine can be removed
' (SS,16/09/15) Change to Sub AddMultiMenuLevel to only show subcategory if it contains at least one enabled product added via option. Also option to show all levels even if there is only one subcategory.
' (SS,17/09/15) Change Function AdjustForFullTextSearch to allow "or" when prefixed with "||"
' ....
' (SS,12/10/15) Added showing of category description and footer when there are no products in the category, changes made to Sub ShowProducts
' (SS,15/10/15)
' (SS,19/10/15) Bug fix to Function RemoveHTMLTags, causing error in googleshopping (also product page fetch) when first or second character of description was a ">"
' (SS,23/10/15) Change to Function GetEmailBody to not show order details when status is order completed, now text is taken from message to customer field
' (SS,26/10/15) modified Function GetEmailBody to use custom confirmation email text from GetSiteDetail("Other", "ConfirmationEmailTemplate")
' (SS,30/10/15) Improvements to Function GetEmailBody, also changes to inc-template-email-html.asp
' (SS,03/11/15) minor change to Sub ShowProducts to sort navigation issue
' (SS,03/12/15) Change to Sub EmailOrder to VAT deducted Intra-Community supply alert email to check VAT no. One of the URLs: isvat.appspot.com had stopped working replaced with vatlayer.com
' In javascript.js change to functions itp_validate_vat and itp_validate_vat_callback to make it work with vatid.eu
' (SS,10/12/15) Change to Function SendEmailByITPEmailer because itpEmailer can't handle SSL (https://) URLs, fixed by fetching the body of AURL using new GetWebPage routine in dbfunctions.asp
' (SS,16/12/15) Change to Sub ShowCanonicalLink to add missing name= from canonical link also removed URL Encode,
' Also to functions GetProductLink and GetProductLinkWithName, to remove URL Encode to prevent duplicates.
' (SS,07/01/16) Rewrite/cleanup of GetEmailBody which was getting complicated and difficult to maintain
' Split to new routines: GetEmailBodyOrderDetails, GetEmailBodyAddressDetails, GetEmailBodyOrderItems, GetEmailBodyMessage
' Moved Function GetWebPageContents to dbfunctions.asp, replaced by GetWebPage which is also in dbfunctions.asp
' (SS,13/01/16) Change to Function GetTrackingURLForCourier for APC tracking
' (SS,26/01/16) For confirming terms and conditions before payment:
' Change to Sub Checkout, moved call to ShowCheckoutTermsAndConditionsCheckBox here from Sage section in SetUpPaymentButton
' Also added new setting Checkout / ConfirmTermAndConditions to make this optional
' (SS,27/01/16) Change to Sub DoGoogleShoppingDataFeed to call optional custom function CustomGoogleShoppingProductAdjust to make adjustments
' E.g. For CIRC to set the correct price and weight for minimum sections for a radiator, Google had complained that the price in datafeed doesn't match the site
' (SS,28/01/16) Fix to Function GetEmailBodyMessage to prevent MessageToCustomer from being added twice when EmailUseMessageToCustomerForDespatch is True, via LMessageToCustomerAlreadyUsed
' (SS,01/02/16) Change to Sub SetStockInfo to show "Out of stock, please contact for availability" with link on "contact" instead of "Out of stock, more due soon"
' In two places, also added Function GetPleaseContactForAvailabilityInfo which can also be called from CustomUtils (i.e. as does HyperFlight) which has CustomGetStockInfo
' (SS,06/02/16) Change to Sub AddMultiMenu to allow up to 3 levels separated by "|" to be passed to AMenuName parameter
' (SS,17/02/16) New functions to help with Rich Snippets / Microdata GetProductPriceSimple, GetStockAvailabilityMicrodata, RemoveCurrencySymbol, GetBrandAndMPN
' (SS,03/03/16) Change to Function GetEmailBodyMessage to show date on despatch emails
' Changes to Sub DoGoogleAnalytics to use the latest method (analytics.js) also to add optional cross domain tracking (for CDC) via new setting FGoogleAnalyticsCrossDomainTrackingList
' (SS,09/03/16) Change to Function GetBulletHTML to allow embedded tokens, i.e. fetch common bullet points from a token
' (SS,24/03/16) Change to Function GetEmailBodyMessage, text "was despatched for delivery" removed "for delivery"
' (SS,11/04/16) Change to Sub DoGoogleAnalyticsEcommerce to fix ecommerce conversions which had stopped working since 3/3/16 when changing to new Universal Analytics, had forgotten to convert to new format for ecommerce tracking
' (SS,15/04/16) Changes to properly handle stock for options, changes were made to routines:
' AddProductToBasketIncludingOptions, AddToBasket, RecalculateBasket, TryProcessOrder, TakeProductsFromStock
' New routines CheckProductInBasketInStock, ClearExistingOrderPlaced, AlertMessageContains,
' ClearBasketOutOfStockMessage, AddBasketOutOfStockMessage, GetBasketOutOfStockMessage
' (SS,15/04/16) Change to Sub GetEmailBody, to also add the order details to bottom of despatch confirmation email.
' (SS,15/04/16) Change to Sub ShowBasketFooter in inc-template-basket.asp to not show VAT Deducted bit.
' (SS,19/04/16) Further improvements to stock handling, especially at check out stage where it redirects to basket page if basket not completely in stock
' Changes made to routines ShowBasket, CheckOut, ProcessOrder, TryProcessOrder, UpdateOrderStatusForOrderPlaced, CheckProductInBasketInStock
' New function CheckProductInBasketInStock
' Bug fix to prevent same order number being used again in same session, new function ClearOrderNo
' Tracking improvements to Function GetTrackingURLForCourier
' Change to ProcessOrderFinalise to clear Order No
' (SS,20/04/16) More elegant solution to reusing same OrderNo. Changes to TryProcessOrder, GetNextOrderNo, ClearOrderNo.
' New routines SetOrderNo, OrderNoCanBeReused, StopOrderNoReuse (renamed from ClearOrderNo)
' This allows OrderNo to be kept for PayPal payment at later stage to show specific URL which was losing the OrderNo and not showing the correct Thank You message.
' Previous solution from yesterday involving clearing order no after the redirect was not so elegant.
' (SS,20/04/16) Change to Sub ClearExistingOrderPlaced to fix issue where order lines for previous order in same session were deleted because it didn't look at new OrderNoCanBeReused
' (SS,10/05/16) Change to GetEmailBodyMessage, to add the text "Tracking may not start working for a few hours"
' (SS,12/05/16) Change to Sub ProcessRedirections to redirect to category/subcategory page if product detail page and product is disabled
' (SS,13/05/16) Change to Sub AddMultiMenuLevel to allow special dummy group/category to have a link set when prefixed with "URL:"
' (SS,19/05/16) Change to Sub DoGoogleShoppingDataFeed to add gtin and identifier_exists, also not to default mpn to product code
' Schema change to all sites manually via: ALTER TABLE products ADD COLUMN GTIN VARCHAR(50) NOT NULL DEFAULT '' AFTER BrandID;
' (SS,27/07/16) Change to Function GetNextOrderNo, and added new Function CheckOrderInStatus to fix issue where Order No was being reused in CIRC after previous order was paid for using PayPal/EPDQ
' (SS,05/07/16) Added SendOrderProgressEmails feature for CIRC, new routines SendOrderProgressEmails, IsTodayWorkingDay, OrderProgressEmailResultHeader, OrderProgressEmailResultFooter, OrderProgressEmailResultRow
' New stored functions GetFirstInteger, GetNthWorkingDay, IsWorkingDay added to common database, and table calender for working day calculations, execute access granted to common database to access these functions
' common.calendar table has working days up to and including 2110.
' It's called via order-progress-emails.asp (and task scheduled to run each weekday)
' (SS,06/07/16) Change to Sub SendOrderProgressEmails to show WorkingDaysToDespatch
' (SS,11/08/16) Added VAT number checking via official website, new routines:
' CheckVATNumber, CheckVATNumberViaWebsite, GetVATCheckResultFromXML, EmailOrderVATAlertAddFields
' Changes to NoteVATValidation and moved to VAT section. Change to Function CheckValidPersonalDetails to call CheckVATNumber to do an extra check.
' Changes to javascript.js to no longer do a VAT check in browser.
' Renamed IsVATNumberValid to IsVATNumberFormatValid
' Also separated alert email code from EmailOrder to Sub SendVATNumberAlertEmail
' (SS,11/08/16) Change to IsCountryVATable, delivery country now used to decide whether VAT is charged
' (SS,21/09/16) Changes to Sub ClearExistingOrderPlaced, Function GetNextOrderNo, Function OrderNoCanBeReused
' To prevent order detail records from being deleted if same customer tries to place another order in same session, previous order's detail records were being wrongly deleted.
' (SS,28/09/16) Changes to Sub DoGoogleShoppingDataFeed, added extra product attributes for google shopping, also added new field to attributes table
' i.e GoogleShopping TINYINT(1) DEFAULT '0' COMMENT 'Added 28/9/2016 to specify if it\'s for Google Shopping'
' To add extra attributes add to attributes table and set the GoogleShopping field to True. They will automatically get added to the data feed
' (SS,24/10/16) Various changes to implement local mode, address lookup
' (SS,02/11/16) Change to Function ProcessOrder to return False when there's a failure, e.g. delivery couldn't be calculated
' It was always returning True and therefore allowing orders with delivery cost of -1 to be placed
' (SS,04/11/16) Minor change to function IsDeliveryCostOverride and sub ClearLocalModeDetails to use IsEmpty and Empty. IsDeliveryCostOverride was returning True when Session("DeliveryCostOverride") was setting i.e. Empty converts to 0
' (SS,11/11/16) Change to Sub AddMultiMenuLevel and others to allow first menuitem to be a group (Clifton)
' (SS,24/11/16) Change to Function GetProductSearchSQL to add filter for min and max price
' (SS,25/11/16) Change to Sub GetEmailBody to show "Order Confirmation & Invoice" in header of email
' (SS,01/12/16) Change to add setting of order priority, new routines SetPriorityDefault, SetPriority, GetPriority, GetDeliveryWillCollect and changes to TryProcessOrder
' (SS,23/01/17) Change to Sub EmailOrder, email subject, now store name, followed by Order Confirmation (& VAT Invoice) / Despatched / Cancelled then order no (no hyphen)
' (SS,27/01/17) Change to Sub NoteVATValidation fixed bug which caused when saving account details because PaymentLog field doesn't exist in customers table, now Notes used if table is customers
' (SS,08/03/17) Change to Sub SendOrderProgressEmails, adjusted gap from 4 to 2 days
' (SS,10/03/17) Change to Sub SetUpPaymentButton (for Clifton), WorldPay logo extension changed from .gif to .png
' (SS,04/04/17) Improvements to local mode/exchange feature for CIRC
' (SS,05/04/17) Change to GetPricingFromProductRecord to add CustomGetNormalOptionsPrice
' (SS,05/04/17) Minor change to Function GetBankTransferPaymentLine. Fix to GetCurrencyFormattedValue to default to pound symbol if currencies table not set up. Also fix to GetLocalModeDetailsFromForm.
' (SS,06/04/17) Change to Sub ClearLocalModeDetails to clear all session variables except LocalMode. Change to GetBankTransferPaymentLine to remove default extra text.
' Renamed ClearLocalModeDetails to ClearLocalModeSettings.
' (SS,13/04/17) Added Sub SendRefundEmail and refund-details.asp
' (SS,27/04/17) Special token handling, change to ReplaceTokens, added new functions IsSpecialToken, HandleSpecialToken, HandleSpecialTokenNumber, FF
' Also added Sub FetchRecordToSession, Function FetchFieldFromSession to dbfunctions.pas to fetch record and fields for above
' (SS,28/04/17) Improvements to special token handling, added new function GetRecordHTML, changes to ComposeDescription and GetTableSetting
' (SS,18/05/17) Added feature to send Trustpilot review requests via AFS when sending order despatched (completed) comfirmation emails
' New routines Sub SendTrustpilotAFSEmail and function GetTrustpilotAFSEmailAddress, change to ShowEmailConfirmationHTML.
' (SS,18/05/17) Change to Sub SendSavedBasketEmail to add DeliveryInfo for CIRC. Also added GetDeliveryInfoForSavedBasket.
' (SS,19/05/17) Changes to Sub SendSavedBasketEmail and SaveBasket to also send name and address as part of email (required for CIRC proforma quotes)
' ---- to be filled in for recent changes
' (SS,13/06/17) Added Function GetSubcategoryLinkForRedirect and made change to ProcessRedirections to call GetSubcategoryLinkForRedirect instead of GetSubcategoryLink, to fix subcategory redirect issue
' (SS,30/06/17) Change to Function GetProductSearchSQL, full text search to order by relevance if no sort field set
' (SS,25/07/17) Various changes for HyperFlight, including changes to and adding of some new routines:
' Sub ShowProducts, Function GetProductSearchSQL, Sub ShowNewProducts, Sub ShowCheckoutFormFooter, Sub SetUpPaymentButton,
' Function GetPaymentBankTranferOptionSubmitLink, Function GetShowReviewsLink, Sub ShowReviews, Function GetProductOptionComboBox
' Function GetGrandTotalForOrder, Sub UpdateOrderDefaultPaymentCurrency, Sub UpdateBankTransferDetails
' (SS,28/07/17) Change to Function GetProductOptionComboBox to move option image rendering to Function GetProductDetailOptionImageHTML in inc-template-product-detail.asp
' (SS,31/07/17) Change to Sub DoGoogleAnalyticsEcommerce added order no existence check to prevent error when no session
' Added date/time (now) to Sub AddToPaymentLog to help with PayPal debugging
' Change to Sub CheckOut, to ensure PaymentLog is saved when returning from payment provider
' (SS,01/08/17) Added new Sub SetCurrencyForOrder, and changes to GetEmailBodyOrderDetails, ShowOrderDetailsMaster, ShowPreviousOrders to call it,
' previously the code existing GetEmailBodyOrderDetails. Now needed by ShowOrderDetailsMaster and ShowPreviousOrders to show correct historical currency values for order orders
' (SS,03/08/17) Change to Function GetLocalModeExchangeReasons, GetLocalModePaymentMethods to return a default value when blank
' (SS,04/08/17) Added new bank transfer methods ptBankTransferEUR = "BANK TRANSFER - EUR" and ptBankTransferUSD = "BANK TRANSFER - USD"
' (SS,08/08/17) Change to Sub ProductOptionsToArrayfixed, fix bug where comma entered into a text input type was result in extra option values to be incorrectly added (call to new GetInputTypeForProductOptionID)
' (SS,08/08/17) Change to Sub GetEmailBodyOrderDetails, call to ShowEmailOrderHeader to also pass on GBP Grand Total. Change also made to inc-template-email-html.asp to include the new parameter.
' (SS,09/08/17) Change to improve showing of bank transfer details and allow simpler (non HTML table entry) in settings.
' Changes to GetBankTransferDetails and GetBankTransferDetailsAsHTML. Added new GetBankTransferDetailsForCurrency and GetTextAsHTMLTable. Also test page via local mode: bank-details.asp.
' (SS,10/08/17) Change to Function GetWebText to include BANK_TRANSFER_DETAILS tokens and added new function BankTransferDetailsToHTML
' (SS,11/08/17) Added Sub SendPaymentReceivedEmail and new template payment-received-email.asp to allow payment received emails to be sent via Shopping Admin
' (SS,23/08/17) Changes to fix VAT No Check not being corrected saved and sent in alert email. Removed SavePaymentLog, changes to AddToPaymentLog, AppUtilsAddToPaymentLog, added GetPaymentLogSeparator and GetPaymentLogSection
' (SS,24/08/17) Changes to prevent XSS injection, notified to use via https://www.openbugbounty.org/reports/281769/
' Improvement to SendVATNumberAlertEmail to send a better formatted email and not with Alert prefix if VAT No valid. Also added SendNotificationEmail.
' Change to Sub UpdateOrderStatusForOrderPlaced to not SendAlertEmail "Cancelled order paid for. Stock automatically decreased.", LBody, False, requested by Neil
' (SS,25/08/17) Added new Function GetActiveProductCode to also return product code set via SetProductCode, rather from query string or SetProductCode, fixes nofity me link issue for wrong product in related products (for HF)
' (SS,12/09/17) Change to Function GetBankTransferPaymentLine to only show one option for GBP (e.g. not GBP or CHF) if currency other than GBP, EUR or USD. Due to HSBC charging high rates if non GBP paid into their account.
' (SS,14/09/17) Changes to Sub ShowRecentReviewsMain and Sub DoReviewsPage, to prevent duplicate reviews from appearing and harming SEO. Customers sometimes post the same review text across several products they purchased.
' (SS,15/09/17) Added Sub ShowProductReviewsInDesc for short reviews in product description
' (SS,20/09/17) Added Sub ProcessTransferWiseCallback to process TransferWise bank transfer payments from email
' (SS,22/09/17) Change to Google Shopping Data feed (subs DoGoogleShoppingDataFeed and AddGoogleShoppingField) to add "condition" attribute. Previously this was defaulting to "new". Now if a product attribute with name "condition" is set then this overrides this default.
' Also added functions GetProductPriceForMicrodata, GetProductConditionForMicrodata.
' (SS,25/09/17) Change to Sub AddGoogleShoppingField to convert "damaged" for Condition to "used". Microdata can be damaged but Google Shopping must map to "used"
' (SS,26/09/17) Change to Sub SendOrderProgressEmails, amended gap from 2 to LTemplateGapsDays, i.e. now getting from email templates
' Change to Function GetStockAvailabilityMicrodata and added new Function GetProductAvailabilityForMicrodata
' (SS,27/09/17) Change to Sub SendOrderProgressEmails to prevent 0-3 i.e. ensure minimum is 1, i.e. 1-3
' (SS,29/09/17) Change to allow extra GBP (TransferWise) account for Bank Transfer
' Changes made to Sub ProcessTransferWiseCallback and GetBankTransferDetailsForCurrency, added ptBankTransferGBP, FBankTransferDetailsGBP
' For HF Modified CustomShowBankTransferModal in custutils.asp
' (SS,04/10/17) Changes to CreateAccount, CheckOut, GetPersonalDetailsFromForm to improvements messages shown when customer can't log in. Improvement to bypass to work when password entered in lower section.
' New function GetCheckoutAccountBypassTextAndLink added.
' Changes to GetDeliveryCost, added new CustomerHasExistingOrder to only give free delivery for add to existing order if existing non-despatched order exists
' (SS,10/10/17) Added Facebook Pixel code to track Facebook adverts.
' DoFacebookConversionTracking is now redundant, renamed to DoFacebookConversionTrackingOld
' Facebook ConversionTrackID setting renamed to PixelID
' new functions added, ActivateFacebookPixelPurchaseConversion, GetConversionValue, DoFacebookPixel
' (SS,03/11/17) Change to Sub DoFacebookPixel, removed the noscript tag, causing missing alt tag error in W3C Validator
' (SS,06/02/18) Improvements to Sub SendOrderProgressEmails to make use of order_progress_email_days table. Also improved fields shown in results email.
' (SS,02/03/18) to 06/03/18 (Version 5.00) (was 4.94)
' Changes to migrate from old version of Google Analytics and AdWords conversion to new Global Site Tag i.e. analytic.js to gtag.js
' Simplified and cleaned existing tracking code. Also corrected any discrepancies e.g. Discount not being taking into account.
' All of this script is now in the HTML head tag as recommended by Google.
' New routines HTMLHeadStart, HTMLHeadEnd, IncrementPageViewCount, GetPageViewCount
' TrackPurchaseConversion, TrackPurchaseConversionDone, TrackPurchaseConversionAddToLog, GoogleAnalyticsEcommerceAddItem
' DoWebAnalytics, FinaliseWebAnalytics, TrackPurchaseConversionUpdateLog
' Complete rewrite of Sub DoGoogleAnalytics
' DoGoogleAnalytics no longer called from inc-template-main.asp. Replaced by HTMLHeadStart and HTMLHeadEnd.
' DoFacebookPixel now called from HTMLHeadEnd instead of Finalise.
' Conversion now only done when returning from payment provider.
' Following references removed: FEcommerceTrackingActivated, FAdWordsPurchaseConversionActivated, FFacebookPurchaseConversionActivated, FGoogleAnalyticsDone
' Following routines removed Sub DoGoogleAdWordsRemarketingTag, Sub SetRemarketingTagTotalValue, Sub ClearRemarketingTagProductIDs, Sub AddRemarketingTagProductID, Function GetRemarketingTagProductIDs,
' Sub ActivateGoogleAnalyticsEcommerceTracking, Sub ActivateGoogleAdWordsPurchaseConversion, Sub ActivateFacebookPixelPurchaseConversion
' !! NB. Need to amend inc-template-main.asp, replacing DisableCache with HTMLHeadStart and add HTMLHeadEnd to end of head tag.
' Also need to enable Enhanced Ecommerce features in Google Analytics.
' !! Don't forget to add the new conversion_tracking_log table.
' (SS,07/03/18) Added to Sub SetUpEPDQForm(AOrderNo), hidden ACCEPTURL to redirect back to site automatically, but wasn't working
' Google AdWords tracking not working. I realised it could be due to missing "AW-" prefix from gtag config and send to. Added the prefix in DoGoogleAnalytics.
' Still was tracking conversions. Later found settings in the AdWords account showing event 'conversion' not 'purchase', so renamed purchase to conversion.
' (SS,12/03/18) Improvements to order confirmation emails. Sorted issue which was resulting in emails appearing with empty attachment in Outlook 2016, even worse in some other email clients.
' Changes made in inc-template-email-html.asp and order-details.asp. Also need to amend inc-main.asp to include inc-template-email-html.asp
' Also to Sub EmailOrder, Sub ShowEmailConfirmationHTML, Function GetEmailBody, Function GetEmailBodyOrderDetails, Function GetEmailBodyAddressDetails, Sub GetEmailBodyOrderItems
' Some subs are now functions which a return a string. We're no longer fetching a webpage to get the content and no longer sending via the ITPEmailer.
' This method should be more reliable and hopefully avoid the empty attachments in some email clients.
' ===============
' MIGRATION NOTES (from 02/03/18 to 13/03/18)
' ===============
' 1. Template inc-template-main.asp, remove DoGoogleAnalytics and DisableCache. Add HTMLHeadStart and HTMLHeadEnd.
' 2. Add new versions of apputils.asp, inc-template-email-html.asp, order-details.asp. Modify inc-main.asp to include inc-template-email-html.asp.
' 3. Add new table conversion_tracking_log
' 4. Make sure Enchanced Ecommerce enabled in Google Analytics.
' (SS,22/03/18) Added SMS feature, new SMS routines: Sub SendSMS, Function SMSEnabled, Function TelephoneIsMobile
' Templates held in "SMS Templates" in sitedetails table. New setting SMS / Enabled.
' ===============
' MIGRATION NOTES
' ===============
' Need to include the latest order-details.asp file because customutils.asp is now included in it. This was due to call to possible function CustomCheckSMS.
' (SS,23/03/18) Added new payment method/type/provide called Payzone. New table called payzonecallbacks was added.
' (SS,06/04/18) Improvements to Payzone.
' (SS,09/04/18) Added GetPayPalUKOnlyCheckout to Sub SetUpPaymentButton, to only show PayPal at normal checkout for UK (if enabled)
' Normal PayPal Express Checkout is shown for all regions.
' New setting Payment / PayPalUKOnlyCheckout, which defaults to False
' (SS,12/04/18) Changes to Sub SetUpPayzonePayForm, and AddPayzoneField, added Function IsPayzoneSafeString.
' To get around character issues with Payzone, i.e. non-ascii and special chars.
' (SS,16/04/18) Removed the direct (2 second) delay pay to payment provide
' Replaced SetUpChequeOrBankTransferPayForm with SetUpChequePayForm and SetUpBankTransferPayForm
' Changes made to javascript.js
' (SS,17/04/18) Removed Const PAYMENT_CONFIRMED and references to it, not used (i.e. there appears to be a Const PAYMENT_CONFIRMED which doesn the same thing)
' Removed payment redirect code: Sub ShowPaymentDirectionInfo, Sub DoPaymentRedirection, Sub DoPaymentRedirection, Sub ActivatePaymentRedirection
' References to Dim FPaymentRedirectionActivated, const REDIRECT_TO_PAYMENT_SITE deleted.
' Removed Sub SetUpPayment (only used for redirection)
' Removed references to DirectPay i.e. taking payments directly from customer. Was used for ExpressMusic but not anymore.
' Removed code for redundant payment methods GoogleCheckout and Direct Pay
' Including functions GetDirectPaySecureSite, GetDirectPayAllowedCardTypes
' Removed Functions IsCreditCardType, CheckValidCreditCardType, CheckValidCreditCardDetails, ValidateCardNumber, AreNumericDigits, ValidCardLengthPrefix, ValidCardCheckDigit
' Removed Subs GetCreditCardTypeFromForm, GetCreditCardDetailsFromForm, SetUpDirectPayForm
' Removed credit card constants Const CC_MASTERCARD, CC_VISA, CC_VISA_DELTA, CC_VISA_ELECTRON, CC_VISA_PURCHASING, CC_JCB, CC_MAESTRO, CC_SOLO, CC_AMERICAN_EXPRESS.
' Removed Dims FDirectPaySecureSite, FDirectPayAllowedCardTypes, FGoogleCheckoutMerchantID, FGoogleCheckoutSiteSeal
' Removed Const ptDirect = "DIRECT" and ptGoogleCheckout = "GOOGLE CHECKOUT"
' Also removed functions IsDirectPayment, IsPayPalPayment, IsWorldPayPayment, IsEPDQPayment, IsSagePayPayment
' Removed routines SetUpGoogleCheckoutPayForm, GetGoogleCheckoutMerchantID, GetDescriptionForGoogleCheckout, GetTotalAndVATRateForGoogleCheckout, GetGoogleCheckoutSiteSeal
' Modified GetCriteriaForCountryCombo to work using only the Enabled field.
' (SS,18/04/18) Added Sub SendWebLogEmail which calls new GetWebLog function in dbfunctions.asp.
' Removed consts for credit card payments:
' Const GET_CREDIT_CARD_TYPE = "GetCreditCardType"
' Const GET_CREDIT_CARD_DETAILS = "GetCreditCardDetails"
' Also removed credit card (If) code from Sub CheckOut
' Following removed from inc-template-check.asp:
' Sub ShowCreditCardTypeForm, Sub ShowCreditCardDetailsForm, Sub ShowCardType, Sub ShowCustomerInfoDirectCardTypes, Sub ShowCheckoutPayNowWithCreditCardButton
' !!! Need to remove credit card fields from orders table
' Change to Sub CheckOut to no longer show account already exists warning and dialog if existing email address used, just continues
' Change to Function TryProcessOrder, to preserve payment log which was being lost when this routine called again for same order
' (SS,19/04/18) Changes to fix PayPal Express when used via PayPal checkout on basket page. Change to inc-template-checkout.asp to add new ShowCheckoutMakePayPalPaymentButton
' (SS,20/04/18) Changes to Sub SetUpPaymentButtons, Sub SetUpPaymentButton. Added Function IsHyperFlight to allow Bank Transfer button to be shown higher for HF.
' Added SetCreditCardPaymentProviderFailure and IsCreditCardPaymentProviderFailure, to override PayPal not being shown by default
' (SS,23/04/18) Added FPayPalExpressShowOnBasketPage, Function GetPayPalExpressShowOnBasketPage to allow PayPal Express checkout button to be optionally show in basket page. Default is True.
' Also made a change to Sub ShowBasketFooter in inc-template-basket.asp to make use of this.
' (SS,25/04/18) Change to Function GetEncryptedEPDQData to use Function GetWebPageViaPOST instead of ASP object SOFTWING.ASPtear
' (SS,30/04/18) Various changes to EPDQ to work using the new method, returning from browser, new table epdq_redirect_callbacks
' ===============
' MIGRATION NOTES
' ===============
' For EPDQ new table epdq_redirect_callbacks, and new Payment / ePDQTestMode setting
'
' (SS,01/05/18) Change to Function EPDQSuccessfulPayment to also allow Status 9. Change also to ProcessEPDQRedirectCallback to call EPDQSuccessfulPayment.
' (SS,22/05/18) Changes in inc-template-checkout.asp and inc-template-account.asp remove HearAboutUs and Subscribe me fields from first stage of checkout.
' Subscribe moved to Thank you page. Terms and condition default can be ticked.
' (SS,23/05/18) Added subscription routines GetCustomerIDForOrder, IsCustomerOptedIn, SetCustomerSubscribed, GetSubscribeLink
' Changes also made to inc-template-checkout.asp and inc-template-account.asp
' (SS,08/06/18) Added ability to add a surcharge for PayPal (non-UK)
' Changes made to PayPalExpressConfirmPayment, UpdateOrderStatusForOrderPlaced
' New setting FPayPalNonUKSurchargePercentage
' New routines GetPayPalSurchargePercentage, IsPayPalSurcharge, PayPalAmountWithSurcharge
' New field added to orders:
' ALTER TABLE orders ADD PaymentAmountReceived DOUBLE NOT NULL DEFAULT '0' COMMENT 'Added 8/3/2018 for HF, Schema 3' AFTER PaymentReceived;
' (SS,25/06/18) Two more feeds added to hold surcharge about and fee separately.
' ALTER TABLE orders
' ADD PaymentProviderSurcharge DOUBLE NOT NULL DEFAULT '0' COMMENT 'Added 11/6/2018 for HF, Schema 3' AFTER PaymentAmountReceived,
' ADD PaymentProviderFee DOUBLE NOT NULL DEFAULT '0' COMMENT 'Added 11/6/2018 for HF, Schema 3' AFTER PaymentProviderSurcharge;
' (SS,25/06/18) Changes to UpdateOrderStatusForOrderPlaced and ProcessOrderThirdPartyPayment to add new PaymentProviderSurcharge and PaymentProviderFee parameters
' (SS,26/06/18) Renamed orders field PaymentAmountReceived to PaymentReceivedAmount, added new field PaymentReceivedCurrencyCode
' Changes made to ProcessOrderThirdPartyPayment, UpdateOrderStatusForOrderPlaced, PayPalExpressConfirmPayment
' Also added new PaymentReceivedCurrencyCode field (PaymentReceivedCurrencyCode CHAR(3) DEFAULT NULL)
' (SS,28/06/18) Correction to Sub ProcessWorldPayCallback
' Added code for DeliveryOption, currently Tracked Delivery, implemented for HyperFlight
' New routines: TrackedDeliveryOptionEnabled, SetTrackedDeliverySelected, GetTrackedDeliverySelected,
' SetTrackedDeliverySelectionAllowed, GetTrackedDeliverySelectionAllowed, SetDeliveryOption, GetDeliveryOption, GetCountryOptions
' Change to Function TryProcessOrder to save DeliveryInfo field
' New Options field added to Countries table via:
' ALTER TABLE countries ADD Options VARCHAR(100) DEFAULT NULL COMMENT 'Added 28/6/2018 for HF, Schema 4' AFTER VATNumberFormat;
' Added TrackedDelivery checkbox to basket page in inc-template-basket.asp
' Change made to Sub SetCountryFromBasketForm. Also to CustomGetDelivery in customutils.asp
' (SS,29/06/18) Minor change to Sub SetCountryFromBasketForm
' (SS,03/07/18) Change to Function GetTrackingURLForCourier to add tracking for new delivery agent names "Tracked Airmail" and "Untracked Royal Mail"
' (SS,19/07/18) Added HTMLEncode in inc-template-subcategory-list.asp to ShowSubcategoryListHeader, ShowSubcategoryListItem and ShowSubcategoryListItemWithPicture
' to prevent XSS (notified to use by OpenBounty checker (see email on this date for further info)
' Also to FPageName in inc-pages.asp.
' (SS,01/08/18) Added additional options info for CIRC radiators via custom call to CustomAddMoreOptionsInfo in customutils.asp
' Via new function AddMoreOptionsInfo, called from Sub ShowBasket, Function GetEmailBodyOrderItems, Sub SendSavedBasketEmail
' Additional info, e.g. Length, BTU, kW, only shown in basket, email and email basket. It's not actually held in the database.
' (SS,02/08/18) Refinement to AddMoreOptionsInfo, to keep the adding code in CustomAddMoreOptionsInfo rather than in AddMoreOptionsInfo
' (SS,10/08/18) Changes to the images references, getpic.asp replaced with jpg file name if new setting UseGetPicASP is False.
' See "Image / Picture Naming Routines" section at the end fo more details.
' Changes made to image URL fetching routines.
' (SS,11/08/18) Change to GetProductImageNameForPictureID to not add caption if SortOrder is 10, i.e. main image.
' (SS,16/08/18) Change to Sub Initialise, call to SetPriorityDefault change from integer 5 to string "5" to fixed issue where it was defaulting to 9 instead of 5.
' (SS,28/09/18) Change to Sub ShowCheckoutFormFooter, removed banktransferoption, not required here, it's passed on via frmBankTransferPay (SetUpBankTransferPayForm)
' Also had to make a fix in javascript.js function itp_payment_banktransfer to refer to frmBankTransferPay (not frmCheckout which was a bug)
' It was result in the wrong bank details being shown and emailed to customer.
' (SS,15/10/18) Change to Sub SendContactEmail and Sub AddContactToDatabaseAndEmail to send email as HTML instead of plain text.
' To help Paul (CIRC), he wants to add signature with less hassle.
' (SS,18/10/18) New setting FProductSubproductsEnabled.
' Change to AddProductToBasketIncludingOptions to call custom function CustomAddSubproductsToBasket
' New functions GetFieldForProductID, GetBasketOptionValueForItemID
' (SS,19/10/18) Change to Function to not fail with delivery error if in local mode.
' (SS,26/10/18) Added Function GetShoppingBasketFilter and change to Function GetOrderItemsQuery to call GetShoppingBasketFilter to ignore subproducts where applicable.
' New parameter AIncludeSubproducts added to Function GetOrderItemsQuery and to all calls to his with either False or True.
' Also change to Sub ShowShoppingStatusMain, added GetShoppingBasketFilter to query to exclude subproducts.
' (SS,29/10/18) Change to Function TryProcessOrder to set new SubproductOrderDetailID field for subproducts
' (SS,30/10/18) New function GetOrderOptionValueForOrderDetailID
' Modified Sub SaveBasket and Sub RestoreBasket to handle subproducts, i.e. not saved in basket, but created when basket restored
' (SS,31/10/18) Added Function GetOrderDetailsFilter, similar to GetShoppingBasketFilter, but works to filter order detail records
' Changes to make use of GetOrderDetailsFilter where applicable i.e. most places where "FROM orderdetails" is used
' Added Function GetProductsFilter, to filter out subproducts from products table. Changes to call this in various places where "FROM products" / "INNER JOIN products" is used.
' (SS,07/11/18) Change to Function GetProductCodeQS to force upper case. Replaced all occurrences of Request.QueryString("code") with GetProductCodeQS
' Following schema changes applied (schema 4):
' CREATE TABLE product_subproducts (
' ProductSubproductID INTEGER(11) NOT NULL AUTO_INCREMENT,
' ProductID INTEGER(11) NOT NULL,
' SubproductID INTEGER(11) NOT NULL,
' PRIMARY KEY (ProductSubproductID),
' UNIQUE KEY idxProductIDSubproductID (ProductID, SubproductID),
' KEY idxProductID (ProductID),
' KEY idxSubproductID (SubproductID)
' )ENGINE=MyISAM
' AUTO_INCREMENT=1 ROW_FORMAT=FIXED CHARACTER SET 'latin1' COLLATE 'latin1_swedish_ci';
'
' ALTER TABLE products
' ADD HasSubproducts TINYINT(1) NOT NULL DEFAULT '0' COMMENT 'Added 27/9/18, for CIRC, Schema 4' AFTER CustomFlag,
' ADD IsSubproduct TINYINT(1) NOT NULL DEFAULT '0' COMMENT 'Added 27/9/18, for CIRC, Schema 4' AFTER HasSubproducts;'
'
' ALTER TABLE shoppingbaskets
' ADD SubproductItemID INTEGER(11) DEFAULT NULL COMMENT 'Added 18/10/18, for CIRC, Schema 4' AFTER Qty,
' ADD KEY idxSubproductItemID (SubproductItemID);
' ALTER TABLE orderdetails
' ADD SubproductOrderDetailID INTEGER(11) DEFAULT NULL COMMENT 'Added 29/10/18, for CIRC, Schema 4' AFTER PrevStock,
' ADD KEY idxSubproductOrderDetailID (SubproductOrderDetailID);
' (SS,16/11/18) An order for HyperFlight had HTML stuffed into the form fields. Possibly a virus adding HTML and javsacript to each form field
' This could result in a malicious site being access when order confirmation details are rendered in a browser.
' Although no harm was done with this example (Order 52961)
' To get around this I added new function CleanRequestForm to dbfunctions.asp which strips HTML tags and converts <> to HTML entities
' Replaced all Request.Form calls with calls to CleanRequestForm about 181 or so (including comments).
' (SS,19/11/18) Replaced nearly all occurrences of Request.QueryString with CleanRequestQueryString.
' Only one I didn't do was the one in Sub ProcessSagePayCallback for LCrypt = Request.QueryString("Crypt")
' (SS,20/11/18) Replaced all occurrences of Request with CleanRequest (CleanRequest first checks QueryString and then Form)
' (SS,21/11/18) Change to WAIT_TIMEOUT from 5 to 10 in Function CheckVATNumberViaWebsite to sort VAT Number check timeout issue.
' (SS,05/12/18) Removed Sub UpdateOrderCardDetails, redundant due to fields being removed.
' Following credit card fields removed from orders:
' CardType, CardNumber, CardHoldersName, CardSecurityCode, CardIssueNumber, CardValidFrom, CardExpiryDate
' Following two new fields added: RefundMethod, RefundAccountLast4Digits
' RefundMethod set to same as PaymentMethod using:
' UPDATE orders SET RefundMethod = PaymentMethod WHERE RefundAmount > 0;
' Schema version set to 5.
' SQL:
' ALTER TABLE orders
' DROP CardType,
' DROP CardNumber,
' DROP CardHoldersName,
' DROP CardSecurityCode,
' DROP CardIssueNumber,
' DROP CardValidFrom,
' DROP CardExpiryDate,
' ADD RefundMethod VARCHAR(20) DEFAULT NULL COMMENT 'Added 4/11/18, for CIRC, Schema 5' AFTER RefundAmount,
' ADD RefundAccountLast4Digits CHAR(4) DEFAULT NULL COMMENT 'Added 4/11/18, for CIRC, Schema 5' AFTER RefundMethod;
'
' Changes to Sub SendRefundEmail to include new RefundAmount and RefundAccountLast4Digits fields.
' Template RefundEmailTemplate needs to be modified to include these two fields
'
' (SS,07/12/18) Change to function HandleSpecialToken to trap error in Eval and return "?ERROR?"
' (SS,13/12/18) Added cancel order facility for orders awaiting payment paid for by cheque or bank transfer
' New functions CancelOrderAllowed, GetCancelOrderURL, sub CancelOrder
' Changes to functions GetEmailBody and GetEmailBodyMessage and Sub ProductsMain.
' Change to Function GetProductsPageTitleV2 to add cancelorder.
' (SS,13/12/18) Added new token feature to allow ProductName to be set.
' Change to Function ComposeDescription and added new Function GetActionHTML
' (SS,14/12/18) Further improvements to token table feature to add APH and RBR
' APH adds product hrefs and RBR allows blank rows to be removed
' Changes to functions ComposeDescription, IsSpecialToken, HandleSpecialToken, added new function RemoveBlankTableRows
' Change to Function GetActionHTML to limit field size to 100 to prevent data too long error
' (SS,19/12/18) Modified Function GetDiscountUsingVoucherCode to handle new MinimumQty field
' Added email address to Payzone (for email receipt to customer) changes to Sub SetUpPayzoneForm and Sub ProcessPayzoneCallback
' (SS,22/01/19) Rounded review stars to 1 dp (instead of nearest half). Google showing stars for products and rounding to 1 dp. For HF product KST-DS215MG
' was showing 4.5 instead of 4.7. Change made to Function GetReviewRatingAndCount.
' (SS,27/02/19) Change to Sub SendOrderProgressEmails to use DateTimePaid instead of DateTimeOrdered due to bank transfer payment delays
' (SS,18/03/19) Change to Sub SaveStockNotification to left name to 50 and email to 100 prevent "Data too long for column 'Name'"
' (SS,05/04/19) Changes to jpg image cache to also work for category, subcategory, option and other images. Previously it work only for product images.
' New functions GetImagesFolder, GetCategoryImagesFolder, GetSubcategoryImagesFolder, GetOptionImagesFolder, GetOtherImagesFolder, GetCategoryImageLink,
' GetSubcategoryImageLink, GetImageNameForCategory, GetImageNameForSubcategory, GetImageNameForProductOptionValueID, GetImageNameForOther
' Changes to Function GetProductImageLink, GetProductOptionValueImageLink, GetStoreLogoLink
' Changes also made to create-image-cache.asp
' (SS,06/04/19) Changes to image cache folder names, images to img, and products to p (later removed p), category to c etc.
' (SS,07/04/19) Further improvements, to automatically create image file in cache if it doesn't already exist
' New functions GetImageName, EnsureImageExists, FileExistsWithSameDateTime, new subs EnsureFolderExistsForFileName, SetFileLastModifiedDate
' Removed functions GetProductImageNameForProductCode, GetImagesFolder, GetCategoryImagesFolder, GetSubcategoryImagesFolder, GetOptionImagesFolder, GetOtherImagesFolder
' Removed functions GetImageNameForCategory, GetImageNameForSubcategory, GetImageNameForProductOptionValueID, GetImageNameForOther
' New routines SetImageCreatedInCache, SetImageCreatedInCache.
' (SS,09/04/19) Added Function GetImageCacheFolder, separated sub ShowTimer into Function GetTimer.
' (SS,09/04/19) Change to Sub DoFacebookPixel to add event tracking for product page view and add to basket. Added function IsAddToBasketPage.
' (SS,10/04/19) Change to Sub DoFacebookPixel to content_type and content_ids to ViewContent and AddToCart. Added function GetBasketProductCode.
' (SS,10/04/19) Renamed DoGoogleAnalyticsEcommerce to DoTrackPurchaseEvent to also work for Facebook.
' Added ATrackType parameter which can be ttGoogle for Google or ttFacebook for Facebook.
' Added new constants ttGoogle, ttFacebook
' Renamed GoogleAnalyticsEcommerceAddItem to TrackPurchaseEventAddItem, added ATrackType to also work for Facebook
' The above was for improved dynamic ads remarketing in Facebook.
' (SS,11/04/19) Change to DoTrackPurchaseEvent to add JavaScriptStrEscape to GetStoreName
' Replaced 2 x SingleQuotedJavascriptString with JavaScriptStrEscape in Sub TrackPurchaseEventAddItem because product name may contain a double quote
' From DoFacebookPixel sub Removed the additional (not necessary I think) <noscript><img> tracking because it was interfering with the Facebook Pixel Helper add-on
' (SS,12/04/19) Change sub DoFacebookPixel to added events Search, InitiateCheckout, InitiateCheckout, AddPaymentInfo
' New functions IsStartOfCheckout, IsCheckoutFilled, IsNewSearch
' Modified function GetTotalItemsInBasket to add GetShoppingBasketFilter(False) to ignore subproducts (missed from before)
' (SS,16/04/19) Change to Sub ShowEmailConfirmationHTML to call Function AuthenticateOrderNo (code from ShowEmailConfirmationHTML move to AuthenticateOrderNo
' Also used by new Sub SendCancelledOrderEmail.
' (SS,18/04/19) Change to Sub Initialise to set Application("WebsiteURL") which is required by global.asa when session is closed for order placed
' Also new setting FCancelledOrderEmailsEnabled. New Sub SendCancelledOrderEmail called from send-cancelled-order-email.asp.
' (SS,25/04/19) Added Function GetOrderFieldValues to return up to 5 values from orders record.
' (SS,01/05/19) Change to Function GetActionHTML to log the product rename to new log_product_renames table for debugging
' Bugfix, change to Sub DoGoogleShoppingDataFeed, to product name renaming for HF, setting the product code global before call to ComposeDescription fixed it
' (SS,03/05/19) Improvement to Add to Existing Order feature. Customer were ticking this unnecessarily. Improved to remove in same
' checkout stage showing alert message if customer doesn't have an existing order that hasn't been completed.
' New subs SetDeliveryAddToExistingOrder, AddToExistingOrderAddMessage, AddToExistingOrderRemoveMessage
' Changes to Sub CheckOut.
' (SS,03/06/19) Change to Sub SendCancelledOrderEmailModified to not send email if another subsequent order exists for same email address
' (SS,28/06/19) Change to Function GetEmailBodyMessage to add additional message to completed order email (VDK)
' (SS,29/08/19) Change to Function GetSEOFriendlyImageName so that size of "" returns the name for small thumbnail, fixes issue with basket page showing "o" original image
' ### Perhaps in future modify inc-template-basket.asp so that it specifically fetches "s" instead of "".
' (SS,26/09/19) Bugfix to Function CustomerHasExistingOrder to fix issue where add to existing order wasn't working in same session due to order no being retained before next one is issued.
' (SS,24/10/19) Added Function GetReviewAggregateRating
' (SS,28/10/19) Google Sitemps, change to 2nd querystring parameter to use "&" instead of "&" investigate issue in Google Search Console
' Change made to Sub AddGoogleSitemapLevel
' (SS,06/12/19) Change to Function GetHTMLDescription, MAX_LENGTH from 250 to 160. Google search now only display first 160 or so characters.
' Also adjusted to replace 3 and 2 spaces with single space.
' Change to Function GetHTMLTitle to not create a title more than 60 characters for product detail page i.e. not add category/subcategory if it results in more than 60 characters
' (SS,10/12/19) Modified Sub SendOrderProgressEmails (for CIRC) to prevent less than 1 appearing in from days. Now the minimum will be 1 - 2.
' (SS,13/01/20) Minor change to Sub SendReviewRequests (for CIRC) to debug an issue
' (SS,14/01/20) Further mod to Sub SendReviewRequests (for HF) to remove the >= 14 Dec and <= 31 Dec (Christmas delay), added different delay for non-uk orders (for HF) if set
' (SS,17/03/20) Change to Sub SendReviewReceivedEmail (for HF) to change from email from FEmailContactFrom to reviewer's email address
' (SS,19/03/20) Modifications to vouchers to add free delivery and extra range
' Removed field SingleUsage (no longer used, some database still had it)
' Ensured that VoucherCode was CHAR (not VARCHAR)
' Increased length of VoucherName from VARCHAR(40) to VARCHAR(80)
' Added new fields: FreeDelivery, MinimumSpend2, MinimumQty2, DiscountAmount2, DiscountPercentage2, FreeDelivery2
' (SS,19/03/20) Modified voucher system to allow free delivery and additional range
' Changes made to Function GetDiscountUsingVoucherCode, Function GetDiscountUsingAutoVoucherCode, Sub GetOrderTotalsForBasket
' Added new function GetDiscountForRange
' (SS,24/03/20) Added feature to temporarily suspend ordering.
' New function OrderingSuspended, new setting Application / OrderingSuspended
' Changes also to function InStock, Sub SetStockInfo
' Also to Sub ShowStockNotificationLink in inc-template-other.asp
' Custom routine Function CustomOrderingSuspended can be added to customutils.asp to fine tune which products to apply to
' (SS,26/03/20) Added mailshot feature for CIRC. New Sub SendMailshot, new functions MailshotResultOverallHeader, MailshotResultOverallFooter, MailshotResultHeader, MailshotResultFooter MailshotResultRow
' Also new function called CustomMailshotGetSQL in customutils.asp.
' (SS,31/03/20) Added form-control to class for combo box in Function GetProductOptionComboBox
' (SS,06/04/20) Changed to Sub SendStockNotificationEmail to add additional text defined in sitedetails table / Other / StockNotificationEmailAdditional
' Change to Function OrderingSuspended to pass ProductID to CustomOrderingSuspended
' Added Function ProductOrderingSuspended, change to Function OrderingSuspended to call this instead
' Modified Sub RestoreBasket to fix loophole of suspended products from being ordered due to this feature
' ### need to investigate the proper and standard way of encoding a URL for sitemap use, canonical link use and html link use, have a routine that
' can be called with parameter true or false to include full path or just relative
' and use it DoGoogleSitemap, Get Product with Name link and ShowCanonicalLink
' (SS,15/10/18) To do - improve formatting for enquiry email. Now HTML was plain text. E.g. add table, full HTML tags. See Sub AddContactToDatabaseAndEmail and SendContactEmail
' *** TO DO 14/2/13 get values from settings for functions GetFeefoLogon, GetFeefoFTPHost, GetFeefoFTPUser, GetFeefoFTPPassword, ReviewTypeIsFeefo
' *** 10/5/13 feefo caching
' *** IMPORTANT *** 8/5/10 need to update ahref parameter in ShowProductListJumpFooterNext & ShowProductListJumpFooterPrevious in inc-template-product-list.asp in all shopping sites
' *** IMPORTANT *** 17/4/07
' install notes for sites besides ExpressMusic
' Remove code from inc-pages.asp from If Request.QueryString("xcmd") = "delete" Then.... to End If
' Add DoBasketOperation to end of inc-main.asp
' need to test all basket operations are working
' check shopping status looks okay
' also check that currency change still works in clifton
'
' also add the latest dbfunction.asp to existing shopping apps
'
' because of various changes to pricing routines, need to check with existing apps, especially contract prices with toner2desk
' Add Finalise to end of inc-template-main.asp to close database etc
' Finalise must be called specifically from call back routines too
' Password code moved from inc-template-main.asp to apputils.asp Initialise sub
' OpenDatabaseCommon... to be test with currency/clifton
' Added "Region" field to Orders and Customers after "Country", moved DeliveryRegion after DeliveryCountry
' GetDeliveryCombo in inc-template-checkout.asp renamed to GetDeliveryRegionCombo
' Countries table, added new fields RegionCode Char(3) and SortOrder Smallint
' DeliveryRegions table, changed RegionCode from VARCHAR(2) to VARCHAR(3)
' DeliveryRegion field can be removed from orders table
' Need to modify express music to work without DeliveryRegion, make sure delivery works correct, modify countries table
' Functions such as ShowSpecialDeliveryRegion, GetDeliveryRegionCombo will need to be removed
' ShowCheckoutOrderDetailsFooter in inc-template-checkout added AInCheckout, AOrderNo parameters
' 22/6/07 Orders table: Deleted fields Region, DeliveryRegion, added TotalWeight
' ============'
' global constants and variables '
' (SS,11/2/04) added payment variables because it is now to handle worldpay and direct payments '
Const ptWorldPay = "WORLDPAY"
Const ptPayPal = "PAYPAL"
Const ptEPDQ = "EPDQ" ' (SS,7/9/06)
Const ptSagePay = "SAGE PAY" ' (SS,20/3/13)
Const ptPayzone = "PAYZONE" ' (SS,22/3/18)
Const ptCheque = "CHEQUE" ' (SS,28/5/09)
Const ptBankTransfer = "BANK TRANSFER" ' (SS,28/5/09), (SS,5/8/11) changed to "BANK TRANSFER"
Const ptPayPalExpress = "PAYPAL EXPRESS" ' (SS,20/11/14)
' (SS,4/8/17) added following special bank transfers to EUR to USD accounts selected via bank transfer modal form
Const ptBankTransferEUR = "BANK TRANSFER - EUR"
Const ptBankTransferUSD = "BANK TRANSFER - USD"
Const ptBankTransferGBP = "BANK TRANSFER - GBP" ' (SS,29/9/17)
' (SS,10/4/19) added following for tracking type
Const ttGoogle = 1
Const ttFacebook = 2
' following are used to determine how many to show across and down '
' and fine tuning the main product listing *** perhaps move these to app variables '
Const PRODUCTS_ACROSS = 4
Const PRODUCTS_DOWN = 2
Const SMALL_THUMBNAIL_WIDTH = 100
Const SMALL_THUMBNAIL_HEIGHT = 100
Const LARGE_THUMBNAIL_WIDTH = 240
Const LARGE_THUMBNAIL_HEIGHT = 240
Const PAGES_PER_GROUP = 10
Const HORIZONTAL_SPACE_TO_IGNORE = 310
Const VERTICAL_SPACE_TO_IGNORE = 160
Const HORIZONTAL_SPACE_PRODUCT = 107
Const VERTICAL_SPACE_PRODUCT = 200
Const MIN_PRODUCTS_ACROSS = 4
Const MIN_PRODUCTS_DOWN = 2
Const MAX_PRODUCT_QTY = 1000
Const ORDER_PLACED_STATUS = "ORDER PLACED"
Const AWAITING_PAYMENT_STATUS = "AWAITING PAYMENT" ' (SS,28/5/09)
Const PAYMENT_RECEIVED_STATUS = "PAYMENT RECEIVED"
Const ORDER_CANCELLED_STATUS = "CANCELLED"
Const PAYMENT_ON_ACCOUNT_STATUS = "PAYMENT ON ACCOUNT" ' to be added (SS,23/8/04) '
Const ORDER_COMPLETED_STATUS = "COMPLETED" ' (SS,15/8/05)
' (SS,12/8/05) following moved here from Sub CheckOut
Const GET_ACCOUNT_TYPE = "GetAccountType" ' (SS,3/8/04) '
Const GET_PERSONAL_DETAILS = "GetPersonalDetails"
Const GET_DELIVERY_ADDRESS = "GetDeliveryAddress" ' (SS,4/8/04) '
' Const CONFIRM_DETAILS = "ConfirmDetails" ' (SS,27/7/05) removed '
Const CONFIRM_ORDER = "ConfirmOrder"
Const CONFIRM_PAYMENT = "ConfirmPayment" ' (SS,29/5/09)used for cheque or bank transfers
Const RETURN_FROM_THIRD_PARTY_PAYMENT = "co" ' (SS,14/8/05)
Const COMPLETE_ORDER = "CompleteOrder" ' (SS,27/7/05) '
Const CS_PAYPAL_EXPRESS_CHECKOUT = "PayPalExpressCheckout" ' (SS,19/11/14) checkout stage for redirecting to PayPal Express
Const CS_PAYPAL_EXPRESS_RETURN = "PayPalExpressReturn" ' (SS,19/11/14) checkout stage for return from PayPal Express
Const CS_PAYPAL_EXPRESS_CANCEL = "PayPalExpressCancel" ' (SS,19/11/14) checkout stage for return from PayPal Express with cancel chosen
Const VATABLE_REGIONCODES = "UK,EU" ' (SS,13/9/06)
' (SS,18/9/06)
Const DEFAULT_CURRENCY_CODE = "GBP"
Const DEFAULT_CURRENCY_RATE = 1
Const DEFAULT_CURRENCY_SYMBOL = "£" ' (SS,28/10/09) replaced "�" with "£"
Const DEFAULT_CURRENCY_SHOW_VAT = True
Const DEFAULT_CURRENCY_COUNTRY = "United Kingdom"
Const MAIN_CURRENCIES = "GBP,USD,EUR" ' (SS,18/12/14) i.e. the major currencies for which you're likely to have bank accounts, used by exchange adjustment routine
Const DEFAULT_DELIVERY_REGION_UK = "UK" ' (SS,19/1/15) required if delivery postcodes used and post hasn't yet been entered
' (SS,12/9/06)
Const DT_ORIGINAL = "original"
Const DT_HIGHEST = "highest"
Const DT_LOWEST = "lowest"
Const DT_TOTAL = "total"
Const DT_CUSTOM = "custom" ' (SS,13/9/07)
Const GROUP_MAIN = "Main" ' (SS,14/5/07)
Const DISCONTINUED_TEXT = "*discontinued*" ' (SS,15/11/07) if this text exists product name then it means it's been discontinued
Const DISCONTINUED_TEXT2 = "*special order*" ' (SS,16/11/07) if this text exists product name then it means it's a special order (behaves like discontinued)
Const INTRA_COMMUNITY_SUPPLY_MESSAGE = "This is an intra-community supply and is exempt from VAT." ' (SS,16/11/07)
' (SS,27/5/09) Account Types
Const AT_COMPULSORY = "Compulsory"
Const AT_OPTIONAL = "Optional"
Const AT_ADMIN = "Admin"
' (SS,4/6/09)
Const MAX_RECENTLY_VIEWED = 5
Const SPECIAL_OFFERS_SEARCH_TEXT = "--special offers--" ' (SS,13/5/10), (SS,25/7/11) changed to lower case
Const NEW_PRODUCTS_SEARCH_TEXT = "--new products--" ' (SS,18/1/13)
' (SS,17/5/10) button types
Const BT_ORIGINAL = "Original"
Const BT_TYPE1 = "Type1"
' (SS,3/6/11) made this into a constant used in several places
Const NEW_LINE_FIX = "@@@"
' (SS,14/12/18) special value used in ComposeDescription to remove a table row if blank, # prefix doesn't work because it gets removed so used $ instead
Const REMOVE_BLANK_ROW = "$REMOVE_BLANK_ROW$"
' (SS,30/6/11) default value for combo prompting for something to be selected
Const COMBO_PLEASE_SELECT = "Please select..."
' (SS,30/6/11) product option input type constants
Const POIT_COMBO_BOX = "Combo Box"
Const POIT_TEXT = "Text"
Const POIT_RADIO = "Radio"
Const POIT_CHECK_BOX = "Check Box"
Const POIT_SPLIT_COMBO = "Split Combo" ' (SS,15/7/11)
Const PO_SPLIT_COMBO_SEPARATOR = "/" ' (SS,14/7/11)
Const DISCOUNT_OVERRIDE_VOUCHER_CODE = "INTERNAL" ' (SS,7/10/16)
Const ADD_TO_PREVIOUS_ORDER_TEXT = "**Add to previous order**" ' (SS,31/3/17)
' (SS,20/3/18) contants for SMS types
Const ST_ORDER_CONFIRMATION = "Order Confirmation"
Const ST_ORDER_PROGRESS = "Order Progress"
Const ST_ANTICIPATED_DESPATCH_UPDATE = "Anticipated Despatch/Update"
Const ST_ORDER_DESPATCHED = "Order Despatched"
Const ST_REVIEW_REQUEST = "Review Request"
' (SS,23/3/05) '
Dim NL
NL = Chr(13) + Chr(10)
' main variables '
Dim FSessionID ' (SS,12/8/05) was gsSessionID
Dim strCommand, strSearch, strPage, strGroup, strCategory, strSubcategory, strProductCode, strQty
Dim strClientWidth, strClientHeight, strOptions
Dim strGroupFromMenu ' (SS,12/2/05) group assigned in ShowMainMenu, passed onto GetProductsPageTitle '
' Dim strImageNo ' (SS,6/7/06) for extra product images; (SS,8/8/18) removed
Dim strStage ' (SS,8/9/06)
' (SS,23/5/11) following used to prevent stack overflow in ReplaceTokens routine
Dim FReplaceTokensRecursionCount
FReplaceTokensRecursionCount = 0
' check out variables '
' Dim strPaymentType, strDescForWorldPay, strTotalAmountForWorldPay ' (SS,10/8/05) removed
Dim gsITP_ErrorMessage ' error message passed back to browser using javascript '
' (SS,20/2/14) used to hold message shown on popup
Dim FPopUpMessage
' (SS,23/7/05) '
' credit card variables '
Dim FErrorCardType, FErrorCardNumber, FErrorSecurityCode, FErrorCardHoldersName, FErrorExpiryDate, FCardCounter
' (SS,12/9/06) new delivery variables
Dim FDeliveryCalculationFailure
Dim FDeliveryType, FIsDeliveryByPrice, FIsDeliveryByWeight, FIsDeliveryByWeightFlagged, FIsDeliveryByWeightNonFlagged
Dim FIsDeliverySpecialRegionByPrice, FIsDeliverySpecialRegionByWeight
' (SS,18/9/06)
Dim FCurrencyCode, FCurrencyRate, FCurrencySymbol, FCurrencyShowVAT, FCurrencyCountry
' (SS,17/5/07) new price values, used to pass values to product templates and calculate prices
' (SS,6/7/11) removed FBasketQty
' (SS,10/9/14) added FPostalWeight
Dim FStdPrice, FSalePrice, FPrice1, FPrice2, FPrice3, FPrice4, FPostalWeight
Dim FMinCurrencyPrice ' (SS,7/11/12)
Dim FDiscountPercentage, FVATBand, FVATRate, FProductCode, FTotalItemsInBasket
Dim FStockInfo ' (SS,16/11/07)
Dim FNumInStock ' (SS,16/5/11)
Dim FProductID, FOptionsPrice, FOptionsWeight ' (SS,6/7/11)
' (SS,17/5/07) new settings
Dim FVATRegistered, FVATBandsEnabled, FPricesEnteredIncludeVAT, FUseVATFlagFromCountries
Dim FFourPricingFieldsEnabled, FUseDiscountFieldForSpecialOffers, FCustomPricingEnabled
Dim FAtFirstProduct, FAtLastProduct, FProductDescription, FProductDimensions
Dim FVATBands(10, 1), FVATBandCount, FVATLabel ' (SS,26/5/07) up to 10 bands
' (SS,30/5/07)
' (SS,27/5/09) replaced FAccountsCompulsory with FAccountTypes
Dim FAccountsEnabled, FAccountTypes, FUseEmailAddressForLogin
Dim FLargeThumbnailWidth, FLargeThumbnailHeight
Dim FSmallThumbnailWidth, FSmallThumbnailHeight ' (SS,19/3/09)
Dim FUseGetPicASP ' (SS,9/8/18)
Dim FHearAboutUsList
Dim FDefaultCountry, FIsBasketEmpty
' (SS,11/6/07) delivery settings moved here from global.asa
Dim FDeliveryUKLimit, FDeliveryUKBelow, FDeliveryUKAbove, FDeliveryUKText
Dim FDeliveryEULimit, FDeliveryEUBelow, FDeliveryEUAbove, FDeliveryEUText
Dim FDeliveryRWLimit, FDeliveryRWBelow, FDeliveryRWAbove, FDeliveryRWText
' (SS,11/6/07) store settings moved here from global.asa
Dim FStoreName, FStoreCompany, FStoreAddress1, FStoreAddress2, FStoreAddress3, FStoreTown, FStoreCounty, FStorePostcode, FStoreCountry
Dim FStoreTelephone, FStoreFax, FStoreEmail, FStoreVATRegNo, FStoreRegards, FStoreURL
' (SS,1/5/09) added following two
Dim FStorePlaceOfRegistration, FStoreRegisteredCompanyNo
' (SS,12/6/07)
Dim FHTMLEmailsEnabled
Dim FEmailOrderConfirmationBcc1, FEmailOrderConfirmationBcc2, FEmailOrderConfirmationFrom
Dim FEmailContactTo, FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom
Dim FEmailSecurityCodeEnabled ' (SS,17/4/09)
Dim FEmailConfirmationIsVATReceipt ' (SS,14/6/10)
Dim FEmailUseMessageToCustomerForDespatch ' (SS,23/10/15)
' (SS,14/6/07) payment settings
' (SS,4/6/09) added FPaymentType3
' (SS,21/5/10) added FPaymentHideCurrency
' (SS,14/11/12) added FWorldPayMD5SecretKey
' (SS,2/10/14) added FEPDQPARAMVAR
Dim FPaymentType, FPaymentType2, FPaymentType3, FWorldPayInstallID, FWorldPayTestMode, FWorldPayMD5SecretKey, FPaymentHideCurrency
Dim FPayPalBusiness, FPayPalTestMode, FPayPalShippingAddressFlag
' (SS,26/4/18) NB. FEPDQClientID is same as PSPID, decided not to add a separate FEPDQPSPID because only one of these is required
Dim FEPDQClientID, FEPDQPassphrase, FEPDQChargeType, FEPDQMerchantDisplayName, FEPDQPARAMVAR
' (SS,26/4/18)
Dim FEPDQTestMode, FEPDQStringToHash
' (SS,26/1/15) added following for PayPal Express
Dim FPayPalExpressEnabled, FPayPalAPIUserName, FPayPalAPIPassword, FPayPalAPISignature
Dim FPayPalUKOnlyCheckout ' (SS,9/4/18)
Dim FPayPalExpressShowOnBasketPage ' (SS,23/4/18)
Dim FPayPalNonUKSurchargePercentage ' (SS,8/6/18)
' (SS,20/3/13) added Sage Pay variables
Dim FSagePayVendorName, FSagePayMode, FSagePayEncryptionKey
' (SS,23/3/18) added Payzone variables
Dim FPayzoneMerchantID, FPayzoneGatewayAccountPassword, FPayzonePreSharedKey
' (SS,28/3/18)
Dim FPayzoneStringToHash
' (SS,18/6/09)
Dim FWorldPaySiteSeal, FPayPalSiteSeal
' (SS,29/5/09)
' (SS,20/2/12) added FBankTransferDetailsEUR, FBankTransferDetailsUSD
' (SS,29/9/17) added FBankTransferDetailsGBP
Dim FAllowCheques, FAllowBankTransfers, FChequesPayableTo, FBankTransferDetails, FBankTransferDetailsEUR, FBankTransferDetailsUSD, FBankTransferDetailsGBP
' (SS,14/6/07)
Dim FBestSellersMax, FBestSellersPastDays, FBestSellersSortOrder
' (SS,14/6/07)
' (SS,12/5/11) added FProductsPerPageFixed
Dim FProductFilesEnabled, FMaxPicturesPerProduct, FProductsAcrossMax, FProductsDownMax, FProductsPerPageFixed, FProductsMaxSpecialOffer
' (SS,1/7/11)
Dim FProductAttributesEnabled, FProductOptionsEnabled
' (SS,18/10/18)
Dim FProductSubproductsEnabled
' (SS,16/1/13)
Dim FProductSearchWithoutDescFirst
' (SS,1/6/15)
Dim FProductSearchFullText
' (SS,10/6/14)
Dim FProductsShowReviewsImmediately
' (SS,26/8/15)
Dim FProductsSortByDefault
' (SS,14/6/07)
Dim FDevelopmentPassword, FAllowDifferentDeliveryAddress
Dim FAllowOutOfStockOrdering, FOutOfStockMessage ' (SS,19/7/10) replaced FAllowProductOrderIfNoStock with FAllowOutOfStockOrdering
Dim FShowIncVAT, FShowExcVAT, FSummaryListEnabled
Dim FMinCurrencyPricesEnabled ' (SS,7/11/12)
' (SS,14/6/07)
Dim FGroupsEnabled, FExtraSearchComboCategory, FExtraSearchComboBlankEntry, FShowProductsOnFullListCat
Dim FCategoryForSubcategoryMenu, FAddSpecialOffersToSubcategoryMenu, FCategoryListEnabled, FSubcategoryListEnabled
Dim FShowCategoryHeadingFooterIfEmpty ' (SS,12/10/15)
' (SS,17/6/07)
Dim FStartTimer, FEndTimer
' (SS,22/6/07)
Dim FWeightUnits, FShowWeightInEmail
' (SS,13/9/07)
Dim FUseAlternateColorForCategories, FDefaultTitle, FMulticurrencyEnabled, FAllowedCurrencies
' (SS,5/6/14)
' (SS,18/12/14) renamed FExchangeRateAdjustmentRatio to FExchangeRateAdjustmentRatioMain, added FExchangeRateAdjustmentRatioOther
Dim FMulticurrencyMethod, FExchangeRateAdjustmentRatioMain, FExchangeRateAdjustmentRatioOther
' (SS,14/11/07)
Dim FCurrencyDefaultCode, FCurrencyDefaultRate, FCurrencyDefaultSymbol, FCurrencyDefaultShowVAT, FCurrencyDefaultCountry
' (SS,14/11/07)
Dim FShowRelatedProductsInGroup, FSchemaVersion, FAllowCollection
Dim FRelatedProductsOutOfStock ' (SS,18/5/12)
' (SS,16/11/07)
' (SS,11/11/10) added FValidateVATNumber
' (SS,12/12/12) added FVATIntraCommunityEmailAlertEnabled
Dim FShowStockInfo, FVATZeroForIntraCommunity, FVATIntraCommunityCountries, FValidateVATNumber, FVATIntraCommunityEmailAlertEnabled
' (SS,23/6/10)
' (SS,20/7/10) added FCustomStockInfoEnabled
' (SS,21/7/10) added FShowOutOfStockMessage
Dim FShowStockAmount, FStockAvailabilityEnabled, FStockAvailability, FStockAvailableDaysFrom, FStockAvailableDaysTo, FCustomStockInfoEnabled, FShowOutOfStockMessage
Dim FBackInStockNotificationsStaggeredDelay ' (SS,14/3/12)
Dim FBackInStockNotificationsStockMultiplier ' (SS,27/3/12)
' (SS,5/12/11)
Dim FStockAmountInfoSpec
' (SS,28/1/08) a special product name token e.g. *pallet*
Dim FProductNameTokensInBasket
' (SS,22/11/13) for special product prefixes e.g. "RMP-"
Dim FProductCodePrefixesInBasket, FProductCodePrefixesPrefix
' (SS,17/9/08) see SetDefaultCategory
Dim FDefaultCategory
' (SS,7/11/08) added following for google analytics
' (SS,27/11/13) added FGoogleAnalyticsDone
' (SS,12/2/15) added FGoogleAnalyticsUseDoubleClickDotNet
' (SS,3/3/16) added FGoogleAnalyticsCrossDomainTrackingList
' (SS,6/3/18) removed FEcommerceTrackingActivated, FGoogleAnalyticsDone
Dim FGoogleAnalyticsWebPropertyID, FGoogleAnalyticsUseDoubleClickDotNet, FEcommerceTrackingEnabled, FGoogleAnalyticsCrossDomainTrackingList
' (SS,10/11/08) added following for conversion tracking
' (SS,28/11/13) added FAdwordsConversionLabelRemarketing
' (SS,6/3/18) removed FAdWordsPurchaseConversionActivated
Dim FAdWordsConversionTrackingID, FAdWordsConversionLabelPurchase, FAdWordsConversionLabelNewAccount, FAdWordsConversionLabelRemarketing, FAdWordsNewAccountConversionActivated
' (SS,1/10/14)
' (SS,9/10/17) replaced FFacebookConversionTrackingID with FFacebookPixelID, and added FFacebookPurchaseConversionActivated
' (SS,6/3/18) removed FFacebookPurchaseConversionActivated
Dim FFacebookPixelID
' (SS,17/4/09)
Dim FChooseDeliveryDate, FMaxDeliveryDays, FMobileEnabled
' (SS,6/5/09)
' (SS,18/5/09) added FBackInStockNotificationsEnabled
' (SS,20/9/12) added FAutoLoginEnabled
Dim FVouchersEnabled, FProductReviewsEnabled, FBackInStockNotificationsEnabled, FAutoLoginEnabled
' (SS,15/1/13)
' (SS,17/1/13) added FCustomPagesEnabled
' (SS,14/1/20) added FReviewRequestsDaysNonUK
Dim FReviewRequestsEnabled, FReviewRequestsDays, FReviewRequestsDaysNonUK, FCustomPagesEnabled
' (SS,10/6/14)
Dim FGeoIPEnabled
' (SS,25/2/15)
Dim FSaveBasketEnabled
' (SS,13/3/15)
Dim FFroogleProductType, FGoogleShoppingDataFeedHeader, FGoogleShoppingDataFeedLine, FGoogleShoppingDataFeedHeaderDone
' (SS,21/10/16) new settings for address lookup and local mode
Dim FAddressLookupEnabled, FAddressLookupType, FAddressLookupAccountCode, FAddressLookupLocalModeOnly
Dim FLocalModeEnabled, FLocalModeExchangeReasons, FLocalModePaymentMethods
' (SS,20/3/18)
Dim FSMSEnabled
' (SS,18/4/19)
Dim FCancelledOrderEmailsEnabled
' (SS,31/7/09)
Dim FCheckoutAlertMessage
' (SS,26/1/16)
Dim FCheckoutConfirmTermAndConditions
' (SS,10/11/09)
Dim FNotifyCustomerRecordUpdate, FCustomerFieldChange, FCustomerDetailsOld, FCustomerDetailsNew, FCustomerFieldsChangedList
' (SS,25/2/10)
Dim FProductImageCount
' (SS,17/3/10)
Dim FTranslationEnabled, FTranslationLanguage, FTransArrayEnglish(), FTransArrayLanguage()
' (SS,17/5/10)
Dim FButtonType
' (SS,20/2/14)
Dim FAddToBasketAlertType
' (SS,15/8/14)
Dim FResponsiveWebDesign
' (SS,24/3/20)
Dim FOrderingSuspended
' (SS,25/5/10)
Dim FVATNumberExample
' (SS,13/5/11)
Dim FShowRelatedProductsCalled, FViewOtherProductsCalled
' (SS,16/5/11)
Dim FMaxRecentlyViewed
' (SS,17/6/11)
Dim FProductPageTabs
' (SS,28/7/11)
Dim FDefaultGroup
' (SS,1/8/11)
Dim FAlwaysDefaultGroupForMenu, FIncludeGroupInHTMLTitleTag
' (SS,6/9/12)
Dim FFeefoFileName
' (SS,17/1/13)
Dim FPageExists
' (SS,20/3/13) for SagePay
Dim FSagePayCrypt
' (SS,28/11/13)
Dim FRemarketingTagTotalValue, FRemarketingTagProductIDs
' (SS,18/2/14)
Dim FPageRedirectURL
' (SS,9/10/14)
Dim FDeliveryInfo
' (SS,20/10/14)
Dim FReviewsType, FFeefoLogon, FFeefoFTPHost, FFeefoFTPUser, FFeefoFTPPassword
' (SS,18/5/17)
Dim FTrustpilotAFSEmailAddress
' (SS,10/4/15)
Dim FUseDeliveryPostcodes
' (SS,21/5/15)
Dim FMultiMenuType
' (SS,11/11/16)
Dim FMultiMenuTopLevelItemCount
' (SS,30/7/15)
Dim FHashCommentLineOptions
' (SS,19/10/15)
Dim FProductNavigationEnabled
' (SS,15/4/16)
Dim FBasketOutOfStockMessage
' (SS,8/4/119)
Dim FImageCreatedInCache
Initialise
' (SS,30/8/04) runs code to be run before all templates
Sub Initialise
FStartTimer = Timer
gsITP_ErrorMessage = ""
FPopUpMessage = "" ' (SS,20/2/14)
' (SS,20/6/11) dbfunctions.pas function, to clear the default class="form" used by combo boxes
' setting no class will cause it to use the browser default "select" instead of ".form"
SetSelectClass("")
'SetDebugLevel 2
'DebugOn
OpenDatabase ' (SS,26/5/07) now only one open is done, close is done in Finalise sub
' (SS,18/5/07) get settings from settings table
GetSettings
' (SS,18/4/19) set website URL in Application, used by global.asa which doesn't have access to Request.ServerVariables and is used to fetch a web page
' following must be after GetSettings
If IsEmpty(Application("WebsiteURL")) Then
Application("WebsiteURL") = Iif(LCase(Request.ServerVariables("HTTPS")) = "on", "https://", "http://") & Request.ServerVariables("SERVER_NAME")
Application("CancelledOrderEmailsEnabled") = FCancelledOrderEmailsEnabled
End If
' (SS,14/11/07) do the restructuring if required
CheckSchema
' (SS,25/11/08) check and change the VAT rate if necessary
CheckVAT
' (SS,26/5/07) moved following code here from inc-template-main.asp
' if password has been set up and not authenticated then get password otherwise continue with normal stuff '
' (SS,6/6/07) added GetScriptName = "products.asp" to allow pages other than products.asp to bypass the password dialog
' necessary to prevent worldpay and other call backs from failing
If GetScriptName = "products.asp" And GetDevelopmentPassword <> "" And Not Session("Authenticated") Then
If UCase(CleanRequestForm("password")) = GetDevelopmentPassword Then
Session("Authenticated") = True
Else
Finalise ' make sure database is closed
Response.Redirect("getpassword.asp")
End If
End If
' (SS,12/4/11) process redirections for product, category and subcategory
' if there is a redirection then this process calls Finalise and does a Response.Redirect
ProcessRedirections
' get SessionID in FSessionID, in making direct payment then original session is used
If CleanRequestForm("DP_SessionID") <> "" Then
Session("MainSession") = CleanRequestForm("DP_SessionID")
CopyMainSessionToSecureSession
End If
If Session("MainSession") <> "" Then
FSessionID = Session("MainSession")
Else
FSessionID = Session.SessionID ' (SS,12/8/05) moved here from dbfunctions.asp
End If
' add referrer to database
' (SS,11/9/15) removed, no longer used
'If Not Session("ReferrerAdded") Then
' AddReferrerToDatabase
' Session("ReferrerAdded") = True
'End If
strGroupFromMenu = "" ' (SS,12/2/05)
' (SS,9/6/14) gets/sets the currency and delivery country from/to cookies using Geo IP, saves to cookie if applicable
SetCurrencyAndDeliveryCountryCookieFromGeoIP
' (SS,20/2/12) save delivery country in cookie, if necessary, this must be done before html is sent
' only if Request("cmd") = "recalculate" and CleanRequestForm("DeliveryCountry") <> ""
SetDeliveryCountryCookie
' (SS,18/9/06) get currency settings
' GetLatestExchangeRatesIfNeeded ' (SS,9/6/14) removed no longer used, task schedule does this
SetCurrencyCode("") ' (SS,28/10/09) added "" parameter
GetCurrencyExchangeRate
' (SS,17/3/10) set up translation, may read from common database
SetUpTranslation
GetVATBands ' (SS,26/5/07) puts VAT Bands into array if required
FIsBasketEmpty = GetIsBasketEmpty ' (SS,5/6/07) sets FIsBasketEmpty for IsBasketEmpty function
FDefaultCategory = "" ' (SS,17/9/08)
' (SS,6/3/18) removed following
'FGoogleAnalyticsDone = False ' (SS,27/11/13)
'FEcommerceTrackingActivated = False ' (SS,10/11/08)
'FAdWordsPurchaseConversionActivated = False ' (SS,12/11/08)
FAdWordsNewAccountConversionActivated = False ' (SS,12/11/08)
FShowRelatedProductsCalled = False ' (SS,13/5/11)
FViewOtherProductsCalled = False ' (SS,13/5/11)
' (SS,1/8/11)
SetBreadcrumbCategory
SetBreadcrumbSubcategory
FProductCode = "" ' (SS,15/2/12) now used in new SetProductCode and GetProductCode
DoAutoLogin ' (SS,20/9/12) automatic log in using cookies
' (SS,22/3/13) used to only show a payment type for payment provider testing, where payment method is only shown if website reached via ?pptest=true (payment provider test)
CheckPaymentProviderTestMode
If FunctionExists("CustomInitialise") Then CustomInitialise ' (SS,17/9/08)
FPageExists = "" ' (SS,17/1/13) empty string effectively means not set, first call to PageExists functions will set this to True or False
FPageRedirectURL = "" ' (SS,18/2/14)
' (SS,6/9/14) for RWD i.e. Bootstrap - change the default class for select combos
If IsRWD Then SetSelectClass("form-control")
FDeliveryInfo = "" ' (SS,9/10/14)
' (SS,4/8/15) clear the hash comment line options, which can pass on from one token to the another, i.e. inherit from parent token or page
' this is to prevent having to add it to every token, e.g. the no convert newline feature
SetHashCommentLineOptions ""
' (SS,19/10/15) production navigation enabled by default, can be disabled by custom utils
EnableProductNavigation
' (SS,15/4/16)
ClearBasketOutOfStockMessage
' (SS,7/10/16)
CheckLocalMode
' (SS,1/12/16)
' (SS,16/8/18) changed from 5 to "5" (integer to string) because it wasn't working i.e. was default to 9 instead, changing to a string fixed it
SetPriorityDefault "5"
End Sub
' (SS,2/3/18) to allow items to be added at the start of the head tag, call to HTMLHeadStart needs to in the correct place in main template
' DisableCache moved here and call to DisableCache in inc-template-main.asp replaced with HTMLHeadStart
Sub HTMLHeadStart
DisableCache
End Sub
' (SS,2/3/18) to allow items to be added just before the end of the head tag, call to HTMLHeadStart needs to in the correct place in main template
' good place for tracking code
Sub HTMLHeadEnd
DoWebAnalytics
End Sub
' (SS,26/5/07) called from end of inc-template-main.asp
Sub Finalise
' (SS,18/11/13) added "If ScriptIsProducts Then" to ensure Google Analytics stuff is only done on products.asp page
' i.e. not on order-details.asp (order confirmation), callback pages and ajax.asp etc. pages
' (SS,27/11/13) removed "If ScriptIsProducts Then" because DoGoogleAnalytics does it, added False parameter for non-async version if async not done, it also does adword conversion in sync mode
' (SS,2/3/18) moved DoGoogleAnalytics and DoFacebookPixel to new HTMLHeadEnd which is called earlier
' DoGoogleAdWordsRemarketingTag now taken care of by DoGoogleAnalytics
'DoGoogleAnalytics(False) ' (SS,10/11/08)
'If ScriptIsProducts Then DoGoogleAdWordsRemarketingTag ' (SS,28/11/13)
'DoFacebookPixel ' (SS,10/10/17) does these tracking and conversion
' (SS,6/3/18) anything to do at the end for Web Analytics e.g. update the conversion_tracking_log with payment method
FinaliseWebAnalytics
' (SS,17/4/18) removed
'DoPaymentRedirection ' (SS,20/11/08)
If FunctionExists("CustomFinalise") Then CustomFinalise ' (SS,10/10/14)
CloseDatabase
ShowAlertMessage
ShowPopUpMessage ' (SS,20/2/14)
FinaliseDBFunctions ' (SS,27/05/10)
If ScriptIsProducts Then ShowTimer ' (SS,20/2/13) moved to Sub ShowTimer ' (SS,18/11/13) now only shown in products.asp page
DoPageRedirection ' (SS,18/2/14) different to DoRedirect because it allows the page to finish and get this far
End Sub
' (SS,9/4/19) returns time taken in seconds to 3 dp, called from ShowTimer
Function GetTimer
FEndTimer = Timer
GetTimer = FormatNumber(FEndTimer - FStartTimer, 3, True) & " secs"
End Function
' (SS,20/2/13) made into routine to allow calling from elsewhere for debugging purposes
' (SS,9/4/19) modified to call GetTimer, i.e. moved some code to GetTimer
Sub ShowTimer
' (SS,27/05/10) added following as HTML comment
Response.Write("<!-- " & GetTimer & " -->")
End Sub
' (SS,5/7/11) new simpler finalise especially for Ajax, which doesn't have analytics, redirection, alerts, and timer
Sub FinaliseAjax
CloseDatabase
FinaliseDBFunctions
End Sub
' (SS,1/7/11) all redirects go through here, also Finalise runs before the redirect takes place
Sub DoRedirect(AURL, AIsPermanent)
' do the permanent redirection
Finalise ' make sure we close the database because Response.End will end the script
If AIsPermanent Then
Response.Status = "301 Moved Permanently"
Response.AddHeader "Location", AURL
Response.End
Else
Response.Redirect AURL
End If
End Sub
' (18/2/14)
Sub SetPageRedirection(ARedirectURL)
FPageRedirectURL = ARedirectURL
End Sub
' (SS,18/2/14) if redirection requested then do it
Sub DoPageRedirection
If FPageRedirectURL <> "" Then Response.Redirect FPageRedirectURL
End Sub
' (SS,18/5/07) get settings from settings file
Sub GetSettings
Dim LGroupName, LFieldName, LFieldValue
' set defaults
FVATRegistered = True
FVATBandsEnabled = False
FVATRate = 0
FVATZeroForIntraCommunity = False ' (SS,16/11/07)
FVATIntraCommunityCountries = "" ' (SS,16/11/07)
FValidateVATNumber = False ' (SS,11/11/10) default to not validating VAT number
FVATIntraCommunityEmailAlertEnabled = False ' (SS,12/12/12)
FPricesEnteredIncludeVAT = True
FFourPricingFieldsEnabled = False
FUseDiscountFieldForSpecialOffers = False
FCustomPricingEnabled = False
FUseVATFlagFromCountries = False ' (SS,11/5/09) missed from before
FWeightUnits = "" ' (SS,22/6/07)
FShowWeightInEmail = False ' (SS,22/6/07)
' (SS,30/5/07)
FAccountsEnabled = False
' (SS,27/5/09) replaced FAccountsCompulsory with FAccountTypes, and False with AT_COMPULSORY
FAccountTypes = AT_COMPULSORY
FUseEmailAddressForLogin = True
' delivery defaults
FDeliveryType = DT_ORIGINAL
FIsDeliveryByPrice = False
FIsDeliveryByWeight = False
FIsDeliveryByWeightFlagged = False ' (SS,22/10/08)
FIsDeliveryByWeightNonFlagged = False ' (SS,22/10/08)
FIsDeliverySpecialRegionByPrice = False
FIsDeliverySpecialRegionByWeight = False
FChooseDeliveryDate = False ' (SS,17/4/09)
FMaxDeliveryDays = 10 ' (SS,17/4/09)
FUseDeliveryPostcodes = False ' (SS,10/4/15)
' picture defaults
FLargeThumbnailWidth = LARGE_THUMBNAIL_WIDTH
FLargeThumbnailHeight = LARGE_THUMBNAIL_HEIGHT
' (SS,19/3/09)
FSmallThumbnailWidth = SMALL_THUMBNAIL_WIDTH
FSmallThumbnailHeight = SMALL_THUMBNAIL_HEIGHT
' (SS,9/8/18)
FUseGetPicASP = True
' default country
FDefaultCountry = ""' (SS,11/7/07) was United Kingdom"
' default emails
FHTMLEmailsEnabled = False
FEmailOrderConfirmationBcc1 = ""
FEmailOrderConfirmationBcc2 = ""
FEmailOrderConfirmationFrom = ""
FEmailContactTo = ""
FEmailContactBcc1 = ""
FEmailContactBcc2 = ""
FEmailContactFrom = ""
FEmailSecurityCodeEnabled = False ' (SS,17/4/09)
FEmailConfirmationIsVATReceipt = False ' (SS,14/6/10)
FEmailUseMessageToCustomerForDespatch = False ' (SS,23/10/15)
' payment details
FPaymentType = "" ' (SS,17/4/18) modified from "DIRECT" to ""
FPaymentType2 = "" ' (SS,28/9/07)
FPaymentType3 = ""' (SS,4/6/09)
FPaymentHideCurrency = False ' (SS,21/5/10)
FWorldPayInstallID = ""
FWorldPayTestMode = ""
FWorldPayMD5SecretKey = "" ' (SS,14/11/12)
FPayPalBusiness = ""
FPayPalTestMode = ""
FPayPalShippingAddressFlag = ""
FPayPalUKOnlyCheckout = False ' (SS,9/4/18)
FPayPalExpressShowOnBasketPage = True ' (SS,23/4/18)
FPayPalNonUKSurchargePercentage = 0 ' (SS,23/4/18)
' (SS,26/1/15)
FPayPalExpressEnabled = False
FPayPalAPIUserName = ""
FPayPalAPIPassword = ""
FPayPalAPISignature = ""
FEPDQClientID = ""
FEPDQPassphrase = ""
FEPDQChargeType = ""
FEPDQMerchantDisplayName = "" ' (SS,10/5/12)
FEPDQPARAMVAR = "" ' (SS,2/10/14)
FEPDQTestMode = False ' (SS,26/4/18)
' (SS,20/3/13)
FSagePayVendorName = ""
FSagePayMode = ""
FSagePayEncryptionKey = ""
' (SS,23/3/18)
FPayzoneMerchantID = ""
FPayzoneGatewayAccountPassword = ""
FPayzonePreSharedKey = ""
' (SS,18/6/09)
FWorldPaySiteSeal = ""
FPayPalSiteSeal = ""
' (SS,29/5/09)
FAllowCheques = False
FAllowBankTransfers = False
FChequesPayableTo = ""
FBankTransferDetails = ""
' (SS,20/2/12)
FBankTransferDetailsEUR = ""
FBankTransferDetailsUSD = ""
FBankTransferDetailsGBP = "" ' (SS,29/9/17)
' best sellers
FBestSellersMax = 5
FBestSellersPastDays = 365
FBestSellersSortOrder = ""
' products
FProductFilesEnabled = False
FMaxPicturesPerProduct = 3
FProductsAcrossMax = 0
FProductsDownMax = 0
FProductsMaxSpecialOffer = 5
FShowIncVAT = False
FShowExcVAT = True
FMinCurrencyPricesEnabled = False ' (SS,7/11/12)
FSummaryListEnabled = False
FProductsPerPageFixed = False ' (SS,12/5/11)
FMaxRecentlyViewed = MAX_RECENTLY_VIEWED ' (SS,16/5/11)
FProductPageTabs = False ' (SS,17/6/11)
FProductAttributesEnabled = False ' (SS,1/7/11)
FProductOptionsEnabled = False ' (SS,1/7/11)
FProductSubproductsEnabled = False ' (SS,18/10/18)
FProductSearchWithoutDescFirst = False ' (SS,16/1/13)
FProductSearchFullText = False ' (SS,1/6/15)
FProductsShowReviewsImmediately = False ' (SS,10/6/14)
FProductsSortByDefault = "" ' (SS,26/8/15)
' categories
FGroupsEnabled = False
FExtraSearchComboCategory = ""
FExtraSearchComboBlankEntry = "--Select--"
FShowProductsOnFullListCat = True
FCategoryForSubcategoryMenu = ""
FAddSpecialOffersToSubcategoryMenu = False
FCategoryListEnabled = True
FSubcategoryListEnabled = True
FUseAlternateColorForCategories = False
FDefaultGroup = GROUP_MAIN ' (SS,28/7/11)
FAlwaysDefaultGroupForMenu = False ' (SS,1/8/11)
FIncludeGroupInHTMLTitleTag = True ' (SS,2/8/11)
FShowCategoryHeadingFooterIfEmpty = False ' (SS,12/10/15)
' other
FDevelopmentPassword = ""
FAllowDifferentDeliveryAddress = True
' FAllowProductOrderIfNoStock = False ' (SS,19/7/10) removed, replaced by FAllowOutOfStockOrdering used on a by product basis
FOutOfStockMessage = "Out of Stock"
' checkout
FMobileEnabled = False ' (SS,17/4/09)
' (SS,13/9/07)
FDefaultTitle = ""
FMulticurrencyEnabled = False
FMulticurrencyMethod = "Original" ' (SS,5/6/14)
FAllowedCurrencies = "GBP,USD,EUR"
FExchangeRateAdjustmentRatioMain = "1" ' (SS,5/6/14) ' (SS,18/12/14) renamed from FExchangeRateAdjustmentRatio to FExchangeRateAdjustmentRatioMain
FExchangeRateAdjustmentRatioOther = "1" ' (SS,18/12/14)
' (SS,14/11/07)
FCurrencyDefaultCode = DEFAULT_CURRENCY_CODE
FCurrencyDefaultRate = DEFAULT_CURRENCY_RATE
FCurrencyDefaultSymbol = DEFAULT_CURRENCY_SYMBOL
FCurrencyDefaultShowVAT = DEFAULT_CURRENCY_SHOW_VAT
FCurrencyDefaultCountry = DEFAULT_CURRENCY_COUNTRY
' (SS,14/11/07)
FShowRelatedProductsInGroup = False
FSchemaVersion = 1
FAllowCollection = False
' (SS,18/5/12)
FRelatedProductsOutOfStock = ""
' (SS,16/11/07)
FShowStockInfo = False
FShowStockAmount = True ' (SS,25/6/10)
FCustomStockInfoEnabled = False ' (SS,20/7/10)
FShowOutOfStockMessage = False ' (SS,21/7/10)
FStockAmountInfoSpec = "" ' (SS,5/12/11)
' (SS,23/6/10)
FStockAvailabilityEnabled = False
FStockAvailability = ""
FStockAvailableDaysFrom = ""
FStockAvailableDaysTo = ""
FBackInStockNotificationsStaggeredDelay = 0 ' (SS,14/3/12)
FBackInStockNotificationsStockMultiplier = 1 ' (SS,27/4/12)
' (SS,7/11/08)
FGoogleAnalyticsWebPropertyID = ""
FEcommerceTrackingEnabled = False
FGoogleAnalyticsUseDoubleClickDotNet = False ' (SS,12/2/15)
' (SS,3/3/16)
FGoogleAnalyticsCrossDomainTrackingList = ""
' (SS,11/11/08)
FAdWordsConversionTrackingID = ""
FAdWordsConversionLabelPurchase = ""
FAdWordsConversionLabelNewAccount = ""
FAdWordsConversionLabelRemarketing = "" ' (SS,28/11/13)
' FFacebookConversionTrackingID = "" ' (SS,1/10/14)
' (SS,9/10/17) replaced above with FFacebookPixelID
FFacebookPixelID = ""
' (SS,6/3/18) removed following
' FFacebookPurchaseConversionActivated = False ' (SS,9/10/17)
' (SS,6/5/09)
FVouchersEnabled = False
FProductReviewsEnabled = False
' (SS,20/9/12)
FAutoLoginEnabled = False
' (SS,15/1/13)
FReviewRequestsEnabled = False
FReviewRequestsDays = 7
FReviewRequestsDaysNonUK = 0 ' (SS,14/1/20) 0 means use the same value as FReviewRequestsDays
FCustomPagesEnabled = False ' (SS,17/1/13)
FGeoIPEnabled = False ' (SS,10/6/14)
FSaveBasketEnabled = False ' (SS,25/2/15)
FCancelledOrderEmailsEnabled = False ' (SS,19/4/19)
' (SS,18/5/09)
FBackInStockNotificationsEnabled = False
' (SS,31/7/09)
FCheckoutAlertMessage = ""
' (SS,26/1/16)
FCheckoutConfirmTermAndConditions = False
' (SS,10/11/09)
FNotifyCustomerRecordUpdate = False
' (SS,17/3/10) default to translation disabled
FTranslationEnabled = False
FTranslationLanguage = ""
' (SS,17/5/10)
FButtonType = BT_ORIGINAL
' (SS,15/8/14)
FResponsiveWebDesign = False
' (SS,24/3/20)
FOrderingSuspended = False
' (SS,20/10/14)
FReviewsType = ""
FFeefoLogon = ""
FFeefoFTPHost = ""
FFeefoFTPUser = ""
FFeefoFTPPassword = ""
' (SS,18/5/17)
FTrustpilotAFSEmailAddress = ""
' (SS,13/3/15)
FFroogleProductType = ""
' (SS,21/10/16)
FAddressLookupEnabled = False
FAddressLookupType = ""
FAddressLookupAccountCode = ""
FAddressLookupLocalModeOnly = True
FLocalModeEnabled = False
FLocalModeExchangeReasons = ""
FLocalModePaymentMethods = ""
' (SS,20/3/18)
FSMSEnabled = False
OpenQuery("SELECT * FROM settings")
Do While Not EndOfQuery
LGroupName = GetFieldValue("GroupName")
LFieldName = GetFieldValue("FieldName")
LFieldValue = NB(GetFieldValue("FieldValue"))
If LGroupName = "Application" Then
' price settings
If LFieldName = "VATRegistered" Then FVATRegistered = StrToBool(LFieldValue)
If LFieldName = "VATBandsEnabled" Then FVATBandsEnabled = StrToBool(LFieldValue)
If LFieldName = "VATRate" Then FVATRate = NZ(LFieldValue)
If LFieldName = "VATZeroForIntraCommunity" Then FVATZeroForIntraCommunity = StrToBool(LFieldValue) ' (SS,16/11/07)
If LFieldName = "VATIntraCommunityCountries" Then FVATIntraCommunityCountries = LFieldValue ' (SS,16/11/07)
If LFieldName = "ValidateVATNumber" Then FValidateVATNumber = LFieldValue ' (SS,11/11/10)
If LFieldName = "VATIntraCommunityEmailAlertEnabled" Then FVATIntraCommunityEmailAlertEnabled = LFieldValue ' (SS,12/12/12)
If LFieldName = "PricesEnteredIncludeVAT" Then FPricesEnteredIncludeVAT = StrToBool(LFieldValue)
If LFieldName = "FourPricingFieldsEnabled" Then FFourPricingFieldsEnabled = StrToBool(LFieldValue)
If LFieldName = "UseDiscountFieldForSpecialOffers" Then FUseDiscountFieldForSpecialOffers = StrToBool(LFieldValue)
If LFieldName = "CustomPricingEnabled" Then FCustomPricingEnabled = StrToBool(LFieldValue)
If LFieldName = "UseVATFlagFromCountries" Then FUseVATFlagFromCountries = StrToBool(LFieldValue)
If LFieldName = "WeightUnits" Then FWeightUnits = LFieldValue ' (SS,22/6/07)
If LFieldName = "ShowWeightInEmail" Then FShowWeightInEmail = StrToBool(LFieldValue) ' (SS,22/6/07)
' other settings
If LFieldName = "AccountsEnabled" Then FAccountsEnabled = StrToBool(LFieldValue)
If LFieldName = "AccountTypes" Then FAccountTypes = LFieldValue ' (SS,27/5/09) was FAccountsCompulsory
If LFieldName = "UseEmailAddressForLogin" Then FUseEmailAddressForLogin = StrToBool(LFieldValue)
If LFieldName = "HearAboutUsList" Then FHearAboutUsList = NB(GetFieldValue("MemoValue"))
If LFieldName = "DefaultCountry" Then FDefaultCountry = LFieldValue
If LFieldName = "DevelopmentPassword" Then FDevelopmentPassword = UCase(LFieldValue)
' (SS,19/7/10) following replaced, setting still used as a default in ShoppingAdmin app
' If LFieldName = "AllowProductOrderIfNoStock" Then FAllowProductOrderIfNoStock = StrToBool(LFieldValue)
If LFieldName = "OutOfStockMessage" Then FOutOfStockMessage = LFieldValue
If LFieldName = "ShowStockInfo" Then FShowStockInfo = StrToBool(LFieldValue) ' (SS,16/11/07)
If LFieldName = "ShowStockAmount" Then FShowStockAmount = StrToBool(LFieldValue) ' (SS,25/6/10)
If LFieldName = "StockAmountInfoSpec" Then FStockAmountInfoSpec = Trim(LFieldValue) ' (SS,5/12/11)
If LFieldName = "CustomStockInfoEnabled" Then FCustomStockInfoEnabled = StrToBool(LFieldValue) ' (SS,20/7/10)
If LFieldName = "ShowOutOfStockMessage" Then FShowOutOfStockMessage = StrToBool(LFieldValue) ' (SS,21/7/10)
If LFieldName = "StockAvailableDaysFrom" Then FStockAvailableDaysFrom = LFieldValue ' (SS,23/6/10)
If LFieldName = "StockAvailableDaysTo" Then FStockAvailableDaysTo = LFieldValue ' (SS,23/6/10)
If LFieldName = "BackInStockNotificationsStaggeredDelay" Then FBackInStockNotificationsStaggeredDelay = LFieldValue ' (SS,14/3/12)
If LFieldName = "BackInStockNotificationsStockMultiplier" Then FBackInStockNotificationsStockMultiplier = LFieldValue ' (SS,27/4/12)
If LFieldName = "AllowDifferentDeliveryAddress" Then FAllowDifferentDeliveryAddress = StrToBool(LFieldValue) ' (SS,17/3/09) added this setting, somehow missed it previously
' (SS,13/9/07)
If LFieldName = "DefaultTitle" Then FDefaultTitle = LFieldValue
' (SS,14/11/07)
If LFieldName = "ShowRelatedProductsInGroup" Then FShowRelatedProductsInGroup = StrToBool(LFieldValue)
If LFieldName = "SchemaVersion" Then FSchemaVersion = CDbl(LFieldValue)
' (SS,18/5/12)
If LFieldName = "RelatedProductsOutOfStock" Then FRelatedProductsOutOfStock = LFieldValue
' (SS,17/3/10)
If LFieldName = "TranslationEnabled" Then FTranslationEnabled = StrToBool(LFieldValue)
If LFieldName = "TranslationLanguage" Then FTranslationLanguage = LFieldValue
' (SS,17/5/10)
If LFieldName = "ButtonType" Then FButtonType = LFieldValue
' (SS,20/2/14)
If LFieldName = "AddToBasketAlertType" Then FAddToBasketAlertType = LFieldValue
' (SS,15/8/14)
If LFieldName = "ResponsiveWebDesign" Then FResponsiveWebDesign = LFieldValue
' (SS,24/3/20)
If LFieldName = "OrderingSuspended" Then FOrderingSuspended = LFieldValue
' (SS,17/4/09)
ElseIf LGroupName = "Checkout" Then
If LFieldName = "MobileEnabled" Then FMobileEnabled = StrToBool(LFieldValue) ' (SS,17/4/09)
If LFieldName = "AlertMessage" Then FCheckoutAlertMessage = LFieldValue ' (SS,31/7/09)
If LFieldName = "ConfirmTermAndConditions" Then FCheckoutConfirmTermAndConditions = StrToBool(LFieldValue) ' (SS,26/1/16)
ElseIf LGroupName = "Currency" Then
' (SS,14/11/07) following 3 moved here from application
If LFieldName = "MulticurrencyEnabled" Then FMulticurrencyEnabled = LFieldValue
If LFieldName = "MulticurrencyMethod" Then FMulticurrencyMethod = LFieldValue ' (SS,5/6/14)
If LFieldName = "AllowedCurrencies" Then FAllowedCurrencies = LFieldValue
If LFieldName = "ExchangeRateAdjustmentRatioMain" Then FExchangeRateAdjustmentRatioMain = LFieldValue ' (SS,5/6/14) ' (SS,18/12/14) renamed from FExchangeRateAdjustmentRatio to FExchangeRateAdjustmentRatioMain
If LFieldName = "ExchangeRateAdjustmentRatioOther" Then FExchangeRateAdjustmentRatioOther = LFieldValue ' (SS,18/12/14)
' (SS,14/11/07) added following 5 new values
If LFieldName = "DefaultCode" Then FCurrencyDefaultCode = LFieldValue
If LFieldName = "DefaultRate" Then FCurrencyDefaultRate = LFieldValue
If LFieldName = "DefaultSymbol" Then FCurrencyDefaultSymbol = CurrencyCharToHTML(LFieldValue) ' (SS,19/5/11) added CurrencyCharToHTML to ensure correct HTML display
If LFieldName = "DefaultShowVAT" Then FCurrencyDefaultShowVAT = LFieldValue
If LFieldName = "DefaultCountry" Then FCurrencyDefaultCountry = LFieldValue
ElseIf LGroupName = "Delivery" Then
' delivery settings (SS,26/5/07) moved here from GetDeliverySettings
If LFieldName = "Type" Then FDeliveryType = LCase(LFieldValue) ' can be Original, Highest, Lowest, Total (SS,10/9/07) added LCase
If LFieldName = "ByPrice" Then FIsDeliveryByPrice = StrToBool(LFieldValue)
If LFieldName = "ByWeight" Then FIsDeliveryByWeight = StrToBool(LFieldValue)
If LFieldName = "ByWeightFlagged" Then FIsDeliveryByWeightFlagged = StrToBool(LFieldValue) ' (SS,22/10/08)
If LFieldName = "ByWeightNonFlagged" Then FIsDeliveryByWeightNonFlagged = StrToBool(LFieldValue) ' (SS,22/10/08)
If LFieldName = "SpecialRegionByPrice" Then FIsDeliverySpecialRegionByPrice = StrToBool(LFieldValue)
If LFieldName = "SpecialRegionByWeight" Then FIsDeliverySpecialRegionByWeight = StrToBool(LFieldValue)
If LFieldName = "ChooseDeliveryDate" Then FChooseDeliveryDate = StrToBool(LFieldValue) ' (SS,17/4/09)
If LFieldName = "MaxDeliveryDays" Then FMaxDeliveryDays = CLng(LFieldValue) ' (SS,17/4/09)
' (SS,11/9/07) removed following
' If LFieldName = "SpecialRegionLimitCountries" Then FIsDeliverySpecialRegionLimitCountries = StrToBool(LFieldValue)
If LFieldName = "UKLimit" Then FDeliveryUKLimit = NZ(LFieldValue)
If LFieldName = "UKBelow" Then FDeliveryUKBelow = NZ(LFieldValue)
If LFieldName = "UKAbove" Then FDeliveryUKAbove = NZ(LFieldValue)
If LFieldName = "UKText" Then FDeliveryUKText = LFieldValue
If LFieldName = "EULimit" Then FDeliveryEULimit = NZ(LFieldValue)
If LFieldName = "EUBelow" Then FDeliveryEUBelow = NZ(LFieldValue)
If LFieldName = "EUAbove" Then FDeliveryEUAbove = NZ(LFieldValue)
If LFieldName = "EUText" Then FDeliveryEUText = LFieldValue
If LFieldName = "RWLimit" Then FDeliveryRWLimit = NZ(LFieldValue)
If LFieldName = "RWBelow" Then FDeliveryRWBelow = NZ(LFieldValue)
If LFieldName = "RWAbove" Then FDeliveryRWAbove = NZ(LFieldValue)
If LFieldName = "RWText" Then FDeliveryRWText = LFieldValue
' (SS,14/11/07)
If LFieldName = "AllowCollection" Then FAllowCollection = LFieldValue
' (SS,10/4/15) added following
If LFieldName = "UseDeliveryPostcodes" Then FUseDeliveryPostcodes = LFieldValue
' (SS,6/5/09)
ElseIf LGroupName = "Features" Then
If LFieldName = "VouchersEnabled" Then FVouchersEnabled = StrToBool(LFieldValue)
If LFieldName = "ProductReviewsEnabled" Then FProductReviewsEnabled = StrToBool(LFieldValue)
' (SS,18/5/09)
If LFieldName = "BackInStockNotificationsEnabled" Then FBackInStockNotificationsEnabled = StrToBool(LFieldValue)
' (SS,10/11/09)
If LFieldName = "NotifyCustomerRecordUpdate" Then FNotifyCustomerRecordUpdate = StrToBool(LFieldValue)
' (SS,23/6/10)
If LFieldName = "StockAvailabilityEnabled" Then FStockAvailabilityEnabled = StrToBool(LFieldValue)
' (SS,20/9/12)
If LFieldName = "AutoLoginEnabled" Then FAutoLoginEnabled = StrToBool(LFieldValue)
' (SS,15/1/13)
If LFieldName = "ReviewRequestsEnabled" Then FReviewRequestsEnabled = StrToBool(LFieldValue)
If LFieldName = "ReviewRequestsDays" Then FReviewRequestsDays = NZL(LFieldValue)
If LFieldName = "ReviewRequestsDaysNonUK" Then FReviewRequestsDaysNonUK = NZL(LFieldValue) ' (SS,14/1/20)
' (SS,17/1/13)
If LFieldName = "CustomPagesEnabled" Then FCustomPagesEnabled = StrToBool(LFieldValue)
' (SS,10/6/14)
If LFieldName = "GeoIPEnabled" Then FGeoIPEnabled = StrToBool(LFieldValue)
' (SS,25/2/15)
If LFieldName = "SaveBasketEnabled" Then FSaveBasketEnabled = StrToBool(LFieldValue)
' (SS,18/4/19)
If LFieldName = "CancelledOrderEmailsEnabled" Then FCancelledOrderEmailsEnabled = StrToBool(LFieldValue)
ElseIf LGroupName = "Pictures" Then
If LFieldName = "LargeThumbnailWidth" Then FLargeThumbnailWidth = NZL(LFieldValue)
If LFieldName = "LargeThumbnailHeight" Then FLargeThumbnailHeight = NZL(LFieldValue)
' (SS,19/3/09)
If LFieldName = "SmallThumbnailWidth" Then FSmallThumbnailWidth = NZL(LFieldValue)
If LFieldName = "SmallThumbnailHeight" Then FSmallThumbnailHeight = NZL(LFieldValue)
' (SS,9/8/18)
If LFieldName = "UseGetPicASP" Then FUseGetPicASP = StrToBool(LFieldValue)
ElseIf LGroupName = "Store" Then
If LFieldName = "Name" Then FStoreName = LFieldValue
If LFieldName = "Company" Then FStoreCompany = LFieldValue
If LFieldName = "Address1" Then FStoreAddress1 = LFieldValue
If LFieldName = "Address2" Then FStoreAddress2 = LFieldValue
If LFieldName = "Address3" Then FStoreAddress3 = LFieldValue
If LFieldName = "Town" Then FStoreTown = LFieldValue
If LFieldName = "County" Then FStoreCounty = LFieldValue
If LFieldName = "Postcode" Then FStorePostcode = LFieldValue
If LFieldName = "Country" Then FStoreCountry = LFieldValue
If LFieldName = "Telephone" Then FStoreTelephone = LFieldValue
If LFieldName = "Fax" Then FStoreFax = LFieldValue
If LFieldName = "Email" Then FStoreEmail = LFieldValue
If LFieldName = "VATRegNo" Then FStoreVATRegNo = LFieldValue
If LFieldName = "Regards" Then FStoreRegards = LFieldValue
If LFieldName = "URL" Then FStoreURL = LFieldValue
If LFieldName = "PlaceOfRegistration" Then FStorePlaceOfRegistration = LFieldValue
If LFieldName = "RegisteredCompanyNo" Then FStoreRegisteredCompanyNo = LFieldValue
ElseIf LGroupName = "Email" Then
If LFieldName = "HTMLEmailsEnabled" Then FHTMLEmailsEnabled = StrToBool(LFieldValue)
If LFieldName = "OrderConfirmationBcc1" Then FEmailOrderConfirmationBcc1 = LFieldValue
If LFieldName = "OrderConfirmationBcc2" Then FEmailOrderConfirmationBcc2 = LFieldValue
If LFieldName = "EmailOrderConfirmationFrom" Then FEmailOrderConfirmationFrom = LFieldValue
If LFieldName = "ContactTo" Then FEmailContactTo = LFieldValue
If LFieldName = "ContactBcc1" Then FEmailContactBcc1 = LFieldValue
If LFieldName = "ContactBcc2" Then FEmailContactBcc2 = LFieldValue
If LFieldName = "ContactFrom" Then FEmailContactFrom = LFieldValue
If LFieldName = "SecurityCodeEnabled" Then FEmailSecurityCodeEnabled = LFieldValue ' (SS,17/4/09)
If LFieldName = "ConfirmationIsVATReceipt" Then FEmailConfirmationIsVATReceipt = LFieldValue ' (SS,14/6/10)
If LFieldName = "UseMessageToCustomerForDespatch" Then FEmailUseMessageToCustomerForDespatch = StrToBool(LFieldValue) ' (SS,23/10/15)
ElseIf LGroupName = "Payment" Then
If LFieldName = "Type" Then FPaymentType = UCase(LFieldValue)
If LFieldName = "Type2" Then FPaymentType2 = UCase(LFieldValue) ' (SS,28/9/07)
If LFieldName = "Type3" Then FPaymentType3 = UCase(LFieldValue) ' (SS,4/6/09)
If LFieldName = "HideCurrency" Then FPaymentHideCurrency = StrToBool(LFieldValue) ' (SS,21/5/10)
If LFieldName = "WorldPayInstallID" Then FWorldPayInstallID = LFieldValue
If LFieldName = "WorldPayTestMode" Then FWorldPayTestMode = LFieldValue
If LFieldName = "WorldPayMD5SecretKey" Then FWorldPayMD5SecretKey = LFieldValue ' (SS,14/11/12)
If LFieldName = "PayPalBusiness" Then FPayPalBusiness = LFieldValue
If LFieldName = "PayPalTestMode" Then FPayPalTestMode = StrToBool(LFieldValue) ' (SS,26/1/15) added StrToBool
If LFieldName = "PayPalShippingAddressFlag" Then FPayPalShippingAddressFlag = LFieldValue
' (SS,26/1/15)
If LFieldName = "PayPalExpressEnabled" Then FPayPalExpressEnabled = StrToBool(LFieldValue)
If LFieldName = "PayPalAPIUserName" Then FPayPalAPIUserName = LFieldValue
If LFieldName = "PayPalAPIPassword" Then FPayPalAPIPassword = LFieldValue
If LFieldName = "PayPalAPISignature" Then FPayPalAPISignature = LFieldValue
If LFieldName = "PayPalUKOnlyCheckout" Then FPayPalUKOnlyCheckout = LFieldValue ' (SS,9/4/18)
If LFieldName = "PayPalExpressShowOnBasketPage" Then FPayPalExpressShowOnBasketPage = LFieldValue ' (SS,23/4/18)
If LFieldName = "PayPalNonUKSurchargePercentage" Then FPayPalNonUKSurchargePercentage = CDbl(LFieldValue) ' (SS,8/6/18)
If LFieldName = "ePDQClientID" Then FEPDQClientID = LFieldValue
If LFieldName = "ePDQPassphrase" Then FEPDQPassphrase = LFieldValue
If LFieldName = "ePDQChargeType" Then FEPDQChargeType = LFieldValue
If LFieldName = "ePDQMerchantDisplayName" Then FEPDQMerchantDisplayName = LFieldValue ' (SS,10/5/12)
If LFieldName = "ePDQPARAMVAR" Then FEPDQPARAMVAR = LFieldValue ' (SS,10/5/12)
If LFieldName = "ePDQTestMode" Then FEPDQTestMode = StrToBool(LFieldValue) ' (SS,26/4/18)
If LFieldName = "SagePayVendorName" Then FSagePayVendorName = LFieldValue ' (SS,20/3/13)
If LFieldName = "SagePayMode" Then FSagePayMode = LFieldValue ' (SS,20/3/13)
If LFieldName = "SagePayEncryptionKey" Then FSagePayEncryptionKey = LFieldValue ' (SS,20/3/13)
' (SS,23/3/18)
If LFieldName = "PayzoneMerchantID" Then FPayzoneMerchantID = LFieldValue
If LFieldName = "PayzoneGatewayAccountPassword" Then FPayzoneGatewayAccountPassword = LFieldValue
If LFieldName = "PayzonePreSharedKey" Then FPayzonePreSharedKey = LFieldValue
' (SS,28/5/09)
If LFieldName = "AllowCheques" Then FAllowCheques = StrToBool(LFieldValue)
If LFieldName = "AllowBankTransfers" Then FAllowBankTransfers = StrToBool(LFieldValue)
If LFieldName = "ChequesPayableTo" Then FChequesPayableTo = LFieldValue
If LFieldName = "BankTransferDetails" Then FBankTransferDetails = NB(GetFieldValue("MemoValue")) ' (SS,29/5/09) held in memo field
If LFieldName = "BankTransferDetailsEUR" Then FBankTransferDetailsEUR = NB(GetFieldValue("MemoValue")) ' (SS,20/2/12) for Euros
If LFieldName = "BankTransferDetailsUSD" Then FBankTransferDetailsUSD = NB(GetFieldValue("MemoValue")) ' (SS,20/2/12) for Dollars
If LFieldName = "BankTransferDetailsGBP" Then FBankTransferDetailsGBP = NB(GetFieldValue("MemoValue")) ' (SS,29/9/12) for GBP
' (SS,18/6/09)
If LFieldName = "WorldPaySiteSeal" Then FWorldPaySiteSeal = LFieldValue
If LFieldName = "PayPalSiteSeal" Then FPayPalSiteSeal = LFieldValue
ElseIf LGroupName = "BestSellers" Then
If LFieldName = "Max" Then FBestSellersMax = NZL(LFieldValue)
If LFieldName = "PastDays" Then FBestSellersPastDays = NZL(LFieldValue)
If LFieldName = "SortOrder" Then FBestSellersSortOrder = LFieldValue
ElseIf LGroupName = "Products" Then
If LFieldName = "FilesEnabled" Then FProductFilesEnabled = StrToBool(LFieldValue)
If LFieldName = "MaxPicturesPerProduct" Then FMaxPicturesPerProduct = NZL(LFieldValue)
If LFieldName = "AcrossMax" Then FProductsAcrossMax = NZL(LFieldValue)
If LFieldName = "DownMax" Then FProductsDownMax = NZL(LFieldValue)
If LFieldName = "ProductsPerPageFixed" Then FProductsPerPageFixed = StrToBool(LFieldValue) ' (SS,12/5/10)
If LFieldName = "MaxSpecialOffer" Then FProductsMaxSpecialOffer = NZL(LFieldValue)
If LFieldName = "ShowIncVAT" Then FShowIncVAT = StrToBool(LFieldValue)
If LFieldName = "ShowExcVAT" Then FShowExcVAT = StrToBool(LFieldValue)
If LFieldName = "MinCurrencyPricesEnabled" Then FMinCurrencyPricesEnabled = StrToBool(LFieldValue) ' (SS,7/11/12)
If LFieldName = "SummaryListEnabled" Then FSummaryListEnabled = StrToBool(LFieldValue)
If LFieldName = "MaxRecentlyViewed" Then FMaxRecentlyViewed = NZL(LFieldValue) ' (SS,16/5/11)
If LFieldName = "ProductPageTabs" Then FProductPageTabs = StrToBool(LFieldValue) ' (SS,17/6/11)
If LFieldName = "AttributesEnabled" Then FProductAttributesEnabled = StrToBool(LFieldValue) ' (SS,1/7/11)
If LFieldName = "OptionsEnabled" Then FProductOptionsEnabled = StrToBool(LFieldValue) ' (SS,1/7/11)
If LFieldName = "SubproductsEnabled" Then FProductSubproductsEnabled = StrToBool(LFieldValue) ' (SS,18/10/18)
If LFieldName = "ProductSearchWithoutDescFirst" Then FProductSearchWithoutDescFirst = StrToBool(LFieldValue) ' (SS,16/1/13)
If LFieldName = "ProductSearchFullText" Then FProductSearchFullText = StrToBool(LFieldValue) ' (SS,1/6/15)
If LFieldName = "ShowReviewsImmediately" Then FProductsShowReviewsImmediately = StrToBool(LFieldValue) ' (SS,10/6/14)
If LFieldName = "SortByDefault" Then FProductsSortByDefault = LFieldValue ' (SS,26/8/15)
' (SS,20/10/14)
ElseIf LGroupName = "Reviews" Then
If LFieldName = "Type" Then FReviewsType = LFieldValue
If LFieldName = "FeefoLogon" Then FFeefoLogon = LFieldValue
If LFieldName = "FeefoFTPHost" Then FFeefoFTPHost = LFieldValue
If LFieldName = "FeefoFTPUser" Then FFeefoFTPUser = LFieldValue
If LFieldName = "FeefoFTPPassword" Then FFeefoFTPPassword = LFieldValue
If LFieldName = "TrustpilotAFSEmailAddress" Then FTrustpilotAFSEmailAddress = LFieldValue ' (SS,18/5/17)
ElseIf LGroupName = "Categories" Then
If LFieldName = "GroupsEnabled" Then FGroupsEnabled = StrToBool(LFieldValue)
If LFieldName = "ExtraSearchComboCategory" Then FExtraSearchComboCategory = LFieldValue
If LFieldName = "ExtraSearchComboBlankEntry" Then FExtraSearchComboBlankEntry = LFieldValue
If LFieldName = "ShowProductsOnFullListCat" Then FShowProductsOnFullListCat = StrToBool(LFieldValue)
If LFieldName = "CategoryForSubcategoryMenu" Then FCategoryForSubcategoryMenu = LFieldValue
If LFieldName = "AddSpecialOffersToSubcategoryMenu" Then FAddSpecialOffersToSubcategoryMenu = StrToBool(LFieldValue)
If LFieldName = "CategoryListEnabled" Then FCategoryListEnabled = StrToBool(LFieldValue)
If LFieldName = "SubcategoryListEnabled" Then FSubcategoryListEnabled = StrToBool(LFieldValue)
If LFieldName = "UseAlternateColorForCategories" Then FUseAlternateColorForCategories = StrToBool(LFieldValue)
If LFieldName = "DefaultGroup" Then FDefaultGroup = LFieldValue ' (SS,28/7/11)
If LFieldName = "AlwaysDefaultGroupForMenu" Then FAlwaysDefaultGroupForMenu = StrToBool(LFieldValue) ' (SS,1/8/11)
If LFieldName = "IncludeGroupInHTMLTitleTag" Then FIncludeGroupInHTMLTitleTag = StrToBool(LFieldValue) ' (SS,2/8/11)
If LFieldName = "ShowCategoryHeadingFooterIfEmpty" Then FShowCategoryHeadingFooterIfEmpty = StrToBool(LFieldValue) ' (SS,12/10/15)
' (SS,7/11/08)
ElseIf LGroupName = "Google" Then
If LFieldName = "GoogleAnalyticsWebPropertyID" Then FGoogleAnalyticsWebPropertyID = LFieldValue
If LFieldName = "GoogleAnalyticsUseDoubleClickDotNet" Then FGoogleAnalyticsUseDoubleClickDotNet = LFieldValue ' (SS,12/2/15)
If LFieldName = "GoogleAnalyticsCrossDomainTrackingList" Then FGoogleAnalyticsCrossDomainTrackingList = LFieldValue ' (SS,3/3/16)
If LFieldName = "EcommerceTrackingEnabled" Then FEcommerceTrackingEnabled = StrToBool(LFieldValue)
If LFieldName = "AdwordsConversionTrackingID" Then FAdWordsConversionTrackingID = LFieldValue
If LFieldName = "AdwordsConversionLabelPurchase" Then FAdWordsConversionLabelPurchase = LFieldValue
If LFieldName = "AdwordsConversionLabelNewAccount" Then FAdWordsConversionLabelNewAccount = LFieldValue
If LFieldName = "AdwordsConversionLabelRemarketing" Then FAdWordsConversionLabelRemarketing = LFieldValue ' (SS,28/11/13)
' (SS,1/10/14)
ElseIf LGroupName = "Facebook" Then
'If LFieldName = "ConversionTrackingID" Then FFacebookConversionTrackingID = LFieldValue
' (SS,9/10/17) replaced above with following, i.e. ConversionTrackingID with PixelID
If LFieldName = "PixelID" Then FFacebookPixelID = LFieldValue
' (SS,13/3/15)
ElseIf LGroupName = "Froogle" Then
If LFieldName = "ProductType" Then FFroogleProductType = LFieldValue
' (SS,21/10/16)
ElseIf LGroupName = "AddressLookup" Then
If LFieldName = "Enabled" Then FAddressLookupEnabled = StrToBool(LFieldValue)
If LFieldName = "Type" Then FAddressLookupType = LFieldValue
If LFieldName = "AccountCode" Then FAddressLookupAccountCode = LFieldValue
If LFieldName = "LocalModeOnly" Then FAddressLookupLocalModeOnly = LFieldValue
' (SS,21/10/16)
ElseIf LGroupName = "LocalMode" Then
If LFieldName = "Enabled" Then FLocalModeEnabled = StrToBool(LFieldValue)
If LFieldName = "ExchangeReasons" Then FLocalModeExchangeReasons = LFieldValue
If LFieldName = "PaymentMethods" Then FLocalModePaymentMethods = LFieldValue
' (SS,20/3/18)
ElseIf LGroupName = "SMS" Then
If LFieldName = "Enabled" Then FSMSEnabled = StrToBool(LFieldValue)
End If
NextQueryRecord
Loop
CloseQuery
' if certain email addresses are blank then default to storeemail address
If FEmailOrderConfirmationBcc1 = "" Then FEmailOrderConfirmationBcc1 = FStoreEmail
If FEmailOrderConfirmationFrom = "" Then FEmailOrderConfirmationFrom = FStoreEmail
If FEmailContactTo = "" Then FEmailContactTo = FStoreEmail
If FEmailContactFrom = "" Then FEmailContactFrom = FStoreEmail
' (SS,14/1/20) if FReviewRequestsDaysNonUK not set i.e. 0 then default to FReviewRequestsDays
If FReviewRequestsDaysNonUK = 0 Then FReviewRequestsDaysNonUK = FReviewRequestsDays
End Sub
' (SS,14/11/07) check schema version and upgrade if necessary
Sub CheckSchema
' (SS,11/11/13) added following to only upgrade if cmd = "upgrade", to prevent page access from other than myself causing upgrade
' and from being called multiple times
If CleanRequestQueryString("cmd") <> "upgrade" Then Exit Sub
' if schema version doesn't exist, i.e. version is 1.00 or less then add it, otherwise SetSchemaVersion will fail
If GetSchemaVersion <= 1 Then AddSetting "Application", "SchemaVersion", 1
' upgrade to version 1.42 if required
'If GetSchemaVersion < 1.42 Then UpgradeSchemaTo142
'If GetSchemaVersion < 1.43 Then UpgradeSchemaTo143
'If GetSchemaVersion < 1.44 Then UpgradeSchemaTo144 ' (SS,21/10/08)
'If GetSchemaVersion < 1.45 Then UpgradeSchemaTo145 ' (SS,17/4/09)
'If GetSchemaVersion < 1.46 Then UpgradeSchemaTo146 ' (SS,23/11/09)
'If GetSchemaVersion < 1.47 Then UpgradeSchemaTo147 ' (SS,27/01/10)
'If GetSchemaVersion < 1.48 Then UpgradeSchemaTo148 ' (SS,28/04/10)
'If GetSchemaVersion < 1.49 Then UpgradeSchemaTo149 ' (SS,23/06/10)
'If GetSchemaVersion < 1.50 Then UpgradeSchemaTo150 ' (SS,29/06/10)
'If GetSchemaVersion < 1.51 Then UpgradeSchemaTo151 ' (SS,22/07/10)
'If GetSchemaVersion < 1.52 Then UpgradeSchemaTo152 ' (SS,21/03/11)
If GetSchemaVersion < 1.59 Then ' (SS,6/3/13) added extra "if" to group the changes prior to 1.59
If GetSchemaVersion < 1.53 Then UpgradeSchemaTo153 ' (SS,14/07/11)
If GetSchemaVersion < 1.54 Then UpgradeSchemaTo154 ' (SS,14/02/12)
If GetSchemaVersion < 1.55 Then UpgradeSchemaTo155 ' (SS,14/03/12)
If GetSchemaVersion < 1.56 Then UpgradeSchemaTo156 ' (SS,16/03/12)
If GetSchemaVersion < 1.57 Then UpgradeSchemaTo157 ' (SS,27/04/12)
If GetSchemaVersion < 1.58 Then UpgradeSchemaTo158 ' (SS,30/05/12)
If GetSchemaVersion < 1.59 Then UpgradeSchemaTo159 ' (SS,19/06/12)
End If
If GetSchemaVersion < 1.60 Then UpgradeSchemaTo160 ' (SS,28/09/12)
If GetSchemaVersion < 1.61 Then UpgradeSchemaTo161 ' (SS,08/11/12)
If GetSchemaVersion < 1.62 Then UpgradeSchemaTo162 ' (SS,06/12/12)
If GetSchemaVersion < 1.63 Then UpgradeSchemaTo163 ' (SS,10/01/13)
If GetSchemaVersion < 1.64 Then UpgradeSchemaTo164 ' (SS,13/02/13)
If GetSchemaVersion < 1.65 Then UpgradeSchemaTo165 ' (SS,28/02/13)
If GetSchemaVersion < 1.66 Then UpgradeSchemaTo166 ' (SS,06/03/13)
If GetSchemaVersion < 1.67 Then UpgradeSchemaTo167 ' (SS,13/12/13)
End Sub
' (SS,14/11/07) upgrade to version 1.42 - add 3 new fields to orders and one to customers
Sub UpgradeSchemaTo142
ExecuteQuery "ALTER TABLE orders ADD VATNumber varchar(20) default '' AFTER ExchangeRate," +_
"ADD IntraCommunitySupply tinyint(1) NOT NULL default '0' AFTER VATNumber," +_
"ADD DeliveryWillCollect tinyint(1) NOT NULL default '0' AFTER GrandTotal"
ExecuteQuery "ALTER TABLE customers ADD VATNumber varchar(20) default '' AFTER Telephone"
SetSchemaVersion(1.42)
End Sub
' (SS,16/11/07) date DateStockDue to products table
Sub UpgradeSchemaTo143
ExecuteQuery "ALTER TABLE products ADD DateStockDue date default NULL AFTER NumInStock"
SetSchemaVersion(1.43)
End Sub
' (SS,21/10/08) added CustomFlag (tinyint) to products table and IntraCommunitySupply to countries
Sub UpgradeSchemaTo144
ExecuteQuery "ALTER TABLE products ADD CustomFlag tinyint(1) NOT NULL default 0 AFTER ProductDisabled"
ExecuteQuery "ALTER TABLE countries ADD IntraCommunitySupply tinyint(1) NOT NULL DEFAULT 0 AFTER ChargeVAT"
SetSchemaVersion(1.44)
End Sub
' (SS,17/4/09) changes to orders table and added new vouchers table
Sub UpgradeSchemaTo145
Dim LSQL
' changed CustomerID from VARCHAR(20) to INT(11) was mistakenly set to VARCHAR(20)
' later removed this changed becauses Toner2Desk uses VARCHAR(20) for CustomerID
' added new fields Mobile, DeliveryDate, VoucherID and Discount, added index idxVoucherID
' (SS,22/5/09) added Courier, TrackingNo & MessageToCustomer
LSQL = "ALTER TABLE orders " +_
"ADD Mobile VARCHAR(100) DEFAULT NULL AFTER Telephone, " +_
"ADD DeliveryDate DATE DEFAULT NULL AFTER DeliveryWillCollect, " +_
"ADD VoucherID INT(11) DEFAULT NULL AFTER TotalWeight, " +_
"ADD Discount DOUBLE NOT NULL DEFAULT '0' AFTER Subtotal, " +_
"ADD Courier VARCHAR(50) DEFAULT NULL AFTER IntraCommunitySupply, " +_
"ADD TrackingNo VARCHAR(255) DEFAULT NULL AFTER Courier, " +_
"ADD MessageToCustomer MEDIUMTEXT AFTER TrackingNo, " +_
"ADD INDEX idxVoucherID (VoucherID)"
ExecuteQuery LSQL
' added Mobile to customers
LSQL = "ALTER TABLE customers ADD Mobile VARCHAR(100) DEFAULT NULL AFTER Telephone"
ExecuteQuery LSQL
' new vouchers table
LSQL = "CREATE TABLE vouchers (" +_
"VoucherID INTEGER(11) NOT NULL AUTO_INCREMENT, " +_
"VoucherCode CHAR(20) DEFAULT NULL, " +_
"VoucherType CHAR(2) DEFAULT NULL, " +_
"VoucherName VARCHAR(40) DEFAULT NULL, " +_
"ValidFromDate DATE DEFAULT NULL, " +_
"ExpiryDate DATE DEFAULT NULL, " +_
"MinimumSpend DECIMAL(10,2) DEFAULT NULL, " +_
"DiscountAmount DECIMAL(10,2) DEFAULT NULL, " +_
"DiscountPercentage DECIMAL(5,2) DEFAULT NULL, " +_
"SingleUsage TINYINT(1) DEFAULT '0', " +_
"Category VARCHAR(50) DEFAULT NULL, " +_
"Subcategory VARCHAR(50) DEFAULT NULL, " +_
"PRIMARY KEY (VoucherID), " +_
"UNIQUE KEY idxVoucherCode (VoucherCode), " +_
"KEY idxCategory (Category), " +_
"KEY idxSubcategory (Subcategory) " +_
")ENGINE=MyISAM " +_
"AUTO_INCREMENT=1;"
ExecuteQuery LSQL
' new MenuShortName to categories table
ExecuteQuery "ALTER TABLE categories ADD MenuShortName VARCHAR(30) DEFAULT NULL AFTER Priority"
' new product reviews table
LSQL = "CREATE TABLE productreviews (" +_
"ReviewID INTEGER(11) NOT NULL AUTO_INCREMENT, " +_
"ReviewType CHAR(1) NOT NULL DEFAULT 'R' COMMENT 'R for Review, C for Comment', " +_
"ReviewDateTime DATETIME NOT NULL, " +_
"ProductCode VARCHAR(20) NOT NULL DEFAULT '', " +_
"ReviewTitle VARCHAR(50) DEFAULT NULL, " +_
"ProductRating TINYINT(1) DEFAULT NULL, " +_
"ReviewText MEDIUMTEXT, " +_
"ReviewerName VARCHAR(50) DEFAULT NULL, " +_
"ReviewerEmail VARCHAR(100) DEFAULT NULL, " +_
"ShowOnSite TINYINT(1) DEFAULT '1', " +_
"PRIMARY KEY (ReviewID), " +_
"KEY idxProductCode (ProductCode) " +_
")ENGINE=MyISAM " +_
"AUTO_INCREMENT=1;"
ExecuteQuery LSQL
' new sitedetails table
LSQL = "CREATE TABLE sitedetails (" +_
"ID INTEGER(11) NOT NULL AUTO_INCREMENT, " +_
"Type VARCHAR(20) NOT NULL DEFAULT '', " +_
"SortOrder INTEGER(11) NOT NULL DEFAULT '0', " +_
"Name VARCHAR(100) DEFAULT NULL, " +_
"Text MEDIUMTEXT, " +_
"File LONGBLOB, " +_
"ExtraField1 VARCHAR(200) DEFAULT NULL, " +_
"ExtraField2 VARCHAR(200) DEFAULT NULL, " +_
"ExtraField3 VARCHAR(200) DEFAULT NULL, " +_
"CheckBoxField TINYINT(1) NOT NULL DEFAULT '1', " +_
"LastUpdated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, " +_
"PRIMARY KEY (ID), " +_
"UNIQUE KEY idxTypeName (Type, Name), " +_
"KEY idxType (Type) " +_
")ENGINE=MyISAM " +_
"AUTO_INCREMENT=1;"
ExecuteQuery LSQL
' new voucherproducts table
LSQL = "CREATE TABLE voucherproducts (" +_
"VoucherID INTEGER(11) NOT NULL," +_
"ProductCode VARCHAR(20) NOT NULL DEFAULT ''," +_
"PRIMARY KEY (VoucherID, ProductCode)" +_
")ENGINE=MyISAM;"
ExecuteQuery LSQL
' (SS,11/5/09) countries table, change ChargeVAT from CHAR(1) to TINYINT(1)
ExecuteQuery "UPDATE countries SET ChargeVAT = '1' WHERE ChargeVAT = 'Y';"
ExecuteQuery "UPDATE countries SET ChargeVAT = '0' WHERE ChargeVAT = 'N';"
ExecuteQuery "ALTER TABLE countries MODIFY ChargeVAT TINYINT(1) DEFAULT '0';"
ExecuteQuery "UPDATE countries SET ChargeVAT = 1 WHERE PostalArea <> 'RW';"
' (SS,22/5/09) new stocknotifications table
LSQL = "CREATE TABLE stocknotifications (" +_
"ID INTEGER(11) NOT NULL AUTO_INCREMENT, " +_
"ProductCode VARCHAR(20) NOT NULL DEFAULT '', " +_
"Name VARCHAR(50) NOT NULL DEFAULT '', " +_
"Email VARCHAR(100) NOT NULL DEFAULT '', " +_
"DateTimeAdded DATETIME NOT NULL, " +_
"DateTimeNotified DATETIME DEFAULT NULL, " +_
"PRIMARY KEY (ID), " +_
"KEY idxProductCode (ProductCode), " +_
"KEY idxDateTimeNotified (DateTimeNotified) " +_
")ENGINE=MyISAM " +_
"AUTO_INCREMENT=1;"
ExecuteQuery LSQL
SetSchemaVersion(1.45)
End Sub
' (SS,23/11/09) added NewCustomer flag to orders table
Sub UpgradeSchemaTo146
ExecuteQuery "ALTER TABLE orders ADD NewCustomer tinyint(1) NOT NULL default 0 AFTER MessageToCustomer;"
SetSchemaVersion(1.46)
End Sub
' (SS,27/1/10) added new loginlog table to help diagnose login issue, originally added just for Petfood site
Sub UpgradeSchemaTo147
Dim LSQL
LSQL = "CREATE TABLE loginlog (" +_
"ID INTEGER(11) NOT NULL AUTO_INCREMENT, " +_
"DateTimeAttempted DATETIME DEFAULT NULL, " +_
"SessionID VARCHAR(20) DEFAULT NULL, " +_
"IPAddress VARCHAR(20) DEFAULT NULL, " +_
"AlreadyLoggedIn TINYINT(1) NOT NULL DEFAULT '0', " +_
"LoginResult VARCHAR(20) DEFAULT NULL, " +_
"CustomerID VARCHAR(12) DEFAULT NULL, " +_
"Email VARCHAR(255) DEFAULT NULL, " +_
"Password VARCHAR(255) DEFAULT NULL, " +_
"Referrer VARCHAR(255) DEFAULT NULL, " +_
"URL VARCHAR(255) DEFAULT NULL, " +_
"QueryString VARCHAR(255) DEFAULT NULL, " +_
"UserAgent VARCHAR(255) DEFAULT NULL, " +_
"PRIMARY KEY (ID) " +_
")ENGINE=MyISAM " +_
"AUTO_INCREMENT=1;"
ExecuteQuery LSQL
SetSchemaVersion(1.47)
End Sub
' (SS,28/4/10) modify the Category and Subcategory fields from 50 to 75 characters (for galleon french/german longer names)
Sub UpgradeSchemaTo148
ExecuteQuery "ALTER TABLE categories MODIFY Category VARCHAR(75) NOT NULL DEFAULT '';"
ExecuteQuery "ALTER TABLE subcategories MODIFY Category VARCHAR(75) NOT NULL DEFAULT '', MODIFY Subcategory VARCHAR(75) NOT NULL DEFAULT '';"
ExecuteQuery "ALTER TABLE productcategories MODIFY Category VARCHAR(75) NOT NULL DEFAULT '', MODIFY Subcategory VARCHAR(75) NOT NULL DEFAULT '';"
ExecuteQuery "ALTER TABLE vouchers MODIFY Category VARCHAR(75) DEFAULT NULL, MODIFY Subcategory VARCHAR(75) DEFAULT NULL;"
SetSchemaVersion(1.48)
End Sub
' (SS,23/6/10) added new suppliers table and three new fields to products table
Sub UpgradeSchemaTo149
Dim LSQL
LSQL = "CREATE TABLE suppliers (" +_
"SupplierID INTEGER(11) NOT NULL AUTO_INCREMENT, " +_
"SupplierName VARCHAR(100) NOT NULL DEFAULT '', " +_
"AddressLine1 VARCHAR(100) DEFAULT NULL, " +_
"AddressLine2 VARCHAR(100) DEFAULT NULL, " +_
"Town VARCHAR(100) DEFAULT NULL, " +_
"County VARCHAR(50) DEFAULT NULL, " +_
"Postcode VARCHAR(20) DEFAULT NULL, " +_
"Country VARCHAR(50) DEFAULT NULL, " +_
"ContactName VARCHAR(100) DEFAULT NULL, " +_
"Telephone VARCHAR(100) DEFAULT NULL, " +_
"EmailAddress VARCHAR(100) DEFAULT NULL, " +_
"Website VARCHAR(120) DEFAULT NULL, " +_
"Notes MEDIUMTEXT, " +_
"AvailabilityDaysFrom SMALLINT(6) DEFAULT NULL, " +_
"AvailabilityDaysTo SMALLINT(6) DEFAULT NULL, " +_
"LastUpdated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, " +_
"PRIMARY KEY (SupplierID), " +_
"UNIQUE KEY idxSupplierName (SupplierName) " +_
")ENGINE=MyISAM " +_
"AUTO_INCREMENT=1;"
ExecuteQuery LSQL
LSQL = "ALTER TABLE products " +_
"ADD SupplierID INTEGER(11) DEFAULT NULL AFTER CustomFlag, " +_
"ADD AvailabilityDaysFrom SMALLINT(6) DEFAULT NULL AFTER SupplierID, " +_
"ADD AvailabilityDaysTo SMALLINT(6) DEFAULT NULL AFTER AvailabilityDaysFrom, " +_
"ADD INDEX idxSupplierID (SupplierID);"
ExecuteQuery LSQL
SetSchemaVersion(1.49)
End Sub
' (SS,29/6/10) update pictures table to add PictureID autoinc field and resize Code and Code2 from 50 to 75 due to category resize done in schema 1.48
Sub UpgradeSchemaTo150
Dim LSQL
LSQL = "ALTER TABLE pictures " +_
"MODIFY Code VARCHAR(75) NOT NULL DEFAULT '', " +_
"MODIFY Code2 VARCHAR(75) NOT NULL DEFAULT '', " +_
"ADD PictureID INTEGER(11) NOT NULL AUTO_INCREMENT FIRST, " +_
"DROP PRIMARY KEY, " +_
"ADD PRIMARY KEY (PictureID), " +_
"ADD UNIQUE INDEX idxTypeAndCode (Type, Code, Code2);"
ExecuteQuery LSQL
SetSchemaVersion(1.50)
End Sub
' (SS,22/7/10) update products to add new AllowOutOfStockOrdering field, orders to add PackedBy field
' also added fields DeliveryAgentName, DeliveryAgentDate, DeliveryAgentCost which only hyperflight had
Sub UpgradeSchemaTo151
ExecuteQuery "ALTER TABLE products ADD AllowOutOfStockOrdering TINYINT(1) DEFAULT '1' AFTER AvailabilityDaysTo;"
' set the default in each product record for AllowOutOfStockOrdering to the global default setting
ExecuteQuery "UPDATE products SET AllowOutOfStockOrdering = " & BoolToInt(StrToBool(GetSettingFromField("Application", "AllowProductOrderIfNoStock", "FieldValue"))) & ";"
Dim LSQL
LSQL = "ALTER TABLE orders " +_
"ADD DeliveryAgentName VARCHAR(50) DEFAULT NULL COMMENT 'Added 23/3/2010 for Hyperflight' AFTER NewCustomer, " +_
"ADD DeliveryAgentDate DATE DEFAULT NULL COMMENT 'Added 23/3/2010 for Hyperflight' AFTER DeliveryAgentName, " +_
"ADD DeliveryAgentCost DECIMAL(11,2) DEFAULT NULL COMMENT 'Added 23/3/2010 for Hyperflight' AFTER DeliveryAgentDate, " +_
"ADD PackedBy VARCHAR(20) DEFAULT NULL COMMENT 'Added 22/7/2010 for Hyperflight' AFTER DeliveryAgentCost;"
ExecuteQuery LSQL
SetSchemaVersion(1.51)
End Sub
' (SS,21/3/11) added Extra text field after FileName, used to hold URL for Banners, but could be used for other things in future
Sub UpgradeSchemaTo152
ExecuteQuery "ALTER TABLE files ADD Extra VARCHAR(255) DEFAULT NULL AFTER FileName;"
SetSchemaVersion(1.52)
End Sub
' (SS,14/7/11) adding of attributes and options, also ShortMenuName to subcategories
Sub UpgradeSchemaTo153
Dim LSQL
' subcategories table, new MenuShortName field
' decided to add manually to each shopping database, due to integration with shopping admin app categories forms
' ExecuteQuery "ALTER TABLE subcategories ADD COLUMN MenuShortName VARCHAR(30) DEFAULT NULL AFTER Priority"
' products table, new ProductID field, which is now the primary key, unique key added for ProductCode field which was the primary key before
ExecuteQuery "ALTER TABLE products DROP PRIMARY KEY"
ExecuteQuery "ALTER TABLE products ADD COLUMN ProductID INTEGER(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST"
ExecuteQuery "ALTER TABLE products ADD UNIQUE idxProductCode (ProductCode)"
' shoppingbaskets table, new ProductID field which also has an index, index idxSessionIDProductCode replaced with idxSessionID
' shoppingbaskets table is also truncated, because existing records will not have the correct product id
ExecuteQuery "ALTER TABLE shoppingbaskets ADD INDEX idxSessionID (SessionID)"
ExecuteQuery "ALTER TABLE shoppingbaskets DROP INDEX idxSessionIDProductCode"
ExecuteQuery "ALTER TABLE shoppingbaskets ADD COLUMN ProductID INTEGER(11) NOT NULL AFTER SessionID"
ExecuteQuery "ALTER TABLE shoppingbaskets ADD INDEX idxProductID (ProductID)"
ExecuteQuery "TRUNCATE TABLE shoppingbaskets"
' orderdetails table, new ProductID field and index, new OptionsList field, index idxOrderNoProductCode replaced with two separate idxOrderNo and idxProductCode indexes
ExecuteQuery "ALTER TABLE orderdetails DROP INDEX idxOrderNoProductCode"
ExecuteQuery "ALTER TABLE orderdetails ADD COLUMN ProductID INTEGER(11) NOT NULL AFTER OrderNo"
ExecuteQuery "ALTER TABLE orderdetails ADD COLUMN OptionsList VARCHAR(255) DEFAULT NULL AFTER ProductName"
ExecuteQuery "ALTER TABLE orderdetails ADD INDEX idxProductID (ProductID)"
ExecuteQuery "ALTER TABLE orderdetails ADD INDEX idxProductCode (ProductCode)"
ExecuteQuery "ALTER TABLE orderdetails ADD INDEX idxOrderNo (OrderNo)"
' new log_cancelled_sessions table, used for debugging global.asa
LSQL = "CREATE TABLE log_cancelled_sessions (" &_
"ID int(11) NOT NULL AUTO_INCREMENT," &_
"DateTimeLogged timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," &_
"SessionID varchar(20) DEFAULT NULL," &_
"OrderNo varchar(10) DEFAULT NULL," &_
"Message varchar(100) DEFAULT NULL," &_
"PRIMARY KEY (ID)" &_
") ENGINE=MyISAM AUTO_INCREMENT=1"
ExecuteQuery LSQL
' new attributes table
LSQL = "CREATE TABLE attributes (" &_
"AttributeID int(11) NOT NULL AUTO_INCREMENT," &_
"AttributeName varchar(50) DEFAULT NULL," &_
"InputType enum('Text','Combo Box','Memo') DEFAULT 'Text'," &_
"Required tinyint(1) NOT NULL DEFAULT '0'," &_
"ValueList varchar(255) DEFAULT NULL COMMENT 'Comma separated list for combo box input type'," &_
"SortOrder int(11) NOT NULL DEFAULT '0'," &_
"PRIMARY KEY (AttributeID)," &_
"UNIQUE KEY idxAttributeName (AttributeName)" &_
") ENGINE=MyISAM AUTO_INCREMENT=1"
ExecuteQuery LSQL
' new product_attributes table
LSQL = "CREATE TABLE product_attributes (" &_
"ProductID int(11) NOT NULL," &_
"AttributeID int(11) NOT NULL," &_
"AttributeValue varchar(100) DEFAULT NULL," &_
"PRIMARY KEY (ProductID,AttributeID)" &_
") ENGINE=MyISAM"
ExecuteQuery LSQL
' new product_options table
LSQL = "CREATE TABLE product_options (" &_
"ProductOptionID int(11) NOT NULL AUTO_INCREMENT," &_
"ProductID int(11) NOT NULL," &_
"OptionName varchar(50) NOT NULL," &_
"InputType enum('Combo Box','Text','Check Box','Radio','Split Combo') NOT NULL DEFAULT 'Combo Box'," &_
"Required tinyint(1) NOT NULL DEFAULT '0'," &_
"SortOrder int(11) NOT NULL DEFAULT '0'," &_
"PRIMARY KEY (ProductOptionID)," &_
"UNIQUE KEY idxProductIDOptionName (ProductID,OptionName)," &_
"KEY idxProductID (ProductID)" &_
") ENGINE=MyISAM AUTO_INCREMENT=1"
ExecuteQuery LSQL
' new product_option_values table
' (SS,1/8/12) changed OptionValue varchar(100) to OptionValue varchar(150)
LSQL = "CREATE TABLE product_option_values (" &_
"ProductOptionValueID int(11) NOT NULL AUTO_INCREMENT," &_
"ProductOptionID int(11) NOT NULL," &_
"ProductID int(11) NOT NULL," &_
"OptionValue varchar(150) NOT NULL DEFAULT ''," &_
"NumInStock int(11) DEFAULT NULL," &_
"PriceAdjust decimal(10,2) NOT NULL DEFAULT '0.00'," &_
"WeightAdjust decimal(10,3) NOT NULL DEFAULT '0.000'," &_
"SortOrder int(11) NOT NULL DEFAULT '0'," &_
"PRIMARY KEY (ProductOptionValueID)," &_
"UNIQUE KEY idxProductOptionIDOptionValue (ProductOptionID,OptionValue)," &_
"KEY idxProductOptionID (ProductOptionID)," &_
"KEY idxProductID (ProductID)" &_
") ENGINE=MyISAM AUTO_INCREMENT=1"
ExecuteQuery LSQL
' new shopping_basket_options table
' (SS,1/8/12) changed OptionValue varchar(100) to OptionValue varchar(150), and OptionName varchar(100) to OptionName varchar(50)
LSQL = "CREATE TABLE shopping_basket_options (" &_
"ShoppingBasketOptionID int(11) NOT NULL AUTO_INCREMENT," &_
"ItemID int(11) NOT NULL," &_
"ProductOptionID int(11) NOT NULL," &_
"ProductOptionValueID int(11) NOT NULL," &_
"OptionName varchar(150) DEFAULT ''," &_
"OptionValue varchar(50) DEFAULT ''," &_
"PRIMARY KEY (ShoppingBasketOptionID)," &_
"KEY idxItemID (ItemID)" &_
") ENGINE=MyISAM AUTO_INCREMENT=1"
ExecuteQuery LSQL
' new order_detail_options table
' (SS,1/8/12) changed OptionValue varchar(100) to OptionValue varchar(150), and OptionName varchar(100) to OptionName varchar(50)
LSQL = "CREATE TABLE order_detail_options (" &_
"OrderDetailOptionID int(11) NOT NULL AUTO_INCREMENT," &_
"OrderNo int(11) NOT NULL," &_
"OrderDetailID int(11) NOT NULL," &_
"ProductOptionID int(11) NOT NULL," &_
"ProductOptionValueID int(11) NOT NULL," &_
"OptionName varchar(50) DEFAULT NULL," &_
"OptionValue varchar(150) DEFAULT NULL," &_
"PRIMARY KEY (OrderDetailOptionID)," &_
"KEY idxOrderDetailID (OrderDetailID)" &_
") ENGINE=MyISAM AUTO_INCREMENT=1"
ExecuteQuery LSQL
' update exist orderdetails records to hold the correct product id instead of 0 for existing records
LSQL = "UPDATE orderdetails, products" &_
" SET orderdetails.ProductID = products.ProductID" &_
" WHERE orderdetails.ProductCode = products.ProductCode AND orderdetails.ProductID = 0"
ExecuteQuery LSQL
SetSchemaVersion(1.53)
End Sub
' (SS,14/2/12) Modify product_options table to add ExtraSettings field after Required
Sub UpgradeSchemaTo154
ExecuteQuery "ALTER TABLE product_options ADD ExtraSettings VARCHAR(50) DEFAULT NULL AFTER Required"
ExecuteQuery "ALTER TABLE stocknotifications ADD ProductOption VARCHAR(100) DEFAULT '' AFTER ProductCode" ' ProductOption used instead of Option due to Option being an internal SQL word
SetSchemaVersion(1.54)
End Sub
' (SS,14/3/12) added DelayUntil field to stocknotifications
Sub UpgradeSchemaTo155
ExecuteQuery "ALTER TABLE stocknotifications ADD DelayUntil DATETIME DEFAULT NULL AFTER DateTimeAdded"
SetSchemaVersion(1.55)
End Sub
' (SS,15/3/12)
Sub UpgradeSchemaTo156
Dim LSQL
' drop loginlog table, no longer used, may add back in future if required for debugging
ExecuteQuery "DROP TABLE loginlog"
' categories table, drop IconFile and LastUpdated fields, add new TitleTag, DescriptionMetaTag and KeywordsMetaTag fields
LSQL = "ALTER TABLE categories " &_
"DROP IconFile, " &_
"DROP LastUpdated, " &_
"ADD TitleTag VARCHAR(66) DEFAULT NULL AFTER GroupName, " &_
"ADD DescriptionMetaTag VARCHAR(165) DEFAULT NULL AFTER TitleTag, " &_
"ADD KeywordsMetaTag VARCHAR(255) DEFAULT NULL AFTER DescriptionMetaTag"
ExecuteQuery LSQL
' subcategories table, add new TitleTag, DescriptionMetaTag and KeywordsMetaTag fields
LSQL = "ALTER TABLE subcategories " &_
"ADD TitleTag VARCHAR(66) DEFAULT NULL AFTER Picture, " &_
"ADD DescriptionMetaTag VARCHAR(165) DEFAULT NULL AFTER TitleTag, " &_
"ADD KeywordsMetaTag VARCHAR(255) DEFAULT NULL AFTER DescriptionMetaTag"
ExecuteQuery LSQL
' groups table, drop ProductListCategorySuffix and FroogleNameSuffix fields, add new TitleTag, DescriptionMetaTag and KeywordsMetaTag fields
LSQL = "ALTER TABLE groups " &_
"DROP ProductListCategorySuffix, " &_
"DROP FroogleNameSuffix, " &_
"ADD TitleTag VARCHAR(66) DEFAULT NULL AFTER SortOrder, " &_
"ADD DescriptionMetaTag VARCHAR(165) DEFAULT NULL AFTER TitleTag, " &_
"ADD KeywordsMetaTag VARCHAR(255) DEFAULT NULL AFTER DescriptionMetaTag"
ExecuteQuery LSQL
' sitedetails table, resize Type from 20 to 25 to allow for "Description Meta Tags"
ExecuteQuery "ALTER TABLE sitedetails MODIFY Type VARCHAR(25) NOT NULL DEFAULT ''"
' attributes table, add new MaxLength field to allow limit to be set for the attribue value field
ExecuteQuery "ALTER TABLE attributes ADD MaxLength INT(11) NOT NULL DEFAULT 0 AFTER ValueList"
' product_attributes table, resize AttributeValue from 100 to 255 to allow Keywords Meta Tag to be held which can be up to 255 chars
ExecuteQuery "ALTER TABLE product_attributes MODIFY AttributeValue VARCHAR(255) DEFAULT NULL"
' attributes table, add new Title Tag, Description Meta Tag and Keywords Meta Tag attributes
LSQL = "INSERT INTO attributes (AttributeName, InputType, Required, ValueList, MaxLength, SortOrder) VALUES " &_
"('Title Tag', 'Text', 0, NULL, 66, 1010), " &_
"('Description Meta Tag', 'Text', 0, NULL, 165, 1020), " &_
"('Keywords Meta Tag', 'Text', 0, NULL, 255, 1030)"
ExecuteQuery LSQL
SetSchemaVersion(1.56)
End Sub
' (SS,27/4/12)
Sub UpgradeSchemaTo157
' product_option_values table, resize OptionValue column from 100 to 150
ExecuteQuery "ALTER TABLE product_option_values MODIFY COLUMN OptionValue varchar(150) NOT NULL DEFAULT ''"
' order_detail_options table, resize OptionValue column from 100 to 150
ExecuteQuery "ALTER TABLE order_detail_options MODIFY COLUMN OptionValue varchar(150) NOT NULL DEFAULT ''"
SetSchemaVersion(1.57)
End Sub
' (SS,30/5/12)
Sub UpgradeSchemaTo158
' added NumInStock to orderdetails to hold a historical number in stock value
' (SS,1/6/12) field renamed from NumInStock to PrevStock
ExecuteQuery "ALTER TABLE orderdetails ADD PrevStock int(11) DEFAULT NULL AFTER PriceEach;"
SetSchemaVersion(1.58)
End Sub
' (SS,19/6/12)
Sub UpgradeSchemaTo159
Dim LSQL
' encrypt the password, add new fields used to reset password: PasswordResetKey, PasswordResetDateTime
' (SS,1/6/12) field renamed from NumInStock to PrevStock
LSQL = "ALTER TABLE customers " &_
"ADD NewPassword varbinary(50) DEFAULT NULL AFTER Password, " &_
"ADD PasswordResetKey varchar(10) DEFAULT NULL AFTER DateTimeLastLogin, " &_
"ADD PasswordResetDateTime datetime DEFAULT NULL AFTER PasswordResetKey"
ExecuteQuery LSQL
' encrypt the Password into NewPassword
ExecuteQuery "UPDATE customers SET NewPassword = " & GetSQLForPasswordEncryption("Password", True)
' drop the original Password field
ExecuteQuery "ALTER TABLE customers DROP Password"
' rename NewPassword to Password
ExecuteQuery "ALTER TABLE customers CHANGE NewPassword Password varbinary(50) DEFAULT NULL"
SetSchemaVersion(1.59)
End Sub
' (SS,28/9/12)
Sub UpgradeSchemaTo160
' review request feature added, new ReviewRequestSentDate field and some extra indexes
ExecuteQuery "ALTER TABLE orders ADD COLUMN ReviewRequestSentDate DATE DEFAULT NULL AFTER PackedBy"
ExecuteQuery "ALTER TABLE orders ADD INDEX idxReviewRequestSentDate (ReviewRequestSentDate)"
ExecuteQuery "ALTER TABLE orders ADD INDEX idxDateTimeOrdered (DateTimeOrdered)"
ExecuteQuery "ALTER TABLE orders ADD INDEX idxDateTimeCompleted (DateTimeCompleted)"
ExecuteQuery "ALTER TABLE orders ADD INDEX idxStatus (Status)"
SetSchemaVersion(1.60)
End Sub
' (SS,08/11/12)
Sub UpgradeSchemaTo161
Dim LSQL
' added 3 new fields to products table
LSQL = "ALTER TABLE products " &_
"ADD MinUSDPrice DECIMAL(10,2) DEFAULT NULL AFTER Price4, " &_
"ADD MinEURPrice DECIMAL(10,2) DEFAULT NULL AFTER MinUSDPrice, " &_
"ADD DateAdded DATE DEFAULT NULL AFTER MPN"
ExecuteQuery LSQL
SetSchemaVersion(1.61)
End Sub
' (SS,06/12/12)
Sub UpgradeSchemaTo162
Dim LSQL
LSQL = "ALTER TABLE customers " &_
"ADD OrderReminderSentDate DATE DEFAULT NULL AFTER PasswordResetDateTime, " &_
"ADD OrderReminderOptOut TINYINT(1) DEFAULT 0 AFTER OrderReminderSentDate"
ExecuteQuery LSQL
SetSchemaVersion(1.62)
End Sub
' (SS,10/01/13)
Sub UpgradeSchemaTo163
Dim LSQL
LSQL = "ALTER TABLE categories " &_
"ADD Heading VARCHAR(100) DEFAULT NULL AFTER MenuShortName, " &_
"ADD Footer MEDIUMTEXT AFTER Description"
ExecuteQuery LSQL
LSQL = "ALTER TABLE subcategories " &_
"ADD Heading VARCHAR(100) DEFAULT NULL AFTER GoogleProductCategory, " &_
"ADD Footer MEDIUMTEXT AFTER Description"
ExecuteQuery LSQL
SetSchemaVersion(1.63)
End Sub
' (SS,13/2/13)
Sub UpgradeSchemaTo164
Dim LSQL
LSQL = "ALTER TABLE orders " &_
"ADD CancelledOrderChasedDate date DEFAULT NULL AFTER ReviewRequestSentDate"
ExecuteQuery LSQL
SetSchemaVersion(1.64)
End Sub
' (SS,28/2/13)
' (SS,5/3/13) modified to only add customer records if customer has a successful order
Sub UpgradeSchemaTo165
Dim LSQL
LSQL = "ALTER TABLE customers " &_
"ADD MarketingEmailSentDate date DEFAULT NULL AFTER OrderReminderOptOut"
ExecuteQuery LSQL
' (SS,28/2/13) for sites with optional accounts, i.e. not Galleon
If AccountsOptional Then
' !!! for shopping sites that don't force customer record creation (i.e. non galleon)
DebugLog -1, "UpgradeSchemaTo165" ' (SS,11/11/13)
' find customer ID for matching email address
LSQL = "UPDATE orders, customers SET orders.CustomerID = customers.CustomerID" &_
" WHERE customers.EmailAddress = orders.EmailAddress" &_
" AND (orders.CustomerID = '' OR orders.CustomerID IS NULL)"
DebugLog -1, LSQL ' (SS,11/11/13)
ExecuteQuery LSQL
' find customer ID for matching address
LSQL = "UPDATE orders, customers SET orders.CustomerID = customers.CustomerID" &_
" WHERE customers.FirstName = orders.FirstName" &_
" AND customers.Surname = orders.Surname" &_
" AND customers.CompanyName = orders.CompanyName" &_
" AND customers.AddressLine1 = orders.AddressLine1" &_
" AND customers.AddressLine2 = orders.AddressLine2" &_
" AND customers.Town = orders.Town" &_
" AND customers.County = orders.County" &_
" AND customers.Postcode = orders.Postcode" &_
" AND customers.Country = orders.Country" &_
" AND (orders.CustomerID = '' OR orders.CustomerID IS NULL)"
DebugLog -1, LSQL ' (SS,11/11/13)
ExecuteQuery LSQL
' select the latest order for those that don't have a customer record, if more then one for the email address then it select the latest order no
' insert the new record into customers
' (SS,5/3/13) modified to only had records for successful orders, i.e. not CANCELLED or ORDER PLACED
LSQL = "INSERT INTO customers" &_
" (EmailAddress, Title, FirstName, Surname, CompanyName, AddressLine1, AddressLine2, Town, County, Postcode, Country," &_
" Telephone, Mobile, VATNumber," &_
" DeliveryAddressSameAsInvoice, DeliveryName, DeliveryCompanyName, DeliveryAddressLine1, DeliveryAddressLine2, DeliveryTown, DeliveryCounty, DeliveryPostcode, DeliveryCountry, Subscribe, HearAboutUs)" &_
" SELECT " &_
" o1.EmailAddress, Title, FirstName, Surname, CompanyName, AddressLine1, AddressLine2, Town, County, Postcode, Country," &_
" Telephone, Mobile, VATNumber," &_
" DeliveryAddressSameAsInvoice, DeliveryName, DeliveryCompanyName, DeliveryAddressLine1, DeliveryAddressLine2, DeliveryTown, DeliveryCounty, DeliveryPostcode, DeliveryCountry, Subscribe, HearAboutUs" &_
" FROM orders o1" &_
" INNER JOIN (SELECT o2.EmailAddress, MAX(o2.OrderNo) AS LatestOrderNo FROM orders o2 WHERE (o2.CustomerID = '' OR o2.CustomerID IS NULL)" &_
" AND o2.Status <> 'CANCELLED' AND o2.Status <> 'ORDER PLACED'" &_
" GROUP BY o2.EmailAddress) latest" &_
" ON latest.LatestOrderNo = o1.OrderNo" &_
" WHERE (CustomerID = '' OR CustomerID IS NULL) AND o1.Status <> 'CANCELLED' AND o1.Status <> 'ORDER PLACED'" &_
" ORDER BY OrderNo"
ExecuteQuery LSQL
DebugLog -1, LSQL ' (SS,11/11/13)
' update the orders with CustomerID from new inserted customer record by running first query again
LSQL = "UPDATE orders, customers SET orders.CustomerID = customers.CustomerID" &_
" WHERE customers.EmailAddress = orders.EmailAddress" &_
" AND (orders.CustomerID = '' OR orders.CustomerID IS NULL)"
ExecuteQuery LSQL
DebugLog -1, LSQL ' (SS,11/11/13)
' (SS,11/11/13) the DateTimeCreated field was null, so set this to the current date
LSQL = "UPDATE customers SET DateTimeCreated = CURDATE() WHERE DateTimeCreated IS NULL"
ExecuteQuery LSQL
DebugLog -1, LSQL ' (SS,11/11/13)
End If
SetSchemaVersion(1.65)
End Sub
' (SS,6/3/13) change CustomerID in orders table from VARCHAR(20) to INT(11), as it is in customers, was VARCHAR for toner2desk
Sub UpgradeSchemaTo166
' convert all the blank strings to null to prevent ALTER TABLE query below from failing with error
ExecuteQuery "UPDATE orders SET CustomerID = NULL WHERE CustomerID = ''"
' change CustomerID to integer
ExecuteQuery "ALTER TABLE orders MODIFY CustomerID int(11) DEFAULT NULL"
SetSchemaVersion(1.66)
End Sub
' (SS,13/12/13) added new Picture flag field to product_option_values table
Sub UpgradeSchemaTo167
ExecuteQuery "ALTER TABLE product_option_values ADD Picture TINYINT(1) DEFAULT 0 NOT NULL"
SetSchemaVersion(1.67)
End Sub
' (SS,14/11/07)
Function GetSchemaVersion
GetSchemaVersion = FSchemaVersion
End Function
' (SS,14/11/07)
Sub SetSchemaVersion(AVersion)
SetSetting "Application", "SchemaVersion", AVersion
End Sub
' (SS,25/11/08) added to change the VAT from 17.5% to 15% on 1/12/2008
' (SS,22/12/09) added VAT change from 15% to 17.5% on 1/1/2010
' (SS,21/12/10) renamed from CheckVAT, can be deleted
Sub CheckVATOld
' if date is >= 1/12/2008 and VAT Rate is 17.5% then change to 15% in settings table and vatbands table
' (SS,22/12/09) added And DateDiff("d", "1/1/2010", Date) < 0 so this isn't called for dates >= 1/12/2008
If DateDiff("d", "1/12/2008", Date) >= 0 And DateDiff("d", "1/1/2010", Date) < 0 And FVATRate = 0.175 Then
FVATRate = 0.15
SetSetting "Application", "VATRate", FVATRate
ExecuteQuery("UPDATE vatbands SET VATRate = 0.15 WHERE VATRate = 0.175")
' (SS,22/12/09) added following for VAT change
' if date is >= 1/1/2010 and VAT Rate is 15% then change to 17.5% in settings table and vatbands table
ElseIf DateDiff("d", "1/1/2010", Date) >= 0 And FVATRate = 0.15 Then
FVATRate = 0.175
SetSetting "Application", "VATRate", FVATRate
ExecuteQuery("UPDATE vatbands SET VATRate = 0.175 WHERE VATRate = 0.15")
End If
End Sub
' (SS,25/11/08) added to change the VAT from 17.5% to 15% on 1/12/2008
' (SS,22/12/09) added VAT change from 15% to 17.5% on 1/1/2010
' (SS,21/12/10) added VAT change from 17.5% to 20% on 4/1/2011, previous code renamed to CheckVATOld see above
Sub CheckVAT
' if date is >= 4/1/2011 and VAT Rate is 17.5% then change to 20% in settings table and vatbands table
If DateDiff("d", "4/1/2011", Date) >= 0 And FVATRate = 0.175 Then
FVATRate = 0.20
SetSetting "Application", "VATRate", FVATRate
ExecuteQuery("UPDATE vatbands SET VATRate = 0.20 WHERE VATRate = 0.175")
End If
End Sub
' (SS,12/4/11) for redirecting pages, useful when product codes, categories or subcategories have been renamed
' and Google is still showing the old values, this will redirect to the required place
' the redirect list is held in the sitedetails table, "Other / RedirectionList" section
' (SS,1/3/13) change to allow semicolon to also be used to separate as well as comma, to allow names to contain a comma
' (SS,12/5/16) modified to redirect to category/subcategory page if product detail page and product is disabled
' (SS,13/6/17) modified to fix redirection issue for subcategories, new GetSubcategoryLinkForRedirect now called instead of GetSubcategoryLink
Sub ProcessRedirections
Dim LPageType, LCode1, LCode2
' determine page type, return immediately if not the appropriate page
If IsProductDetailsPage Then
LPageType = "P"
LCode1 = GetProductCode
LCode2 = ""
ElseIf IsCategoryPage Then
LPageType = "C"
LCode1 = GetCategory
LCode2 = ""
ElseIf IsSubcategoryPage Then
LPageType = "S"
LCode1 = GetCategory
LCode2 = GetSubcategory
Else
Exit Sub
End If
' return if redirection list is empty
Dim LRedirectionList
LRedirectionList = TrimAll(GetSiteDetail("Other", "RedirectionList"))
If LRedirectionList = "" Then Exit Sub
Dim LLines, i, LCols, LFound, LRedirectionType, LRedirectionCode1, LRedirectionCode2, LRedirectionCode3, LRedirectionCode4, LSeparator
LLines = Split(LRedirectionList, NL, -1, 0)
LFound = False
For i = LBound(LLines) to UBound(LLines)
' Response.Write "###" & i & ", " & LLines(i) & "###<br>"
' (SS,1/3/13) added following to allow semicolon to be used as separator as well, useful when date contains a comma, it takes priority over comma
If InStr(LLines(i), ";") > 0 Then
LSeparator = ";"
Else
LSeparator = ","
End If
LCols = Split(LLines(i), LSeparator, -1, 0) ' (SS,1/3/13) replaced "," with LSeparator
If UBound(LCols) > 0 Then ' ignore lines with no content and just one column, e.g. comments allowed with # prefix
LRedirectionType = UCase(Trim(LCols(0)))
If LRedirectionType = LPageType Then
If UBound(LCols) >= 1 Then LRedirectionCode1 = Trim(LCols(1)) Else LRedirectionCode1 = ""
If UBound(LCols) >= 2 Then LRedirectionCode2 = Trim(LCols(2)) Else LRedirectionCode2 = ""
If UBound(LCols) >= 3 Then LRedirectionCode3 = Trim(LCols(3)) Else LRedirectionCode3 = ""
If UBound(LCols) >= 4 Then LRedirectionCode4 = Trim(LCols(4)) Else LRedirectionCode4 = ""
If LPageType = "S" Then
If LCode1 = LRedirectionCode1 And LCode2 = LRedirectionCode2 And LRedirectionCode3 <> "" And LRedirectionCode4 <> "" Then
LFound = True
Exit For
End If
Else
If LCode1 = LRedirectionCode1 And LRedirectionCode2 <> "" Then
LFound = True
Exit For
End If
End If
End If
End If
Next
Dim LURL
LURL = "" ' (SS,12/5/16)
' if match found then do the redirection
If LFound Then
If LPageType = "P" Then
LURL = GetProductLink(LRedirectionCode2)
ElseIf LPageType = "C" Then
LURL = GetCategoryLink(LRedirectionCode2)
Else ' "S"
' (SS,13/6/17) replaced GetSubcategoryLink with GetSubcategoryLinkForRedirect because & doesn't work correctly
LURL = GetSubcategoryLinkForRedirect(LRedirectionCode3, LRedirectionCode4)
End If
' (SS,12/5/16) if product page and product exists and is disabled then redirect to category / subcategory page
ElseIf LPageType = "P" And LCode1 <> "" Then
If Not CheckEnabledProductExists(LCode1) Then
Dim LGroup, LCategory, LSubcategory
GetFirstCatSubcatForProduct LCode1, LGroup, LCategory, LSubcategory
If LCategory <> "" Then
' (SS,13/6/17) replaced GetSubcategoryLink with GetSubcategoryLinkForRedirect because & doesn't work correctly
LURL = GetSubcategoryLinkForRedirect(LCategory, LSubcategory)
End If
End If
End If
If LURL <> "" Then
' prefix with store URL which includes http:// if it hasn't already been added by GetCategoryLink etc.
If Left(LURL, 7) <> "http://" Then LURL = GetStoreURL + "/" + LURL
' do the permanent redirection
' (SS,1/7/11) redirection code moved to new routine DoRedirect
DoRedirect LURL, True
End If
End Sub
' (SS,16/5/11) makes use of the redirection list to lookup replacement product code for given product
Function GetReplacementProductCode(AProductCode)
Dim LResult, LRedirectionList
LResult = AProductCode ' assume no match, i.e. return same code
' return straightaway if redirection list is empty, else find a match
LRedirectionList = TrimAll(GetSiteDetail("Other", "RedirectionList"))
If LRedirectionList <> "" Then
Dim LLines, i, LCols, LFound
LLines = Split(LRedirectionList, NL, -1, 0)
LFound = False
For i = LBound(LLines) to UBound(LLines)
LCols = Split(LLines(i), ",", -1, 0)
If UBound(LCols) = 2 Then ' ignore lines with no content and just one column, e.g. comments allowed with # prefix, i.e. only look at lines with 3 values
If UCase(Trim(LCols(0))) = "P" And Trim(LCols(1)) = AProductCode Then
If Trim(LCols(2)) <> "" Then LResult = Trim(LCols(2))
LFound = True
Exit For
End If
End If
Next
End If
GetReplacementProductCode = LResult
End Function
' (SS,10/6/07) *** followings settings to be held in a common database
Function GetMailServer(AServerNo)
If AServerNo = 1 Then
GetMailServer = "mail.itpartnership.com"
ElseIf AServerNo = 2 Then
GetMailServer = "mail.ontheworldweb.com"
Else
GetMailServer = ""
End If
End Function
' (SS,6/6/07) returns name of current script file, in most cases this will be products.asp
' but could also be worldpay-callback.asp etc
Function GetScriptName
Dim LScriptName
LScriptName = LCase(Request.ServerVariables("SCRIPT_NAME"))
If Left(LScriptName, 1) = "/" Then LScriptName = Mid(LScriptName, 2, Len(LScriptName) - 1)
GetScriptName = LScriptName
End Function
' (SS,18/11/13) returns true if script name is products.asp
Function ScriptIsProducts
ScriptIsProducts = GetScriptName = "products.asp"
End Function
' (SS,19/2/13)
Function GetURLForPage(APageName)
GetURLForPage = "products.asp?page=" & APageName
End Function
' (SS,14/6/07)
Function GetDevelopmentPassword
GetDevelopmentPassword = FDevelopmentPassword
End Function
' (SS,21/5/07)
Function FourPricingFieldsEnabled
FourPricingFieldsEnabled = FFourPricingFieldsEnabled
End Function
' (SS,21/5/07)
Function PricesEnteredIncludeVAT
PricesEnteredIncludeVAT = FPricesEnteredIncludeVAT
End Function
' (SS,21/5/07)
Function UseDiscountFieldForSpecialOffers
UseDiscountFieldForSpecialOffers = FUseDiscountFieldForSpecialOffers
End Function
' (SS,21/5/07)
Function VATBandsEnabled
VATBandsEnabled = FVATBandsEnabled
End Function
' (SS,26/5/07)
Function CustomPricingEnabled
CustomPricingEnabled = FCustomPricingEnabled
End Function
' (SS,13/9/07)
Function GetDeliveryType
If IsNull(FDeliveryType) Or FDeliveryType = "" Then
GetDeliveryType = DT_ORIGINAL
Else
GetDeliveryType = FDeliveryType
End If
End Function
' (SS,26/5/07)
' (SS,13/9/07) replaced FCustomDeliveryEnabled with FDeliveryType = DT_CUSTOM
Function CustomDeliveryEnabled
CustomDeliveryEnabled = FDeliveryType = DT_CUSTOM
End Function
' (SS,27/5/07)
Function UseVATFlagFromCountries
UseVATFlagFromCountries = FUseVATFlagFromCountries
End Function
' (SS,22/6/07)
Function GetWeightUnits
GetWeightUnits = FWeightUnits
End Function
' (SS,22/6/07)
Function GetShowWeightInEmail
GetShowWeightInEmail = FShowWeightInEmail
End Function
' (SS,30/5/07)
Function LargeThumbnailWidth
LargeThumbnailWidth = FLargeThumbnailWidth
End Function
' (SS,30/5/07)
Function LargeThumbnailHeight
LargeThumbnailHeight = FLargeThumbnailHeight
End Function
' (SS,19/3/09)
Function SmallThumbnailWidth
SmallThumbnailWidth = FSmallThumbnailWidth
End Function
' (SS,19/3/09)
Function SmallThumbnailHeight
SmallThumbnailHeight = FSmallThumbnailHeight
End Function
' (SS,30/5/07)
Function HearAboutUsList
HearAboutUsList = FHearAboutUsList
End Function
' (SS,5/6/07)
Function GetDefaultCountry
GetDefaultCountry = FDefaultCountry
End Function
' (SS,14/6/07)
Function GetAccountID
GetAccountID = Session("AccountID")
End Function
' (SS,20/9/12) alias for GetAccountID
Function GetCustomerID
GetCustomerID = GetAccountID
End Function
' (SS,5/6/07)
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
Function PaymentOnAccountEnabled
PaymentOnAccountEnabled = False
If AccountLoggedIn And FieldExistsInTable("customers", "PaymentOnAccount") Then
PaymentOnAccountEnabled = IntToBool(GetValueFromQuery("PaymentOnAccount", "SELECT PaymentOnAccount FROM customers WHERE CustomerID = '" & CleanSQLStr(Session("AccountID")) & "'"))
End If
End Function
' (SS,14/6/07)
Function GetPaymentType
GetPaymentType = FPaymentType
End Function
' (SS,16/6/09) same as GetPaymentType
Function GetPaymentType1
GetPaymentType1 = FPaymentType
End Function
' (SS,28/9/07)
Function GetPaymentType2
GetPaymentType2 = FPaymentType2
End Function
' (SS,4/6/09)
Function GetPaymentType3
GetPaymentType3 = FPaymentType3
End Function
' (SS,21/5/10)
Function GetPaymentHideCurrency
GetPaymentHideCurrency = FPaymentHideCurrency
End Function
' (SS,16/6/09) returns number of different payment type including cheque and bank transfer
Function PaymentTypeCount
Dim LCount
LCount = 0
If GetPaymentType1 <> "" Then LCount = LCount + 1
If GetPaymentType2 <> "" Then LCount = LCount + 1
If GetPaymentType3 <> "" Then LCount = LCount + 1
If AllowCheques Then LCount = LCount + 1
If AllowBankTransfers Then LCount = LCount + 1
PaymentTypeCount = LCount
End Function
' (SS,20/11/08) returns chosen payment type from hidden frmCheckout field
Function GetChosenPaymentType
GetChosenPaymentType = CleanRequestForm("paymenttype")
End Function
' (SS,29/5/09)
Function IsChosenPaymentCheque
IsChosenPaymentCheque = GetChosenPaymentType = ptCheque
End Function
' (SS,29/5/09)
' (SS,4/8/17) modified to return true if starts if "BANK TRANSFER" i.e. can be "BANK TRANSFER - EUR", "BANK TRANSFER - USD" etc.
Function IsChosenPaymentBankTransfer
' IsChosenPaymentBankTransfer = GetChosenPaymentType = ptBankTransfer
' (SS,4/8/17) replaced above with following
IsChosenPaymentBankTransfer = IsPaymentBankTransfer(GetChosenPaymentType)
End Function
' (SS,4/8/17) added following due to new ptBankTransferEUR and ptBankTransferUSD
Function IsPaymentBankTransfer(APaymentMethod)
IsPaymentBankTransfer = APaymentMethod = ptBankTransfer Or APaymentMethod = ptBankTransferEUR Or APaymentMethod = ptBankTransferUSD Or APaymentMethod = ptBankTransferGBP ' (SS,29/9/17) added ptBankTransferGBP
End Function
' (SS,10/12/14)
Function IsChosenPaymentPayPalExpress
IsChosenPaymentPayPalExpress = GetChosenPaymentType = ptPayPalExpress
End Function
' (SS,16/12/14)
Function ReturnFromPayPalExpressLaterStage
ReturnFromPayPalExpressLaterStage = CleanRequestQueryString("xcmd") = "paypallaterstage"
End Function
' (SS,21/3/13)
Function ReturnFromPaymentProvider(APaymentType)
ReturnFromPaymentProvider = (CleanRequest("stage") = RETURN_FROM_THIRD_PARTY_PAYMENT) And (CleanRequest("pt") = APaymentType)
End Function
' (SS,14/6/07)
Function GetWorldPayInstallID
GetWorldPayInstallID = FWorldPayInstallID
End Function
' (SS,14/6/07)
Function GetWorldPayTestMode
GetWorldPayTestMode = FWorldPayTestMode
End Function
' (SS,14/11/12)
Function GetWorldPayMD5SecretKey
GetWorldPayMD5SecretKey = FWorldPayMD5SecretKey
End Function
' (SS,14/6/07)
Function GetPayPalBusiness
GetPayPalBusiness = FPayPalBusiness
End Function
' (SS,14/6/07)
Function GetPayPalTestMode
GetPayPalTestMode = FPayPalTestMode
End Function
' (SS,22/6/07)
Function GetPayPalShippingAddressFlag
GetPayPalShippingAddressFlag = FPayPalShippingAddressFlag
End Function
' (SS,15/12/14)
' (SS,26/1/15) now alias to GetPayPalExpressEnabled
Function UsePayPalExpress
UsePayPalExpress = GetPayPalExpressEnabled
End Function
' (SS,26/1/15)
Function GetPayPalExpressEnabled
GetPayPalExpressEnabled = FPayPalExpressEnabled
End Function
' (SS,26/1/15)
Function GetPayPalAPIUserName
GetPayPalAPIUserName = FPayPalAPIUserName
End Function
' (SS,26/1/15)
Function GetPayPalAPIPassword
GetPayPalAPIPassword = FPayPalAPIPassword
End Function
' (SS,26/1/15)
Function GetPayPalAPISignature
GetPayPalAPISignature = FPayPalAPISignature
End Function
' (SS,9/4/18)
Function GetPayPalUKOnlyCheckout
GetPayPalUKOnlyCheckout = FPayPalUKOnlyCheckout
End Function
' (SS,23/4/18)
Function GetPayPalExpressShowOnBasketPage
GetPayPalExpressShowOnBasketPage = FPayPalExpressShowOnBasketPage
End Function
' (SS,8/6/18)
Function GetPayPalSurchargePercentage
If IsPayPalSurcharge Then
GetPayPalSurchargePercentage = FPayPalNonUKSurchargePercentage
Else
GetPayPalSurchargePercentage = 0
End If
End Function
' (SS,8/6/18)
Function IsPayPalSurcharge
IsPayPalSurcharge = FPayPalNonUKSurchargePercentage > 0 And Not IsUK(GetInvoiceCountry)
End Function
' (SS,8/6/18) apply surcharge if applicable
Function PayPalAmountWithSurcharge(AAmount)
Dim LNewAmount
'SendWebLogEmail "Original Amount: " & AAmount
If IsPayPalSurcharge Then
LNewAmount = Round2dp(AAmount * (1 + (GetPayPalSurchargePercentage / 100)))
Else
LNewAmount = AAmount
End If
'SendWebLogEmail "Amount with Surcharge: " & LNewAmount
PayPalAmountWithSurcharge = LNewAmount
End Function
' (SS,14/6/07)
Function GetEPDQClientID
GetEPDQClientID = FEPDQClientID
End Function
' (SS,14/6/07)
Function GetEPDQPassphrase
GetEPDQPassphrase = FEPDQPassphrase
End Function
' (SS,14/6/07)
Function GetEPDQChargeType
GetEPDQChargeType = FEPDQChargeType
End Function
' (SS,10/5/12)
Function GetEPDQMerchantDisplayName
If FEPDQMerchantDisplayName = "" Then
GetEPDQMerchantDisplayName = GetStoreName
Else
GetEPDQMerchantDisplayName = FEPDQMerchantDisplayName
End If
End Function
' (SS,2/10/14)
Function GetEPDQPARAMVAR
GetEPDQPARAMVAR = FEPDQPARAMVAR
End Function
' (SS,26/4/18)
Function GetEPDQTestMode
GetEPDQTestMode = FEPDQTestMode
End Function
' (SS,20/3/13)
Function GetSagePayVendorName
GetSagePayVendorName = FSagePayVendorName
End Function
' (SS,20/3/13)
Function GetSagePayMode
GetSagePayMode = FSagePayMode
End Function
' (SS,20/3/13)
Function GetSagePayEncryptionKey
GetSagePayEncryptionKey = FSagePayEncryptionKey
End Function
' (SS,22/3/13) activate payment provider test mode if pptest = true in URL
Sub CheckPaymentProviderTestMode
If LCase(CleanRequest("pptest")) = "true" Then
Session("PaymentProviderTestModeActivated") = True
End If
End Sub
' (SS,22/3/13)
Function IsPaymentProviderTestModeActivated
IsPaymentProviderTestModeActivated = Session("PaymentProviderTestModeActivated")
End Function
' (SS,22/3/13)
Function IsSagePayTestMode
IsSagePayTestMode = GetSagePayMode <> "LIVE"
End Function
' (SS,28/5/09)
' (SS,17/2/12) added And IsCurrencyGBP
Function AllowCheques
AllowCheques = FAllowCheques And IsCurrencyGBP
End Function
' (SS,29/5/09)
Function AllowBankTransfers
AllowBankTransfers = FAllowBankTransfers
End Function
' (SS,29/5/09)
Function GetChequesPayableTo
GetChequesPayableTo = FChequesPayableTo
End Function
' (SS,29/5/09)
' (SS,20/2/12) now handles details for different countries if set i.e. EUR and USD
' (SS,19/12/14) modified to make use of the new DefaultPaymentCurrencyCode field in the orders table, now assumes orders database accessible with GetFieldValue
' (SS,9/8/17) new version which calls new GetBankTransferDetailsForCurrency below (allows call for testing via bank-details.asp (in local mode)
Function GetBankTransferDetails
Dim LCurrencyCode
' (SS,19/12/14) added following to make use of DefaultPaymentCurrencyCode
LCurrencyCode = GetCurrencyCode
If IsMulticurrency And IsMulticurrencyMethodLocalTable And GetCurrencyCode <> GetFieldValue("DefaultPaymentCurrencyCode") Then
LCurrencyCode = GetFieldValue("DefaultPaymentCurrencyCode")
End If
GetBankTransferDetails = GetBankTransferDetailsForCurrency(LCurrencyCode, GetFieldValue("Country")) ' (SS,29/9/17) added GetFieldValue("Country")
End Function
' (SS,9/8/17) new separated version which allows call for testing via bank-details.asp (in local mode)
' (SS,29/9/17) added ABillingCountry because we have two different GBP accounts
Function GetBankTransferDetailsForCurrency(ACurrencyCode, ABillingCountry)
Dim LResult
LResult = ""
If ACurrencyCode = "EUR" Then
LResult = FBankTransferDetailsEUR
ElseIf ACurrencyCode = "USD" Then
LResult = FBankTransferDetailsUSD
End If
' (SS,29/9/17) if GBP and UK
If ACurrencyCode = "GBP" And IsUK(ABillingCountry) Then
LResult = FBankTransferDetailsGBP
End If
If LResult = "" Then ' i.e. not set above e.g. currency other than EUR, USD or GBP, or GBP and not UK
LResult = FBankTransferDetails
End If
GetBankTransferDetailsForCurrency = LResult
End Function
' (SS,29/5/09)
' (SS,20/2/12) replaced FBankTransferDetails with LBankTransferDetails which calls GetBankTransferDetails
' (SS,9/8/17) modified to call GetTextAsHTMLTable to convert to HTML table
Function GetBankTransferDetailsAsHTML
Dim LBankTransferDetails
LBankTransferDetails = GetBankTransferDetails
' if it already contains a <br> or <td> then assume it's already in HTML format and don't a convert newlines to BRs
If InStr(1, LBankTransferDetails, "<br>", vbTextCompare) > 0 Or InStr(1, LBankTransferDetails, "<td>", vbTextCompare) > 0 Then
GetBankTransferDetailsAsHTML = LBankTransferDetails
Else
'GetBankTransferDetailsAsHTML = ReplaceNewLinesWithBR(LBankTransferDetails)
' (SS,9/8/17) replaced above with following to convert text to HTML table
GetBankTransferDetailsAsHTML = BankTransferDetailsToHTML(LBankTransferDetails)
End If
End Function
' (SS,10/8/17) returns given bank transfer details converted to HTML (i.e. table added)
Function BankTransferDetailsToHTML(ABankTransferDetails)
BankTransferDetailsToHTML = GetTextAsHTMLTable(ABankTransferDetails, ":", True, True, "right")
End Function
' (SS,9/8/17) adds HTML to given text, NL is used to separate lines, AColumnSeparator to separate columns, if AKeepSeparator is True then the separate is kept at the end of the column
' e.g. GetTextAsHTMLTable(ABankTransferText, ":", True, True) for
' Account name: HyperFlight Ltd
' Sort Code: 40-27-06
' Account number: 52202921
' AFirstColAlign can be "" for no change in align, "right" or "left" or "center" i.e. anything applicable to text-align in css
' NL deliberately not added to the HTML due to possibility of conversion of them to BRs in subsequent code (e.g. order emails) and result in unnecessary spacing
Function GetTextAsHTMLTable(AText, AColumnSeparator, AKeepSeparator, AFirstColIsHeading, AFirstColAlign)
' split text into lines
' split line into columns
' add the table HTML
Dim LResult, LText
LText = Trim(AText)
LResult = ""
If LText <> "" Then
LResult = LResult & "<table>"
Dim i, LLines, LLine, c, LColumns, LColumn, LFirstCol, LLastCol, LColTag
LLines = Split(AText, NL, -1, 0)
For i = LBound(LLines) to UBound(LLines)
LResult = LResult & "<tr>"
LLine = Trim(LLines(i))
LColumns = Split(LLine, AColumnSeparator, -1, 0)
LFirstCol = LBound(LColumns)
LLastCol = UBound(LColumns)
For c = LFirstCol to LLastCol
If c = LFirstCol And AFirstColIsHeading Then
LColTag = "th"
Else
LColTag = "td"
End If
LColumn = Trim(LColumns(c))
LResult = LResult & "<" & LColTag
If c = LFirstCol And AFirstColIsHeading And AFirstColAlign <> "" Then LResult = LResult & " style=""text-align:" & AFirstColAlign & """"
LResult = LResult & ">" & LColumn
If c <> LLastCol And AKeepSeparator Then LResult = LResult & AColumnSeparator & " " ' add non breaking space to ensure gap
LResult = LResult & "</" & LColTag & ">"
Next
LResult = LResult & "</tr>"
Next
LResult = LResult & "</table>"
End If
GetTextAsHTMLTable = LResult
End Function
' (SS,18/6/09)
Function GetWorldPaySiteSeal
GetWorldPaySiteSeal = FWorldPaySiteSeal
End Function
' (SS,18/6/09)
Function GetPayPalSiteSeal
GetPayPalSiteSeal = FPayPalSiteSeal
End Function
' (SS,8/6/07)
Function HTMLEmailsEnabled
HTMLEmailsEnabled = FHTMLEmailsEnabled
End Function
' (SS,17/4/09)
Function EmailSecurityCodeEnabled
EmailSecurityCodeEnabled = FEmailSecurityCodeEnabled
End Function
' (SS,14/6/10)
Function EmailConfirmationIsVATReceipt
EmailConfirmationIsVATReceipt = FEmailConfirmationIsVATReceipt
End Function
' (SS,23/10/15)
Function EmailUseMessageToCustomerForDespatch
EmailUseMessageToCustomerForDespatch = FEmailUseMessageToCustomerForDespatch
End Function
' (SS,17/4/09)
Function ChooseDeliveryDate
ChooseDeliveryDate = FChooseDeliveryDate
End Function
' (SS,17/4/09)
Function MaxDeliveryDays
MaxDeliveryDays = FMaxDeliveryDays
End Function
' (SS,17/4/09)
Function MobileEnabled
MobileEnabled = FMobileEnabled
End Function
' (SS,26/1/16)
Function CheckoutConfirmTermAndConditions
CheckoutConfirmTermAndConditions = FCheckoutConfirmTermAndConditions
End Function
' (SS,11/6/07)
Function DeliveryUKLimit
DeliveryUKLimit = FDeliveryUKLimit
End Function
Function DeliveryUKBelow
DeliveryUKBelow = FDeliveryUKBelow
End Function
Function DeliveryUKAbove
DeliveryUKAbove = FDeliveryUKAbove
End Function
Function DeliveryUKText
DeliveryUKText = FDeliveryUKText
End Function
Function DeliveryEULimit
DeliveryEULimit = FDeliveryEULimit
End Function
Function DeliveryEUBelow
DeliveryEUBelow = FDeliveryEUBelow
End Function
Function DeliveryEUAbove
DeliveryEUAbove = FDeliveryEUAbove
End Function
Function DeliveryEUText
DeliveryEUText = FDeliveryEUText
End Function
Function DeliveryRWLimit
DeliveryRWLimit = FDeliveryRWLimit
End Function
Function DeliveryRWBelow
DeliveryRWBelow = FDeliveryRWBelow
End Function
Function DeliveryRWAbove
DeliveryRWAbove = FDeliveryRWAbove
End Function
Function DeliveryRWText
DeliveryRWText = FDeliveryRWText
End Function
' (SS,8/9/06)
Function GetStoreName
GetStoreName = FStoreName
End Function
' (SS,20/4/18) returns True for HyperFlight only, to allow HF specific options
Function IsHyperFlight
IsHyperFlight = GetStoreName = "HyperFlight"
End Function
' (SS,13/6/07)
Function GetStoreCompany
GetStoreCompany = FStoreCompany
End Function
Function GetStoreAddress1
GetStoreAddress1 = FStoreAddress1
End Function
Function GetStoreAddress2
GetStoreAddress2 = FStoreAddress2
End Function
Function GetStoreAddress3
GetStoreAddress3 = FStoreAddress3
End Function
Function GetStoreTown
GetStoreTown = FStoreTown
End Function
Function GetStoreCounty
GetStoreCounty = FStoreCounty
End Function
Function GetStorePostcode
GetStorePostcode = FStorePostcode
End Function
Function GetStoreCountry
GetStoreCountry = FStoreCountry
End Function
Function GetStoreTelephone
GetStoreTelephone = FStoreTelephone
End Function
Function GetStoreFax
GetStoreFax = FStoreFax
End Function
Function GetStoreEmail
GetStoreEmail = FStoreEmail
End Function
Function GetStoreVATRegNo
GetStoreVATRegNo = FStoreVATRegNo
End Function
Function GetStoreRegards
GetStoreRegards = FStoreRegards
End Function
Function GetStoreURL
GetStoreURL = FStoreURL
End Function
' (SS,1/5/09)
Function GetStorePlaceOfRegistration
GetStorePlaceOfRegistration = FStorePlaceOfRegistration
End Function
' (SS,1/5/09)
Function GetStoreRegisteredCompanyNo
GetStoreRegisteredCompanyNo = FStoreRegisteredCompanyNo
End Function
Function GetStoreURLNoHttp
Dim LURL
LURL = Trim(FStoreURL)
If Left(LCase(LURL), 7) = "http://" Then LURL = Mid(LURL, 8)
GetStoreURLNoHttp = LURL
End Function
' (SS,8/3/13) added SpacesToNBS to convert spaces to non-breaking spaces, if on one line
Function GetStoreAddress(AOneLine, ACompanyNameInBold)
Dim LResult, LSeparator
If AOneLine Then
LSeparator = ", "
Else
LSeparator = NL
End If
LResult = IIf(ACompanyNameInBold, "<b>", "") + GetStoreCompany + IIf(ACompanyNameInBold, "</b>", "")
LResult = LResult + IIf(GetStoreAddress1 = "", "", LSeparator + IIf(AOneLine, SpacesToNBS(GetStoreAddress1), GetStoreAddress1))
LResult = LResult + IIf(GetStoreAddress2 = "", "", LSeparator + IIf(AOneLine, SpacesToNBS(GetStoreAddress2), GetStoreAddress2))
LResult = LResult + IIf(GetStoreAddress3 = "", "", LSeparator + IIf(AOneLine, SpacesToNBS(GetStoreAddress3), GetStoreAddress3))
LResult = LResult + IIf(GetStoreTown = "", "", LSeparator + IIf(AOneLine, SpacesToNBS(GetStoreTown), GetStoreTown))
LResult = LResult + IIf(GetStoreCounty = "", "", LSeparator + IIf(AOneLine, SpacesToNBS(GetStoreCounty), GetStoreCounty))
LResult = LResult + IIf(GetStorePostcode = "", "", LSeparator + IIf(AOneLine, SpacesToNBS(GetStorePostcode), GetStorePostcode))
LResult = LResult + IIf(GetStoreCountry = "", "", LSeparator + IIf(AOneLine, SpacesToNBS(GetStoreCountry), GetStoreCountry))
If Not AOneLine Then LResult = LResult + LSeparator
GetStoreAddress = LResult
End Function
Function GetStoreAddressAsHTML()
GetStoreAddressAsHTML = ReplaceNewLinesWithBR(GetStoreAddress(False, False))
End Function
Function GetBestSellersMax
GetBestSellersMax = FBestSellersMax
End Function
Function GetBestSellersPastDays
GetBestSellersPastDays = FBestSellersPastDays
End Function
Function GetBestSellersSortOrder
GetBestSellersSortOrder = FBestSellersSortOrder
End Function
Function GetProductFilesEnabled
GetProductFilesEnabled = FProductFilesEnabled
End Function
Function GetProductMoreImagesEnabled
GetProductMoreImagesEnabled = FMaxPicturesPerProduct > 1
End Function
' (SS,22/11/12)
Function GetMaxPicturesPerProduct
GetMaxPicturesPerProduct = FMaxPicturesPerProduct
End Function
Function GetProductsAcrossMax
GetProductsAcrossMax = FProductsAcrossMax
End Function
Function GetProductsDownMax
GetProductsDownMax = FProductsDownMax
End Function
' (SS,12/5/11)
Function GetProductsPerPageFixed
GetProductsPerPageFixed = FProductsPerPageFixed
End Function
Function GetProductsMaxSpecialOffer
GetProductsMaxSpecialOffer = FProductsMaxSpecialOffer
End Function
' (SS,16/5/11)
Function GetMaxRecentlyViewed
GetMaxRecentlyViewed = FMaxRecentlyViewed
End Function
' (SS,17/6/11) returns true if using tabs on product page
Function ProductPageTabs
ProductPageTabs = FProductPageTabs
End Function
' (SS,1/7/11)
Function ProductAttributesEnabled
ProductAttributesEnabled = FProductAttributesEnabled
End Function
' (SS,1/7/11)
Function ProductOptionsEnabled
ProductOptionsEnabled = FProductOptionsEnabled
End Function
' (SS,18/10/18)
Function ProductSubproductsEnabled
ProductSubproductsEnabled = FProductSubproductsEnabled
End Function
' (SS,16/1/13)
Function ProductSearchWithoutDescFirst
ProductSearchWithoutDescFirst = FProductSearchWithoutDescFirst
End Function
' (SS,1/6/15)
Function ProductSearchFullText
ProductSearchFullText = FProductSearchFullText
End Function
' (SS,10/6/14)
Function ProductsShowReviewsImmediately
ProductsShowReviewsImmediately = FProductsShowReviewsImmediately
End Function
' (SS,26/8/15)
Function ProductsSortByDefault
ProductsSortByDefault = FProductsSortByDefault
End Function
' (SS,19/7/10) removed
'Function AllowProductOrderIfNoStock
' AllowProductOrderIfNoStock = FAllowProductOrderIfNoStock
'End Function
Function GetOutOfStockMessage
GetOutOfStockMessage = FOutOfStockMessage
End Function
Function ShowIncVAT
ShowIncVAT = FShowIncVAT
End Function
Function ShowExcVAT
ShowExcVAT = FShowExcVAT
End Function
' (SS,7/11/12)
Function MinCurrencyPricesEnabled
MinCurrencyPricesEnabled = FMinCurrencyPricesEnabled
End Function
' (SS,27/3/07) originally added on this date, moved to settings table on 14/6/07
Function SummaryListEnabled
SummaryListEnabled = FSummaryListEnabled
End Function
Function GroupsEnabled
GroupsEnabled = FGroupsEnabled
End Function
Function GetExtraSearchComboCategory
GetExtraSearchComboCategory = FExtraSearchComboCategory
End Function
Function GetExtraSearchComboBlankEntry
GetExtraSearchComboBlankEntry = FExtraSearchComboBlankEntry
End Function
Function ShowProductsOnFullListCat
ShowProductsOnFullListCat = FShowProductsOnFullListCat
End Function
Function GetCategoryForSubcategoryMenu
GetCategoryForSubcategoryMenu = FCategoryForSubcategoryMenu
End Function
Function AddSpecialOffersToSubcategoryMenu
AddSpecialOffersToSubcategoryMenu = FAddSpecialOffersToSubcategoryMenu
End Function
' (SS,27/3/07) originally added on this date, moved to settings table on 14/6/07
Function CategoryListEnabled
CategoryListEnabled = FCategoryListEnabled
End Function
' (SS,27/3/07) originally added on this date, moved to settings table on 14/6/07
Function SubcategoryListEnabled
SubcategoryListEnabled = FSubcategoryListEnabled ' (SS,13/9/07) corrected from FCategoryListEnabled
End Function
' (SS,13/9/07)
Function UseAlternateColorForCategories
UseAlternateColorForCategories = FUseAlternateColorForCategories
End Function
' (SS,28/7/11)
Function GetDefaultGroup
GetDefaultGroup = FDefaultGroup
End Function
' (SS,1/8/11)
Function AlwaysDefaultGroupForMenu
AlwaysDefaultGroupForMenu = FAlwaysDefaultGroupForMenu
End Function
' (SS,2/8/11)
Function IncludeGroupInHTMLTitleTag
IncludeGroupInHTMLTitleTag = FIncludeGroupInHTMLTitleTag
End Function
' (SS,12/10/15)
Function ShowCategoryHeadingFooterIfEmpty
ShowCategoryHeadingFooterIfEmpty = FShowCategoryHeadingFooterIfEmpty
End Function
' (SS,13/9/07)
Function GetDefaultTitle
GetDefaultTitle = FDefaultTitle
End Function
' (SS,14/11/07)
Function ShowRelatedProductsInGroup
ShowRelatedProductsInGroup = FShowRelatedProductsInGroup
End Function
' (SS,18/5/12)
Function RelatedProductsOutOfStockEnd
RelatedProductsOutOfStockEnd = FRelatedProductsOutOfStock = "End"
End Function
' (SS,18/5/12)
Function RelatedProductsOutOfStockHide
RelatedProductsOutOfStockHide = FRelatedProductsOutOfStock = "Hide"
End Function
' (SS,6/5/09)
Function VouchersEnabled
VouchersEnabled = FVouchersEnabled
End Function
' (SS,6/5/09)
Function ReviewsEnabled
ReviewsEnabled = FProductReviewsEnabled
End Function
' (SS,18/5/09)
Function BackInStockNotificationsEnabled
BackInStockNotificationsEnabled = FBackInStockNotificationsEnabled
End Function
' (SS,20/9/12)
Function AutoLoginEnabled
AutoLoginEnabled = FAutoLoginEnabled
End Function
' (SS,15/1/13)
Function ReviewRequestsEnabled
ReviewRequestsEnabled = FReviewRequestsEnabled
End Function
' (SS,15/1/13)
Function ReviewRequestsDays
ReviewRequestsDays = FReviewRequestsDays
End Function
' (SS,14/1/20)
Function ReviewRequestsDaysNonUK
ReviewRequestsDaysNonUK = FReviewRequestsDaysNonUK
End Function
' (SS,17/1/13)
Function CustomPagesEnabled
CustomPagesEnabled = FCustomPagesEnabled
End Function
' (SS,10/6/14)
Function GeoIPEnabled
GeoIPEnabled = FGeoIPEnabled
End Function
' (SS,25/2/15)
Function SaveBasketEnabled
SaveBasketEnabled = FSaveBasketEnabled
End Function
' (SS,14/11/07)
' (SS,18/12/14) modified to only return true if delivery country is the default country i.e. UK
Function AllowCollection
AllowCollection = FAllowCollection And GetDeliveryCountry = FDefaultCountry ' (SS,18/12/14) added And GetDeliveryCountry = FDefaultCountry
End Function
' (SS,16/11/07)
Function ShowStockInfo
ShowStockInfo = FShowStockInfo
End Function
' (SS,25/6/10)
Function ShowStockAmount
ShowStockAmount = FShowStockAmount
End Function
' (SS,5/12/11)
Function StockAmountInfoSpec
StockAmountInfoSpec = FStockAmountInfoSpec
End Function
' (SS,20/7/10)
Function CustomStockInfoEnabled
CustomStockInfoEnabled = FCustomStockInfoEnabled
End Function
' (SS,21/7/10)
Function ShowOutOfStockMessage
ShowOutOfStockMessage = FShowOutOfStockMessage
End Function
' (SS,21/8/12) max length of customer message in checkout, now restricted to 600
Function GetMaxMessageLength
GetMaxMessageLength = 600
End Function
' (SS,30/4/10)
Function GetTranslationLanguage
GetTranslationLanguage = FTranslationLanguage
End Function
' (SS,21/5/10)
Function IsEnglish
IsEnglish = GetTranslationLanguage = "" Or GetTranslationLanguage = "English"
End Function
' (SS,30/4/10)
Function GetWorldPayLanguageCode
' (SS,30/4/10) added language, for more help see http://www.rbsworldpay.com/support/kb/bg/customisingadvanced/custa7605.html
If GetTranslationLanguage = "German" Then
GetWorldPayLanguageCode = "de"
ElseIf GetTranslationLanguage = "French" Then
GetWorldPayLanguageCode = "fr"
Else
GetWorldPayLanguageCode = "en"
End If
End Function
' (SS,22/5/18) returns True for HyperFlight, False for others, whether to tick terms and conditions by default or not
Function GetCheckoutTermsAndConditionsDefault
GetCheckoutTermsAndConditionsDefault = IsHyperFlight
End Function
' (SS,15/8/14) returns True if using Responsive Web Design (e.g. Bootstrap), used to allow different HTML to be sent where applicable and held in apputils.asp
Function IsRWD
IsRWD = FResponsiveWebDesign
End Function
' (SS,30/9/04) '
' (SS,22/3/06) added Left(.., MAX_SIZE) to ensure max field size of 255 isn't exceeded
' add this after noticing following in iislog files for toner2desk:
' [MySQL][ODBC_3.51_Driver][mysqld-5.0.16-nt-log]Data_too_long_for_column_'Referrer'_at_row_1
' (SS,16/2/12) modified to only add if referrer isn't blank, most records were blank many due to search engine bots
' (SS,11/9/15) now redundant, can be removed. referrers table no longer exists.
Sub AddReferrerToDatabase
Const MAX_SIZE = 255
Dim LReferrer, LURL, LQueryString, LUserAgent
LReferrer = Left(Request.ServerVariables("HTTP_REFERER"), MAX_SIZE)
If Trim(LReferrer) <> "" Then ' (SS,16/2/12)
LURL = Left(Request.ServerVariables("URL"), MAX_SIZE)
LQueryString = Left(Request.ServerVariables("QUERY_STRING"), MAX_SIZE)
LUserAgent = Left(Request.ServerVariables("HTTP_USER_AGENT"), MAX_SIZE)
' (SS,12/6/12) added CleanSQLStr before FSessionID to prevent SQL injection
ExecuteQuery("INSERT INTO referrers SET DateTime=NOW(), Date=Now(), SessionID='" & CleanSQLStr(FSessionID) & "', Referrer='" & CleanSQLStr(LReferrer) & "', URL='" & CleanSQLStr(LURL) & "', QueryString='" & CleanSQLStr(LQueryString) & "', UserAgent='" & CleanSQLStr(LUserAgent) & "', IPAddress='" & CleanSQLStr(Request.ServerVariables("REMOTE_ADDR")) & "'")
End If
End Sub
' (SS,16/9/04) called from inc-template-main.asp to get title for web page'
' (SS,11/2/05) added group '
' (SS,15/3/05) added group that was missed from non IsProductDetailsPage '
' (SS,20/9/06) removed repetitives
' (SS,01/7/08) moved ProductName in front of group / category, don't use space in separator in more than so many characters long
' (SS,27/7/11) added HTMLEncode
' (SS,02/2/12) fixed double encoding product name issue
' (SS,16/3/12) modified to get the title tag if available from categories, subcategories, groups, product_attributes and sitedetails tables
' (SS,6/12/19) Google only shows about 60, Bing complains when more than 100 with error in Webmaster, modified for product detail page
' to not add the category & subcategory if the total would result in more than 100 characters
Function GetHTMLTitle(ADefaultTitle)
Const vbTextCompare = 1
Const SEPARATOR = "#-#"
Const SEPARATOR_NORMAL = " - "
Const SEPARATOR_COMPACT = "-"
Const MAX_BEFORE_COMPACT = 999 ' was 66 but make titles look untidy, 999 means don't use the compact separator
Const MAX_RECOMMENDED = 60 ' (SS,6/12/19) used to keep product detail title short
Dim LProductCode, LGroup, LCategory, LSubcategory, LProductName, LResult
LResult = ""
If IsProductDetailsPage Then
LProductCode = GetProductCodeQS
' (SS,16/3/12) try to get title tag from product attribute, if not found then do as before
Dim LProductID
LProductID = GetProductIDForProductCode(LProductCode)
LResult = GetTitleTagForProduct("T", LProductID)
' (SS,16/3/12) following code is as before, i.e. builds the tag from product name, category etc.
If LResult = "" Then
GetFirstCatSubcatForProduct LProductCode, LGroup, LCategory, LSubcategory
' (SS,15/3/05) added Iif(Application("EnableGroups") '
' (SS,26/7/11) replaced GetFieldForProduct with GetProductName which also does a HTMLEncode
' (SS,2/2/12) replaced GetProductName with GetProductNameNoHTMLEncode, & was being encode as "&amp;", i.e. encoded twice, we only want to encode right at the end
LProductName = GetProductNameNoHTMLEncode(LProductCode)
' (SS,20/9/06) if group, category or subcategory exists in product name then don't include it
If Not GroupsEnabled Or InStr(LProductName, LGroup) > 0 Then LGroup = ""
' (SS,2/8/11) added following to exclude group when IncludeGroupInHTMLTitleTag is False, also lookup short names
If GroupsEnabled Then
If Not IncludeGroupInHTMLTitleTag Then LGroup = ""
LSubcategory = GetShortNameForSubcategory(LCategory, LSubcategory) ' (SS,11/1/13) added missing LCategory
LCategory = GetShortNameForCategory(LCategory)
End If
If InStr(1, LProductName, LCategory, vbTextCompare) > 0 Then LCategory = ""
If InStr(1, LProductName, LSubcategory, vbTextCompare) > 0 Then LSubcategory = ""
' (SS,1/7/08) if group matches category, or category matches subcategory then don't repeat it
If LGroup = LCategory Then LCategory = ""
If LCategory = LSubcategory Then LSubcategory = ""
' LResult = Iif(LGroup <> "", LGroup + SEPARATOR, "") + Iif(LCategory <> "", LCategory + SEPARATOR, "") + Iif(LSubcategory <> "", LSubcategory + SEPARATOR, "") + LProductName
' (SS,1/7/08) product name moved to the front, above replaced with following
LResult = LProductName + Iif(LGroup <> "", SEPARATOR + LGroup, "") + Iif(LCategory <> "", SEPARATOR + LCategory, "") + Iif(LSubcategory <> "", SEPARATOR + LSubcategory, "")
' (SS,6/12/19) added following to use only the product name if length more than max (60)
If Len(LResult) >= MAX_RECOMMENDED Then
LResult = LProductName
End If
End If
Else
' (SS,15/3/05) get group name for category '
LCategory = CleanRequestQueryString("cat")
LSubcategory = CleanRequestQueryString("subcat")
' (SS,16/3/12) added following because LCategory, LSubcategory get set to short names in "If GroupsEnabled" code below
Dim LFullCategory, LFullSubcategory
LFullCategory = LCategory
LFullSubcategory = LSubcategory
If GroupsEnabled And LCategory <> "" Then
LGroup = GetGroupForCat(LCategory)
Else
LGroup = CleanRequestQueryString("grp") ' (SS,1/7/08) was ""
End If
' (SS,2/8/11) added following to exclude group when IncludeGroupInHTMLTitleTag is False, also lookup short names
If GroupsEnabled Then
If Not IncludeGroupInHTMLTitleTag Then LGroup = ""
LSubcategory = GetShortNameForSubcategory(LCategory, LSubcategory) ' (SS,11/1/13) added LCategory
LCategory = GetShortNameForCategory(LCategory)
End If
If LCategory <> "" And LSubcategory <> "" Then
' (SS,16/3/12) try to get title tag from subcategories table, if blank then do as before
LResult = GetTitleTagForSubcategory("T", LFullCategory, LFullSubcategory)
' (SS,16/3/12) following code is as before, i.e. builds the tag from product name, category etc.
If LResult = "" Then
' (SS,15/3/05) added Iif(LGroup <> "".. '
' (SS,20/9/06) if group, category exists in subcategory then don't include it
If Not GroupsEnabled Or InStr(LSubcategory, LGroup) > 0 Then LGroup = ""
If InStr(1, LSubcategory, LCategory, vbTextCompare) > 0 Then LCategory = ""
If LGroup = LCategory Then LGroup = "" ' (SS,1/7/08) don't repeat if group same as category
LResult = Iif(LGroup <> "", LGroup + SEPARATOR, "") + Iif(LCategory <> "", LCategory + SEPARATOR, "") & LSubcategory
End If
ElseIf LCategory <> "" Then
' (SS,16/3/12) try to get title tag from categories table, if blank then do as before
LResult = GetTitleTagForCategory("T", LFullCategory)
' (SS,16/3/12) following code is as before, i.e. builds the tag from product name, category etc.
If LResult = "" Then
' (SS,15/3/05) added Iif(LGroup <> "".. '
If LGroup = LCategory Then LGroup = "" ' (SS,1/7/08) don't repeat if group same as category
LResult = LResult & Iif(LGroup <> "", LGroup + SEPARATOR, "") & LCategory
If ADefaultTitle <> "" Then
LResult = LResult + SEPARATOR + ADefaultTitle
End If
End If
' (SS,1/7/08)
ElseIf LGroup <> "" Then
' (SS,16/3/12) try to get title tag from groups table, if blank then do as before
LResult = GetTitleTagForGroup("T", LGroup)
' (SS,16/3/12) following code is as before, i.e. builds the tag from product name, category etc.
If LResult = "" Then
LResult = LGroup
If ADefaultTitle <> "" Then
LResult = LResult + SEPARATOR + ADefaultTitle
End If
End If
Else
' (SS,16/3/12) try to get title tag from site details table, if blank then try the default
LResult = GetTitleTagForPage("T", GetPageName)
If LResult = "" Then LResult = GetTitleTagForPage("T", "Default")
' (SS,16/3/12) following is as before, if still blank then use the one passed to this routine as a parameter
If LResult = "" Then LResult = ADefaultTitle
End If
End If
' (SS,1/7/08) if length is > 66 then don't use spaces around the '-' to make it fit into 66 or less
If Len(LResult) > MAX_BEFORE_COMPACT Then
LResult = ReplaceStr(LResult, SEPARATOR, SEPARATOR_COMPACT)
Else
LResult = ReplaceStr(LResult, SEPARATOR, SEPARATOR_NORMAL)
End If
GetHTMLTitle = HTMLEncode(LResult) ' (SS,27/7/11) added HTMLEncode
End Function
' (SS,16/9/04) called from inc-template-main.asp to get meta tag description for web page '
' (SS,19/9/06) improved
' (SS,27/7/11) added HTMLEncode
' (SS,16/3/12) modified to get the description meta tag if available from categories, subcategories, groups, product_attributes and sitedetails tables
' (SS,21/3/12) slight improvement for product page, to not prefix with product name if description is longer than name
' (SS,16/7/13) change to use ComposeDescription for to convert tokens and suffix with "..." if longer than max length
' (SS,6/12/19) changed MAX_LENGTH from 250 to 160, Also adjusted to replace 3 and 2 spaces with single space.
Function GetHTMLDescription(ADefaultDescription)
Const MAX_LENGTH = 160
Dim LResult
If IsProductDetailsPage Or IsCategoryPage Or IsSubcategoryPage Then
Dim LCategory, LSubcategory
LCategory = CleanRequestQueryString("cat")
LSubcategory = CleanRequestQueryString("subcat")
If IsCategoryPage Then
' (SS,16/3/12) try to get description meta tag from categories table, if blank then do as before
LDescription = GetTitleTagForCategory("D", LCategory)
' (SS,16/3/12) following code after the "if" is as before, i.e. builds the tag from category, subcategory etc.
If LDescription = "" Then LDescription = LCategory & " - " & GetSubcategoryListForCategory(LCategory)
ElseIf IsSubcategoryPage Then
' (SS,16/3/12) try to get description meta tag from subcategories table, if blank then do as before
LDescription = GetTitleTagForSubcategory("D", LCategory, LSubcategory)
' (SS,16/3/12) following code after the "if" is as before, i.e. builds the tag from category and subcategory
If LDescription = "" Then LDescription = LCategory & " - " & LSubcategory
Else ' i.e. IsProductDetailsPage
Dim LProductCode, LDescription, LProductName
LProductCode = GetProductCodeQS
' (SS,16/3/12) try to get description meta tag from product attribute, if not found then do as before
Dim LProductID
LProductID = GetProductIDForProductCode(LProductCode)
LDescription = GetTitleTagForProduct("D", LProductID)
' (SS,16/3/12) following code is as before, i.e. builds the tag from product name and description
If LDescription = "" Then
' (SS,26/7/11) was GetFieldForProduct(LProductCode, "ProductName")
' (SS,21/3/12) replaced GetProductName with GetProductNameNoHTMLEncode to avoid encoding twice
LProductName = GetProductNameNoHTMLEncode(LProductCode)
LDescription = GetFieldForProduct(LProductCode, "ProductDescription")
LDescription = ComposeDescription(LDescription, True, False) ' (SS,16/7/13) added to process tokens to prevent curly brackets appearing in Google search
If LDescription = "" Then
LDescription = ADefaultDescription
' (SS,21/3/12) only prefix with name if length of ProductDescription is less than length of ProductName, it used to always prefix with name before
ElseIf Len(LDescription) < Len(LProductName) Then
LDescription = LProductName & " - " & LDescription
End If
End If
' remove HTML Tags
LDescription = RemoveHTMLTags(LDescription)
' replace quote with single quote '
LDescription = ReplaceStr(LDescription, """", "'")
' replace new lines with space '
LDescription = ReplaceStr(LDescription, Chr(13) + Chr(10) , " ")
' (SS,6/12/19) replaced 3 spaces with single space, also 2 spaces with single space
LDescription = ReplaceStr(LDescription, " ", " ")
LDescription = ReplaceStr(LDescription, " ", " ")
End If
' LResult = Left(LDescription, MAX_LENGTH)
' (SS,16/7/13) replaced above with following to reduce size and suffix with "..." if more than max
If Len(LDescription) > MAX_LENGTH Then
LResult = Mid(LDescription, 1, MAX_LENGTH) + "..."
Else
LResult = LDescription
End If
Else
' (SS,16/3/12) added following for group page
If IsGroupPage Then
' try to get description meta tag from groups table, if blank then do as before
LResult = GetTitleTagForGroup("D", GetGroup)
Else ' page
' try to get title tag from site details table
LResult = GetTitleTagForPage("D", GetPageName)
End If
' if blank then try the default
If LResult = "" Then LResult = GetTitleTagForPage("D", "Default")
' (SS,27/7/11) added HTMLEncode
' (SS,16/3/12) removed HTMLEncode because we're doing it below
' (SS,16/3/12) following after the "if", is as before, if still blank then use the one passed to this routine as a parameter
If LResult = "" Then LResult = ADefaultDescription
End If
' (SS,27/7/11) added HTMLEncode
GetHTMLDescription = HTMLEncode(LResult)
End Function
' (SS,19/9/06)
' (SS,27/7/11) added HTMLEncode
' (SS,16/3/12) modified to get the keywords meta tag if available from categories, subcategories, groups, product_attributes and sitedetails tables
Function GetHTMLKeyWords(ADefaultKeyWords)
Const MAX_LENGTH = 255 ' (SS,16/3/12) increased from 250 to 255
Dim LResult
If IsProductDetailsPage Or IsCategoryPage Or IsSubcategoryPage Then
Dim LCategory, LSubcategory, LKeyWords
LCategory = CleanRequestQueryString("cat")
LSubcategory = CleanRequestQueryString("subcat")
' (SS,16/3/12) removed following
' LKeyWords = ADefaultKeyWords
If IsCategoryPage Then
' (SS,16/3/12) try to get keywords meta tag from categories table, if blank then do as before
LKeyWords = GetTitleTagForCategory("K", LCategory)
' (SS,16/3/12) following code after the "if" is as before, i.e. builds the tag from category, subcategory etc.
If LKeyWords = "" Then LKeyWords = LCategory & ", " & GetSubcategoryListForCategory(LCategory) & ", " & ADefaultKeyWords
ElseIf IsSubcategoryPage Then
' (SS,16/3/12) try to get keywords meta tag from subcategories table, if blank then do as before
LKeyWords = GetTitleTagForSubcategory("K", LCategory, LSubcategory)
' (SS,16/3/12) following code after the "if" is as before, i.e. builds the tag from category and subcategory
If LKeyWords = "" Then LKeyWords = LCategory & ", " & LSubcategory & ", " & ADefaultKeyWords
Else ' ie. Product Details page
Dim LProductCode, LProductName
LProductCode = GetProductCodeQS
' (SS,16/3/12) try to get keyword meta tag from product attribute, if not found then do as before
Dim LProductID
LProductID = GetProductIDForProductCode(LProductCode)
LKeyWords = GetTitleTagForProduct("K", LProductID)
' (SS,16/3/12) following code is as before, i.e. builds the tag from product name and description
If LKeyWords = "" Then
' (SS,26/7/11) replaced GetFieldForProduct(LProductCode, "ProductName") with GetProductName(LProductCode)
' which also does a HTMLEncode so RemoveHTMLTags and Replace quote below not required
LProductName = GetProductName(LProductCode)
' remove HTML Tags
'LProductName = RemoveHTMLTags(LProductName)
' replace quote with single quote '
'LProductName = ReplaceStr(LProductName,"""", "'")
Dim LGroup
GetFirstCatSubcatForProduct LProductCode, LGroup, LCategory, LSubcategory
' remove repeated words
If LCategory <> "" Then
LProductName = Replace(LProductName, LCategory, "")
' LKeyWords = Replace(LKeyWords, LSubcategory, "")
End if
If LSubcategory <> "" Then
LProductName = Replace(LProductName, LSubcategory, "")
' LKeyWords = Replace(LKeyWords, LSubcategory, "")
End If
'LKeyWords = Replace(LKeyWords, " ", " ")
LProductName = RemoveCommonWords(LProductName)
LProductName = Replace(LProductName, " ", " ")
LProductName = Replace(LProductName, " ", ", ")
LKeyWords = LCategory + ", " + LSubcategory + ", " + LProductName + ", " & ADefaultKeyWords
End If
End If
LResult = Left(LKeyWords, MAX_LENGTH)
Else
' (SS,16/3/12) added following for group page
If IsGroupPage Then
' try to get description meta tag from groups table, if blank then do as before
LResult = GetTitleTagForGroup("K", GetGroup)
Else ' page
' try to get title tag from site details table
LResult = GetTitleTagForPage("K", GetPageName)
End If
' if blank then try the default
If LResult = "" Then LResult = GetTitleTagForPage("K", "Default")
' (SS,16/3/12) following after the "if" is as before, if still blank then use the one passed to this routine as a parameter
If LResult = "" Then LResult = ADefaultKeyWords
End If
GetHTMLKeyWords = HTMLEncode(LResult)
End Function
' (SS,16/3/12) returns title, description or keyword meta tag, Type can be T, D, or K
' (SS,17/1/13) modified so that if page doesn't exist in site details and CustomPagesEnabled it tries to get the values from CustomGetTitleTagForPage instead which is in CustomUtils.asp
' (SS,5/2/13) was returning wrong values for home page, this was due to PageExists returning False for home page i.e. where APage is "", now corrected
Function GetTitleTagForPage(AType, APage)
' if page is "" then get the Home page, + "s" because it's plural in the site details table
If PageExists(APage) Then
GetTitleTagForPage = Trim(GetSiteDetail(GetTagNameForType(AType, False) + "s", IIf(APage = "", "Home", APage)))
ElseIf CustomPagesEnabled Then
GetTitleTagForPage = CustomGetTitleTagForPage(AType, APage)
Else
GetTitleTagForPage = ""
End If
End Function
' (SS,16/3/12) returns title, description or keyword meta tag, Type can be T, D, or K
Function GetTitleTagForProduct(AType, AProductID)
GetTitleTagForProduct = Trim(GetProductAttributeByName(AProductID, GetTagNameForType(AType, False)))
End Function
' (SS,16/3/12) returns title, description or keyword meta tag, Type can be T, D, or K
Function GetTitleTagForCategory(AType, ACategory)
GetTitleTagForCategory = Trim(NB(GetSQLValue("SELECT " & GetTagNameForType(AType, True) & " FROM categories WHERE Category = '" & CleanSQLStr(ACategory) & "'")))
End Function
' (SS,16/3/12) returns title, description or keyword meta tag, Type can be T, D, or K
Function GetTitleTagForSubcategory(AType, ACategory, ASubcategory)
GetTitleTagForSubcategory = Trim(NB(GetSQLValue("SELECT " & GetTagNameForType(AType, True) & " FROM subcategories WHERE Category = '" & CleanSQLStr(ACategory) & "' AND Subcategory = '" & CleanSQLStr(ASubcategory) & "'")))
End Function
' (SS,16/3/12) returns title, description or keyword meta tag, Type can be T, D, or K
Function GetTitleTagForGroup(AType, AGroup)
GetTitleTagForGroup = Trim(NB(GetSQLValue("SELECT " & GetTagNameForType(AType, True) & " FROM groups WHERE GroupName = '" & CleanSQLStr(AGroup) & "'")))
End Function
' (SS,16/3/12) returns the full name for type, AType can be T, D, or K, if AForField true then it returns name without spaces
Function GetTagNameForType(AType, AForField)
Dim LResult
If AType = "T" Then
LResult = "Title Tag"
ElseIf AType = "D" Then
LResult = "Description Meta Tag"
Else ' AType = "K"
LResult = "Keywords Meta Tag"
End If
If AForField Then LResult = ReplaceStr(LResult, " ", "") ' remove spaces
GetTagNameForType = LResult
End Function
' (SS,10/8/11) used to tell Facebook which image to use for the like button, it was taking image from related items
' (SS,10/5/13) added NL
Sub ShowFacebookLikeImageMeta
If IsProductDetailsPage Then
Response.Write "<meta property=""og:image"" content=""" & GetFullProductImageLink(GetProductCode, "s") & """ />" & NL
End If
End Sub
' (SS,10/5/13) canonical link to tell search engines that products.asp without a query string is the same as the home page without any page name
' i.e. for http://www.coventry-demolition.com/products.asp the canonical link is http://www.coventry-demolition.com
' it will also be shown for the root page i.e. http://www.coventry-demolition.com, in ASP there's no way of checking if URL was without the default page name
' (SS,2/3/15) added canonical link for product details page containing code and name
' ### need to investigate the proper and standard way of encoding a URL for sitemap use, canonical link use and html link use, have a routine that
' can be called with parameter true or false to include full path or just relative
' (SS,16/12/15) added missing "name=" from URL, and URLEncode()
' (SS,24/8/17) changed to not show canonical when product code not found
Sub ShowCanonicalLink
If Request.ServerVariables("QUERY_STRING") = "" And GetScriptName = "products.asp" Then
Response.Write "<link rel=""canonical"" href=""" & Iif(LCase(Request.ServerVariables("HTTPS")) = "on", "https://", "http://") & Request.ServerVariables("SERVER_NAME") & """ />" & NL
' (SS,1/3/15) added following canonical link for product page when product page URL contains no name, page containing the name is to be the main product page
ElseIf IsProductDetailsPage And CleanRequestQueryString("name") = "" Then
Dim LURL, LProductCode, LProductName
LProductCode = GetProductCodeQS
LProductName = GetProductName(LProductCode)
' (SS,24/8/17) added If check for product name, i.e. product code found, don't show show canonical if product doesn't exist
If LProductName <> "" Then
' (SS,16/12/15) added missing "name=", removed URLEncode() from LProductCode and SEOFriendlyStr, not necessary, just causing duplicates to be detected
LURL = GetStoreURL + "/products.asp?code=" + LProductCode + "&name=" & SEOFriendlyStr(LProductName)
Response.Write "<link rel=""canonical"" href=""" & LURL & """ />" & NL
End If
End If
End Sub
' (SS,20/9/06)
' (SS,5/10/11) modified to replace break tags with a space
' (SS,21/3/13) HTML comment tags caused an issue if they had other tags inside, fixed by checking for <!-- and --> to ignore everything in comment tags
' (SS,19/10/15) bug fix to prevent error when first or second characters are ">" (added And i > 2)
Function RemoveHTMLTags(AValue)
Dim i, c, LLen, LNewValue, LInTag, LValue, LInCommentTag
' (SS,5/10/11) replace the breaks with a space
LValue = ReplaceStr(AValue, BR, " ") ' i.e. <br />
LValue = Replace(LValue, "<br>", " ", 1, -1, 1) ' case insensitive version
LLen = Len(LValue)
LNewValue = ""
LInTag = False
LInCommentTag = False
For i = 1 to LLen
c = Mid(LValue, i, 1)
If c = "<" Then
If Mid(LValue, i, 4) = "<!--" Then
LInCommentTag = True
Else
LInTag = True
End If
ElseIf c = ">" And i > 2 Then ' (SS,19/10/15) added And i > 2
' (SS,19/10/15) Mid function was failing when i was 1, i.e. first char a ">"
' fixed by first checking that i > 2 in above ElseIf
If Mid(LValue, i - 2, 3) = "-->" Then
LInCommentTag = False
Else
LInTag = False
End If
ElseIf Not LInTag And Not LInCommentTag Then
LNewValue = LNewValue + c
End If
Next
RemoveHTMLTags = LNewValue
End Function
' (SS,20/9/06)
Function RemoveCommonWords(AValue)
Const COMMON_WORDS = "the, a, and, in, a, of, that, to, this, as, or"
Dim LValue, LWords, LPos, LWord
LValue = " " & AValue & " "
LWords = COMMON_WORDS
Do While LWords <> ""
LPos = InStr(LWords, ",")
If LPos = 0 Then LPos = Len(LWords) + 1
LWord = Trim(Mid(LWords, 1, LPos - 1))
LWords = Trim(Mid(LWords, LPos + 1, Len(LWords)))
' following gets repeated for each word in COMMON_WORDS
LValue = Replace(LValue, " " & LWord & " ", " ")
LValue = Replace(LValue, " " & UCase(LWord) & " ", " ")
LValue = Replace(LValue, " " & UCase(Left(LWord,1)) + Mid(LWord,2,Len(LWord)) & " ", " ")
Loop
RemoveCommonWords = Trim(LValue)
End Function
' (SS,28/11/13) returns true if on home page
Function IsHomePage
Dim LPageName
LPageName = LCase(CleanRequestQueryString("page"))
IsHomePage = LPageName = "home" Or (LPageName = "" And CleanRequestQueryString("cmd") = "" And GetProductCodeQS = "" And CleanRequestQueryString("cat") = "" And CleanRequestQueryString("grp") = "")
End Function
' (SS,28/11/13) returns true if on basket page
Function IsBasketPage
Dim LCmd
LCmd = CleanRequestQueryString("cmd") ' (20/11/18) replaced multiple Request.QueryString with this
IsBasketPage = LCmd = "basket" Or LCmd = "buy" Or LCmd = "remove" Or LCmd = "recalculate"
End Function
' (SS,18/2/14)
Function GetBasketPageURL
GetBasketPageURL = "products.asp?cmd=basket"
End Function
' (SS,18/2/14) requested by Clifton's AdWord person, for unique URL for different steps, redirection done to this URL
Function GetCheckoutConfirmOrderURL
GetCheckoutConfirmOrderURL = "products.asp?cmd=checkout&xcmd=filled"
End Function
' (SS,20/2/15) for new save basket feature
Function GetSaveBasketURL
GetSaveBasketURL = "products.asp?cmd=savebasket"
End Function
' (SS,20/2/15) for new save basket feature
Function GetRestoreBasketURL(ASavedBasketID, AEmailAddress)
GetRestoreBasketURL = GetStoreURL & "/products.asp?cmd=restorebasket&id=" & ASavedBasketID & "&email=" & URLEncode(AEmailAddress)
End Function
' (SS,16/9/04) called to check whether page requested is a product details page
Function IsProductDetailsPage
Dim LCommand, LProductCode
LCommand = CleanRequestQueryString("cmd")
If LCommand = "" Then strCommand = CleanRequestForm("cmd")
LProductCode = GetProductCodeQS
If LCommand = "remove" Or strCommand = "recalculate" Or strCommand = "basket" Or strCommand = "checkout" Or strCommand = "payment" Then
IsProductDetailsPage = False
ElseIf LProductCode <> "" Then
IsProductDetailsPage = True
Else
IsProductDetailsPage = False
End If
End Function
' (SS,9/4/19) added following for Facebook Pixel AddToCart event tracking
Function IsAddToBasketPage
Dim Lcmd, Lcode
' xcmd takes priority over cmd
Lcmd = CleanRequest("xcmd")
Lcode = CleanRequest("xcode")
If Lcmd = "" Then
Lcmd = CleanRequest("cmd")
Lcode = CleanRequest("code")
End if
IsAddToBasketPage = (Lcmd = "buy" Or Lcmd = "buyl") and Lcode <> ""
End Function
' (SS,9/4/19) added following for Facebook Pixel AddToCart event tracking, returns the product code for product added to basket
Function GetBasketProductCode
Dim Lcode
Lcode = CleanRequest("xcode")
If Lcode = "" Then Lcode = CleanRequest("code")
GetBasketProductCode = Lcode
End Function
' (SS,12/2/05) called to check whether page requested is a search page '
Function IsSearchPage
' (SS,25/7/11) added Or GetSearch <> ""
If CleanRequestQueryString("cmd") = "search" Or GetSearch <> "" Or (CleanRequestQueryString("cmd") = "page" And CleanRequestQueryString("cat") = "" And CleanRequestQueryString("subcat") = "") Then
IsSearchPage = True
Else
IsSearchPage = False
End If
End Function
' (SS,19/9/06)
Function IsCategoryPage
If CleanRequestQueryString("cat") <> "" And CleanRequestQueryString("subcat") = "" Then
IsCategoryPage = True
Else
IsCategoryPage = False
End If
End Function
' (SS,19/9/06)
Function IsSubcategoryPage
If CleanRequestQueryString("cat") <> "" And CleanRequestQueryString("subcat") <> "" Then
IsSubcategoryPage = True
Else
IsSubcategoryPage = False
End If
End Function
' (SS,16/3/12)
Function IsGroupPage
If GroupsEnabled And CleanRequestQueryString("grp") <> "" Then
IsGroupPage = True
Else
IsGroupPage = False
End If
End Function
' (SS,27/11/13) returns true if payment page (i.e. the payment redirection page), used by google analytics for ecommerce tracking
Function IsPaymentPage
IsPaymentPage = CleanRequestQueryString("cmd") = "checkout" And CleanRequestQueryString("xcmd") = "pay"
End Function
' (SS,3/3/14) is return from Payment Provider page, after payment has been made
Function IsReturnFromPaymentProviderPage
IsReturnFromPaymentProviderPage = CleanRequestQueryString("cmd") = "checkout" And CleanRequestQueryString("stage") = "co"
End Function
' (SS,30/4/18) added to return true if browser return from EPDQ or Payzone are successful payments
Function IsSuccessfulReturnFromPaymentProviderPage
Dim LResult, LPaymentType
If IsReturnFromPaymentProviderPage Then
LPaymentType = CleanRequestQueryString("pt")
If LPaymentType = ptEPDQ Then
LResult = EPDQSuccessfulPayment
ElseIf LPaymentType = ptPayzone Then
LResult = PayzoneSuccessfulPayment
Else
LResult = True ' assume successful for others without any additional check
End If
Else
LResult = False ' i.e. not a return from payment provider
End If
IsSuccessfulReturnFromPaymentProviderPage = LResult
End Function
' (SS,31/5/07)
Function InCheckout
' If CleanRequestQueryString("cmd") = "checkout" Or CleanRequestForm("cmd") = "checkout" Then
' (SS,14/6/11) replaced above with following
If CleanRequest("cmd") = "checkout" Then
InCheckout = True
Else
InCheckout = False
End If
End Function
' (SS,12/4/19) start of checkout (used by Facebook Pixel 'InitiateCheckout' event)
' !!! NB will to track if PayPal checkout used from basket page
Function IsStartOfCheckout
Dim LResult, LCurrentStage
LResult = InCheckout And CleanRequestQueryString("xcmd") <> "filled"
If LResult Then
LCurrentStage = CleanRequestForm("stage")
If LCurrentStage = "" Then LCurrentStage = CleanRequestQueryString("stage")
LResult = LCurrentStage = ""
End If
IsStartOfCheckout = LResult
End Function
' (SS,12/4/19) returns true when checkout filled (used by Facebook Pixel 'AddPaymentInfo' event)
Function IsCheckoutFilled
IsCheckoutFilled = InCheckout And CleanRequestQueryString("xcmd") = "filled" And GetOrderNo <> ""
End Function
' (SS,20/5/04) called from inc-products.asp '
' (SS,12/2/05) improved to show better info '
' (SS,18/3/10) add tr() for translation
Function GetProductsPageTitle
Dim LCmd
Lcmd = CleanRequestQueryString("cmd") ' (SS,20/11/18) replaced multiple Request.QueryString with variable and CleanRequestQueryString
If Lcmd = "basket" Or Lcmd = "buy" Or Lcmd = "remove" Or Lcmd = "recalculate" Then
GetProductsPageTitle = tr("Your Basket")
ElseIf LCmd = "checkout" Or CleanRequestForm("cmd") = "checkout" Then
GetProductsPageTitle = tr("Checkout")
Else
If IsSearchPage Then
GetProductsPageTitle = tr("Product Search")
ElseIf LCmd = "cat" Then
If CleanRequestQueryString("grp") <> "" Then
GetProductsPageTitle = tr("Categories for") & " " & CleanRequestQueryString("grp")
Else
GetProductsPageTitle = tr("Categories")
End If
ElseIf LCmd = "subcat" Then
If CleanRequestQueryString("subcat") = "" Then
If strGroupFromMenu = "" Then
GetProductsPageTitle = tr("Subcategories for") & " " & CleanRequestQueryString("cat")
Else
GetProductsPageTitle = strGroupFromMenu & " - " & CleanRequestQueryString("cat")
End If
Else
GetProductsPageTitle = tr("Products")
End If
' (SS,15/5/07) changed And to Or to fix bug where "Product" was showing instead of "Products" when category chosen
ElseIf (LCmd = "list") Or (CleanRequestQueryString("cat") <> "") Or (CleanRequestQueryString("subcat") <> "") Then
GetProductsPageTitle = tr("Products")
Else
GetProductsPageTitle = tr("Product")
End If
End If
End Function
' (SS,17/5/11) new version 2, AOptions parameter not currently used
Function GetProductsPageTitleV2(AOptions)
Dim LResult, LCmd
LCmd = CleanRequestQueryString("cmd")
If LCmd = "basket" Or LCmd = "remove" Or LCmd = "recalculate" Then ' (SS,12/9/12) removed LCmd = "buy" because "Your Basket " was incorrectly showing when adding item to basket for new Galleon with combo add
LResult = tr("Your Basket")
ElseIf LCmd = "checkout" Then
LResult = tr("Checkout")
' (SS,13/12/18) for new cancel order facility
ElseIf LCmd = "cancelorder" Then
LResult = tr("Cancel Order")
Else
Dim LGrp, LCat, LSubcat
LGrp = GetGroup
LCat = GetCategory
LSubcat = GetSubcategory
If IsSearchPage Then
LResult = tr("Product Search")
ElseIf LCmd = "cat" Then
If LGrp <> "" Then
LResult = tr("Categories for") & " " & LGrp
Else
LResult = tr("Categories")
End If
ElseIf LCmd = "subcat" Then
If LSubcat = "" Then
If strGroupFromMenu = "" Then
LResult = tr("Subcategories for") & " " & LCat
Else
LResult = strGroupFromMenu & " - " & LCat
End If
Else
LResult = tr("Products")
End If
' (SS,15/5/07) changed And to Or to fix bug where "Product" was showing instead of "Products" when category chosen
ElseIf LCmd = "list" Then
LResult = tr("Products")
' (SS,11/1/13) replaced GetProductCode in following with GetProductCodeQS because in some cases it stopped showing the breadcrumb when in category or subcategory
ElseIf GetProductCodeQS = "" Then ' only if it's not a product code, product detail has it's own heading for breadcrumb
' (SS,17/5/11) instead of the word "product", show a breadcrumb
LResult = GetBreadcrumb(LGrp, LCat, LSubcat, AOptions) ' return a breadcrumb to show in title/heading
End If
End If
GetProductsPageTitleV2 = LResult
End Function
' (SS,17/5/11) new routine which returns a breadcrumb to show near the top of the product section
' called from GetProductsPageTitleV2 and from Sub ShowProductDetailYouAreHere in inc-template-product-detail.asp
' AOptions parameter is for future expansion, not currently used
' (SS,26/7/11) added GetShortNameForCategory and GetShortNameForSubcategory to make use of short names
Function GetBreadcrumb(AGroup, ACategory, ASubcategory, AOptions)
Dim LResult
LResult = ""
' (SS,15/8/14)
LResult = LResult & GetBreadcrumbHeader
If GroupsEnabled Then
Dim LGroup
If AGroup = "" Then
If ACategory <> "" Then LGroup = GetGroupForCategory(ACategory)
Else
LGroup = AGroup
End If
If LGroup <> "" Then LResult = LResult & GetBreadcrumbLink(LGroup, GetGroupLink(LGroup))
End If
If ACategory <> "" Then
If LResult <> "" Then LResult = LResult & GetBreadcrumbSeparator
LResult = LResult & GetBreadcrumbLink(GetShortNameForCategory(ACategory), GetCategoryLink(ACategory))
End If
If ASubcategory <> "" Then
If LResult <> "" Then LResult = LResult & GetBreadcrumbSeparator
LResult = LResult & GetBreadcrumbLink(GetShortNameForSubcategory(ACategory, ASubcategory), GetSubcategoryLink(ACategory, ASubcategory)) ' (SS,11/1/13) added missing ACategory to GetShortNameForSubcategory
End If
' (SS,15/8/14)
LResult = LResult & GetBreadcrumbFooter
GetBreadcrumb = LResult
End Function
' (SS,15/8/14) added for compatibility, function with same name to override this may exist in inc-template-products.asp
Function GetBreadcrumbHeader
GetBreadcrumbHeader = ""
End Function
' (SS,17/5/11)
' (SS,26/7/11) added HTMLEncode to ensure people don't try to add html tags to the names
' (SS,15/8/14) overriding version of this may exist in inc-template-products.asp
Function GetBreadcrumbLink(AName, AHRef)
GetBreadcrumbLink = "<span class=""breadcrumb""><a href=""" & AHRef & """>" & HTMLEncode(AName) & "</a></span>"
End Function
' (SS,17/5/11)
' (SS,15/8/14) overriding version of this may exist in inc-template-products.asp
Function GetBreadcrumbSeparator
GetBreadcrumbSeparator = "<span class=""breadcrumb-separator""> » </span>"
End Function
' (SS,15/8/14) added for compatibility, function with same name to override this may exist in inc-template-products.asp
Function GetBreadcrumbFooter
GetBreadcrumbFooter = ""
End Function
' (SS,3/2/04) looks at list of form fields specified (comma separated) '
' returns list of the ones that have a blank value '
' i.e. have not been entered '
Function GetListOfMissingFields(ARequiredList)
Dim i, c, LLength, LFieldName, LMissingFields
LMissingFields = ""
LLength = Len(ARequiredList)
LFieldName = ""
For i = 1 To LLength
c = Mid(ARequiredList, i, 1)
' if comma or last char, i.e. we have a field '
If c <> "," Then LFieldName = LFieldName + c
If c = "," Or i = LLength Then
LFieldName = Trim(LFieldName)
If LFieldName <> "" Then
If CleanRequestForm(LFieldName) = "" Then
LMissingFields = LMissingFields + Iif(LMissingFields = "", "", ", ") + LFieldName
End If
End If
LFieldName = ""
End If
Next
GetListOfMissingFields = LMissingFields
End Function
' (SS,24/5/04) now uses template instead of separate contact.asp amd contact-email.asp pages '
' (SS,18/3/10) added tr() to translation the two error message
Sub DoContactForm
Dim LErrorMessage, LShowContactForm
LErrorMessage = ""
LShowContactForm = True
ShowContactHeader
' if previously tried to post form then check telephone and email address entered'
If CleanRequestForm("postform") = "yes" Then
' (SS,6/3/13) removed (CleanRequestForm("Name") = "") because name is no longer compulsory
If (CleanRequestForm("Email") = "" And CleanRequestForm("Telephone") = "") Then
LErrorMessage = tr("Please enter either your email address or telephone") + "." ' (SS,6/3/13) removed "your Name and "
' (SS,5/3/13) added check for enquiry
ElseIf TrimAll(CleanRequestForm("Enquiry")) = "" Then
LErrorMessage = tr("Please enter your enquiry") + "."
' (SS,17/4/09) added following security code check
ElseIf EmailSecurityCodeEnabled Then
If Not SecurityOK(False) Then
LErrorMessage = tr("Please enter the correct Security Code") + "."
End If
End If
If LErrorMessage = "" Then
ShowContactThankYou
LShowContactForm = False
' add enquiry to contact database
' (SS,5/3/13) removed Address and Postcode, added Country
' (SS,13/11/13) added Addrews and Postcode back in, required by some sites for samples etc.
AddContactToDatabaseAndEmail "Contact", "Name, Address, Postcode, Country, Telephone, Email", "Enquiry", True
End If
End If
If LShowContactForm Then ShowContactForm(LErrorMessage)
ShowContactFooter
End Sub
' (SS,15/2/12)
' (SS,17/8/15) modified for Trustpilot
Sub DoReviewsPage
Dim LSQL, LReviewCount, LDoneCount, LProductCode, LProductName, LReviewerName, LReviewText, LReviewRating, LProductIsDisabled
If ReviewTypeIsTrustpilot Then
LSQL = "SELECT *, DATE_FORMAT(ReviewDateTime, '%e %b %Y') AS ReviewDate FROM trustpilot_reviews WHERE ReviewID > 1" ' Review ID 1 is a special one holding average
Else
' decided not to use time ( %l:%i %p), decided to show disabled by removing NOT p.ProductDisabled, but marked as (discontinued) with links removed
' (SS,14/9/17) added GROUP BY ReviewText, DATE(ReviewDateTime), ReviewerName, to prevent duplicate ReviewTexts appearing and harming SEO
LSQL = "SELECT pr.*, p.ProductDisabled, DATE_FORMAT(ReviewDateTime, '%e %b %Y') AS ReviewDate" &_
" FROM productreviews pr" &_
" INNER JOIN products p ON p.ProductCode = pr.ProductCode" &_
" WHERE ShowOnSite AND ReviewType = 'R'" &_
" GROUP BY ReviewText, DATE(ReviewDateTime), ReviewerName"
End If
LSQL = LSQL & " ORDER BY ReviewDateTime DESC"
' (SS,26/5/17) set limit of 100 due to too many appearing for HyperFlight causing "response buffer limit exceeded" error
LSQL = LSQL & " LIMIT 100"
OpenQuery(LSQL)
LReviewCount = GetQueryRecordCount
ShowReviewsPageHeader
If LReviewCount = 0 Then
ShowReviewsPageEmpty
Else
LDoneCount = 0
Do While Not EndOfQuery
LDoneCount = LDoneCount + 1
' (SS,17/8/15) added Trustpilot
If ReviewTypeIsTrustpilot Then
LProductCode = ""
LProductName = ""
LReviewText = GetQueryValue("ReviewBody")
LReviewRating = GetQueryValue("ReviewRating")
LProductIsDisabled = False
Else
LProductCode = GetQueryField("ProductCode")
LProductName = GetProductName(LProductCode)
LReviewText = ParagraphiseText(GetQueryValue("ReviewText"))
LReviewRating = GetQueryValue("ProductRating")
LProductIsDisabled = IntToBool(GetQueryField("ProductDisabled"))
End If
LReviewerName = GetQueryValue("ReviewerName")
If LReviewerName = "" Then LReviewerName = "Anonymous" ' (SS,15/5/09)
ShowReviewsPageItem LDoneCount, GetQueryField("ReviewID"), LProductCode, LProductName, LProductIsDisabled, GetQueryField("ReviewDate"), GetQueryValue("ReviewTitle"), LReviewRating, LReviewText, LReviewerName
NextQueryRecord
Loop
End If
CloseQuery
ShowReviewsPageFooter
End Sub
' (SS,27/7/04)'
' (SS,1/6/07) major mods for new account method
Sub DoCreateAccountForm
Dim LErrorMessage, LShowCreateAccountForm, LCreateNew
LErrorMessage = ""
LShowCreateAccountForm = True
LCreateNew = Not AccountLoggedIn
ShowCreateAccountHeader(LCreateNew) ' (SS,14/6/11) added LCreateNew
' if previously tried to post form then check telephone and email address entered'
If CleanRequestForm("postform") = "yes" Then
' (SS,5/3/13) removed old code which was only applicable for old account method (from the Else section), removed the "If NewAccountMethod" condition which was no longer applicable
GetPersonalDetailsFromForm
GetDeliveryDetailsFromForm
CheckValidPersonalDetails
LErrorMessage = gsErrorMessage
If Not AllowDifferentDeliveryAddress Then Session("DeliveryAddressSameAsInvoice") = True ' (SS,19/6/09) because we don't want to validate delivery details because they won't have been entered
If LErrorMessage = "" And Not Session("DeliveryAddressSameAsInvoice") Then
CheckValidDeliveryAddress
LErrorMessage = gsErrorMessage
End If
' (SS,18/6/12) moved code to new function CheckNewPassword, for use elsewhere
' (SS,19/6/12) check now only done when creating new account, password fields not shown when editing account details
If LCreateNew Then
Dim LPasswordMsg
LPasswordMsg = CheckNewPassword
If LPasswordMsg <> "" Then LErrorMessage = LErrorMessage + Iif(LErrorMessage = "", "", "<br>") + LPasswordMsg
End If
If LErrorMessage = "" Then
LShowCreateAccountForm = False
' (SS,5/3/13) removed old code which was only applicable for old account method (from the Else section), removed the "If NewAccountMethod" condition which was no longer applicable
If LCreateNew Then
If CreateAccount(True, 0) Then
' ensure we're logged in with all details in Session, this will always return True
' (SS,19/6/12) removed first parameter 0, no longer applicable
If GetAccountDetails(CleanRequestForm("Email"), CleanRequestForm("Password"), "") Then
SaveAutoLoginToCookie ' (SS,20/9/12) save login to cookie if feature enabled
ShowCreateAccountThankYou
End If
Else
LShowCreateAccountForm = True
LErrorMessage = gsErrorMessage
End If
Else
If CreateAccount(False, Session("AccountID")) Then
ShowUpdateAccountThankYou
Else
LShowCreateAccountForm = True
LErrorMessage = gsErrorMessage
End If
End If
End If
Else
SetCheckoutAccountDefaults
End If
If LErrorMessage <> "" Then SetAlertMessage(LErrorMessage)
If LShowCreateAccountForm Then
ShowCreateAccountForm LCreateNew, LErrorMessage
Else
If NewAccountMethod Then ShowPreviousOrders ' (SS,7/11/07) added If NewAccountMethod Then
End If
ShowCreateAccountFooter
End Sub
' (SS,18/6/12) moved here from DoCreateAccountForm above because it's also called from reset password
Function CheckNewPassword
Dim LResult
' (SS,19/3/10) added tr for translation
If CleanRequestForm("Password") = "" Then
LResult = tr("Password must be entered") & "."
Else
If CleanRequestForm("Password") <> "" And Len(CleanRequestForm("Password")) < 5 Then
LResult = tr("Password must be at least 5 characters long") & "."
ElseIf CleanRequestForm("Password") <> CleanRequestForm("Password2") Then
LResult = tr("Password and Retype Password must match") & "."
Else
LResult = ""
End If
End If
CheckNewPassword = LResult
End Function
' (SS,30/5/07) new sub to call various account routines
Sub DoAccount
Dim LCmd
LCmd = CleanRequestQueryString("cmd")
If LCmd = "logout" Then
DoAccountLogout
ElseIf LCmd = "forgotpassword" Then
DoForgotPassword
' (SS,18/6/12) added reset password
ElseIf LCmd = "rp" Then
DoResetPassword
' (SS,23/5/18) added subscribe (newsletter/offers optin)
ElseIF LCmd = "subscribe" Then
DoSubscribe
ElseIf LCmd = "create" Or LCmd = "update" Then
DoCreateAccountForm
Else
DoAccountLogin
End If
End Sub
' (SS,5/8/04) '
' (SS,14/8/05) modified to show account details
' (SS,19/6/12) removed LAccountID and UseEmailAddressForLogin, no longer used
Sub DoAccountLogin
Dim LNewValidLogIn, LAlreadyLoggedIn, LOrderNo, LErrorMessage
LNewValidLogIn = False
LErrorMessage = "" ' (SS,14/6/11)
LAlreadyLoggedIn = AccountLoggedIn
DoLoginLog("Initial") ' (SS,6/1/10)
' (SS,14/6/11) moved checking log in here from below, so that we can display correct name in heading
If Not LAlreadyLoggedIn Then ' if not already logged in
' validate the account ID and password and get address details '
' (SS,30/5/07) added CleanRequestForm("Email") to following
Dim LEmail, LPassword
LEmail = Trim(CleanRequestForm("Email"))
LPassword = Trim(CleanRequestForm("Password"))
' (SS,14/6/07) GetAccountDetails also sets AccountID returned using GetAccountID
' (SS,19/6/12) removed LAccountID parameter, no longer used
If GetAccountDetails(LEmail, LPassword, "") Then
SetDateTimeOfLastLogin ' (SS,20/9/12) code moved to new routine
LNewValidLogIn = True
SaveAutoLoginToCookie ' (SS,20/9/12) save login to cookie if feature enabled
Else
If LEmail <> "" Or LPassword <> "" Then LErrorMessage = tr("Email and/or Password is incorrect")
' ShowAccountLoginMain(LErrorMessage)
End If
DoLoginLog(iif(LNewValidLogIn, "Success", "Failure")) ' (SS,6/1/10)
End If
If Not InCheckout Then
' (SS,31/5/07) added If Not InCheckout because title will already have been shown if in checkout
' (SS,14/6/11) added Or LNewValidLogIn
ShowAccountLoginHeader(LAlreadyLoggedIn Or LNewValidLogIn)
End If
If LAlreadyLoggedIn Or LNewValidLogIn Then
' if order no specified then show the details
LOrderNo = CleanRequestQueryString("orderno")
If LOrderNo <> "" And IsOrderForThisAccount(LOrderNo) Then
ShowOrderDetailsMaster False, LOrderNo
ShowOrderDetails False, LOrderNo
Else
ShowAccountDetails(LAlreadyLoggedIn) ' (SS,14/6/11) replaced LAlreadyLoggedIn with True
ShowPreviousOrders ' (SS,12/3/13) moved here from below, to only not show on same page as order details master
End If
Else
ShowAccountLoginMain(LErrorMessage) ' (SS,14/6/11) moved here
End If
' (SS,13/6/11) moved ShowAccountLoginFooter here and added check for InCheckout (which might not be necessary, not certain if routine called InCheckOut)
If Not InCheckout Then ShowAccountLoginFooter
End Sub
' (SS,20/9/12) moved from DoAccountLogin to here, now also called from DoAutoLogin
Sub SetDateTimeOfLastLogin
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
ExecuteQuery("UPDATE customers SET DateTimeLastLogin = NOW() WHERE CustomerID = '" & CleanSQLStr(GetAccountID) & "'") ' (SS,14/6/07) store date/time of login
End Sub
' (SS,6/1/10) added following to help diagnose log in problem with some users
' (SS,15/3/12) no longer used, code commented out, table loginlog has been dropped for security reasons in Sub UpgradeSchemaTo156, can be added back in future if required, see UpgradeSchemaTo147
Sub DoLoginLog(AResult)
' (SS,15/3/12) following removed until further notice, table loginlog also dropped
'Const MAX_SIZE = 255
'Dim LReferrer, LURL, LQueryString, LUserAgent
'LReferrer = Left(Request.ServerVariables("HTTP_REFERER"), MAX_SIZE)
'LURL = Left(Request.ServerVariables("URL"), MAX_SIZE)
'LQueryString = Left(Request.ServerVariables("QUERY_STRING"), MAX_SIZE)
'LUserAgent = Left(Request.ServerVariables("HTTP_USER_AGENT"), MAX_SIZE)
'ExecuteQuery("INSERT INTO loginlog SET LoginResult = '" & AResult & "', Email = '" & CleanSQLStr(Trim(CleanRequestForm("Email"))) & "', Password = '" & CleanSQLStr(Trim(CleanRequestForm("Password"))) & "', CustomerID = '" & GetAccountID & "', AlreadyLoggedIn=" & AccountLoggedIn &_
' ", DateTimeAttempted=NOW(), SessionID='" & Session.SessionID & "', IPAddress='" & CleanSQLStr(Request.ServerVariables("REMOTE_ADDR")) & "'" & ", Referrer='" & CleanSQLStr(LReferrer) & "', URL='" & CleanSQLStr(LURL) & "', QueryString='" & CleanSQLStr(LQueryString) & "', UserAgent='" & CleanSQLStr(LUserAgent) & "'")
End Sub
' (SS,13/9/12) returns customer using customer id in session or cookie, used in welcome message
' (SS,20/9/12) renamed from GetCustomerNameUsingCookie to GetCustomerNameForWelcome, now returns from session variables, rather than table
Function GetCustomerNameForWelcome
Dim LResult, LCustomerID
LCustomerID = GetCustomerID ' replaced GetSetCookieCustomerID with GetCustomerID
If LCustomerID <> "" Then
LResult = Trim(Session("Title") & " " & Session("FirstName") & " " & Session("Surname"))
Else
LResult = ""
End If
GetCustomerNameForWelcome = LResult
End Function
' (SS,20/9/12) if not already logged in and auto login is enabled then validate cookie and log in if valid
Sub DoAutoLogin
If AccountLoggedIn Or Not AutoLoginEnabled Then Exit Sub
Dim LCustomerIDCookie, LAuthCookie, LDecryptedAuthCookie
LCustomerIDCookie = GetCookie("CustomerID")
LAuthCookie = GetCookie("CustomerAuth")
' only login if both cookies exist
If LCustomerIDCookie <> "" And LAuthCookie <> "" Then
' decrypt using store name, special suffix and customer id from cookie
LDecryptedAuthCookie = AES_DecryptHex(LAuthCookie, GetStoreName & "~itp743#" & LCustomerIDCookie)
' compare, i.e customerid = customerid, if match then login
If LDecryptedAuthCookie = LCustomerIDCookie Then
If GetAccountDetails("", "", LCustomerIDCookie) Then
SetDateTimeOfLastLogin
End If
End If
End If
End Sub
' (SS,20/9/12) called when customer logs in successfully, if cookie login enabled, save customer id to cookie and encrypted version
Sub SaveAutoLoginToCookie
If Not AutoLoginEnabled Then Exit Sub
Dim LCustomerID
LCustomerID = CStr(GetAccountID) ' CStr ensures string, probably not necessary
SetCookie "CustomerID", LCustomerID
SetCookie "CustomerAuth", AES_EncryptHex(LCustomerID, GetStoreName & "~itp743#" & LCustomerID)
End Sub
' (SS,20/9/120 clears login cookies to disable auto log in
Sub ClearAutoLoginFromCookie
If Not AutoLoginEnabled Then Exit Sub
SetCookie "CustomerID", ""
SetCookie "CustomerAuth", ""
End Sub
' (SS,1/6/07) can be called to create new or update existing
' ACustomerID is valid when updating existing
' (SS,6/10/11) added NoteVATValidation
' (SS,12/6/12) added CleanSQLStr before LCustomerID to prevent SQL injection
Function CreateAccount(ACreateNew, ACustomerID)
Dim LCustomerID
CreateAccount = False
' if creating new then check to use if account already exists with same email address, abort with error if it does
If ACreateNew Then
Dim LAutoAccountExists ' (SS,22/2/13)
LAutoAccountExists = False
' If GetValueFromQuery("RecordCount", "SELECT COUNT(*) As RecordCount FROM customers WHERE EmailAddress = '" & CleanSQLStr(Session("Email") ) & "'") > 0 Then
' (SS,22/2/13) replaced above SQL with following which also check that account exist with blank password
' Null is returned if account/customer record doesn't exist, True i.e. Integer 1 is returned account exist but password is null/empty, i.e. an auto created account, CustomerID is also returned if it exists
Dim LPasswordIsEmpty
If GetSQL2Values("SELECT CustomerID, Password IS NULL AS PasswordIsEmpty FROM customers WHERE EmailAddress = '" & CleanSQLStr(Session("Email") ) & "'", LCustomerID, LPasswordIsEmpty) Then
LAutoAccountExists = IntToBool(LPasswordIsEmpty)
' (SS,22/2/13) if the automatically created account exists then we continue because we want to allow user to save the password
If Not LAutoAccountExists Then
' gsErrorMessage = tr("An account with this email address already exists.<br>Please choose 'Forgot password' to have your password emailed to you.")
' (SS,19/6/12) replaced above with following, because password can't be sent, it can only be reset, this message will also be displayed in a dialog Window (in create an account)
' (SS,4/10/17) modified to show different message in checkout e.g. link to allow bypass
gsErrorMessage = tr("An account with this email address already exists") & "."
If InCheckOut Then
gsErrorMessage = gsErrorMessage & BR & tr("Please log in by entering email/password above and pressing the 'Log in' button") & "." & BR & GetCheckoutAccountBypassTextAndLink
Else
gsErrorMessage = gsErrorMessage & BR & tr("Please log in") & "."
End If
Exit Function
End If
End If
' (SS,22/2/13) added "If Not LAutoAccountExists Then" because we can't create another customer record with same email address, we use the existing one and save the password etc
If Not LAutoAccountExists Then
' insert new customer record and get the CustomerID, fields are placed later
ExecuteQuery("INSERT INTO customers SET FirstName = '', Surname = '', AddressLine1 = '', DateTimeCreated = NOW()") ' these 3 fields are essential else query fails
LCustomerID = CLng(GetValueFromQuery("LastInsertID", "SELECT LAST_INSERT_ID() AS LastInsertID"))
End If
' (SS,12/11/08) Google AdWords conversion tracking for new account
ActivateGoogleAdWordsNewAccountConversion
DoLoginLog("CreateAccount") ' (SS,7/1/10)
Else
LCustomerID = ACustomerID
DoLoginLog("UpdateAccount") ' (SS,7/1/10)
' check that another account with same email address doesn't exist
If GetValueFromQuery("RecordCount", "SELECT COUNT(*) As RecordCount FROM customers WHERE EmailAddress = '" & CleanSQLStr(Session("Email") ) & "' AND CustomerID <> '" & CleanSQLStr(LCustomerID) & "'") > 0 Then
gsErrorMessage = tr("Another account with this email address already exists. Please choose another.")
Exit Function
End If
End If
ClearDeliveryDetailsIfSameAsInvoice ' (SS,15/6/07)
' (SS,10/11/09) sends an email of customer details change and this option is enabled
If Not ACreateNew AND FNotifyCustomerRecordUpdate Then
CheckCustomerRecordChange ACustomerID
End If
' update the customer record
Dim LMySQL
' (SS,16/11/07) added VATNumber
' (SS,12/6/12) added CleanSQLStr before LCustomerID to prevent SQL injection
LMySQL = "UPDATE customers SET" &_
" EmailAddress = '" & CleanSQLStr(Session("Email")) & "'"
' (SS,18/6/12) added GetSQLForPasswordEncryption to Password to hash and encrypt it
' (SS,19/6/12) password now only saved then creating new account, no longer when editing account details
If ACreateNew Then
LMySQL = LMySQL &_
", Password = " & GetSQLForPasswordEncryption(Session("Password"), False)
End If
LMySQL = LMySQL &_
", Title = '" & CleanSQLStr(Session("Title")) & "'" &_
", FirstName = '" & CleanSQLStr(Session("FirstName")) & "'" &_
", Surname = '" & CleanSQLStr(Session("Surname")) & "'" &_
", CompanyName = '" & CleanSQLStr(Session("CompanyName")) & "'" &_
", AddressLine1 = '" & CleanSQLStr(Session("Address1")) & "'" &_
", AddressLine2 = '" & CleanSQLStr(Session("Address2")) & "'" &_
", Town = '" & CleanSQLStr(Session("Town")) & "'" &_
", County = '" & CleanSQLStr(Session("County")) & "'" &_
", Postcode = '" & CleanSQLStr(Session("Postcode")) & "'" &_
", Country = '" & CleanSQLStr(Session("Country")) & "'" &_
", Telephone = '" & CleanSQLStr(Session("Telephone")) & "'" &_
", DeliveryAddressSameAsInvoice = " & BoolToInt(Session("DeliveryAddressSameAsInvoice")) &_
", DeliveryName = '" & CleanSQLStr(Session("DeliveryName")) & "'" &_
", DeliveryCompanyName = '" & CleanSQLStr(Session("DeliveryCompanyName")) & "'" &_
", DeliveryAddressLine1 = '" & CleanSQLStr(Session("DeliveryAddress1")) & "'" &_
", DeliveryAddressLine2 = '" & CleanSQLStr(Session("DeliveryAddress2")) & "'" &_
", DeliveryTown = '" & CleanSQLStr(Session("DeliveryTown")) & "'" &_
", DeliveryCounty = '" & CleanSQLStr(Session("DeliveryCounty")) & "'" &_
", DeliveryPostcode = '" & CleanSQLStr(Session("DeliveryPostcode")) & "'" &_
", DeliveryCountry = '" & CleanSQLStr(Session("DeliveryCountry")) & "'" &_
", Subscribe = " & BoolToInt(Session("Subscribe")) &_
", HearAboutUs = '" & CleanSQLStr(Session("HearAboutUs")) & "'" &_
", VATNumber='" & CleanSQLStr(Session("VATNumber")) & "'" &_
" WHERE CustomerID = '" & CleanSQLStr(LCustomerID) & "'"
' (SS,19/6/12) added error handling to prevent exposure of SQL if following fails, we don't want to show the hash/encryption functions
On Error Resume Next
ExecuteQuery(LMySQL)
' (SS,19/6/12) check for error, unlikely to ever occur, prevent exposure of SQL if it ever does
If Err.Number <> 0 Then
LogErrorToFile("Following error occurred while in CreateAccount: " & Err.Description)
Response.Write("Updating account details failed. Sorry for any inconvenience caused. Please contact us.")
Exit Function
End If
On Error GoTo 0 ' cancel the error handling
' (SS,6/10/11) to check and note VAT Number validation
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
NoteVATValidation "customers", "CustomerID = '" & CleanSQLStr(LCustomerID) & "'"
CreateAccount = True
End Function
' (SS,10/11/09)
Sub CheckCustomerFieldChange(AName, AName2)
Dim LName2, LFieldNameFormatted, LFieldValueOld, LFieldValueNew
LFieldNameFormatted = AName & ": " & Space(12 - Len(AName))
LName2 = AName2
If LName2 = "" Then LName2 = AName
LFieldValueOld = Trim(NB(GetQueryValue(AName)))
LFieldValueNew = Trim(Session(LName2))
FCustomerDetailsOld = FCustomerDetailsOld & LFieldNameFormatted & LFieldValueOld & NL
FCustomerDetailsNew = FCustomerDetailsNew & LFieldNameFormatted & LFieldValueNew & NL
If LFieldValueOld <> LFieldValueNew Then
FCustomerFieldChange = True
FCustomerFieldsChangedList = FCustomerFieldsChangedList + iif(FCustomerFieldsChangedList = "", "", ", ") + AName
End If
End Sub
' (SS,10/11/09)
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
Sub CheckCustomerRecordChange(ACustomerID)
Dim LTitle, LFirstName, LSurname, LCompanyName, LAddressLine1, LAddressLine2, LTown, LCounty, LPostcode, LCountry, LTelephone, LEmailAddress, LVATNumber
OpenQuery("SELECT * FROM customers WHERE CustomerID = '" & CleanSQLStr(ACustomerID) & "'")
FCustomerFieldChange = False
FCustomerDetailsOld = ""
FCustomerDetailsNew = ""
FCustomerFieldsChangedList = ""
CheckCustomerFieldChange "Title", ""
CheckCustomerFieldChange "FirstName", ""
CheckCustomerFieldChange "Surname", ""
CheckCustomerFieldChange "CompanyName", ""
CheckCustomerFieldChange "AddressLine1", "Address1"
CheckCustomerFieldChange "AddressLine2", "Address2"
CheckCustomerFieldChange "Town", ""
CheckCustomerFieldChange "County", ""
CheckCustomerFieldChange "Postcode", ""
CheckCustomerFieldChange "Country", ""
CheckCustomerFieldChange "EmailAddress", "Email"
CheckCustomerFieldChange "VATNumber", ""
CloseQuery
' if field changed then send the email showing what fields were changed
If FCustomerFieldChange Then
Dim LBody
LBody = "Details for customer " & ACustomerID & " have been changed from:" & NL & NL & FCustomerDetailsOld & NL & "to: " & NL & NL & FCustomerDetailsNew
LBody = LBody & NL & "Fields changed: " & FCustomerFieldsChangedList
SendEmail FEmailContactTo, FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom, GetStoreName & " customer record change notification", LBody, False
End If
End Sub
' (SS,15/8/05) returns true if order is for current account, to prevent users from view orders of other customers
Function IsOrderForThisAccount(AOrderNo)
IsOrderForThisAccount = GetValueFromQuery("RecordCount", "SELECT COUNT(*) As RecordCount FROM orders WHERE OrderNo = '" & CleanSQLStr(AOrderNo ) & "' AND CustomerID = '" + CleanSQLStr(Session("AccountID")) + "'") = 1
End Function
' (SS,8/3/13) modifies the status suitable for showing to customer, i.e. COMPLETED is converted to "DESPATCHED"
Function StatusForCustomer(AStatus)
If AStatus = ORDER_COMPLETED_STATUS Then
StatusForCustomer = "DESPATCHED"
Else
StatusForCustomer = AStatus
End If
End Function
' (SS,14/8/05) list of orders to go here
' (SS,5/6/07) added LOrdersExist parameter, removed ShowAccountPreviousOrdersNone routine
' (SS,13/11/13) added code to only show despatch details columns if there are such details
Sub ShowPreviousOrders
Dim LOrderNo, LDateOfOrder, LTotal, LStatus, LHRef, LOrdersExist, LQueryOpened, LCourier, LTrackingNo, LTrackingURL, LTrackingPostcode
Dim LShowDespatchDetails ' (SS,13/11/13)
If Session("AccountID") = "" Then
LOrdersExist = False ' make sure orders with no customer id aren't shown
LQueryOpened = False
Else
' (SS,13/11/13) added following to detect whether there are despatch details, i.e. DeliveryAgent, Courier, TrackingNo and Packed By etc. have been entered for customer, and only then show them, show sites don't fill in these fields
LShowDespatchDetails = GetSQLValue("SELECT COUNT(*) FROM orders WHERE CustomerID = '" + CleanSQLStr(Session("AccountID")) + "' AND NOT (DeliveryAgentDate IS NULL AND TRIM(COALESCE(DeliveryAgentName, '')) = '' AND TRIM(COALESCE(Courier, '')) = '' AND TRIM(COALESCE(TrackingNo, '')) = '' AND TRIM(COALESCE(PackedBy, '')) = '')") > 0
LQueryOpened = True
Dim LWhere
' (SS,28/5/09) added AWAITING_PAYMENT_STATUS
LWhere = "(Status = '" + AWAITING_PAYMENT_STATUS + "' OR Status = '" + PAYMENT_RECEIVED_STATUS + "' OR Status = '" + PAYMENT_ON_ACCOUNT_STATUS + "' OR Status = '" + ORDER_COMPLETED_STATUS + "')"
' (SS,12/5/10) replaced '%d %M %Y' with GetLanguageDateFormat(True)
' (SS,8/3/13) added DateDespatchedFormatted
OpenQuery("SELECT *, DATE_FORMAT(DateTimeOrdered, '" & GetLanguageDateFormat(True) & "') AS DateOfOrderLong, DATE_FORMAT(DeliveryAgentDate, '" & GetLanguageDateFormat(False) & "') AS DateDespatchedFormatted FROM orders WHERE CustomerID = '" + CleanSQLStr(Session("AccountID")) + "' AND " + LWhere + " ORDER BY DateTimeOrdered DESC")
LOrdersExist = Not EndOfQuery ' (SS,5/6/07)
End If
' (SS,5/6/07) added parameters
' (SS,13/11/13) added LShowDespatchDetails
ShowAccountPreviousOrdersHeader LOrdersExist, Session("AccountID"), GetFullName(Session("Title"), Session("FirstName"), Session("Surname")), Session("Email"), LShowDespatchDetails
If LQueryOpened Then
Do While Not EndOfQuery
LOrderNo = GetQueryValue("OrderNo")
LDateOfOrder = GetQueryValue("DateOfOrderLong")
' (SS,1/8/17) set the currency code, rate, symbols for calls to CorrectCurrencyNV etc to work correctly to show the values using the actual historical currency and exchange rate
SetCurrencyForOrder GetFieldValue("CurrencyCode"), GetFieldValue("ExchangeRate")
LTotal = CorrectCurrency(GetQueryValue("GrandTotal")) ' (SS,12/5/10) replaced FormatNumber with CorrectCurrency
LStatus = GetQueryValue("Status")
LHRef = "products.asp?page=account&orderno=" & LOrderNo
LCourier = GetQueryValue("Courier")
LTrackingNo = Trim(GetQueryValue("TrackingNo"))
' if tracking no is a URL then use that and take the number after the last query (after last "=")
If IsURL(LTrackingNo) Then
LTrackingURL = LTrackingNo
LTrackingNo = GetLastQueryStringValue(LTrackingURL)
Else
' (SS,16/5/13) added Postcode because some courier sites require it
LTrackingPostcode = NB(GetQueryValue("DeliveryPostcode"))
If LTrackingPostcode = "" Then LTrackingPostcode = NB(GetQueryValue("Postcode"))
LTrackingURL = GetTrackingURLForCourier(GetQueryValue("DeliveryAgentName"), LCourier, LTrackingNo, LTrackingPostcode)
End If
' (SS,8/3/13) added StatusForCustomer to convert COMPLETED to DESPATCHED, added PackedBy, DeliveryAgentDate, Courier, TrackingNo, TrackingURL
' (SS,13/11/13) added LShowDespatchDetails
ShowAccountPreviousOrdersDetail LOrderNo, LDateOfOrder, LTotal, StatusForCustomer(LStatus), LHRef, LShowDespatchDetails, GetQueryValue("PackedBy"), GetQueryValue("DateDespatchedFormatted"), LCourier, LTrackingNo, LTrackingURL
NextQueryRecord
Loop
End If
If LQueryOpened Then CloseQuery
ShowAccountPreviousOrdersFooter(LOrdersExist)
End Sub
' (SS,8/3/13) in future add a common table for holding courier tracking details, or maybe a local lookup table called couriers
' (SS,16/5/13) added APostcode for APC Streetwise
' (SS,14/4/14) added UKMail and UPS
' (SS,12/9/14) added ParcelForce
' (SS,19/4/16) added direct tracking URLs for ParcelForce, RoyalMail and APC
' (SS,3/7/18) tracking not working for air mail due to new delivery agents "Tracked Airmail" and "Untracked Royal Mail"
Function GetTrackingURLForCourier(ADeliveryAgentName, ACourier, ATrackingNo, APostcode)
Dim LDeliveryAgentName, LCourier, LResult, LTrackingNo, LPos
LDeliveryAgentName = UCase(Trim(ADeliveryAgentName))
LCourier = UCase(Trim(ACourier))
LTrackingNo = Trim(ATrackingNo) ' (SS,16/5/13)
If LTrackingNo = "" Or LCourier = "" Then
LResult = ""
' (SS,16/5/13) added LDeliveryAgentName = "TNT" previously it was just LCourier = "TNT"
ElseIf LDeliveryAgentName = "TNT" Or LCourier = "TNT" Then
' LResult = "http://www.tnt.com/webtracker/tracking.do?respCountry=us&respLang=en&navigation=1&page=1&sourceID=1&sourceCountry=ww&plazaKey=&refs=&requesttype=GEN&searchType=CON&ref=" & ATrackingNo
' (SS,12/3/13) replaced above with following
LResult = "http://www.tnt.com/webtracker/tracking.do?navigation=1&searchType=CON&respLang=en&respCountry=GENERIC&genericSiteIdent=.&cons=" & LTrackingNo
' (SS,19/4/16) added Or LDeliveryAgentName = "AIRMAIL"
' (SS,3/7/18) added "TRACKED AIRMAIL" and "TRACKED ROYAL MAIL"
ElseIf LDeliveryAgentName = "ROYALMAIL" Or LDeliveryAgentName = "AIRMAIL" Or LDeliveryAgentName = "TRACKED AIRMAIL" Or LDeliveryAgentName = "TRACKED ROYAL MAIL" Then
LResult = "http://www.royalmail.com/track-trace?trackNumber=" & LTrackingNo ' (SS,19/4/16) added ?trackNumber=" & LTrackingNo
' (SS,12/9/14) added ParcelForce
ElseIf LDeliveryAgentName = "PARCELFORCE" Then
LResult = "http://www.parcelforce.com/track-trace?trackNumber=" & LTrackingNo ' (SS,19/4/16) added ?trackNumber=" & LTrackingNo
' (SS,16/5/13) added APC and streetwise
ElseIf LDeliveryAgentName = "APC-OVERNIGHT.COM" Or LDeliveryAgentName = "APC" Then
' ignore the "Consignment Number: " prefix if there is one
'If LCase(Left(LTrackingNo, 19)) = "consignment number:" Then LTrackingNo = Trim(Mid(LTrackingNo, 20))
' if there's a space then get the number after the last space
'LPos = InStrRev(LTrackingNo, " ")
'If LPos > 0 Then LTrackingNo = Trim(Mid(LTrackingNo, LPos + 1))
' LResult = "http://www.apc-overnight.com/apc/quickpod.php?txtpostcode=" & URLEncode(APostcode) & "&txtconno=" & URLEncode(LTrackingNo) & "&Track=Track&type=1"
' (SS,13/1/16) replaced above with following which is no longer working, i.e. tracking no can't be passed to tracking site
'LResult = "'https://apc-overnight.com/receiving-a-parcel/tracking"
' (SS,19/4/16) replaced above with following
LResult = "https://emea.netdespatch.com/mba/23906x0/track/?type=3&ref=" & LTrackingNo
' (SS,14/4/14) added UKMail and UPS
ElseIf LDeliveryAgentName = "UKMAIL" Then
LResult = "https://www.ukmail.com/manage-my-delivery/manage-my-delivery" ' we can add the tracking number here, customers will have to copy and paste
ElseIf LDeliveryAgentName = "UPS" Then
' (SS,21/8/14) corrected "1Z57606X6899460764" to URLEncode(LTrackingNo)
LResult = "http://wwwapps.ups.com/WebTracking/track?track.x=Track&trackNums=" & URLEncode(LTrackingNo)
Else
LResult = ""
End If
GetTrackingURLForCourier = ReplaceStr(LResult, "&", "&")
End Function
' (SS,6/8/04) '
Sub DoAccountLogout()
Session("AccountID") = ""
ClearAutoLoginFromCookie ' (SS,20/9/12) to prevent cookies from auto loggin in, if feature enabled
ShowAccountLogoutHeader ' (SS,15/6/11) was ShowAccountLoginHeader(False)
ShowAccountLogoutSuccess
ShowAccountLogoutFooter ' (SS,15/6/11)
End Sub
' (SS,6/8/04)
Sub DoForgotPassword()
Dim LErrorMessage, LShowForgotPasswordForm, LEmail
LErrorMessage = ""
LShowForgotPasswordForm = True
ShowForgotPasswordHeader
' if previously tried to post form then check email address entered'
If CleanRequestForm("postform") = "yes" Then
LEmail = Trim(CleanRequestForm("Email"))
If LEmail = "" Then
LErrorMessage = tr("Email address must be entered") & "."
ElseIf Not IsValidEmailAddress(LEmail) Then ' (SS,19/5/09) replaced InStr..etc with Not IsValidEmailAddress
LErrorMessage = tr("Valid Email address must be entered") & "."
End If
If LErrorMessage = "" Then
If SendForgottenPassword(LEmail) Then
ShowForgotPasswordThankYou
LShowForgotPasswordForm = False
' add enquiry to contact database '
AddContactToDatabaseAndEmail "Forgot Password", "Email", "", False
Else
LErrorMessage = tr("Email address not found. Please enter your correct Email address.")
End If
End If
End If
If LShowForgotPasswordForm Then ShowForgotPasswordForm(LErrorMessage)
ShowForgotPasswordFooter
End Sub
' (SS,6/8/04) searches customer database for given email address and sends the password '
' (SS,18/6/12) modified to make more secure, reset password rather than sending the password
Function SendForgottenPassword(AEmailAddress)
Dim NL
NL = Chr(13) + Chr(10)
' (SS,18/6/12) renamed LAccountID to LCustomerID, and LPassword to LResetKey
Dim LCustomerID, LResetKey, LFound
OpenQuery("SELECT * FROM customers WHERE EmailAddress = '" & CleanSQLStr(AEmailAddress) & "'")
If EndOfQuery Then
LFound = False
Else
LCustomerID = GetQueryValue("CustomerID")
' LPassword = GetQueryValue("Password")
' (SS,18/6/12) above replaced with following to generate a random key for resetting password
LResetKey = GetRandomPassword(10)
' (SS,18/6/12) save the key and date/time of request in customer record
ExecuteQuery("UPDATE customers SET PasswordResetKey = '" & CleanSQLStr(LResetKey) & "', PasswordResetDateTime = NOW() WHERE CustomerID = '" & CleanSQLStr(LCustomerID) & "'")
LFound = True
End If
CloseQuery
If LFound Then
Dim LBody
' (SS,18/6/12) changed "Your Account ID and password are displayed below" to Please click the link below to reset your password
LBody = trx("This email has been sent to you because you informed us that you forgot your password for <x>", GetStoreName) & "." & NL & NL &_
tr("Please click the link below to reset your password") & ":" & NL & NL
' (SS,28/6/10) added tr to following two lines
'LBody = LBody + tr("Account ID") & ": " & LAccountID & NL
'LBody = LBody + tr("Password") & ": " & LPassword
' (SS,18/6/12) replaced above with following, i.e. a link to reset the password
LBody = LBody + GetStoreURL & "?page=account&cmd=rp&id=" & LCustomerID & "&key=" & LResetKey
' (SS,19/6/12) added following to warn about expiry
LBody = LBody & NL & NL & tr("This link will expire in 4 hours.")
SendEmail AEmailAddress, "", "", FEmailContactFrom, GetStoreName & " " & tr("Password Reset"), LBody, False
End If
SendForgottenPassword = LFound
End Function
' (SS,18/6/12)
Sub DoResetPassword
' check that id, reset key are valid and date/time is less than 4 hours
' make sure password is the minimum length and that both password are the same
Dim LCustomerID, LKey, LErrorMessage, LMessage, LShowResetPasswordForm
LCustomerID = CleanSQLStr(CleanRequestQueryString("id"))
LKey = CleanSQLStr(CleanRequestQueryString("key"))
LErrorMessage = ""
LMessage = ""
LShowResetPasswordForm = True
ShowResetPasswordHeader
' checks of customer id match, key match, and key not blank, and date time hasn't expired, currently 4 hours allowed
If GetSQLValueAsString("SELECT CustomerID FROM customers WHERE CustomerID = '" & LCustomerID & "' AND PasswordResetKey = '" & LKey & "' AND COALESCE(PasswordResetKey, '') <> '' AND DATE_ADD(PasswordResetDateTime, INTERVAL 4 HOUR) > NOW()") = "" Then
LErrorMessage = tr("Invalid or expired Password Reset URL")
LShowResetPasswordForm = False
' if previously tried to post form then check password entered
ElseIf CleanRequestForm("postform") = "yes" Then
LErrorMessage = CheckNewPassword
' if password okay then save the password
If LErrorMessage = "" Then
LShowResetPasswordForm = False
On Error Resume Next ' to prevent exposure of SQL command if error does occur, highly unlikely to happen
ExecuteQuery("UPDATE customers SET Password = " + GetSQLForPasswordEncryption(CleanRequestForm("Password"), False) + " WHERE CustomerID = '" & LCustomerID & "'")
' check for error, unlikely to ever occur, prevent exposure of SQL if it ever does
If Err.Number <> 0 Then
LogErrorToFile("Following error occurred while in DoResetPassword: " & Err.Description)
Response.Write("Resetting password failed. Sorry for any inconvenience caused. Please contact us.")
Exit Sub
End If
On Error GoTo 0 ' cancel the error handling
LMessage = tr("Your password has been reset.")
End If
End If
ShowResetPasswordForm LErrorMessage, LMessage, LShowResetPasswordForm
ShowResetPasswordFooter
End Sub
Sub DoCreateAccount
ShowCreateAccountHeader
ShowCreateAccountForm("")
ShowCreateAccountFooter
End Sub
' use for adding web form contact enquiry to database
' called from email asps
' (SS,3/2/04) changed to allow any number of form values to be added, as listed in AFieldList
' also up to 3 special values
' (SS,4/2/04) wanted to used optional parameters but they don't work in vbscript
' (SS,6/8/04) added ASendMail parameter, so things can be added to database without email being sent
' (SS,5/3/13) removed the NL Dim, which was for two lines, doubled up the NLs
' contacts table also restructured to removed the fields Company, Position, AddToMailingList
' fields Address, Postcode will become redundant in new template, moved these to the end, retain for backward compatibility for old template sites
' new fields Country and DateTimeFollowedUp added
' (SS,15/10/18) added "<br>"'s because email has changed from plain text to HTML
Sub AddContactToDatabaseAndEmail(AType, AFieldList, AEnquiryFieldList, ASendEmail)
Dim LEmailBody
Dim i, c, LLength, LFieldName, LFieldValue, LEnquiryFieldValue
LEmailBody = ""
OpenTable "contacts"
AddRecord
PutFieldValue "Type", AType
PutFieldValue "DateTimeContacted", Now
' go through list of fields placing the values
LLength = Len(AFieldList)
LFieldName = ""
For i = 1 To LLength
c = Mid(AFieldList, i, 1)
' if comma or last char, i.e. we have a field '
If c <> "," Then LFieldName = LFieldName + c
If c = "," Or i = LLength Then
LFieldName = Trim(LFieldName)
If LFieldName <> "" Then
LFieldValue = CleanRequestForm(LFieldName)
If LFieldValue <> "" Then
PutFieldValue LFieldName, LFieldValue
LEmailBody = LEmailBody + LFieldName + ": " & LFieldValue & NL & NL
End If
LFieldName = ""
End If
End If
Next
' go through list of fields in Enquiry Fields placing the values
LLength = Len(AEnquiryFieldList)
LFieldName = ""
LEnquiryFieldValue = ""
For i = 1 To LLength
c = Mid(AEnquiryFieldList, i, 1)
' if comma or last char, i.e. we have a field '
If c <> "," Then LFieldName = LFieldName + c
If c = "," Or i = LLength Then
LFieldName = Trim(LFieldName)
If LFieldName <> "" Then
LFieldValue = CleanRequestForm(LFieldName)
If LFieldValue <> "" Then
' (SS,6/3/13) added If... to only added fieldname, colon and newlines if AEnquiryFieldList has more than one value, this never actually occurs in this app
If InStr(AEnquiryFieldList, ",") = 0 Then
LEnquiryFieldValue = LFieldValue
Else
LEnquiryFieldValue = LEnquiryFieldValue + LFieldName + ": " & LFieldValue & NL & NL
End If
LEmailBody = LEmailBody + LFieldName + ": " & LFieldValue & NL & NL
End If
LFieldName = ""
End If
End If
Next
If LEnquiryFieldValue <> "" Then
PutFieldValue "Enquiry", LEnquiryFieldValue
End If
PostRecord
CloseTable
' (SS,28/6/10) added GetStoreName & " " & for email subject to be more obvious e.g. Galleon Supplies Contact Form (instead of just Contact Form)
' (SS,30/6/10) as requested by Neil, changed "Contact" to "Enquiry" instead of "Contact Form", to allow reply without renaming of subject
Dim LSubject
If AType = "Contact" Then
LSubject = "Enquiry"
Else
LSubject = AType + " Form" ' (SS,30/6/10) how it was
End If
' (SS,25/11/11) added GetSQLLastInsertID to keep enquiry threads separate in Outlook, requested by Neil, I have the same issue in my Outlook
If ASendEmail Then
' (SS,15/10/18) added following line to replace all linefeed and carriage returns with HTML BRs, because email is now HTML
' perhaps added full HTML tags in future, i.e. HTML, BODY etc. and set the font. Perhaps put in a table.
LEmailBody = ReplaceNewLinesWithBR(LEmailBody)
SendContactEmail GetStoreName & " " & LSubject & " - " & GetSQLLastInsertID, LEmailBody
End If
End Sub
' (SS,3/2/04) new routine which just sends the email '
' given subject and body, uses mail server and email addresses from table '
' (SS,27/7/05) modified to call SendMail '
' (SS,5/9/18) modified to send as HTML instead of Plain Text
Sub SendContactEmail(ASubject, ABody)
' try to use the original email address as the from address, if blank use the default one '
Dim LFromEmailAddress
LFromEmailAddress = Trim(CleanRequestForm("Email"))
If LFromEmailAddress = "" Then LFromEmailAddress = FEmailContactFrom
SendEmail FEmailContactTo, FEmailContactBcc1, FEmailContactBcc2, LFromEmailAddress, ASubject, ABody, True ' (SS,5/9/18) changed False to True to send as HTML
End Sub
' (SS,27/11/12) used to send email to site administration to alert them about something
' (SS,24/8/17) added AIsHTML parameter, changed False to AIsHTML in call to SendEmail
Sub SendAlertEmail(ASubject, ABody, AIsHTML)
Dim LSubject
LSubject = "ALERT!!! " + ASubject
SendEmail FEmailOrderConfirmationBcc1, FEmailOrderConfirmationBcc2, "", FEmailOrderConfirmationFrom, LSubject, ABody, AIsHTML
End Sub
' (SS,18/4/18) special debug web log email sent only to contactforms@itpartnership.com
Sub SendWebLogEmail(AMessage)
Dim LSubject, LBody
LSubject = "Web Log for " & GetStoreName
LBody = GetWebLog
If AMessage <> "" Then
LBody = AMessage & NL & LBody
End If
SendEmail "contactforms@itpartnership.com", "", "", FEmailOrderConfirmationFrom, LSubject, LBody, False
End Sub
' (SS,24/8/17) same as SendAlertEmail but without adding "ALERT!!! " unless AIsAlert is True
Sub SendNotificationEmail(ASubject, ABody, AIsAlert, AIsHTML)
Dim LSubject
If AIsAlert Then
LSubject = "ALERT!!! " + ASubject
Else
LSubject = ASubject
End If
SendEmail FEmailOrderConfirmationBcc1, FEmailOrderConfirmationBcc2, "", FEmailOrderConfirmationFrom, LSubject, ABody, AIsHTML
End Sub
' (SS,23/8/17) new version which now calls SendEmailByCDO instead of SendEmailByDundas, previous SendMail renamed to SendEmailByDundas
Function SendEmail(AEmailAddress, ABCCEmailAddress, ABCCEmailAddress2, AFromEmailAddress, ASubject, ABody, AIsHTML)
SendEmail = SendEmailByCDO(AEmailAddress, ABCCEmailAddress, ABCCEmailAddress2, AFromEmailAddress, ASubject, ABody, "", AIsHTML, "", "")
End Function
' (SS,6/8/04) sends email to given address '
' (SS,27/7/05) added two new parameters ABCCEmailAddress, AFromEmailAddress '
' also added secondary mailserver '
' (SS,4/8/05) added ABCC2EmailAddress parameter
' (SS,24/3/06) modified to ensure script does not end with runtime error if email fails
' also an attempt is now made to send the emails again individually
' if main email address fails, then the BCCs are sent with error message added to the start of body
' hopefully this will stop some Worldpay callback failures that occurred with
' following in response - "The RCPT command failed. The specified account does not exsist."
' (SS,8/6/07) added AIsHTML parameter, changed from Sub to Function, returns True if email successful
' (SS,23/8/17) renamed from SendEmail to SendEmailByDundas, now deprecated
Function SendEmailByDundas(AEmailAddress, ABCCEmailAddress, ABCCEmailAddress2, AFromEmailAddress, ASubject, ABody, AIsHTML)
' send the email using Dundas Mailer Control
Dim objEmail 'Mailer control
Set objEmail = Server.CreateObject("Dundas.Mailer")
' (SS,10/6/07) replaced Application("MailServer") with GetMailServer(1) and Application("MailServer2") with GetMailServer(1)
objEmail.SMTPRelayServers.Add GetMailServer(1)
If GetMailServer(2) <> "" Then objEmail.SMTPRelayServers.Add GetMailServer(2)
'set Mailer control properties and collection items
objEmail.TOs.Add AEmailAddress
If ABCCEmailAddress <> "" Then objEmail.BCCs.Add ABCCEmailAddress
If ABCCEmailAddress2 <> "" Then objEmail.BCCs.Add ABCCEmailAddress2
objEmail.FromAddress = AFromEmailAddress
objEmail.Subject = ASubject
If AIsHTML Then
objEmail.HTMLBody = ABody
Else
objEmail.Body = ABody
End If
'send email
On Error Resume Next ' (SS,24/3/06) to trap error
objEmail.SendMail
' (SS,24/3/06) added following to check for error and show message
' also resend the emails individually
If Err.Number <> 0 Then
' try sending email again
objEmail.BCCs.Clear
On Error Resume Next
objEmail.SendMail
If Err.Number <> 0 Then
' add error message to start of body of the BCCs '
objEmail.Body = "*** Sending Email to " + AEmailAddress + " failed: " & Err.Description + "***" & NL & NL & objEmail.Body
Response.Write "Sending Email to " + AEmailAddress + " failed: " & Err.Description
End If
' send the BCC emails if assigned
If ABCCEmailAddress <> "" Then
objEmail.TOs.Clear
objEmail.TOs.Add ABCCEmailAddress
On Error Resume Next
objEmail.SendMail
End If
If ABCCEmailAddress2 <> "" Then
objEmail.TOs.Clear
objEmail.TOs.Add ABCCEmailAddress2
On Error Resume Next
objEmail.SendMail
End If
End If
SendEmailByDundas = Err.Number <> 0 ' (SS,10/6/07) return true if successful
'you can test for the success/failure of the operation by examining VBScript's Err object here
Set objEmail = Nothing
End Function
' (SS,10/6/06) send email using CDO, returns False if there was a failure
' (SS,28/9/12) added AEmbeddedImage to allow image to be embedded inside the email
Function SendEmailByCDO(ATo, ABcc, ABcc2, AFrom, ASubject, ABody, AFiles, AIsHTML, AUrl, AEmbeddedImage)
' Create CDO message object
Dim objMessage
Set objMessage = CreateObject("CDO.Message")
' Set configuration fields.
With objMessage.Configuration.Fields
Const SCHEMA_PREFIX = "http://schemas.microsoft.com/cdo/configuration/"
' Original sender email address
.Item(SCHEMA_PREFIX & "sendemailaddress") = AFrom
' SMTP settings - without authentication, using standard port 25 on host smtp
.Item(SCHEMA_PREFIX & "sendusing") = 2 ' cdoSendUsingPort
.Item(SCHEMA_PREFIX & "smtpserverport") = 25
.Item(SCHEMA_PREFIX & "smtpserver") = GetMailServer(1)
' SMTP Authentication
.Item(SCHEMA_PREFIX & "smtpauthenticate") = 0 ' cdoAnonymous
' Timeout
.Item(SCHEMA_PREFIX & "smtpconnectiontimeout") = 10
.Update
End With
' Set other message fields.
With objMessage
' From, To, Subject And Body are required.
.From = AFrom
.To = ATo
.Subject = ASubject
' if AUrl is provided then build the email from given web page
If AUrl <> "" Then
.CreateMHTMLBody AUrl
ElseIf AIsHTML Then
' if HTML then set the HTML body
.HTMLBody = ABody
' (SS,28/9/12) embed image if supplied, image must be in the images folder
If AEmbeddedImage <> "" Then
' (SS,28/9/12) embed the image, the HTML must contain something like <img src="cid:myimage.gif">
' with help from http://support.jodohost.com/threads/tut-how-to-add-embedded-images-in-cdo-mail.7692/
Dim objBP
' Const CdoReferenceTypeName = 1
Set objBP = objMessage.AddRelatedBodyPart(Server.MapPath("/images/" & AEmbeddedImage), AEmbeddedImage, 1)
objBP.Fields.Item("urn:schemas:mailheader:Content-ID") = "<" & AEmbeddedImage & ">"
objBP.Fields.Update
End If
Else
' else normal plain text
.TextBody = ABody
End If
' Blind copy and attachments are optional.
If ABcc <> "" Then .BCC = ABcc
If ABcc2 <> "" Then .BCC = .BCC + ";" + ABcc2
If AFiles <> "" Then .AddAttachment AFiles
' Send the email and check for failure
On Error Resume Next
.Send
' if failed then try other mail servers
Dim LMailServer, LMailServerNo
LMailServerNo = 2
Do While Err.Number <> 0
LMailServer = GetMailServer(LMailServerNo)
If LMailServer = "" Then Exit Do
LMailServerNo = LMailServerNo + 1
With objMessage.Configuration.Fields
.Item(SCHEMA_PREFIX & "smtpserver") = LMailServer
.Update
End With
On Error Resume Next ' cancels the previous error message, i.e. Err.Number starts again at zero
.Send
Loop
End With
' Returns zero If succesfull. Error code otherwise
SendEmailByCDO = Err.Number = 0
' clean up
Set objMessage = Nothing
End Function
' (SS,30/1/08) send email using itpMailer, returns False if there was a failure
' (SS,10/12/15) itpEmailer can't handle SSL (https://) URLs, fixed by fetching the body of AURL using new GetWebPage routine in dbfunctions.asp
Function SendEmailByITPEmailer(ATo, ABcc1, ABcc2, AFrom, ASubject, ABody, AIsHTML, AURL)
Dim objEmail
Set objEmail = Server.CreateObject("itpEmailer.itpEmailer")
objEmail.SMTPServer1 = GetMailServer(1)
objEmail.SMTPServer2 = GetMailServer(2)
objEmail.ToAddress = ATo
objEmail.FromAddress = AFrom
objEmail.Bcc1 = ABcc1
objEmail.Bcc2 = ABcc2
objEmail.Subject = ASubject
If AURL <> "" Then
' objEmail.URL = AURL
' (SS,10/12/15) replaced above with following because itpEmailer can't handle SSL
objEmail.HTMLBody = GetWebPage(AURL)
ElseIf AIsHTML Then
objEmail.HTMLBody = ABody
Else
objEmail.Body = ABody
End If
On Error Resume Next ' to trap error
objEmail.Send
If objEmail.ErrorMessage <> "" Then
SendEmailByITPEmailer = False
Response.Write("*** Failed to send email: " + objEmail.ErrorMessage)
ElseIf Err.Number <> 0 Then
SendEmailByITPEmailer = False
Response.Write("*** Failed to send email: Error " & Err.Number & " - " & Err.Description)
Else
SendEmailByITPEmailer = True
End If
Set objEmail = Nothing
End Function
' (SS,17/4/07) all the modify basket commands now go through here
' this is do be done before inc-template-shopping-status.asp and inc-pages.asp
' is actually called from end of inc-main.asp
' also removed dedundant code from inc-pages.asp
Sub DoBasketOperation
Dim Lcmd, Lcode, Litemid
' xcmd takes priority over cmd
Lcmd = CleanRequest("xcmd")
Lcode = CleanRequest("xcode")
If Lcmd = "" Then
Lcmd = CleanRequest("cmd")
Lcode = CleanRequest("code")
Litemid = CleanRequest("itemid") ' (SS,5/7/11)
End if
' abort if no basket operation
If Lcmd = "" Then Exit Sub
If Lcmd = "buy" Or Lcmd = "buyl" Then ' (SS,28/5/07) added "buyl" for buy from product list (ensures it stays in list)
AddToBasket Lcode, CleanRequest("qty")
' (SS,25/10/18) following removed it's now redundant (was used by old detailed shopping status)
'ElseIf Lcmd = "delete" Then
' If Litemid <> "" Then
' DeductFromBasket Litemid ' (SS,5/7/11) replaced Lcode with Litemid
' Else ' (SS,12/9/12) if itemid is blank then use Lcode i.e. xcode (shopping status delete) which now actually holds the itemid
' DeductFromBasket Lcode
' End If
ElseIf Lcmd = "remove" Then
RemoveFromBasket Litemid ' (SS,5/7/11) replaced Lcode with Litemid
ElseIf Lcmd = "recalculate" Then
RecalculateBasket
End If
End Sub
' (SS,20/5/04) main products routine (was in inc-products-view.asp) '
' called from inc-products.asp
Sub ProductsMain
strCommand = CleanRequestQueryString("cmd")
If strCommand = "" Then strCommand = CleanRequestForm("cmd")
strSearch = CleanRequestQueryString("search")
strPage = CleanRequestQueryString("pageno")
strGroup = CleanRequestQueryString("grp") ' (SS,11/2/05)
strCategory = GetCategory ' (SS,17/9/08) replaced CleanRequestQueryString("cat") with GetCategory
strSubcategory = GetSubcategory ' (SS,26/7/11) replaced CleanRequestQueryString("subcat") with GetSubcategory
strProductCode = GetProductCodeQS ' (SS,7/11/18) replaced CleanRequestQueryString("code") with GetProductCodeQS
strQty = CleanRequestQueryString("qty")
strClientWidth = CleanRequestQueryString("cw") ' was clientwidth '
strClientHeight = CleanRequestQueryString("cl") ' was clientheight '
' (SS,13/8/15) set the sort by if
if CleanRequestQueryString("sortby") <> "" Then SetSortBy CleanRequestQueryString("sortby")
Dim Lxcmd
Lxcmd = CleanRequestQueryString("xcmd")
' (SS,20/5/04) changed to add to basket and stay in small page '
' (SS,19/8/06) added Lxcmd = "currency"
If Lxcmd = "delete" Or Lxcmd = "buy" Or Lxcmd = "currency" Then
' do nothing this would have been done in inc-pages.asp '
' (SS,17/4/07) now done in DoBasketOperations above, called before ProductsMain
ElseIf strCommand = "buy" Then
' AddToBasket strProductCode, strQty
' (SS,17/4/07) removed above, this operation is now done in DoBasketOperations, called before ProductsMain
' (SS,28/5/07) added following to ensure it doesn't go to product details page when buy
' was done on a product list (i.e. category/subcategory or search)
ElseIf strCommand = "buyl" Then ' (SS,28/5/07) buy from list
strProductCode = "" ' ensure we stay in the same list (i.e. don't go to product details page)
End If
' (SS,22/9/09) added following to do a seach when disabled product referred to from search engine
Dim LDoSearch
LDoSearch = False
' (SS,19/4/16) if order was created and user is actively viewing product, category, basket etc. page then make sure stock reserved (order placed) is added back into stock
If Not InCheckout Then
ClearExistingOrderPlaced False
End If
If strCommand = "remove" Then
' RemoveFromBasket strProductCode
' (SS,17/4/07) removed above, this operation is now done in DoBasketOperations, called before ProductsMain
ShowBasket
ElseIf strCommand = "recalculate" Then
' RecalculateBasket
' (SS,17/4/07) removed above, this operation is now done in DoBasketOperations, called before ProductsMain
ShowBasket
ElseIf strCommand = "basket" Then
ShowBasket
ElseIf strCommand = "checkout" Then
CheckOut
ElseIf strCommand = "payment" Then ' called if payment details are taken directly '
TakePayment
ElseIf strProductCode <> "" Then
' (SS,22/9/09) added following to check if product code exist, and do a search if it doesn't
' allows disabled products referred to from google to do a search rather than show product not found
If CheckEnabledProductExists(strProductCode) Then
ShowSingleProduct strProductCode
Else
strSearch = GetSearchForPhraseFromReferrer
If strSearch = "" Then ' if no search phrase then do the normal routine
ShowSingleProduct strProductCode
Else ' else do the search for the phrase from referrer
LDoSearch = True
End If
End If
'ElseIf strCommand = "sitemap" Then ' special product list to help with search engines, (SS,15/2/13) renamed from "list" to "sitemap"
' ShowHTMLSiteMap ' (SS,15/2/13) renamed from ShowFullList to ShowHTMLSiteMap, also removed strCategory, strSubcategory parameters
' (SS,19/2/13) remove, now called via PageContent sitemap with {SITEMAP} token
' (SS,19/2/15) new save basket feature
ElseIf strCommand = "savebasket" Then
SaveBasket
' (SS,19/2/15) new restore basket feature
ElseIf strCommand = "restorebasket" Then
RestoreBasket
' (SS,12/12/18) added cancel order feature
ElseIf strCommand = "cancelorder" Then
CancelOrder
' (SS,27/3/07) added SubcategoryListEnabled, to allow all products to be listed instead for the category
ElseIf SubcategoryListEnabled And (strSearch = "") And (strCategory <> "") And (strSubcategory = "") Then
ShowSubcategories(strCategory)
' (SS,27/3/07) added CategoryListEnabled, to allow all products to be listed instead for the group
ElseIf CategoryListEnabled And (strSearch = "") And (strCategory = "") And (strPage = "") Then
ShowCategories(strGroup) ' (SS,11/2/05) added strGroup '
Else
LDoSearch = True ' (SS,22/9/09) code from here moved below, because it's now called from two conditions
End If
If LDoSearch Then
' if search has a value then empty category and subcategory
If strSearch <> "" Then
strGroup = "" ' (SS,27/3/07)
strCategory = ""
strSubcategory = ""
End If
' (SS,1/7/11) added following for attribute search between min and max
' (SS,14/3/12) added LVal, to search for exact value instead min max
Dim LAttributeID, LMin, LMax, LVal
LAttributeID = CleanRequestQueryString("attributeid")
LMin = CleanRequestQueryString("min")
LMax = CleanRequestQueryString("max")
LVal = CleanRequestQueryString("val")
' (SS,27/3/07) added strGroup to following
' (SS,1/7/11) added LAttributeID, LMin, LMax
' (SS,14/3/12) added LVal parameter
ShowProducts strGroup, strCategory, strSubcategory, strSearch, strPage, LAttributeID, LMin, LMax, LVal
End If
End Sub
' (SS,5/8/04)
' (SS,26/5/07) removed parameters AProductCode, AStdPrice, ASalePrice
' (SS,6/7/11) added FOptionsPrice, GetPricingFromProductRecord must be called before calling this routine
' (SS,28/8/14) also gets called from GetPricingFromProductRecord with FOptionsPrice set to zero to get the price without options to pass to a custom routine
' (SS,28/3/17) now calls function CustomGetPriceWithoutOptions if it exists to allow price to be overridden
Function GetProductPrice
Dim LSalePrice, LPrice
LSalePrice = GetSalePrice ' (SS,22/5/07) removed AProductCode, ASalePrice from GetSalePrice parameters
If Not IsNull(LSalePrice) Then
LPrice = LSalePrice
Else
LPrice = GetStdPrice ' (SS,22/5/07) removed AProductCode, ASalePrice from GetStdPrice parameters
End If
' (SS,7/11/12) if MinCurrencyPricesEnabled and minimum current price (in GBP) is greater than price above then set to minimum price
If MinCurrencyPricesEnabled Then
' (SS,9/11/12) added LPrice > 0 to allow negative prices (credit) to work as before
If LPrice > 0 And FMinCurrencyPrice > LPrice Then LPrice = FMinCurrencyPrice
End If
' (SS,28/3/17) added following to override main price if custom routine defined, used in local mode to override normal prices
If FunctionExists("CustomGetPriceWithoutOptions") Then
LPrice = CustomGetPriceWithoutOptions(LPrice)
End If
' (SS,6/7/11) added FOptionsPrice
GetProductPrice = LPrice + FOptionsPrice
End Function
' (SS,10/9/14)
Function GetProductWeight
GetProductWeight = FPostalWeight
End Function
' (SS,5/8/04) added this to cope with Contract Prices '
' if Account Holder then prices are obtained from ContractPrices
' if they exist otherwise normal price is return '
' (SS,21/5/07) removed parameters AProductCode, AStdPrice replaced with FProductCode, FStdPrice
' Added CustomPricingEnabled to get from CustomGetStdPrice
' (SS,8/11/12) modified to return min currency price is minimum price facility enabled and set to this product
' (SS,7/3/13) removed references to removed table contractprices
Function GetStdPrice
Dim LResult
If CustomPricingEnabled Then
LResult = CustomGetStdPrice
Else
LResult = FStdPrice
End If
' (SS,8/11/12) if MinCurrencyPricesEnabled and minimum current price (in GBP) is greater than price above then set to minimum price
If MinCurrencyPricesEnabled Then
' (SS,9/11/12) added LResult > 0 to allow negative prices (credit) to work as before
If LResult > 0 And FMinCurrencyPrice > LResult Then LResult = FMinCurrencyPrice
End If
GetStdPrice = LResult
End Function
' (SS,5/8/04) if on contract then sale price is only return '
' if it's lower than the contract price '
' (SS,21/5/07) removed parameters AProductCode, ASalePrice replaced with FProductCode, FSalePrice
' Added CustomPricingEnabled to get from CustomGetSalePrice
' (SS,8/11/12) modified to return min currency price is minimum price facility enabled and set to this product
' (SS,7/3/13) removed references to contract prices
Function GetSalePrice
Dim LResult
If CustomPricingEnabled Then
LResult = CustomGetSalePrice
ElseIf FUseDiscountFieldForSpecialOffers Then
If IsDiscounted Then
LResult = GetDiscountedPrice(FStdPrice)
Else
LResult = Null
End If
Else
LResult = FSalePrice
End If
' (SS,8/11/12) if MinCurrencyPricesEnabled and minimum current price (in GBP) is greater than sale price then clear the sale price
If MinCurrencyPricesEnabled And Not IsNull(LResult) Then
' (SS,9/11/12) added LResult > 0 to allow negative prices (credit) to work as before
If LResult > 0 And FMinCurrencyPrice > LResult Then LResult = Null
End If
GetSalePrice = LResult
End Function
' (SS,18/5/07)
Function GetDiscountedPrice(APrice)
If IsDiscounted Then
GetDiscountedPrice = Round(APrice * (1 - FDiscountPercentage / 100), 2)
Else
GetDiscountedPrice = APrice
End If
End Function
' (SS,18/5/07)
Function IsDiscounted
If FUseDiscountFieldForSpecialOffers Then
IsDiscounted = FDiscountPercentage > 0
Else
IsDiscounted = False
End If
End Function
' (SS,29/8/12) because all Galleon products are discounted by 2 percentage
Function IsDiscountedOver2PC
If FUseDiscountFieldForSpecialOffers Then
IsDiscountedOver2PC = FDiscountPercentage > 2
Else
IsDiscountedOver2PC = False
End If
End Function
' (SS,26/5/07) moved here from ShowSpecialOffers and improved
Function GetDiscountPercentage
Dim LDiscountPercentage
' (SS,18/5/07) added If FUseDiscountFieldForSpecialOffers etc for new discount field
If FUseDiscountFieldForSpecialOffers Then
LDiscountPercentage = FDiscountPercentage
Else
Dim LStdPrice, LSalePrice
LStdPrice = GetStdPrice
LSalePrice = GetSalePrice
If IsNull(LSalePrice) Then
LDiscountPercentage = 0
' (SS,23/10/14) added following ElseIf to prevent overflow error if LStdPrice is 0
ElseIf LStdPrice = 0 Then
LDiscountPercentage = 0
Else
LDiscountPercentage = (LStdPrice - LSalePrice) / LStdPrice * 100
End If
End If
GetDiscountPercentage = LDiscountPercentage
End Function
' (SS,21/5/07)
' returns list of fields for use in SQL, takes into account various flags
' ATableName is used to prefix each field following by a dot, ATableName can be ""
' in following format "StdPrice, SalePrice, Price2, Price3, Price4" without leading and trailing commas
Function GetPricingFieldsForSQL(ATableName)
Dim LSQL
' (SS,28/8/14) added PostalWeight to allow it to be passed to CustomGetOptionsPriceAndWeight
LSQL = ", StdPrice, SalePrice, PostalWeight"
If FourPricingFieldsEnabled Then LSQL = LSQL + ", Price2, Price3, Price4"
If MinCurrencyPricesEnabled Then LSQL = LSQL + ", MinUSDPrice, MinEURPrice" ' (SS,7/11/12)
If UseDiscountFieldForSpecialOffers Then LSQL = LSQL + ", Discount"
If VATBandsEnabled Then LSQL = LSQL + ", VATBand"
LSQL = Replace(LSQL, ", ", ", " + Iif(ATableName = "", "", ATableName + "."))
GetPricingFieldsForSQL = Mid(LSQL, 3, Len(LSQL)) ' also removes leading comma
End Function
' (SS,18/5/07) gets the pricing values from product record and places in globals
' value in ABasketQty is only applicable when looking at the basket, this should be Qty value from basket, 0 otherwise
' (SS,6/7/11) removed ABasketQty, doesn't seem to be used for anything, added ACalculateOptionsPrice
' when ACalculateOptionsPrice is True FOptionsPrice is calculated from options in basket, else set to 0, and place in FOptionsPrice
' query GetOrderItemsQuery is based on a link between shoppingbaskets and products, this is open before calling this routine
' (SS,7/11/12) added code for Min Currency Prices
' (SS,23/4/13) added rounding to 2dp to FMinCurrencyPrice
' (SS,28/8/14) added calling of CustomGetOptionsPriceAndWeight routine if CUSTOM extra setting in settings, Options Price and Weight are adjusted by this routine
Sub GetPricingFromProductRecord(ACalculateOptionsPrice)
' FBasketQty = ABasketQty ' (SS,6/7/11) removed
FProductCode = GetFieldValue("ProductCode") ' because product code can be used to look up contract prices
FProductID = GetFieldValue("ProductID") ' (SS,6/7/11) because we need to calculate the options price
FPostalWeight = NZ(GetFieldValue("PostalWeight")) ' (SS,10/9/14)
' NZ necessary because values are DECIMAL MYSQL which ODBC is fetching as VarType 14 (not IsNumeric) TypeMismatch error otherwise, NZ converts this to double
If FourPricingFieldsEnabled Then
FPrice1 = NZ(GetFieldValue("StdPrice"))
FPrice2 = NZ(GetFieldValue("Price2"))
FPrice3 = NZ(GetFieldValue("Price3"))
FPrice4 = NZ(GetFieldValue("Price4"))
Else
FStdPrice = ENKN(GetFieldValue("StdPrice")) ' ENKN ensures number keeping null
FSalePrice = ENKN(GetFieldValue("SalePrice")) ' (SS,14/9/07) missed from previous version
' (SS,7/11/12) added following to get min currency price if MinCurrencyPricesEnabled from either MinUSDPrice of MinEURPrice
If MinCurrencyPricesEnabled Then
If IsCurrencyUSD Then
FMinCurrencyPrice = NZ(GetFieldValue("MinUSDPrice"))
ElseIf IsCurrencyEUR Then
FMinCurrencyPrice = NZ(GetFieldValue("MinEURPrice"))
Else
FMinCurrencyPrice = 0
End If
' if not zero then convert to GBP by dividing by currency rate
' (SS,23/4/13) added Round(.., 2) to ensure subtotal is the same as line total when single item
If FMinCurrencyPrice <> 0 Then FMinCurrencyPrice = Round(FMinCurrencyPrice / GetCurrencyRate, 2)
End If
End If
If UseDiscountFieldForSpecialOffers Then FDiscountPercentage = NZ(GetFieldValue("Discount"))
If VATBandsEnabled Then FVATBand = GetFieldValue("VATBand")
' (SS,6/7/11) total up chosen options in basket for this item
FOptionsPrice = 0
FOptionsWeight = 0
If ProductOptionsEnabled And ACalculateOptionsPrice Then
' (SS,28/8/14) calculate price without options which is passed onto custom routine later, must be called while FOptionsPrice is 0 setting
Dim LPriceWithoutOptions
LPriceWithoutOptions = GetProductPrice
' FOptionsPrice, FOptionsWeight globals used
Dim LSQL
' short alias names not used, i.e. so, po, because LOCK TABLES requires the tablename, and it'll be confusing if it used so, po
LSQL = "SELECT SUM(PriceAdjust), SUM(WeightAdjust) FROM shopping_basket_options " &_
"INNER JOIN product_option_values ON product_option_values.ProductOptionValueID = shopping_basket_options.ProductOptionValueID " &_
"WHERE ItemID = " & GetFieldValue("ItemID") & " AND shopping_basket_options.ProductOptionValueID <> 0"
If GetSQL2Values(LSQL, FOptionsPrice, FOptionsWeight) Then
' nulls converted to 0, also ensures field type is numeric
FOptionsPrice = NZ(FOptionsPrice)
FOptionsWeight = NZ(FOptionsWeight)
End If
' (SS,28/8/14) added following to call a custom routine to calculate the options price and weight
' routine is called with an dictionary list of each of the options that has "custom" in the extra settings
LSQL = "SELECT * FROM shopping_basket_options " &_
"INNER JOIN product_options ON product_options.ProductOptionID = shopping_basket_options.ProductOptionID " &_
"WHERE ItemID = " & GetFieldValue("ItemID") & " AND product_options.ExtraSettings LIKE '%CUSTOM%'"
OpenQuery2(LSQL)
' if custom option exists and function exists then pass on the values via dictionary, routine will add what i needs to to FOptionsPrice and FOptionsWeight
If Not EndOfQuery2 Then
If FunctionExists("CustomGetOptionsPriceAndWeight") Then
Dim objOptionsDictionary
Set objOptionsDictionary = CreateObject("Scripting.Dictionary")
objOptionsDictionary.CompareMode = vbTextCompare 'makes the keys case insensitive'
Do While Not EndOfQuery2
objOptionsDictionary.Add GetQueryValue2("OptionName"), GetQueryValue2("OptionValue")
NextQueryRecord2
Loop
' routine will use the options to add to FOptionsPrice and FOptionsWeight
CustomGetOptionsPriceAndWeight objOptionsDictionary, LPriceWithoutOptions, NZ(GetFieldValue("PostalWeight")), FOptionsPrice, FOptionsWeight
Set objOptionsDictionary = Nothing
End If
Else ' (SS,5/4/17) following is for normal options, i.e. without product_options.ExtraSettings LIKE '%CUSTOM%'
If FunctionExists("CustomGetNormalOptionsPrice") Then
FOptionsPrice = CustomGetNormalOptionsPrice(FOptionsPrice)
End If
End If
CloseQuery2
End If
End Sub
' (SS,21/5/07) returns product description up to maximum length
' if AMaxLengh is 0 then the full description is returned
' if AClean is True then the table and HTML tags are removed
' (SS,19/9/11) added ComposeDescription to also process the tokens, and removed Trim(RemoveComposes(LDesc)) which isn't applicable now due to ComposeDescription, tables and bullets will remain
Function GetProductDescription(AClean, AMaxLength)
Dim LDesc
LDesc = FProductDescription
If IsNull(LDesc) Then
LDesc = ""
Else
If AClean Then LDesc = Trim(RemoveComposes(LDesc)) ' (SS,13/9/12) remove the special tables and bullets for clean description
LDesc = ComposeDescription(LDesc, True, False) ' (SS,19/9/11)
If AClean Then LDesc = Trim(RemoveHTMLTags(LDesc))
If AMaxLength > 0 And AMaxLength < Len(LDesc) Then
LDesc = Mid(LDesc, 1, AMaxLength) + "..."
End If
End If
GetProductDescription = LDesc
End Function
' (SS,21/5/07)
Sub SetProductDescription(AValue)
FProductDescription = AValue
End Sub
' (SS,24/7/11) for use by shopping admin via getdesc.asp to show just description, ProductCode is used if ProductID is 0 or ""
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
Function GetProductDescriptionForID(AProductID, AProductCode)
Dim LSQL
LSQL = "SELECT ProductDescription FROM products WHERE "
If AProductID <> 0 And AProductID <> "" Then
LSQL = LSQL & "ProductID = '" & CleanSQLStr(AProductID) & "'"
Else
LSQL = LSQL & "ProductCode = '" & CleanSQLStr(AProductCode) & "'"
End If
GetProductDescriptionForID = ComposeDescription(GetSQLValueAsString(LSQL), True, False)
End Function
' (SS,4/8/11) used by getdesc.asp and shopping admin, as a temporary store for previewing product description, replaces GetProductDescriptionForID
' (SS,5/8/11) added pc for PC name, better for multiuser
Function GetProductDescriptionScratch
GetProductDescriptionScratch = ComposeDescription(GetSettingMemo("Scratch", "ProductDescription-" & CleanRequestQueryString("pc")), True, False)
End Function
' (SS,26/5/07)
Function GetProductDimensions
GetProductDimensions = FProductDimensions
End Function
' (SS,26/5/07)
Sub SetProductDimensions(AValue)
FProductDimensions = NB(AValue)
End Sub
' (SS,16/2/12) returns page link given page e.g. reviews, uses script page name e.g. products.asp
Function GetPageLink(APageName)
GetPageLink = GetScriptName + "?page=" & APageName
End Function
' (SS,13/3/13)
Function GetAccountLink
GetAccountLink = GetPageLink("account")
End Function
' (SS,11/2/05)
' (SS,16/2/12) removed following because another one exists by the same name
'Function GetGroupLink(AGroup)
' GetGroupLink = AddStoreURL("products.asp?cmd=cat&grp=" & Server.URLEncode(AGroup))
'End Function
' (SS,23/3/05) changed from javascript to normal link to help with search engines '
Function GetCategoryLink(ACategory)
' quote in category or subcategory is prefixed by backslash else javascript will fail '
'GetCategoryLink = "javascript:itp_subcat('" + ReplaceStr(ACategory,"'", "\'") + "','')"
GetCategoryLink = AddStoreURL("products.asp?cat=" & Server.URLEncode(ACategory)) ' (SS,17/9/08) added AddStoreURL
End Function
' (SS,26/3/07)
Function GetGroupLink(AGroup)
GetGroupLink = AddStoreURL("products.asp?grp=" & Server.URLEncode(AGroup))
End Function
' (SS,23/3/05) special one for javascript link '
Function GetCategoryJavaLink(ACategory)
' quote in category or subcategory is prefixed by backslash else javascript will fail '
' GetCategoryJavaLink = "javascript:itp_subcat('" + ReplaceStr(ACategory,"'", "\'") + "','')"
' (SS,18/5/07) replaced above with following
GetCategoryJavaLink = "javascript:itp_subcat('" + FixLinkForJavascript(ACategory) + "','')"
End Function
' (SS,20/7/07) new non javascript version, previous one renamed to GetSubcategoryJavaLink
' (SS,17/1/13) replaced & with & to reduce HTML errors in http://validator.w3.org
Function GetSubcategoryLink(ACategory, ASubCategory)
GetSubcategoryLink = AddStoreURL("products.asp?cat=" & Server.URLEncode(ACategory) & "&subcat=" & Server.URLEncode(ASubcategory)) ' (SS,17/9/08) added AddStoreURL
End Function
' (SS,13/6/17) special redirect version that doesn't encode & as &, because & doesn't work for redirects
Function GetSubcategoryLinkForRedirect(ACategory, ASubCategory)
GetSubcategoryLinkForRedirect = AddStoreURL("products.asp?cat=" & Server.URLEncode(ACategory) & "&subcat=" & Server.URLEncode(ASubcategory)) ' (SS,17/9/08) added AddStoreURL
End Function
' (SS,20/7/07) renamed from GetSubcategoryLink to GetSubcategoryJavaLink
Function GetSubcategoryJavaLink(ACategory, ASubCategory)
' quote in category or subcategory is prefixed by backslash else javascript will fail '
' GetSubcategoryLink = "javascript:itp_subcat('" + ReplaceStr(ACategory, "'", "\'") + "','" + ReplaceStr(ASubCategory, "'", "\'") + "')"
' (SS,18/5/07) was failing for double quote, replaced with FixLinkForJavascript (previous line above)
GetSubcategoryJavaLink = "javascript:itp_subcat('" + FixLinkForJavascript(ACategory) + "','" + FixLinkForJavascript(ASubCategory) + "')"
'GetSubcategoryLink = "javascript:itp_subcat('" + Server.HTMLEncode(ACategory) + "','" + Server.HTMLEncode(ASubCategory) + "')"
End Function
' (SS,18/5/07)
Function FixLinkForJavascript(ALink)
Dim LLink ' LLink used otherwise we'll end up changing original ALink value
LLink = ALink
LLink = Replace(LLink, "'", "\'")
' "\""" doesn't work URL encode %22 does, URLEncode can't be used because it result it '+' be returned for space
' may be a better way?
LLink = Replace(LLink, """", "%22")
FixLinkForJavascript = LLink
End Function
' (SS,6/7/06) added strImageNo
' (SS,17/1/13) replaced & with & to reduce HTML errors in http://validator.w3.org
' (SS,8/8/18) removed strImageNo
Function GetProductLink(AProductCode)
Dim LLink
' (SS,16/12/15) removed Server.URLEncode from AProductCode, helps prevent duplicates being detected in Screaming Frog SEO
LLink = "products.asp?code=" & AProductCode
GetProductLink = AddStoreURL(LLink) ' (SS,17/9/08) added AddStoreURL
End Function
' (SS,6/7/10) for SEO
' (SS,19/7/10) replaced Server.URLEncode(AProductName) with SEOFriendlyStr(AProductName)
' (SS,17/1/13) replaced & with & to reduce HTML errors in http://validator.w3.org
Function GetProductLinkWithName(AProductCode, AProductName)
Dim LLink
' LLink = "products.asp?name=" & SEOFriendlyStr(AProductName) & "&code=" & Server.URLEncode(AProductCode)
' (SS,7/2/11) replaced above with following to reverse the code and name, requested by Neil for easier emailing
' (SS,26/7/11) added HTMLDecode because AProductName will be passed here after being HTMLEncoded, we don't want a quote to appear as the word quote
' (SS,16/12/15) removed Server.URLEncode from AProductCode and SEOFriendlyStr, helps prevent duplicates being detected in Screaming Frog SEO
LLink = "products.asp?code=" & AProductCode & "&name=" & SEOFriendlyStr(HTMLDecode(AProductName))
GetProductLinkWithName = AddStoreURL(LLink) ' (SS,17/9/08) added AddStoreURL
End Function
' (SS,18/5/09) used in emails
Function GetFullProductLink(AProductCode)
GetFullProductLink = GetStoreURL + "/" + GetProductLink(AProductCode)
End Function
' (SS,16/3/15) full version of GetProductLinkWithName
Function GetFullProductLinkWithName(AProductCode, AProductName)
GetFullProductLinkWithName = GetStoreURL + "/" + GetProductLinkWithName(AProductCode, AProductName)
End Function
' (SS,8/8/18) moved image URL fetching code from here to new section at the end
' (SS,6/7/06) new
' (SS,12/9/12) GetProductFileLinkCached below calls this and adds cache=y to it
' (SS,17/1/13) replaced & with & to reduce HTML errors in http://validator.w3.org
Function GetProductFileLink(AProductCode, AFileNo)
Dim LLink
LLink = "getfile.asp?code=" & Server.URLEncode(AProductCode) + "&code2=" & Server.URLEncode(AFileNo)
GetProductFileLink = LLink
End Function
' (SS,12/9/12) special version which caches the file (used by banner download which is cached for 60 minutes)
' (SS,17/1/13) replaced & with & to reduce HTML errors in http://validator.w3.org
Function GetProductFileLinkCached(AProductCode, AFileNo)
GetProductFileLinkCached = GetProductFileLink(AProductCode, AFileNo) & "&cache=y"
End Function
Function GetBuyLink(AProductCode)
GetBuyLink = "javascript:itp_buy('" & AProductCode & "')"
End Function
' (SS,13/9/12) calls itp_buyq to add given quantity to basket
Function GetBuyQLink(AProductCode, AQty)
GetBuyQLink = "javascript:itp_buyq('" & AProductCode & "'," & AQty & ")"
End Function
Function GetBuyBLink(AProductCode)
GetBuyBLink = "javascript:itp_buyb('" & AProductCode & "')"
End Function
' (SS,6/5/10) used when there are a list of products on same page
Function GetBuyLLink(AProductCode)
Dim LProductDashAsUnderscore
LProductDashAsUnderscore = Replace(AProductCode, "-", "_")
GetBuyLLink = "javascript:itp_buyl('" & AProductCode & "','QTY_" + LProductDashAsUnderscore + "')"
End Function
' (SS,1/7/11) new buy with options link, which posts using post method the frmBasket form section
' AProductID parameter is currently ignored
Function GetBuyOLink(AProductID)
'GetBuyOLink = "javascript:frmBasket.submit()"
GetBuyOLink = "javascript:itp_buyo()"
End Function
' (SS,16/5/11) version called from previous order view (inc-template-checkout.asp) uses replacement product code if it exists
Function GetBuyLinkPreviousOrders(AProductCode)
GetBuyLinkPreviousOrders = GetBuyLink(GetReplacementProductCode(AProductCode))
End Function
' (SS,4/12/12) href version used in order reminder emails, also with qty
' (SS,17/1/13) replaced & with & to reduce HTML errors in http://validator.w3.org
Function GetBuyLinkHRef(AProductCode, AQty)
GetBuyLinkHRef = GetFullProductLink(AProductCode) & "&cmd=buy&qty=" & AQty
End Function
' (SS,29/9/04) allows us to optimise the full product list for search engines '
Function ForProductList()
' (SS,14/6/06) changed Request("name") to CleanRequestQueryString("name")
' because name was clashing with name in contacts page and causing category menu not to show
' (SS,6/7/10) added GetScriptName = "products2.asp" because "name" is now being used in products.asp for SEO
ForProductList = (CleanRequest("cmd") = "list" Or (GetScriptName = "products2.asp" And CleanRequestQueryString("name") <> ""))
End Function
' (SS,18/5/09)
Function GetProductName(AProductCode)
' (SS,26/7/11) added HTMLEncode
GetProductName = HTMLEncode(GetFieldForProduct(AProductCode, "ProductName"))
End Function
' (SS,13/1/11) added to return without encoding for plain text emails because & gets encoded as &
Function GetProductNameNoHTMLEncode(AProductCode)
GetProductNameNoHTMLEncode = GetFieldForProduct(AProductCode, "ProductName")
End Function
' (SS,1/12/16)
Function GetDeliveryWillCollect
GetDeliveryWillCollect = AllowCollection And Session("DeliveryWillCollect")
End Function
' (SS,31/3/17)
Function GetDeliveryAddToExistingOrder
GetDeliveryAddToExistingOrder = Session("DeliveryAddToExistingOrder")
End Function
' (SS,3/5/19) allow setting of DeliveryAddToExistingOrder to True or False
Sub SetDeliveryAddToExistingOrder(AValue)
Session("DeliveryAddToExistingOrder") = AValue
End Sub
' (SS,3/5/19) code moved here from Sub CheckOut
' To add special message when customer ticks "Don't charge delivery, add to existing order"
' Separated into two routines, one to add another to remove
' This routine adds the message if it doesn't already exist
Sub AddToExistingOrderAddMessage
Dim LMessage
LMessage = Session("Message")
If InStr(LMessage, ADD_TO_PREVIOUS_ORDER_TEXT) = 0 Then
LMessage = ADD_TO_PREVIOUS_ORDER_TEXT & " " & LMessage
End if
Session("Message") = Trim(LMessage)
End Sub
' (SS,3/5/19) removes the ADD_TO_PREVIOUS_ORDER_TEXT which might be added by AddToExistingOrderAddMessage above
' this is called when user unticks the "Don't charge delivery, add to existing order" box or doesn't have a valid outstanding order
Sub AddToExistingOrderRemoveMessage
Session("Message") = Trim(ReplaceStr(Session("Message"), ADD_TO_PREVIOUS_ORDER_TEXT, ""))
End Sub
' (SS,2/10/17) returns true if customer has an existing non-completed order (awaiting payment, payment received or payment on account, excluding current order about to be placed)
Function CustomerHasExistingOrder(AEmail)
Dim LSQL, LOrderNo
LSQL = "SELECT OrderNo FROM orders WHERE EmailAddress = '" & CleanSQLStr(AEmail) & "' AND (Status = '" & AWAITING_PAYMENT_STATUS & "' OR Status = '" & PAYMENT_RECEIVED_STATUS & "' OR Status = '" & PAYMENT_ON_ACCOUNT_STATUS & "')"
LOrderNo = GetOrderNo
' (SS,26/9/19) fixed bug which was preventing customer from adding to existing order in same session due to previous order no being retained until next one is created
' i.e. replaced LOrderNo <> "" with OrderNoCanBeReused(LOrderNo)
If OrderNoCanBeReused(LOrderNo) Then LSQL = LSQL & " AND OrderNo <> '" & CleanSQLStr(LOrderNo) & "'"
CustomerHasExistingOrder = GetSQLRecordExists(LSQL)
End Function
' (SS,11/2/05) added AGroup, modified to include GroupName field '
Sub OpenCategoriesQuery(AGroup)
Dim LSQl
LSQL = "SELECT categories.GroupName, productcategories.Category, categories.Description" &_
" FROM productcategories" &_
" INNER JOIN categories ON categories.Category = productcategories.Category" &_
" INNER JOIN groups ON groups.GroupName = categories.GroupName"
If AGroup <> "" Then
LSQL = LSQL & " WHERE categories.GroupName = '" & CleanSQLStr(AGroup) & "'"
End If
LSQL = LSQL &_
" GROUP BY Category" &_
" ORDER BY groups.SortOrder, categories.GroupName, categories.SortOrder, productcategories.Category"
OpenQuery(LSQL)
End Sub
Sub OpenSubcategoriesQuery(ACategory)
OpenQuery("SELECT productcategories.Subcategory" &_
" FROM productcategories" &_
" INNER JOIN subcategories ON subcategories.Category = productcategories.Category AND subcategories.Subcategory = productcategories.Subcategory" &_
" WHERE productcategories.Category = '" & CleanSQLStr(ACategory) & "'" &_
" GROUP BY Subcategory" &_
" ORDER BY subcategories.SortOrder, productcategories.Subcategory")
End Sub
' (SS,11/2/05) added GroupName feature
' (SS,16/3/12) removed references to ProductListCategorySuffix, field has now been dropped from groups table, routine probably isn't used anymore
' (SS,15/2/13) complete rewrite, now used as sitemap, renamed from ShowFullList to ShowHTMLSiteMap, parameters ACategory, ASubcategory also removed
' (SS,19/2/13) improvements and made into function to return the HTML as a string, renamed to GetHTMLSiteMap
' (SS,20/2/13) speed improvement by using just the one query
' (SS,21/2/13) added group
Function GetHTMLSiteMap
'OpenQuery(GetCategoryMenuSQL(""))
' (SS,20/2/13) replaced above with following, to use one query for extra speed
' (SS,21/2/13) added Group
' (SS,31/10/18) added GetProductsFilter to filter out subproducts
Dim LSQL
LSQL = "SELECT categories.GroupName, productcategories.Category, productcategories.Subcategory, categories.MenuShortName AS CategoryMenuShortName, subcategories.MenuShortName AS SubcategoryMenuShortName," &_
" products.ProductCode, products.ProductName" &_
" FROM productcategories" &_
" INNER JOIN categories ON categories.Category = productcategories.Category" &_
" INNER JOIN groups ON groups.GroupName = categories.GroupName" &_
" INNER JOIN subcategories ON subcategories.Category = productcategories.Category AND subcategories.Subcategory = productcategories.Subcategory" &_
" INNER JOIN products ON products.ProductCode = productcategories.ProductCode" &_
" WHERE products.ProductDisabled = FALSE" & GetProductsFilter &_
" GROUP BY GroupName, Category, Subcategory, ProductCode" &_
" ORDER BY groups.SortOrder, groups.GroupName, categories.SortOrder, productcategories.Category, subcategories.SortOrder, productcategories.Subcategory, products.SortOrder, products.ProductName, products.ProductCode"
OpenQuery(LSQL)
Dim LHTML
LHTML = ShowSitemapHeader
Dim LGroup, LPrevGroup, LCat, LPrevCat, LSubcat, LPrevSubcat, LCategoryMenuShortName, LSubcategoryMenuShortName
LPrevGroup = ""
LPrevCat = ""
LPrevSubcat = ""
Dim LCount
LCount = 0
LGroup = GetQueryValue("GroupName")
LCat = GetQueryValue("Category")
LSubcat = GetQueryValue("Subcategory")
LCategoryMenuShortName = NB(GetQueryValue("CategoryMenuShortName"))
LSubcategoryMenuShortName = NB(GetQueryValue("SubcategoryMenuShortName"))
Do While Not EndOfQuery
If GroupsEnabled And LGroup <> LPrevGroup Then
LHTML = LHTML & ShowSitemapGroupHeader
LHTML = LHTML & ShowSitemapGroup(LGroup, GetGroupLink(LGroup))
End If
If LCat <> LPrevCat Then
LHTML = LHTML & ShowSitemapCategoryHeader
LHTML = LHTML & ShowSitemapCategory(Iif(LCategoryMenuShortName = "", LCat, LCategoryMenuShortName), GetCategoryLink(LCat))
End If
If (LSubcat <> LPrevSubcat) Or (LCat <> LPrevCat) Then
LHTML = LHTML & ShowSitemapSubcategoryHeader
LHTML = LHTML & ShowSitemapSubcategory(Iif(LSubcategoryMenuShortName = "", LSubcat, LSubcategoryMenuShortName), GetSubCategoryLink(LCat, LSubcat))
LHTML = LHTML & ShowSitemapProductHeader ' (SS,20/2/13)
End If
LHTML = LHTML & ShowSitemapProduct(GetQueryValue("ProductCode"), HTMLEncode(GetQueryValue("ProductName")), GetProductLinkWithName(GetQueryValue("ProductCode"), GetQueryValue("ProductName")))
LPrevGroup = LGroup
LPrevCat = LCat
LPrevSubcat = LSubcat
LCount = LCount + 1
NextQueryRecord
If EndOfQuery Then
LHTML = LHTML & ShowSitemapProductFooter ' (SS,20/2/13)
LHTML = LHTML & ShowSitemapSubcategoryFooter
LHTML = LHTML & ShowSitemapCategoryFooter
If GroupsEnabled Then LHTML = LHTML & ShowSitemapGroupFooter ' (SS,21/2/13)
Else
LGroup = GetQueryValue("GroupName")
LCat = GetQueryValue("Category")
LSubcat = GetQueryValue("Subcategory")
LCategoryMenuShortName = NB(GetQueryValue("CategoryMenuShortName"))
LSubcategoryMenuShortName = NB(GetQueryValue("SubcategoryMenuShortName"))
If (LSubcat <> LPrevSubcat) Or (LCat <> LPrevCat) Then
LHTML = LHTML & ShowSitemapProductFooter ' (SS,20/2/13)
LHTML = LHTML & ShowSitemapSubcategoryFooter
End If
If LCat <> LPrevCat Then LHTML = LHTML & ShowSitemapCategoryFooter
If GroupsEnabled And LGroup <> LPrevGroup Then LHTML = LHTML & ShowSitemapGroupFooter ' (SS,21/2/13)
End If
Loop
CloseQuery
LHTML = LHTML & ShowSitemapFooter
GetHTMLSiteMap = LHTML
End Function
' (SS,11/2/05) added AGroup '
Sub ShowCategories(AGroup)
Dim LCount, LColumn, LCategory, LCategoryDescription, LIsColStartItem, LIsColEndItem
ShowCategoryListHeader
OpenCategoriesQuery AGroup ' (SS,11/2/05) added AGroup
LCount = 0
LColumn = 0
' (SS,17/2/05) added Sorry message if no categories '
If EndOfQuery Then
ShowMessage(tr("Sorry, no products currently available") & ".")
Else
Do While Not EndOfQuery
LCount = LCount + 1
LIsColStartItem = (LColumn = 0)
LIsColEndItem = (LColumn = (MAX_CATEGORY_LIST_COLUMNS - 1))
If LIsColStartItem Then
ShowCategoryListStartRow ' start new table row '
If LCount > 1 Then ShowCategoryLineSeparator
End If
LCategory = GetQueryValue("Category")
LCategoryDescription = GetQueryValue("Description")
If PictureExists("C", LCategory, "") Then
ShowCategoryListItemWithPicture LCategory, LCategoryDescription, LIsColStartItem, LIsColEndItem
Else
ShowCategoryListItem LCategory, LCategoryDescription, LIsColStartItem, LIsColEndItem
End If
NextQueryRecord
If LIsColEndItem Then
LColumn = 0
ShowCategoryListEndRow ' end the table row '
Else
LColumn = LColumn + 1
End If
Loop
End If
CloseQuery
If LColumn > 0 Then
' complete the missing columns across '
ShowCategoryListMissingColumns MAX_CATEGORY_LIST_COLUMNS - LColumn
ShowCategoryListEndRow ' end the table row '
End If
ShowCategoryListFooter
End Sub
Sub ShowSubcategories(ACategory)
Dim LCategoryDescription, LShowCategoryPicture
OpenQuery("SELECT * FROM categories WHERE Category = '" & CleanSQLStr(ACategory) & "'")
' (SS,16/8/05) added "If Not EndOfQuery" to following to prevent BOF/EOF no current record error
If Not EndOfQuery Then
' (SS,17/5/11) changed from ConvertNewlinesToHTML to ConvertNewlinesEtcToHTML for quotes and pounds to show correctly
' (SS,25/5/11) changed from ConvertNewlinesEtcToHTML to ComposeDescription, to also handle tokens
LCategoryDescription = ComposeDescription(GetQueryValue("Description"), True, False)
Else
LCategoryDescription = ""
End If
CloseQuery
LShowCategoryPicture = PictureExists("C", ACategory, "")
ShowSubcategoryListHeader ACategory, LCategoryDescription, LShowCategoryPicture
Dim LCount, LColumn, LSubcategory, LIsColStartItem, LIsColEndItem
OpenSubcategoriesQuery ACategory
LCount = 0
LColumn = 0
' (SS,17/2/05) added Sorry message if no subcategories '
If EndOfQuery Then
ShowMessage(tr("Sorry, no products currently available") & ".")
Else
Do While Not EndOfQuery
LCount = LCount + 1
LIsColStartItem = (LColumn = 0)
LIsColEndItem = (LColumn = (MAX_SUBCATEGORY_LIST_COLUMNS - 1))
If LIsColStartItem Then
ShowSubcategoryListStartRow ' start new table row '
If LCount > 1 Then ShowSubcategoryLineSeparator
End If
LSubcategory = GetQueryValue("Subcategory")
If PictureExists("S", ACategory, LSubcategory) Then
ShowSubcategoryListItemWithPicture ACategory, LSubcategory, LIsColStartItem, LIsColEndItem
Else
ShowSubcategoryListItem ACategory, LSubcategory, LIsColStartItem, LIsColEndItem
End If
NextQueryRecord
If LIsColEndItem Then
LColumn = 0
ShowSubcategoryListEndRow ' end the table row '
Else
LColumn = LColumn + 1
End If
Loop
End If
CloseQuery
If LColumn > 0 Then
' complete the missing columns across '
ShowSubcategoryListMissingColumns MAX_SUBCATEGORY_LIST_COLUMNS - LColumn
ShowSubcategoryListEndRow ' end the table row '
End If
ShowSubcategoryListFooter
End Sub
' (SS,11/2/05) added GroupName '
' (SS,27/3/07) added AGroup parameter
' (SS,1/7/11) added AAttributeID, AMin, AMax parameters to search for given attribute between min and max values
' (SS,27/7/11) renamed ShowProductListJumpFooter.. routines to ShowProductListNav.., also changed to use divs etc.
' (SS,14/3/12) added AVal parameter to use with attribute search for single value rather than min max
' (SS,3/11/15) minor change to navigation to sort issue
Sub ShowProducts(AGroup, ACategory, ASubcategory, ASearchFor, APage, AAttributeID, AMin, AMax, AVal)
' determine how many products to show across and per page using client width and height '
Dim LProductsAcross, LProductsDown, LProductsPage, LTableCellWidthPercentage
LProductsAcross = PRODUCTS_ACROSS
LProductsDown = PRODUCTS_DOWN
' (SS,12/5/11) added If GetProductsPerPageFixed to force fixed products per page, old code in Else section
If GetProductsPerPageFixed Then
LProductsAcross = Max(1, GetProductsAcrossMax)
LProductsDown = Max(1, GetProductsDownMax)
Else
If strClientWidth <> "" And strClientHeight <> "" Then
LProductsAcross = (CInt(strClientWidth) - HORIZONTAL_SPACE_TO_IGNORE) \ HORIZONTAL_SPACE_PRODUCT
LProductsDown = (CInt(strClientHeight) - VERTICAL_SPACE_TO_IGNORE) \ VERTICAL_SPACE_PRODUCT
If LProductsAcross < MIN_PRODUCTS_ACROSS Then LProductsAcross = MIN_PRODUCTS_ACROSS
If LProductsDown < MIN_PRODUCTS_DOWN Then LProductsDown = MIN_PRODUCTS_DOWN
' (SS,7/7/06) set maximum no of products across '
If GetProductsAcrossMax > 0 Then LProductsAcross = GetProductsAcrossMax
' (SS,27/3/07) set maximum no of producsts down '
If GetProductsDownMax > 0 Then LProductsDown = GetProductsDownMax
End If
End If
LProductsPage = LProductsAcross * LProductsDown
LTableCellWidthPercentage = 100 \ LProductsAcross
' (SS,19/7/17) added following to prevent navigation being shown on home page where new products are listed (for HF)
Dim LNewProductList
LNewProductList = ASearchFor = NEW_PRODUCTS_SEARCH_TEXT
If LNewProductList And AMax <> "" Then LProductsPage = AMax
Dim LSQL, LIsSearch, LIsSpecialOffer, LSummaryList, LProductsFound
' (SS,16/1/13) SQL building code moved to GetProductSearchSQL, parameters LIsSearch, LIsSpecialOffer, LSummaryList are modified by this routine, if first parameter is False then don't search description
' the reason for doing this was because Galleon now have links to products in their description and searching for a specific product code is finding two many products
' resolved this by not doing an initial search on the description, if this returns nothing then another search is done which includes the description
If ProductSearchWithoutDescFirst And AAttributeID = "" And IsNormalSearch(ASearchFor, AGroup, ACategory, ASubcategory) Then
LSQL = GetProductSearchSQL(False, AGroup, ACategory, ASubcategory, ASearchFor, APage, AAttributeID, AMin, AMax, AVal, LIsSearch, LIsSpecialOffer, LSummaryList, LProductsFound)
Else
LProductsFound = 0
End If
If LProductsFound = 0 Then
LSQL = GetProductSearchSQL(True, AGroup, ACategory, ASubcategory, ASearchFor, APage, AAttributeID, AMin, AMax, AVal, LIsSearch, LIsSpecialOffer, LSummaryList, LProductsFound)
End If
Dim LProductCode, LFoundText, LColumn, LStartRecordNo, LCurrentPage
' if only one product found then show it straightaway only if category not selected '
' (SS,27/3/07) added AGroup = ""
If LProductsFound = 1 And AGroup = "" And ACategory = "" And ASubcategory = "" Then
OpenQuery("SELECT DISTINCT products.* " + LSQL)
LProductCode = GetFieldValue("ProductCode")
CloseQuery
SingleProductRedirect LProductCode ' (SS,12/9/12) replaced ShowSingleProduct with SingleProductRedirect
Exit Sub
End If
If APage = "" Or APage = "0" Then
LCurrentPage = 1
Else
LCurrentPage = ParseInt(APage) ' (SS,13/6/12) replaced CInt with ParseInt, to better handle SQL injection
If LCurrentPage = 0 Then LCurrentPage = 1 ' (SS,13/6/12) because ParseInt will return 0 if APage isn't a valid integer
End If
LStartRecordNo = (LCurrentPage - 1) * LProductsPage ' assumes first record is 0 '
' if page no is greater than the range then recalculate the current page and record no '
If LStartRecordNo >= LProductsFound Then
LCurrentPage = ((LProductsFound - 1) \ LProductsPage) + 1
LStartRecordNo = (LCurrentPage - 1) * LProductsPage ' assumes first record is 0 '
End If
' (SS,18/3/10) added trl, translation table may contain leading uppercase for Product(s) so trl used instead of tr
LFoundText = PluralString(LProductsFound, trl("product"), trl("products"))
' actual query which selects the records
' (SS,11/2/05) added GroupName
' (SS,27/3/07) added productcategories.Category, productcategories.Subcategory (for new summary list)
' (SS,27/3/07) discovered than these affect DISTINCT and can cause same product to appear more than once
' if in more than one category, changed so that category, subcategory and group and only fetched for summary list
' Limit not applied for summary list
Dim LSQL2
' (SS,15/5/07) added ProductDescription, Price2, Price3, Price4, Discount, VATBand
' (SS,21/5/07) replaced StdPrice, SalePrice, Price2, Price3, Price4, Discount, VATBand with GetPricingFieldsForSQL("")
' (SS,26/5/07) added Dimensions
' (SS,26/5/10) added DateStockDue
' (SS,23/6/10) added the 4 availability from/to fields
' (SS,6/7/11) added products.ProductID
LSQL2 = "SELECT DISTINCT products.ProductID, products.ProductCode, ProductName, ProductDescription, Dimensions, " + GetPricingFieldsForSQL("") + ", NumInStock, DateStockDue, New " &_
", " & GetStockAvailabilityFieldList & " " &_
IIf(LSummaryList, ", GroupName, productcategories.Category, productcategories.Subcategory ", "") &_
LSQL &_
IIf(LSummaryList, "", " LIMIT " & LStartRecordNo & ", " & LProductsPage)
OpenQuery(LSQL2)
' (SS,12/10/15) because we're looking up whether cat subcat desc/footer exist before showing products don't exist message
Dim LCatSubcatDesc, LCatSubcatFooter
LCatSubcatDesc = ""
LCatSubcatFooter = ""
' (SS,27/3/07) added AGroup, ASubcategory
If AGroup = "" And ACategory = "" And ASubcategory = "" Then
' (SS,24/1/12) if AAttributeID = "" so that products found count not shown if searching for attribute
' (SS,19/7/17) added And Not LNewProductList
If AAttributeID = "" And Not LNewProductList Then
ShowProductListHeaderForSearch LFoundText, HTMLEncode(ASearchFor), LProductsFound = 0 ' (SS,24/8/17) added HTMLEncode to prevent XSS injection
End If
Else
' (SS,12/10/15) moved here from below, also LCategoryDescription renamed to LCatSubcatDesc, also merged with footer code
If Not LSummaryList Then
' (SS,10/1/13) Added LCategoryDescription to show description from category record, only at category level, for subcategory level we fetch as before
If ACategory <> "" And ASubcategory = "" Then
LCatSubcatDesc = ComposeDescription(GetFieldForCategory(ACategory, "Description"), False, False)
LCatSubcatFooter = ComposeDescription(GetFieldForCategory(ACategory, "Footer"), False, False) ' (SS,12/10/15) moved here from footer
Else
' (SS,25/5/11) replaced old code with call to ComposeDescription, which also handles tokens
' (SS,10/1/13) modified to call GetFieldForSubcategory instead of GetSubcategoryDescription
' added "Description" for fieldname parameter and and changed first True to False so that newlines stay as they are
LCatSubcatDesc = ComposeDescription(GetFieldForSubcategory(ACategory, ASubcategory, "Description"), False, False)
LCatSubcatFooter = ComposeDescription(GetFieldForSubcategory(ACategory, ASubcategory, "Footer"), False, False) ' (SS,12/10/15) moved here from footer
End If
End If
' (SS,17/2/05) added following to fix bug where GetQueryValue("GroupName") was causing BOF error when all products disabled for category '
If EndOfQuery Then
' (SS,12/10/15) added if, to only show this message and exit if not ShowCategoryHeadingFooterIfEmpty or cat/subcat desc is blank
If Not ShowCategoryHeadingFooterIfEmpty Or (LCatSubcatDesc + LCatSubcatFooter = "") Then
ShowMessage(tr("Sorry, no products currently available in this category") & ".")
CloseQuery
Exit Sub
End If
End If
' (SS,11/2/05) added GroupName '
' (SS,27/3/07) added If Not LSummaryList
If Not LSummaryList Then
' (SS,11/1/13) added following to lookup category and subcategory heading, or create it from AGroup, ACategory and ASubcategory
Dim LHeading
If ASubcategory <> "" Then
LHeading = HTMLEncode(GetFieldForSubcategory(ACategory, ASubcategory, "Heading"))
If LHeading = "" Then LHeading = HTMLEncode(GetShortNameForCategory(ACategory)) + " ‐ " + HTMLEncode(GetShortNameForSubcategory(ACategory, ASubcategory))
ElseIf ACategory <> "" Then
LHeading = HTMLEncode(GetFieldForCategory(ACategory, "Heading"))
If LHeading = "" Then LHeading = HTMLEncode(GetShortNameForCategory(ACategory))
Else
LHeading = HTMLEncode(AGroup)
End If
' (SS,27/3/07) replaced GetQueryValue("GroupName") in following with AGroup
' (SS,10/1/13) replaced ACategory with LCategoryHeading and ASubcategory with LSubcategoryHeading
' (SS,11/1/13) replaced AGroup, LCategoryHeading, LSubcategoryHeading with LHeading
ShowProductListHeaderForCategory LHeading
' (SS,12/10/15) setting code moved above, renamed LCatSubcatDesc, also renamed LCategoryDescription to LCatSubcatDesc
ShowProductListHeaderForSubcategory(LCatSubcatDesc) ' (SS,25/5/11) moved setting code above so that either category or subcategory description can be shown
End If
End If
' also added If LProductsFound > 0 to only do the following if products found
If LProductsFound > 0 Then
' (SS,11/8/15) added following to show navigation at top of page for RWD
'If IsRWD And (Not LSummaryList And LProductsFound > LProductsPage) Then
' (SS,19/7/17) added And Not LNewProductList to also not show navigation for new product list (shown on home page)
If IsRWD And Not LSummaryList And Not LNewProductList Then
ShowProductListNavigation LCurrentPage, LProductsFound, LProductsPage, LStartRecordNo
End If
' (SS,27/3/07) added If LSummaryList Then ShowProductSummaryListHeader
If LSummaryList Then
ShowProductSummaryListHeader
Dim LShowLarge, LWidth, LHeight
Else
' (SS,19/7/17) different header for new product list
If LNewProductList Then
ShowProductListHeaderNewProducts
Else
ShowProductListHeader
End If
End If
LColumn = 0
Dim LClassSuffix
Dim LStdPrice, LSalePrice
' (SS,16/1/13) moved here from above
Dim LGroup, LCategory, LSubcategory, LProductName, LProductCount
' (SS,27/3/07) added following for summary list
Dim LPrevCategory, LPrevSubcategory
LPrevCategory = ""
LPrevSubcategory = ""
LProductCount = 0
' (SS,27/3/07) added (LSummaryList Or) to list all if summary list
Do While Not EndOfQuery And (LSummaryList Or LProductCount < LProductsPage)
LProductCount = LProductCount + 1
' (SS,18/5/07) added following to allow templates to determine whether at first or last records
SetAtFirstOrLastProduct LProductCount, LProductsFound
' (SS,27/3/07) added If Not LSummaryList
If Not LSummaryList Then
If LColumn = 0 then ShowProductListStartRow ' start new table row '
End If
' (SS,18/5/07)
' (SS,6/7/11) removed 0 parameter, added False, no need to look up options price
GetPricingFromProductRecord(False)
LProductCode = GetFieldValue("ProductCode")
LStdPrice = GetStdPrice
LSalePrice = GetSalePrice
' (SS,19/9/06)
LStdPrice = CorrectCurrencyNV(LStdPrice)
LSalePrice = CorrectCurrencyNV(LSalePrice)
' (SS,21/5/07) added following to pass description and dimensions to inc-template-product-list
SetProductDescription(GetFieldValue("ProductDescription"))
SetProductDimensions(GetFieldValue("Dimensions"))
' (SS,23/6/10) must be before SetStockInfo because SetStockInfo might use the result of SetStockAvailability
SetStockAvailabilityFromFields
' (SS,26/5/10)
SetStockInfo GetFieldValue("NumInStock"), GetFieldValue("DateStockDue")
' (SS,19/2/05) added LIsSearch, LIsSpecialOffer, LGroup, LCategory, LSubcategory '
' (SS,27/3/07) added If LSummaryList Then code for new summary list
LProductName = HTMLEncode(GetFieldValue("ProductName")) ' (SS,26/7/11)
If LSummaryList Then
LCategory = GetFieldValue("Category")
LSubcategory = GetFieldValue("Subcategory")
If LCategory <> LPrevCategory Then ShowProductSummaryListCategory(LCategory)
If LSubcategory <> LPrevSubcategory Then ShowProductSummaryListSubcategory LCategory, LSubcategory
' (SS,8/8/18) commented out following, now redundant, not used since RWD framework, LWidth, LHeight will have undefined values when passed to ShowProductSummaryListItem
' GetProductPictureSize LProductCode, LWidth, LHeight ' (SS,17/4/07)
' (SS,15/5/07) added GetFieldValue("ProductDescription")
' (SS,21/5/07) removed GetFieldValue("ProductDescription") added SetProductDescription above to pass the parameter instead
' (SS,26/7/11) replaced GetFieldValue("ProductName") with LProductName, which has now been HTMLEncoded above
ShowProductSummaryListItem LProductCode, LProductName, LSalePrice, LStdPrice, GetFieldValue("NumInStock"), GetFieldValue("New") <> 0, LIsSearch, LIsSpecialOffer, LGroup, LCategory, LSubcategory, LWidth, LHeight
LPrevCategory = LCategory
LPrevSubcategory = LSubcategory
Else
' (SS,19/2/05) added following to get Group and Category for product '
GetFirstCatSubcatForProduct LProductCode, LGroup, LCategory, LSubcategory
' (SS,15/5/07) added GetFieldValue("ProductDescription")
' (SS,21/5/07) removed GetFieldValue("ProductDescription") added SetProductDescription above to pass the parameter instead
' (SS,26/7/11) replaced GetFieldValue("ProductName") with LProductName, which has now been HTMLEncoded above
ShowProductListItem LTableCellWidthPercentage, LProductCode, LProductName, LSalePrice, LStdPrice, GetFieldValue("NumInStock"), GetFieldValue("New") <> 0, LIsSearch, LIsSpecialOffer, LGroup, LCategory, LSubcategory
End If
NextQueryRecord
' (SS,27/3/07) added If Not LSummaryList
If Not LSummaryList Then
LColumn = LColumn + 1
If LColumn = LProductsAcross Then
LColumn = 0
ShowProductListEndRow ' end the table row '
End If
End If
Loop
If Not LSummaryList And LColumn > 0 Then ' (SS,27/3/07) added Not LSummaryList And
' complete the missing columns across
ShowProductListMissingColumns LProductsAcross - LColumn
ShowProductListEndRow ' end the table row '
End If
' (SS,12/10/15) footer code moved from here to below
' if there are more products than allowed per page then show the jump to footer '
' (SS,27/3/07) added Not LSummaryList And
' (SS,27/7/11) major change to use divs, routines were also renamed
' (SS,3/11/15) moved code below
End If ' If LProductsFound > 0 Then
' (SS,27/3/07) added If LSummaryList Then ShowProductSummaryListFooter
' (SS,12/10/15) moved following code here from inside "if" above to allow category/subcategory footer to be shown if not products
If LSummaryList Then
ShowProductSummaryListFooter
Else
' (SS,19/7/17) separate footer for new product list
If LNewProductList Then
ShowProductListFooterNewProducts
Else
' (SS,12/10/15) moved setting of LCategoryFooter above, renamed LCategoryFooter to LCatSubcatFooter
ShowProductListFooter LCatSubcatFooter ' (SS,10/1/13) added LCategoryFooter
End If
End If
' (SS,3/11/15) moved following here from end of "If LProductsFound > 0 Then" above. Was causing bottom nav to appear below first one due to isotope grid tidy,
' also breaking HTML. Moving here solved the problem.
' (SS,19/7/17) added And Not LNewProductList to also not show navigation for new product list (shown on home page)
If Not LSummaryList And LProductsFound > LProductsPage And Not LNewProductList Then
ShowProductListNavigation LCurrentPage, LProductsFound, LProductsPage, LStartRecordNo
End If
CloseQuery
SaveSettingsForContinue
AddSearchToLog
End Sub
' (SS,16/1/13) separated the SQL building code from ShowProducts above, it can now be called twice from ShowProducts
' it has the same parameters as ShowProducts, plus addition 3 used to passback values AIsSearch, AIsSpecialOffer, ASummaryList, AProductsFound
' new parameter ASearchDesc can be false to prevent searching in description
' (SS,13/8/15) modified to override order if sort by specified
' (SS,15/10/15) modified to allow a specific list of product IDs to be shown when AAttributeID is -1, and comma separated product ID list is in AVal
' (SS,24/1/16) added filter for max and min price i.e. maxprice=30&minprice=10
' (SS,30/6/17) change to full text search to order by relevance if no sort field set
' (SS,19/7/17) change for NEW_PRODUCTS_SEARCH_TEXT to filter out products that don't have images and are out of stock
Function GetProductSearchSQL(ASearchDesc, AGroup, ACategory, ASubcategory, ASearchFor, APage, AAttributeID, AMin, AMax, AVal, ByRef AIsSearch, ByRef AIsSpecialOffer, ByRef ASummaryList, ByRef AProductsFound)
Dim LSQL, LWhere, LOrderBy ' (SS,1/7/11) added LWhere
LSQL = "FROM products "
LSQL = LSQL + "LEFT JOIN productcategories ON products.ProductCode = productcategories.ProductCode "
LSQL = LSQL + "LEFT JOIN categories ON categories.Category = productcategories.Category "
' (SS,27/3/07) added following to get subcategory sort order
' LSQL = LSQL + "LEFT JOIN subcategories ON subcategories.Subcategory = productcategories.Subcategory "
' (SS,3/6/10) replaced above with following because it wasn't matching using category name,
' and causing it to match wrong subcategory sometimes if another subcategory with same name but different category exists
' Tim of Galleon, mentioned that subcategories on the left were in a different order to products shown
LSQL = LSQL + "LEFT JOIN subcategories ON subcategories.Category = productcategories.Category AND subcategories.Subcategory = productcategories.Subcategory "
' (SS,22/6/10) added following to lookup stock availability from suppliers table
LSQL = LSQL + "LEFT JOIN suppliers ON suppliers.SupplierID = products.SupplierID " ' (SS,21/6/10) to lookup for stock availability from new suppliers table
' (SS,1/7/11) modified to store in LWhere instead of adding to LSQL, allow attribute join to be added later
' (SS,31/10/18) added GetProductsFilter to filter out subproducts
LWhere = "ProductDisabled = 0" + GetProductsFilter
' if category and subcategory chosen then order by SortOrder First (SS,4/6/04) '
' (SS,27/3/07) added productcategories.Category and Subcategory sort order, changed the If to LSummaryList
' (SS,27/3/07) also added productcategories.Category and subcategories.Subcategory in case SortOrder is the same for all
' (SS,4/6/07) sort order is now the same for both, removed " ORDER BY ProductName" when not LSummaryList
' (SS,7/6/11) moved here from below, now the default orderby which can be overriden
LOrderBy = "categories.SortOrder, productcategories.Category, subcategories.SortOrder, subcategories.Subcategory, products.SortOrder, ProductName"
' (SS,13/3/15) added following to override normal sort order if sort order specified
Dim LSortByField
LSortByField = GetSortByField
If LSortByField <> "" Then LOrderBy = LSortByField + ", " + LOrderBy
' (SS,19/2/04) '
AIsSearch = False
AIsSpecialOffer = False
ASummaryList = False ' (SS,27/3/07)
' (SS,19/5/04) added special offers search '
' (SS,1/7/11) changed LSQL to LWhere
If LCase(ASearchFor) = SPECIAL_OFFERS_SEARCH_TEXT Then
' (SS,18/5/07) added FUseDiscountFieldForSpecialOffers etc to use new Discount field for special offers
If FUseDiscountFieldForSpecialOffers Then
LWhere = LWhere + " AND IFNULL(Discount, 0) > 0"
Else
LWhere = LWhere + " AND NOT ISNULL(SalePrice)"
End If
AIsSpecialOffer = True ' (SS,19/2/04) '
' (SS,18/1/13) added new products search
ElseIf LCase(ASearchFor) = NEW_PRODUCTS_SEARCH_TEXT Then
' LWhere = LWhere + " AND products.DateAdded IS NOT NULL"
' (SS,18/7/17) replaced above with following to include only those in stock or out of stock ordering allowed
LWhere = LWhere + " AND products.DateAdded IS NOT NULL AND (ISNULL(NumInStock) OR NumInStock > 0 OR AllowOutOfStockOrdering)"
' (SS,18/7/17) added following to include only those that have pictures
LWhere = LWhere + " AND EXISTS(SELECT PictureID FROM pictures WHERE Type = 'P' AND Code = products.ProductCode AND Enabled)"
LOrderBy = "products.DateAdded DESC, products.SortOrder"
ElseIf IsNormalSearch(ASearchFor, AGroup, ACategory, ASubcategory) Then ' (SS,27/3/07) replaced ACategory = "" with ASearchFor <> "" also added Or AGroup, ACat.. & ASubcat.., (SS,16/1/13) now uses IsNormalSearch
AIsSearch = True ' (SS,19/2/04) '
' (SS,2/11/06) added following to trim the leading and trailing spaces
Dim LSearchFor
LSearchFor = Trim(ASearchFor)
' (SS,1/7/11) added "if" to search for attribute range
' (SS,14/3/12) added Or AVal <> ""
If LSearchFor = "" And AAttributeID <> "" And ((AMin <> "" And AMax <> "") Or AVal <> "") Then
' (SS,15/10/15) added special case where AAttributeID = "-1" to only show specified products
If AAttributeID = "-1" Then
LWhere = LWhere + " AND INSTR('," & CleanSQLStr(AVal) & ",', CONCAT(',', products.ProductID, ',')) > 0"
LOrderBy = "categories.SortOrder, subcategories.SortOrder, products.SortOrder"
Else
LSQL = LSQL + "INNER JOIN product_attributes ON product_attributes.ProductID = products.ProductID "
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
LWhere = LWhere + " AND product_attributes.AttributeID = '" & CleanSQLStr(AAttributeID) & "'"
' (SS,14/3/12) added "if" to do search for exact value
If AVal <> "" Then
LWhere = LWhere + " AND AttributeValue = '" & CleanSQLStr(AVal) & "'"
Else ' (SS,14/3/12) as before
' convert to decimal and find all between min and max values
LWhere = LWhere + " AND CONVERT(AttributeValue, DECIMAL(10, 2)) BETWEEN '" & CleanSQLStr(AMin) & "' AND '" & CleanSQLStr(AMax) & "'"
LOrderBy = "CONVERT(AttributeValue, DECIMAL(10, 2))" ' sort in attribute value order converted to decimal
End If
End If
Else
' (SS,1/6/15) added following for full text search (when not searching description)
If ProductSearchFullText And Not ASearchDesc Then
' assumes full text index exists, created using following
' ALTER TABLE products ADD FULLTEXT KEY idxFTProductCodeAndName (ProductCode, ProductName)
' (SS,30/6/17) added LFullTextClause, used in two places
Dim LFullTextClause
LFullTextClause = "MATCH(products.ProductCode, products.ProductName) AGAINST ('" + CleanSQLStr(AdjustForFullTextSearch(LSearchFor)) + "'" ' NB. without ending bracket
LWhere = LWhere + " AND " + LFullTextClause + " IN BOOLEAN MODE)"
' (SS,13/8/15) improved by selecting IN BOOLEAN MODE if search for contains + or - or * or "
' (SS,26/8/15) natural language mode finds two many matches, so removed
' found that prefixing every word with + worked best in boolean mode, now using just boolean mode, AdjustForFullTextSearch above adjusts the LSearchFor, prefixing each word with "+"
' following code removed
' If InStr(LSearchFor, "+") Or InStr(LSearchFor, "-") Or InStr(LSearchFor, "*") Or InStr(LSearchFor, """") Then
' LWhere = LWhere + "' IN BOOLEAN MODE)"
' Else
' LWhere = LWhere + "' IN NATURAL LANGUAGE MODE)"
' End If
' (SS,13/8/15) replaced "" with LSortByField
LOrderBy = LSortByField
' (SS,30/6/17) if sort by field is blank, i.e. sort by relevance then add order by relevance
If LOrderBy = "" Then
LOrderBy = LFullTextClause + ") DESC"
End If
Else ' (SS,1/6/15) as before
LWhere = LWhere + " AND (ProductName LIKE ""%" & CleanSQLStr(LSearchFor) & "%"" OR productcategories.Category LIKE ""%" & CleanSQLStr(LSearchFor) & "%"" OR subcategories.Subcategory LIKE ""%" & CleanSQLStr(LSearchFor) & "%"""
LWhere = LWhere + " OR products.ProductCode LIKE ""%" & CleanSQLStr(LSearchFor) & "%"""
' (SS,2/11/06) added following two lines to allow searching for product code ignoring the
' dashes and treating dashes as a space, as requested by pneuride
LWhere = LWhere + " OR REPLACE(products.ProductCode, ""-"", """") LIKE ""%" & CleanSQLStr(LSearchFor) & "%"""
LWhere = LWhere + " OR REPLACE(products.ProductCode, ""-"", "" "") LIKE ""%" & CleanSQLStr(LSearchFor) & "%"""
' (SS,16/1/13) added ASearchDesc
If ASearchDesc Then LWhere = LWhere + " OR ProductDescription LIKE ""%" & CleanSQLStr(LSearchFor) & "%"""
LWhere = LWhere + ")"
End If
End If
' (SS,27/3/07) changes to following for individual where for Group, Category and Subcategory
Else
If AGroup <> "" Then
LWhere = LWhere + " AND categories.GroupName = """ + CleanSQLStr(AGroup) + """"
End If
If ACategory <> "" Then
LWhere = LWhere + " AND productcategories.Category = """ + CleanSQLStr(ACategory) + """"
End If
If ASubcategory <> "" Then
LWhere = LWhere + " AND subcategories.Subcategory = """ + CleanSQLStr(ASubcategory) + """"
End If
' (SS,27/3/07) if subcategory is blank and summary list enabled then enable summary list (instead of normal detailed list)
If ASubcategory = "" And SummaryListEnabled Then ASummaryList = True
End If
' (SS,24/11/16) add filter for max price and min price, takes the lowest from StdPrice and SalePrice (Discount field isn't used here, maybe add in future if any site is using it)
Dim LMaxPrice
LMaxPrice = CleanRequestQueryString("maxprice")
If LMaxPrice <> "" Then
If IsNumeric(LMaxPrice) Then
LWhere = LWhere + " AND IF(SalePrice IS NULL, StdPrice, LEAST(StdPrice, SalePrice)) <= " & CleanSQLStr(LMaxPrice)
End If
End If
Dim LMinPrice
LMinPrice = CleanRequestQueryString("minprice")
If LMinPrice <> "" Then
If IsNumeric(LMinPrice) Then
LWhere = LWhere + " AND IF(SalePrice IS NULL, StdPrice, LEAST(StdPrice, SalePrice)) >= " & CleanSQLStr(LMinPrice)
End If
End If
' (SS,1/7/11) add LWhere which was separated, also LOrderBy
LSQL = LSQL + "WHERE " + LWhere
' (SS,1/6/15) added [If LOrderBy <> "" Then] because LOrderBy can now be blank due to full text search
If LOrderBy <> "" Then LSQL = LSQL + " ORDER BY " + LOrderBy
' get count of the amount of products
' (SS,16/1/13) modified to use GetSQLValue, 3 lines (OpenQuery, GetFieldValue, CloseQuery) replaced with just one
AProductsFound = GetSQLValue("SELECT Count(DISTINCT products.ProductCode) As Count " + LSQL)
'Response.Write "#" & LSQL & "#" & BR ' !***
GetProductSearchSQL = LSQL ' (SS,16/1/13)
End Function
' (SS,26/8/15) adjusts for MySQL full text search in boolean mode
' by prefixing each word not already prefixed with "+" or "-" with a "+"
' (SS,17/9/15) if prefixed with "||" then don't prefix each with +, i.e. it'll work as an "or"
Function AdjustForFullTextSearch(ASearchStr)
Dim LWords, LWord, LLeftChr, LResult, LNewWord
LResult = ""
If Left(ASearchStr, 2) = "||" Then
LResult = Mid(ASearchStr, 3) ' remove the leading ||
Else
LWords = Split(ASearchStr, " ")
For Each LWord In LWords
If LWord <> "" Then
LLeftChr = Left(LWord, 1)
If LLeftChr <> "+" And LLeftChr <> "-" Then
LNewWord = "+" + LWord
Else
LNewWord = LWord
End If
LResult = LResult + iif(LResult = "", "", " ") + LNewWord
End If
Next
End If
AdjustForFullTextSearch = LResult
End Function
' (SS,13/8/15)
Function SetSortBy(ASortBy)
If ASortBy <> "" Then
Session("SortBy") = ASortBy
End If
End Function
' (SS,13/8/15)
Function GetSortBy
Dim LResult
LResult = Session("SortBy")
If LResult = "" Then LResult = ProductsSortByDefault
GetSortBy = LResult
End Function
' (SS,13/8/15)
' 1 = Sort by Relevance, existing default search
' 2 = Price High to Low
' 3 = Price Low to High
' 4 = Product Name
Function GetSortByField
Dim LResult
Select Case GetSortBy
Case "2"
LResult = "StdPrice DESC"
Case "3"
LResult = "StdPrice"
Case "4"
LResult = "ProductName"
Case Else
LResult = ""
End Select
GetSortByField = LResult
End Function
' (SS,16/1/13) returns true if doing a normal search, used by GetProductSearchSQL and ShowProducts above
Function IsNormalSearch(ASearchFor, AGroup, ACategory, ASubcategory)
IsNormalSearch = ASearchFor <> "" Or (AGroup = "" And ACategory = "" And ASubcategory = "")
End Function
' (SS,19/10/15)
Sub EnableProductNavigation
FProductNavigationEnabled = True
End Sub
' (SS,19/10/15)
Sub DisableProductNavigation
FProductNavigationEnabled = False
End Sub
' (SS,19/10/15)
Function ProductNavigationEnabled
ProductNavigationEnabled = FProductNavigationEnabled
End Function
' (SS,27/7/11) moved here from ShowProducts
' (SS,11/8/15) calls ShowProductListNavNonRWD
Sub ShowProductListNavigation(ACurrentPageNo, AProductCount, AProductsPerPage, AStartRecordNo)
' (SS,19/10/15) added If ProductNavigationEnabled Then
If ProductNavigationEnabled Then
If IsRWD Then
ShowProductListNavRWD ACurrentPageNo, AProductCount, AProductsPerPage, AStartRecordNo
Else
ShowProductListNavNonRWD ACurrentPageNo, AProductCount, AProductsPerPage, AStartRecordNo
End If
End If
End Sub
' (SS,27/7/11) moved here from ShowProducts
' (SS,11/8/15) renamed from ShowProductListNavigation to ShowProductListNavNonRWD
' original version for backward compatibility called from ShowProductListNavigation above for non-RWD sites
Sub ShowProductListNavNonRWD(ACurrentPageNo, AProductCount, AProductsPerPage, AStartRecordNo)
Dim LPages, i, LRecordNo
LPages = ((AProductCount - 1) \ AProductsPerPage) + 1 ' \ is Div '
ShowProductListNavHeader
If ACurrentPageNo > 1 Then
ShowProductListNavLeft "javascript:itp_page(" & ACurrentPageNo - 1 & ")"
Else
ShowProductListNavLeft ""
End If
' pages are shown in groups of 10 '
Dim LStartPageNo
LStartPageNo = (((ACurrentPageNo - 1) \ PAGES_PER_GROUP) * PAGES_PER_GROUP) + 1 ' \ is Div '
ShowProductListNavMiddleHeader
ShowProductListNavItemsHeader
' if there are pages before this one then show the [...] to allow jump to previous pages
If LStartPageNo > 1 then
ShowProductListNavItem GetProductListNavItem("previous", LStartPageNo - 1, ACurrentPageNo)
End If
For i = LStartPageNo To LStartPageNo + (PAGES_PER_GROUP - 1)
If i > LPages Then Exit For ' if limit of pages reach then exit for loop
ShowProductListNavItem GetProductListNavItem("page", i, ACurrentPageNo)
Next
' if there are more pages then show the [...]
If i < LPages then
ShowProductListNavItem GetProductListNavItem("next", i, ACurrentPageNo)
End If
ShowProductListNavItemsFooter
' (SS,8/5/10) removed "Viewing ", added tr to "of"
' (SS,27/7/11) added tr("Showing")
ShowProductListNavInfo tr("Showing") & " " & (AStartRecordNo + 1) & " - " & (AStartRecordNo + AProductsPerPage) & " " & tr("of") & " " & AProductCount
ShowProductListNavMiddleFooter
If ACurrentPageNo < LPages Then
ShowProductListNavRight "javascript:itp_page(" & ACurrentPageNo + 1 & ")"
Else
ShowProductListNavRight ""
End If
ShowProductListNavFooter
End Sub
' returns product list navigation item, called from ShowProducts above
' (SS,27/7/11) AType can be "page", "previous", "next"
Function GetProductListNavItem(AType, APageNo, ACurrentPageNo)
Dim LClass, LAltTag, LText
LClass = "product-list-nav-item"
LText = "..."
If AType = "previous" Then
LAltTag = "Previous pages"
ElseIf AType = "next" Then
LAltTag = "More pages"
Else ' i.e. page
LText = CStr(APageNo)
LAltTag = "Page " & APageNo
If APageNo = ACurrentPageNo Then LClass = "product-list-nav-item-active"
End If
GetProductListNavItem = "<a class=""" & LClass & """ href=""javascript:itp_page(" & APageNo & ")"" alt=""" & LAltTag & """>" & LText & "</a>"
End Function
' (SS,27/7/11) moved here from ShowProducts
' (SS,11/8/15) renamed from ShowProductListNavigation to ShowProductListNavRWD
' new verison, modified for RWD sites
Sub ShowProductListNavRWD(ACurrentPageNo, AProductCount, AProductsPerPage, AStartRecordNo)
Const PAGES_PER_GROUP = 5
Dim LPages, i, LRecordNo
LPages = ((AProductCount - 1) \ AProductsPerPage) + 1 ' \ is Div '
ShowProductListNavHeader
' pages are shown in groups of 10 '
Dim LStartPageNo
LStartPageNo = (((ACurrentPageNo - 1) \ PAGES_PER_GROUP) * PAGES_PER_GROUP) + 1 ' \ is Div '
' (SS,8/5/10) removed "Viewing ", added tr to "of"
' (SS,27/7/11) added tr("Showing")
' (SS,12/8/15) added Min()
ShowProductListNavInfo tr("Showing") & " " & (AStartRecordNo + 1) & " - " & Min(AStartRecordNo + AProductsPerPage, AProductCount) & " " & tr("of") & " " & AProductCount
' (SS,12/8/15) added If, to only do if there is more than one page because above is routine is now always called
If AProductCount > AProductsPerPage Then
ShowProductListNavItemsHeader
' if there are previous pages
If ACurrentPageNo > 1 Then
ShowProductListNavItem GetProductListNavItemRWD("previous page", ACurrentPageNo - 1, ACurrentPageNo)
End If
' if there are pages before this one then show the [...] to allow jump to previous pages
If LStartPageNo > 1 then
ShowProductListNavItem GetProductListNavItemRWD("previous", LStartPageNo - 1, ACurrentPageNo)
End If
For i = LStartPageNo To LStartPageNo + (PAGES_PER_GROUP - 1)
If i > LPages Then Exit For ' if limit of pages reach then exit for loop
ShowProductListNavItem GetProductListNavItemRWD("page", i, ACurrentPageNo)
Next
' if there are more pages then show the [...]
If i < LPages then
ShowProductListNavItem GetProductListNavItemRWD("next", i, ACurrentPageNo)
End If
' if there are next pages
If ACurrentPageNo < LPages Then
ShowProductListNavItem GetProductListNavItemRWD("next page", ACurrentPageNo + 1, ACurrentPageNo)
End If
ShowProductListNavItemsFooter
End If
ShowProductListNavFooter
End Sub
' returns product list navigation item, called from ShowProducts above
' (SS,27/7/11) AType can be "page", "previous", "next"
' (SS,11/8/15) new version for RWD, added "previous page" and "next page" types
Function GetProductListNavItemRWD(AType, APageNo, ACurrentPageNo)
Dim LClass, LText
LClass = ""
If AType = "page" Then
LText = CStr(APageNo)
If APageNo = ACurrentPageNo Then LClass = " class=""active"""
ElseIf AType = "previous page" Then
LText = "<"
ElseIf AType = "next page" Then
LText = ">"
Else ' i.e. "previous" or "next" (group of pages)
LText = "..."
End If
GetProductListNavItemRWD = "<li" + LClass + "><a href=""javascript:itp_page(" & APageNo & ")"">" & LText & "</a></li>"
End Function
' (SS,26/5/07) added following for routines to allow templates to determine whether at first or last records
Sub SetAtFirstOrLastProduct(ACount, ARecordCount)
FAtFirstProduct = ACount = 1
FAtLastProduct = ACount = ARecordCount
End Sub
Function AtFirstProduct
AtFirstProduct = FAtFirstProduct
End Function
Function AtLastProduct
AtLastProduct = FAtLastProduct
End Function
' added If CleanRequestQueryString("grp")...
' (SS,6/6/07) added Server.HTMLEncode to cat and subcat because cat or subcat name may contain " or other odd characters
' (SS,17/9/08) replaced CleanRequestQueryString("cat") with GetCategory, also added AddStoreURL
' (SS,11/7/11) added "/" just before ending ">" for xml compatibility, also added id="frmSearch" to allow getElementById function to find the form, removed cw, cl
' (SS,20/2/12) replaced "products.asp" with GetScriptName, mainly for development use, but it's better anyway
' (SS,17/1/13) removed the ending "/" tag from form because it means the end of the form it shouldn't be here
' (SS,12/8/15) added sortby, removed extra combo search code including If IsSearchVisible Then
' (SS,12/8/15) changed qty from "1" to "" to stop it being returned in search URL
' (SS,13/8/15) moved qty to end (from after code to after sortby) for a tidier URL, also moved pageno from after cmd to after page
' (SS,24/8/17) added Server.HTMLEncode to all variables to prevent xss, including cmd, code, grp, page, pageno, orderno, previously only cat and subcat had it
Sub ShowSearch
%>
<form name="frmSearch" id="frmSearch" method="get" action="<%=AddStoreURL(GetScriptName)%>" onsubmit="return itp_onsubmitsearch()" class="nospace">
<%
' (SS,6/7/07) added HTMLEncode to following to allow quotes etc when previous search value is displayed
Dim LSearchDefault
LSearchDefault = HTMLEncode(CleanRequestQueryString("search"))
ShowSearchMain LSearchDefault, ""
%>
<input type="hidden" name="cmd" value="<%=Server.HTMLEncode(CleanRequestQueryString("cmd"))%>" />
<%If CleanRequestQueryString("grp") <> "" Then Response.Write("<input type=""hidden"" name=""grp"" value=""" & Server.HTMLEncode(CleanRequestQueryString("grp")) & """>") End If%>
<input type="hidden" name="cat" value="<%=Server.HTMLEncode(GetCategory)%>" />
<input type="hidden" name="subcat" value="<%=Server.HTMLEncode(CleanRequestQueryString("subcat"))%>" />
<input type="hidden" name="code" value="<%=Server.HTMLEncode(GetProductCodeQS)%>" />
<input type="hidden" name="page" value="<%=Server.HTMLEncode(CleanRequestQueryString("page"))%>" />
<input type="hidden" name="pageno" value="<%=Server.HTMLEncode(CleanRequestQueryString("pageno"))%>" />
<input type="hidden" name="xcmd" value="" />
<input type="hidden" name="xcode" value="" />
<input type="hidden" name="orderno" value="<%=Server.HTMLEncode(CleanRequestQueryString("orderno"))%>" />
<input type="hidden" name="sortby" value="" />
<input type="hidden" name="qty" value="" />
</form>
<%
End Sub
' (SS,9/12/04) invisible form required to allow javascript to work on these pages '
' (SS,17/1/13) added ending / to input tag
' (SS,12/8/15) removed cbsubcat, no longer used
' (SS,24/8/17) added HTMLEncode to page to prevent xss
Sub ShowSearchForStreamline
%>
<form name="frmSearch" method="get" action="products.asp" onsubmit="return itp_onsubmitsearch()" class="nospace">
<input type="hidden" name="search" value="" />
<input type="hidden" name="cmd" value="" />
<input type="hidden" name="pageno" value="" />
<input type="hidden" name="cat" value="" />
<input type="hidden" name="subcat" value="" />
<input type="hidden" name="code" value="" />
<input type="hidden" name="qty" value="1" />
<input type="hidden" name="cw" value="" />
<input type="hidden" name="cl" value="" />
<input type="hidden" name="page" value="<%=Server.HTMLEncode(CleanRequestQueryString("page"))%>" />
<input type="hidden" name="xcmd" value="" />
<input type="hidden" name="xcode" value="" />
</form>
<%
End Sub
' (SS,25/7/11) following four routines moved here from ShowCategoryMenu
Sub SetMenuCategory(AMenuCategory)
Session("MenuCategory") = AMenuCategory
End Sub
Sub SetMenuSubcategory(AMenuSubcategory)
Session("MenuSubcategory") = AMenuSubcategory
End Sub
Function GetMenuCategory
GetMenuCategory = Session("MenuCategory")
End Function
Function GetMenuSubcategory
GetMenuSubcategory = Session("MenuSubcategory")
End Function
' (SS,7/6/11) mod to only show category if category contains at least one enabled product
' (SS,26/7/11) added MenuShortName and HTMLEncoded of Category and Subcategory names
' (SS,18/4/11) added Special Offers
Sub ShowCategoryMenu
ShowMenuHeader
Dim LMenuCategory, LMenuSubcategory, LCategory, LSubcategory, LPrevCategory, LEndSubcategoryTable, LDoEndTable
Dim LHRefCat, LHRefCatJava, LHRefSubcat, LSubcatCount, LCatCount
' get menu category from query string, if not assigned then get from session '
LMenuCategory = CleanRequestQueryString("cat")
If LMenuCategory = "" Then
LMenuCategory = GetMenuCategory
End If
LMenuSubcategory = CleanRequestQueryString("subcat")
If LMenuSubcategory = "" Then
LMenuSubcategory = GetMenuSubcategory
End If
' if a search was done or cat command then clear the menu selection '
If (CleanRequestQueryString("search") <> "") or (CleanRequestQueryString("cmd") = "cat") Then
LMenuCategory = ""
LMenuSubcategory = ""
End If
' (SS,17/5/11) added following to allow category menu to be shown for selected group
Dim LGroup
If GroupsEnabled Then
LGroup = GetGroup
If LGroup = "" Then LGroup = GetGroupForCategory(LMenuCategory)
If AlwaysDefaultGroupForMenu Then LGroup = GetDefaultGroup ' (SS,1/8/11)
Else
LGroup = ""
End If
' if not subcat specified then do not select it '
If CleanRequestQueryString("subcat") = "" Then LMenuSubcategory = ""
' (SS,27/7/11) if product/group/category/subcategory not specified then reset then category etc.
If Not CategoryOrProductSpecified Then
LGroup = GetDefaultGroup
LMenuCategory = ""
LMenuSubcategory = ""
' (SS,28/7/11) if only group is specified then clear the category and subcategory
ElseIf GroupOnlySpecified Then
LMenuCategory = ""
LMenuSubcategory = ""
End If
' if viewing of single product then find its category '
If CleanRequestQueryString("cmd") = "" And GetProductCodeQS <> "" Then
' (SS,1/8/11) added If AlwaysDefaultGroupForMenu and GetDefaultGroupCatSubcatForProduct to get the cat/sub for the default group
If AlwaysDefaultGroupForMenu Then
GetDefaultGroupCatSubcatForProduct GetProductCode, LMenuCategory, LMenuSubcategory
Else
GetFirstCatSubcatForProduct GetProductCode, "", LMenuCategory, LMenuSubcategory
End If
End If
' preserve for next time '
SetMenuCategory(LMenuCategory)
SetMenuSubcategory(LMenuSubcategory)
' (SS,19/2/13) moved SQL from to separate GetCategoryMenuSQL routine
OpenQuery(GetCategoryMenuSQL(LGroup))
LPrevCategory = ""
LEndSubcategoryTable = False
LSubcatCount = 0
LCatCount = 1 ' (SS,3/3/06)
' (SS,26/7/11) added following to use short name if specified and to also HTMLEncode
Dim LCategoryDisplayName, LSubcategoryDisplayName
Do While Not EndOfQuery
LCategory = GetQueryValue("Category")
LSubcategory = GetQueryValue("Subcategory")
LHRefCat = GetCategoryLink(LCategory)
LHRefCatJava = GetCategoryJavaLink(LCategory)
LHRefSubcat = GetSubcategoryLink(LCategory, LSubcategory)
' (SS,26/7/11)
LCategoryDisplayName = NB(GetQueryValue("CategoryMenuShortName"))
If LCategoryDisplayName = "" Then LCategoryDisplayName = LCategory
LCategoryDisplayName = HTMLEncode(LCategoryDisplayName)
LSubcategoryDisplayName = NB(GetQueryValue("SubcategoryMenuShortName"))
If LSubcategoryDisplayName = "" Then LSubcategoryDisplayName = LSubcategory
LSubcategoryDisplayName = HTMLEncode(LSubcategoryDisplayName)
If IsNull(LSubcategory) Then
If LCategory = LMenuCategory Then
If LCatCount Mod 2 = 1 Then
ShowMenuCategorySelected LCategoryDisplayName, LHRefCat
Else
ShowMenuCategorySelectedAlternateColor LCategoryDisplayName, LHRefCat
End If
Else
If LCatCount Mod 2 = 1 Then
ShowMenuCategory LCategoryDisplayName, LHRefCat
Else
ShowMenuCategoryAlternateColor LCategoryDisplayName, LHRefCat
End If
End If
ElseIf LCategory = LMenuCategory Then
If LPrevCategory <> LCategory Then
LEndSubcategoryTable = True
If LCatCount Mod 2 = 1 Then
ShowMenuSubcategoryHeader LCategoryDisplayName, LHRefCat
Else
ShowMenuSubcategoryHeaderAlternateColor LCategoryDisplayName, LHRefCat
End If
LSubcatCount = 0
If UseAlternateColorForCategories = True Then LCatCount = LCatCount + 1 ' (SS,3/3/06)
End If
LSubcatCount = LSubcatCount + 1
If LMenuSubcategory = LSubcategory Then
If LSubcatCount Mod 2 = 1 Then
ShowMenuSubcategorySelected LCategoryDisplayName, LSubcategoryDisplayName, LHRefSubcat
Else
ShowMenuSubcategorySelectedAlternateColor LCategoryDisplayName, LSubcategoryDisplayName, LHRefSubcat
End If
Else
If LSubcatCount Mod 2 = 1 Then
ShowMenuSubcategory LCategoryDisplayName, LSubcategoryDisplayName, LHRefSubcat
Else
ShowMenuSubcategoryAlternateColor LCategoryDisplayName, LSubcategoryDisplayName, LHRefSubcat
End If
End If
ElseIf LPrevCategory <> LCategory Then
If LCatCount Mod 2 = 1 Then
ShowMenuCategoryHasSubcategories LCategoryDisplayName, LHRefCat
Else
ShowMenuCategoryHasSubcategoriesAlternateColor LCategoryDisplayName, LHRefCat
End If
If UseAlternateColorForCategories = True Then LCatCount = LCatCount + 1 ' (SS,3/3/06)
End If
LPrevCategory = LCategory
NextQueryRecord
' make sure the sub category table is closed '
If LEndSubcategoryTable Then
LDoEndTable = False
If EndOfQuery Then
LDoEndTable = True
Else
LDoEndTable = LPrevCategory <> GetQueryValue("Category")
End If
If LDoEndTable Then
LEndSubcategoryTable = False
ShowMenuSubcategoryFooter
End If
End If
Loop
CloseQuery
' (SS,17/4/12) added special offers link from ShowSubCategoryMenu below
If AddSpecialOffersToSubcategoryMenu Then
LCategory = "Special Offers"
LHRefCat = "javascript:itp_sosearch()"
If LCase(CleanRequestQueryString("search")) = SPECIAL_OFFERS_SEARCH_TEXT Then
ShowMenuCategorySelected LCategory, LHRefCat
Else
If LCatCount Mod 2 = 1 Then
ShowMenuCategory LCategory, LHRefCat
Else
ShowMenuCategoryAlternateColor LCategory, LHRefCat
End If
End If
End If
ShowMenuFooter
End Sub
' (SS,19/2/13) moved SQL from ShowCategoryMenu to here, now also called from ShowHTMLSiteMap
' (SS,20/2/13) no longer called from ShowHTMLSiteMap
Function GetCategoryMenuSQL(AGroup)
Dim LSQL
' (SS,21/5/04) select all unique categories and subcategories that have been used '
' (SS,7/6/11) added join to products to look up ProductsDisabled, so that only categories containing at least one products are listed in the menu
LSQL = "SELECT productcategories.Category, productcategories.Subcategory, categories.MenuShortName AS CategoryMenuShortName, subcategories.MenuShortName AS SubcategoryMenuShortName" &_
" FROM productcategories" &_
" INNER JOIN categories ON categories.Category = productcategories.Category" &_
" INNER JOIN subcategories ON subcategories.Category = productcategories.Category AND subcategories.Subcategory = productcategories.Subcategory" &_
" INNER JOIN products ON products.ProductCode = productcategories.ProductCode"
' (SS,7/6/11) added following to ignore products which are disabled, allowing only categories that contain products to be shown
' (SS,31/10/18) added GetProductsFilter to filter out subproducts
LSQL = LSQL + " WHERE products.ProductDisabled = FALSE" & GetProductsFilter
' (SS,17/5/11) added following to filter records if group selected
' (SS,7/6/11) replaced WHERE with AND, because WHERE added above
' (SS,22/7/11) added the missing " " before AND
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
If AGroup <> "" Then LSQL = LSQL + " AND GroupName = '" + CleanSQLStr(AGroup) + "'"
LSQL = LSQL &_
" GROUP BY Category, Subcategory" &_
" ORDER BY categories.SortOrder, productcategories.Category, subcategories.SortOrder, productcategories.Subcategory"
GetCategoryMenuSQL = LSQL
End Function
' (SS,20/6/05) added for use in Cufflinks, where a single category is used '
Sub ShowSubCategoryMenu()
Dim LMenuCategory, LMenuSubcategory, LCategory, LSubcategory, LHRefSubcat, LSubcatCount
' get menu category from query string, if not assigned then get from session '
LMenuCategory = CleanRequestQueryString("cat")
If LMenuCategory = "" Then
LMenuCategory = Session("MenuCategory")
End If
LMenuSubcategory = CleanRequestQueryString("subcat")
If LMenuSubcategory = "" Then
LMenuSubcategory = Session("MenuSubcategory")
End If
' if a search was done or cat command then clear the menu selection '
If (CleanRequestQueryString("search") <> "") or (CleanRequestQueryString("cmd") = "cat") or (CleanRequestQueryString("subcat") = "") Then
LMenuCategory = ""
LMenuSubcategory = ""
End If
ShowMenuHeader
' if viewing of single product then find its category '
If CleanRequestQueryString("cmd") = "" And GetProductCodeQS <> "" Then
GetFirstCatSubcatForProduct GetProductCodeQS, "", LMenuCategory, LMenuSubcategory
End If
' preserve for next time '
Session("MenuCategory") = LMenuCategory
Session("MenuSubcategory") = LMenuSubcategory
Dim LSQL
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
LSQL = "SELECT * FROM subcategories WHERE Category = '" & CleanSQLStr(GetCategoryForSubcategoryMenu) & "' ORDER BY SortOrder"
OpenQuery(LSQL)
LSubcatCount = 0
Do While Not EndOfQuery
LCategory = GetQueryValue("Category")
LSubcategory = GetQueryValue("Subcategory")
LHRefSubcat = GetSubcategoryLink(LCategory, LSubcategory)
LSubcatCount = LSubcatCount + 1
If LMenuSubcategory = LSubcategory Then
ShowMenuSubcategorySelected LCategory, LSubcategory, LHRefSubcat
Else
If LSubcatCount Mod 2 = 1 Then
ShowMenuSubcategory LCategory, LSubcategory, LHRefSubcat
Else
ShowMenuSubcategoryAlternateColor LCategory, LSubcategory, LHRefSubcat
End If
End If
NextQueryRecord
Loop
' add special offers link '
If AddSpecialOffersToSubcategoryMenu Then
LSubcatCount = LSubcatCount + 1
LSubcategory = "Special Offers"
LHRefSubcat = "javascript:itp_sosearch()"
If LCase(CleanRequestQueryString("search")) = SPECIAL_OFFERS_SEARCH_TEXT Then
ShowMenuSubcategorySelected LCategory, LSubcategory, LHRefSubcat
Else
If LSubcatCount Mod 2 = 1 Then
ShowMenuSubcategory LCategory, LSubcategory, LHRefSubcat
Else
ShowMenuSubcategoryAlternateColor LCategory, LSubcategory, LHRefSubcat
End If
End If
End If
CloseQuery
ShowMenuFooter
End Sub
' (SS,23/3/07) new type of menu that starts from group and shows 3 levels, i.e. groups, categories and subcategories
' AShowAll parameter allows all categories to be shown even if they don't contain a product
Sub ShowGroupMenu(AShowAll)
ShowMenuHeader
' (SS,23/4/09) added LMenuCategoryName
Dim LMenuGroup, LMenuCategory, LMenuSubcategory, LCategory, LSubcategory, LMenuCategoryName, LPrevCategory, LMenuSelected
Dim LHRefCat, LHRefCatJava, LHRefSubcat
Dim LGroup, LPrevGroup, LHRefGrp
Dim LFirstInGroup, LInGroup, LFirstInCategory, LInCategory, LInSubcategory, LCategoryHeaderShown, LSubcategoryHeaderShown
Dim LCategoryCount, LSubcategoryCount, LShowCategories, LShowSubcategories
' get menu category from query string, if not assigned then get from session '
LMenuGroup = CleanRequestQueryString("grp")
If LMenuGroup = "" Then
LMenuGroup = Session("MenuGroup")
Else ' if group selected then clear the category and subcategories in the session
Session("MenuCategory") = ""
Session("MenuSubcategory") = ""
End If
LMenuCategory = CleanRequestQueryString("cat")
If LMenuCategory = "" Then
LMenuCategory = Session("MenuCategory")
End If
LMenuSubcategory = CleanRequestQueryString("subcat")
If LMenuSubcategory = "" Then
LMenuSubcategory = Session("MenuSubcategory")
End If
' if code, cat and subcat not specified then clear selection
' (SS,20/7/07) was if (CleanRequestQueryString("search") <> "") or (CleanRequestQueryString("cmd") = "cat")
' If (CleanRequestQueryString("code") = "") and (CleanRequestQueryString("grp") = "") and (CleanRequestQueryString("cat") = "") and (CleanRequestQueryString("subcat") = "") Then
' (SS,27/7/11) replaced above with If Not CategoryOrProductSpecified Then
If Not CategoryOrProductSpecified Then
LMenuGroup = ""
LMenuCategory = ""
LMenuSubcategory = ""
End If
' if not subcat specified then do not select it '
If CleanRequestQueryString("subcat") = "" Then LMenuSubcategory = ""
' if viewing of single product then find its category '
If CleanRequestQueryString("cmd") = "" And GetProductCodeQS <> "" Then
GetFirstCatSubcatForProduct GetProductCodeQS, "", LMenuCategory, LMenuSubcategory
End If
LMenuSelected = LMenuGroup <> "" Or LMenuCategory <> "" Or LMenuSubcategory <> "" ' (SS,20/7/07)
' (SS,14/5/07) added following to always open main group main if only one group exists
If Not GroupsEnabled Then LMenuGroup = GROUP_MAIN
' if category isn't blank then find it's group
If LMenuCategory <> "" Then
LMenuGroup = GetGroupForCategory(LMenuCategory)
End If
' preserve for next time '
Session("MenuGroup") = LMenuGroup
Session("MenuCategory") = LMenuCategory
Session("MenuSubcategory") = LMenuSubcategory
Dim LSQL
' (SS,23/3/07) selects all subcategories, and looks up the group
' (SS,31/3/09) added c.GroupName to ORDER BY in case SortOrder is the same for all groups
' (SS,23/4/09) added c.MenuShortName
If AShowAll Then
LSQL = "SELECT c.GroupName, s.Category, s.Subcategory, c.MenuShortName" &_
" FROM subcategories s" &_
" INNER JOIN categories c ON c.Category = s.Category" &_
" INNER JOIN groups g ON g.GroupName = c.GroupName" &_
" GROUP BY GroupName, Category, Subcategory" &_
" ORDER BY g.SortOrder, c.GroupName, c.SortOrder, s.Category, s.SortOrder, s.Subcategory"
Else
LSQL = "SELECT c.GroupName, pc.Category, pc.Subcategory, c.MenuShortName" &_
" FROM productcategories pc" &_
" INNER JOIN categories c ON c.Category = pc.Category" &_
" INNER JOIN subcategories s ON s.Category = pc.Category AND s.Subcategory = pc.Subcategory" &_
" INNER JOIN groups g ON g.GroupName = c.GroupName" &_
" GROUP BY Category, Subcategory" &_
" ORDER BY g.SortOrder, c.GroupName, c.SortOrder, pc.Category, s.SortOrder, pc.Subcategory"
End If
' store the list in multi dimensional array
OpenQuery(LSQL)
Dim LArray, i, LItemCount
' (SS,23/4/09) added SHORT_CATEGORY_NAME
Const GROUP = 0, CATEGORY = 1, SUBCATEGORY = 2, SHORT_CATEGORY_NAME = 3, CATEGORY_COUNT = 4, SUBCATEGORY_COUNT = 5
Const MAX_FIRST_ELEMENT = 5 ' (SS,23/4/09)
i = 0
' (SS,23/4/09) replace 4 with MAX_FIRST_ELEMENT
Redim LArray(MAX_FIRST_ELEMENT, 1)
Do While Not EndOfQuery
' (SS,23/4/09) replace 4 with MAX_FIRST_ELEMENT
If i Mod 20 = 0 Then Redim Preserve LArray(MAX_FIRST_ELEMENT, i + 20) ' redimension in blocks of 20 to save resources
LArray(GROUP, i) = GetQueryValue("GroupName")
LArray(CATEGORY, i) = GetQueryValue("Category")
LArray(SUBCATEGORY, i) = GetQueryValue("Subcategory")
LArray(SHORT_CATEGORY_NAME, i) = NB(GetQueryValue("MenuShortName")) ' (SS,23/4/09)
NextQueryRecord
i = i + 1
Loop
LItemCount = i
CloseQuery
' go backwards through the array setting count of categories and subcategories
' this is used later to not show a category or subcategory if only one item exists in it
LPrevGroup = ""
LPrevCategory = ""
LCategoryCount = 0
LSubcategoryCount = 0
For i = LItemCount - 1 To 0 STEP -1
LGroup = LArray(GROUP, i)
LCategory = LArray(CATEGORY, i)
If LGroup = LPrevGroup Then
If LCategory = LPrevCategory Then
LSubcategoryCount = LSubcategoryCount + 1
Else
LCategoryCount = LCategoryCount + 1
LSubcategoryCount = 1
End If
Else
LCategoryCount = 1
LSubcategoryCount = 1
End If
LArray(CATEGORY_COUNT, i) = LCategoryCount
LArray(SUBCATEGORY_COUNT, i) = LSubcategoryCount
LPrevCategory = LCategory
LPrevGroup = LGroup
Next
LPrevGroup = ""
LPrevCategory = ""
LCategoryHeaderShown = False
LSubcategoryHeaderShown = False
For i = 0 To LItemCount - 1
LGroup = LArray(GROUP, i)
LCategory = LArray(CATEGORY, i)
LSubcategory = LArray(SUBCATEGORY, i)
' (SS,23/4/09) use short name if assigned
LMenuCategoryName = LArray(SHORT_CATEGORY_NAME, i)
If LMenuCategoryName = "" Then LMenuCategoryName = LCategory
LHRefGrp = GetGroupLink(LGroup)
LHRefCat = GetCategoryLink(LCategory)
LHRefCatJava = GetCategoryJavaLink(LCategory)
LHRefSubcat = GetSubcategoryLink(LCategory, LSubcategory)
LFirstInGroup = (LGroup <> LPrevGroup)
LInGroup = (LGroup = LMenuGroup)
LFirstInCategory = (LCategory <> LPrevCategory)
LInCategory = (LCategory = LMenuCategory)
LInSubcategory = (LSubcategory = LMenuSubcategory)
' if group has changed i.e. when first encountered in the list
If LFirstInGroup Then
' to only show categories if more than one exists for this group
LShowCategories = LArray(CATEGORY_COUNT, i) > 1
' close the subcategory header if it was already shown in previous group
If LSubcategoryHeaderShown Then
ShowMenuSubcategoryFooter
LSubcategoryHeaderShown = False
End If
' close the category header if it was already shown in previous group
If LCategoryHeaderShown Then
ShowMenuCategoryFooter
LCategoryHeaderShown = False
End If
' if group chosen in menu then show selected version, else unselected version
If LInGroup Then
ShowMenuGroupSelected LGroup, LHRefGrp
Else
ShowMenuGroup LGroup, LHRefGrp
End If
End If
' if in chosen group and first in category
If LInGroup And LFirstInCategory Then
' to only show categories if more than one exists for this category
LShowSubcategories = LArray(SUBCATEGORY_COUNT, i) > 1
' close the subcategory header if it was already shown in previous category
If LSubcategoryHeaderShown Then
ShowMenuSubcategoryFooter
LSubcategoryHeaderShown = False
End If
If LShowCategories Then
' if first category in group then show the category header
If LFirstInGroup And Not LCategoryHeaderShown Then
ShowMenuCategoryHeader
LCategoryHeaderShown = True
End If
' if category chosen in menu then show selected version, else unselected version
If LInCategory Then
ShowMenuCategorySelected LMenuCategoryName, LHRefCat ' (SS,29/4/09) replaced LCategory with LMenuCategoryName
Else
' (SS,20/7/07) if menu selected and groups aren't being used then use Javascript link
' to cause search engine not to follow it, else normal link
If LMenuSelected And Not GroupsEnabled Then
ShowMenuCategory LMenuCategoryName, LHRefCatJava ' (SS,29/4/09) replaced LCategory with LMenuCategoryName
Else
ShowMenuCategory LMenuCategoryName, LHRefCat ' (SS,29/4/09) replaced LCategory with LMenuCategoryName
End If
End If
End If
End If
' if in chosen category, or if in chosen group and categories are not shown because there's only one
If LInCategory Or (LInGroup And Not LShowCategories) Then
If LShowSubcategories Then
' if first subcategory in category then show the subcategory header
If LFirstInCategory And Not LSubcategoryHeaderShown Then
ShowMenuSubcategoryHeader
LSubcategoryHeaderShown = True
End If
' if subcategory chosen in menu then show selected version, else unselected version
If LInSubcategory Then
ShowMenuSubcategorySelected LSubcategory, LHRefSubcat
Else
ShowMenuSubcategory LSubcategory, LHRefSubcat
End If
End If
End If
LPrevCategory = LCategory
LPrevGroup = LGroup
Next
' make sure subcategory is closed
If LSubcategoryHeaderShown Then ShowMenuSubcategoryFooter
' make sure category is closed
If LCategoryHeaderShown Then ShowMenuCategoryFooter
ShowMenuFooter
End Sub
' (SS,11/5/15) AGroupList is a semicolon separated list, if empty string then use groups table
' based on Sub ShowMenuForCategories (below)
Sub ShowMenuForGroups(AGroupList)
ShowMenuHeader
If AGroupList = "" Then
' all groups with a category in sort order
OpenQuery("SELECT DISTINCT g.GroupName, g.SortOrder FROM groups g INNER JOIN categories c ON g.GroupName = c.GroupName ORDER BY g.SortOrder, g.GroupName")
Do While Not EndOfQuery
AGroupList = AGroupList + Iif(AGroupList = "", "", ";") + GetQueryValue("GroupName")
NextQueryRecord
Loop
CloseQuery
End If
Dim LGroups, LGroup
LGroups = Split(AGroupList, ";")
For Each LGroup In LGroups
ShowMenuForGroup LGroup
Next
ShowMenuFooter
End Sub
' (SS,11/5/15) create menu for given group, used for creating popup type menu for individual top level categories
Sub ShowMenuForGroup(AGroup)
ShowMenuCategoryHeader AGroup, GetGroupLink(AGroup)
Dim LCategory
OpenQuery2("SELECT * FROM categories WHERE GroupName = '" & CleanSQLStr(AGroup) & "' ORDER BY SortOrder, Category")
Do While Not EndOfQuery2
LCategory = GetQueryValue2("Category")
' (SS,14/5/15) if single level
'ShowMenuCategory LCategory, GetCategoryLink(LCategory)
ShowMenuForCategory LCategory
NextQueryRecord2
Loop
CloseQuery2
ShowMenuCategoryFooter
End Sub
' (SS,18/5/15)
' AType can be "G" for group, "C" for category
' (SS,15/9/15) can include "A" for all levels (i.e. include if only one subcategory)
' "E" to include empty subcategories
' e.g. AType of "CAE" means category level, all levels and include empty
' (SS,11/11/16) "F" means first menu item is group, Allows first to be group and rest to be category
Sub StartMultiMenu(AType)
SetMultiMenuType AType
ShowMenuHeader
End Sub
' (SS,21/5/15)
Sub SetMultiMenuType(AType)
FMultiMenuType = AType
FMultiMenuTopLevelItemCount = 0 ' (SS,11/11/16)
End Sub
' (SS,21/5/15)
' (SS,15/9/15) added InStr because FMultiMenuType can now contains more options
Function IsMultiMenuTypeGroup
' (SS,11/11/16) added [Or (FMultiMenuTopLevelItemCount = 1 And InStr(FMultiMenuType, "F") > 0)] to allow first item to be group where rest are category
IsMultiMenuTypeGroup = InStr(FMultiMenuType, "G") > 0
End Function
' (SS,11/11/16) to allow first item to be group where rest are category
Function IsMultiMenuTypeGroupForFirst
IsMultiMenuTypeGroupForFirst = FMultiMenuTopLevelItemCount = 1 And InStr(FMultiMenuType, "F") > 0
End Function
' (SS,21/5/15)
Function IsMultiMenuTypeAll
IsMultiMenuTypeAll = InStr(FMultiMenuType, "A") > 0
End Function
' (SS,21/5/15)
Function IsMultiMenuTypeIncludeEmpty
IsMultiMenuTypeIncludeEmpty = InStr(FMultiMenuType, "E") > 0
End Function
' (SS,22/5/15) used in inc-template-category-menu.asp to call the recursive AddMultiMenuLevel below with correct starting level number and default AMenuName2 of ""
' (SS,6/2/16) amended to allow AMenuName to have up to 3 levels separated by "|", normally applicable when called from MultiMenuAdditions where AMenuShortName and AMenuAdditionList are blank
Sub AddMultiMenu(AMenuName, AMenuShortName, AMenuAdditionList)
FMultiMenuTopLevelItemCount = FMultiMenuTopLevelItemCount + 1 ' (SS,11/11/16) used by IsMultiMenuTypeGroup to allow first item to be a group and rest to be category
Dim LLevelNo
' (SS,11/1/16) added Or IsMultiMenuTypeGroupForFirst
If IsMultiMenuTypeGroup Or IsMultiMenuTypeGroupForFirst Then
LLevelNo = 1
Else
LLevelNo = 2
End If
' (SS,6/2/16) two levels can now be passed on, default to second being none, only added if more than one level found in AMenuName, separated by "|"
Dim LMenuName1, LMenuName2
LMenuName1 = AMenuName
LMenuName2 = ""
' (SS,6/2/16 when called form MultiMenuAdditions we want to allow individual category and/or subcategory
' i.e. assuming | used as a divider, split into levels up to 3 for groups, category and subcategory
' added following to allow more specific category / subcategory separated by "|"
' if group type then AMenuName may contain 3 levels e.g. Pro Audio | Pro Audio | Headphones
If InStr(AMenuName, "|") > 0 Then
Dim LLevel1Name, LLevel2Name, LLevel3Name
LLevel1Name = Trim(NthSeparatedValue(AMenuName, "|", 1))
LLevel2Name = Trim(NthSeparatedValue(AMenuName, "|", 2))
LLevel3Name = Trim(NthSeparatedValue(AMenuName, "|", 3))
' determine LLevelNo and values for LMenuName1 and LMenuName2
' if group then ignore first level, just pass on the second which should be category and 3 if it exists as subcategory and there are 3 then start from LevelNo2 and use 2nd and 3rd
' (SS,11/1/16) added Or IsMultiMenuTypeGroupForFirst
If IsMultiMenuTypeGroup Or IsMultiMenuTypeGroupForFirst Then
' remember that LMenuName1 and LMenuName2 are reversed, if subcat and cat exist then subcat goes in LMenuName1 and cat in LMenuName2
If LLevel3Name <> "" Then
LLevelNo = 3
LMenuName1 = LLevel3Name ' subcategory
LMenuName2 = LLevel2Name ' category
Else
LLevelNo = 2
LMenuName1 = LLevel2Name ' category only
End If
Else ' for category there should always be two level names passed, so set level no to 3 and pass category and subcategory
LLevelNo = 3
LMenuName1 = LLevel2Name ' subcategory
LMenuName2 = LLevel1Name ' category
End If
End If
' (SS,6/2/16) as before, but added LMenuName2 after AMenuName to replace "" and replaced AMenuName with LMenuName1
AddMultiMenuLevel LLevelNo, LMenuName1, LMenuName2, AMenuShortName, "", AMenuAdditionList
End Sub
' check how many come off first level (i.e. categories in group or subcategories in category)
' recursive
' (SS,13/5/16) change to allow special dummy group/category to have a link set when prefixed with "URL:"
Sub AddMultiMenuLevel(ALevelNo, AMenuName, AMenuName2, AMenuShortName, AMenuShortName2, AMenuAdditionList)
Dim LMenuCaption, LMenuCaption2, LSQL, LSubmenuCount, LLink, LField1, LField2, LField3, LField4
' Response.Write "#" & AMenuName & ", " & AMenuName2 & ", " & AMenuShortName & ", " & AMenuShortName2 & BR
' determine captions depending on existence of short menu names
LMenuCaption = AMenuName
If AMenuShortName = "" Then
LMenuCaption = AMenuName
Else
LMenuCaption = AMenuShortName
End If
If AMenuShortName2 = "" Then
LMenuCaption2 = AMenuName2
Else
LMenuCaption2 = AMenuShortName2
End If
' Level 1 is Group, Level 2 is Category, Level 3 is Subcategory
If ALevelNo = 1 Then
' get all categories for given group
' (SS,1/7/15) added SortOrder, which if negative, can be used to hide it, quicker than running a query to see if there are any enabled products
' (SS,11/11/16) added If IsMultiMenuTypeGroupForFirst for new group query that only shows categories containing enabled products
' (SS,31/10/18) added GetProductsFilter to filter out subproducts
If IsMultiMenuTypeGroupForFirst Then
LSQL = "SELECT DISTINCT c.Category, c.MenuShortName, c.SortOrder FROM categories c" &_
" INNER JOIN productcategories pc ON pc.Category = c.Category" &_
" INNER JOIN products p ON p.ProductCode = pc.ProductCode" &_
" WHERE p.ProductDisabled = FALSE AND GroupName = '" + CleanSQLStr(AMenuName) + "'" & GetProductsFilter &_
" ORDER BY c.SortOrder, c.Category"
Else ' (SS,11/11/16) as it was
LSQL = "SELECT Category, MenuShortName, SortOrder FROM categories WHERE GroupName = '" + CleanSQLStr(AMenuName) + "' ORDER BY SortOrder, Category"
End If
LLink = GetGroupLink(AMenuName)
LField1 = "Category"
LField2 = "MenuShortName"
LField3 = ""
LField4 = ""
ElseIf ALevelNo = 2 Then ' category level
' (SS,15/9/15) added code to only include subcategories containing at least one enabled product
If IsMultiMenuTypeIncludeEmpty Then
LSQL = "SELECT s.Category, s.Subcategory, c.MenuShortName AS CategoryMenuShortName, s.MenuShortName AS SubcategoryMenuShortName, s.SortOrder FROM subcategories s INNER JOIN categories c ON c.Category = s.Category WHERE s.Category = '" & CleanSQLStr(AMenuName) & "' ORDER BY s.SortOrder, s.Subcategory"
Else ' (SS,15/9/15) added following to exclude empty subcategories
' (SS,31/10/18) added GetProductsFilter to filter out subproducts
LSQL = "SELECT DISTINCT s.Category, s.Subcategory, c.MenuShortName AS CategoryMenuShortName, s.MenuShortName AS SubcategoryMenuShortName, s.SortOrder" &_
" FROM subcategories s" &_
" INNER JOIN categories c ON c.Category = s.Category" &_
" INNER JOIN productcategories pc ON pc.Category = s.Category AND pc.Subcategory = s.Subcategory" &_
" INNER JOIN products p ON p.ProductCode = pc.ProductCode" &_
" WHERE p.ProductDisabled = FALSE AND s.Category = '" & CleanSQLStr(AMenuName) & "'" & GetProductsFilter &_
" ORDER BY s.SortOrder, s.Subcategory"
End If
LLink = GetCategoryLink(AMenuName)
LField1 = "Subcategory"
LField2 = "SubcategoryMenuShortName"
LField3 = "Category"
LField4 = "CategoryMenuShortName"
Else ' i.e. last level 3, subcategory
LSQL = ""
LSubmenuCount = 0
LLink = GetSubcategoryLink(AMenuName2, AMenuName)
End If
If LSQL <> "" Then
Dim LArray, i
Redim LArray(3, -1)
OpenQuery(LSQL)
LSubmenuCount = GetQueryRecordCount
Do While Not EndOfQuery
' (SS,1/7/15) added "If" to only add if SortOrder isn't negative
If NZ(GetQueryValue("SortOrder")) >= 0 Then
Redim Preserve LArray(3, UBound(LArray, 2) + 1)
LArray(0, UBound(LArray, 2)) = NB(GetQueryValue(LField1))
LArray(1, UBound(LArray, 2)) = NB(GetQueryValue(LField2))
If LField3 <> "" Then
LArray(2, UBound(LArray, 2)) = NB(GetQueryValue(LField3))
LArray(3, UBound(LArray, 2)) = NB(GetQueryValue(LField4))
Else
LArray(2, UBound(LArray, 2)) = ""
LArray(3, UBound(LArray, 2)) = ""
End If
End If
NextQueryRecord
Loop
CloseQuery
End If
' if there are sub items then add to dropdown, else show the single item
If LSubmenuCount = 0 Then
If AMenuAdditionList = "" Then
' show this menu item
ShowMenuSingle LMenuCaption, LLink, ALevelNo
Else
' ALink is set to "" because it's a dummy i.e. not a proper group
' (SS,13/5/16) added following to allow the link to be set when prefixed with URL:
Dim LLinkOverride
If Left(AMenuName, 4) = "URL:" Then
LLinkOverride = Mid(AMenuName, 5)
Else
LLinkOverride = ""
End If
ShowMenuDropdownHeader LMenuCaption, LLinkOverride, ALevelNo ' (SS,13/5/16) replaced "" with LLinkOverride
MultiMenuAdditions AMenuAdditionList
ShowMenuDropdownFooter
End If
Else
' if only one and name matches current then don't do a dropdown
'If LSubmenuCount > 1 Or LArray(0, 0) <> AMenuName Then ShowMenuDropdownHeader LMenuCaption, LLink, ALevelNo
' (SS,15/9/15) added "Or IsMultiMenuTypeAll" to always include if option chosen
If LSubmenuCount > 1 Or IsMultiMenuTypeAll Then ShowMenuDropdownHeader LMenuCaption, LLink, ALevelNo
' iterate array recursively calling this routine
For i = LBound(LArray, 2) To UBound(LArray, 2)
AddMultiMenuLevel ALevelNo + 1, LArray(0, i), LArray(2, i), LArray(1, i), LArray(3, i), ""
Next
MultiMenuAdditions AMenuAdditionList
'If LSubmenuCount > 1 Or LArray(0, 0) <> AMenuName Then ShowMenuDropdownFooter
' (SS,15/9/15) added "Or IsMultiMenuTypeAll" to always include if option chosen
If LSubmenuCount > 1 Or IsMultiMenuTypeAll Then ShowMenuDropdownFooter
End If
End Sub
' (SS,22/5/15)
Sub MultiMenuAdditions(AMenuAdditionList)
' do the addition list
If AMenuAdditionList <> "" Then
Dim LAdditions, LAddition, LAdditionClean
LAdditions = Split(AMenuAdditionList, ";")
For Each LAddition In LAdditions
' if empty then it means separator/divider
LAdditionClean = Trim(LAddition)
If LAdditionClean = "" Then
ShowMenuDivider
Else
AddMultiMenu LAdditionClean, "", ""
End If
' ALevelNo, AMenuName, AMenuName2, AMenuShortName, AMenuShortName2, AMenuAdditionList
'If AMenuAdditionList <> "" Then AddMultiMenuLevel ALevelNo, AMenuAdditionList, "", "", "", ""
Next
End If
End Sub
' (SS,18/5/15)
Sub EndMultiMenu
ShowMenuFooter
End Sub
' (SS,13/8/14) ACategoryList is a semicolon separated list, if empty string then use categories table
Sub ShowMenuForCategories(ACategoryList)
ShowMenuHeader
If ACategoryList = "" Then
OpenQuery("SELECT * FROM categories ORDER BY SortOrder, Category")
Do While Not EndOfQuery
ACategoryList = ACategoryList + Iif(ACategoryList = "", "", ";") + GetQueryValue("Category")
NextQueryRecord
Loop
CloseQuery
End If
Dim LCategories, LCategory
LCategories = Split(ACategoryList, ";")
For Each LCategory In LCategories
ShowMenuForCategory LCategory
Next
ShowMenuFooter
End Sub
' (SS,13/8/14) create menu for given category, used for creating popup type menu for individual top level categories
Sub ShowMenuForCategory(ACategory)
ShowMenuSubcategoryHeader ACategory, GetCategoryLink(ACategory)
Dim LSubcategory
OpenQuery("SELECT * FROM subcategories WHERE Category = '" & CleanSQLStr(ACategory) & "' ORDER BY SortOrder, Subcategory")
Do While Not EndOfQuery
LSubcategory = GetQueryValue("Subcategory")
ShowMenuSubcategory ACategory, LSubcategory, GetSubcategoryLink(ACategory, LSubcategory)
NextQueryRecord
Loop
CloseQuery
ShowMenuSubcategoryFooter
End Sub
' (SS,12/2/05) added Request.ServerVariables("QUERY_STRING") '
' added LHighlightFor, LHighlightValue '
Sub ShowMainMenu()
ShowMainMenuHeader
OpenQuery("SELECT * FROM menu ORDER BY SortOrder")
Dim LCurrentPageName, LPos, LIsSamePage, LHighlightFor, LHighlightValue
LCurrentPageName = Request.ServerVariables("SCRIPT_NAME")
If Request.ServerVariables("QUERY_STRING") <> "" Then LCurrentPageName = LCurrentPageName & "?" & Request.ServerVariables("QUERY_STRING")
LPos = InStrRev(LCurrentPageName, "/")
If LPos > 0 Then LCurrentPageName = Mid(LCurrentPageName, LPos + 1, Len(LCurrentPageName))
' (SS,12/2/05) determine category and group '
Dim LProductCode, LGroup, LCat, LSubcategory
LProductCode = GetProductCodeQS
LCat = CleanRequestQueryString("cat")
If LCat = "" Then
If LProductCode <> "" Then
' GetFirstCatSubcatForProduct LProductCode, LGroup, LCat, LSubcategory
' (SS,1/8/11) replaced above with following, to height correct menu item for group
GetBreadcrumbGroupCatSubcatForProduct LProductCode, LGroup, LCat, LSubcategory
Else
LGroup = ""
End If
Else
LGroup = GetGroupForCat(LCat)
End If
' (SS,12/2/05) used in GetProductsPageTitle '
If GroupsEnabled Then
strGroupFromMenu = LGroup
Else
strGroupFromMenu = ""
End If
Do While Not EndOfQuery
If (GetQueryValue("PageName") = LCurrentPageName) Or (GetQueryValue("Tag") = 1 And IsSearchPage) Then
LIsSamePage = True
Else
LIsSamePage = False
' (SS,12/2/05) added following to highlight for group or category '
LHighlightFor = GetQueryValue("HighlightFor")
LHighlightValue = GetQueryValue("HighlightValue")
If LHighlightFor = "cat" Then
If LCat = LHighlightValue Then
LIsSamePage = True
End If
ElseIf LHighlightFor = "grp" Then
If LCat <> "" Then
If LGroup = LHighlightValue Then
LIsSamePage = True
End If
End If
End if
End If
ShowMainMenuItem GetQueryValue("MenuItemName"), GetQueryValue("PageName"), GetQueryValue("Width"), LIsSamePage, GetQueryValue("ExtraHTML"), GetQueryValue("Tag")
NextQueryRecord
Loop
CloseQuery
ShowMainMenuFooter
End Sub
' (SS,15/8/05) new horizontal menu
Sub ShowMainMenuHorizontal()
ShowMainMenuHorizontalHeader
OpenQuery("SELECT * FROM menu ORDER BY SortOrder")
Dim LCurrentPageName, LPos, LIsSamePage, LHighlightFor, LHighlightValue
LCurrentPageName = Request.ServerVariables("SCRIPT_NAME")
If Request.ServerVariables("QUERY_STRING") <> "" Then LCurrentPageName = LCurrentPageName & "?" & Request.ServerVariables("QUERY_STRING")
LPos = InStrRev(LCurrentPageName, "/")
If LPos > 0 Then LCurrentPageName = Mid(LCurrentPageName, LPos + 1, Len(LCurrentPageName))
Do While Not EndOfQuery
If (GetQueryValue("PageName") = LCurrentPageName) Or (GetQueryValue("Tag") = 1 And IsSearchPage) Then
LIsSamePage = True
Else
LIsSamePage = False
End If
ShowMainMenuHorizontalItem GetQueryValue("MenuItemName"), GetQueryValue("PageName"), GetQueryValue("Width"), LIsSamePage, GetQueryValue("ExtraHTML"), GetQueryValue("Tag")
NextQueryRecord
Loop
CloseQuery
ShowMainMenuHorizontalFooter
End Sub
' (SS,12/2/05) returns Group for Cat, blank string if Category not found '
Function GetGroupForCat(ACat)
OpenQuery2("SELECT GroupName FROM categories WHERE Category = '" & CleanSQLStr(ACat) & "'")
If Not EndOfQuery2 Then
GetGroupForCat = GetQueryValue2("GroupName")
Else
GetGroupForCat = ""
End If
CloseQuery2
End Function
' (SS,28/4/05) returns product list category suffix from group table for given category '
' (SS,16/3/12) removed, no longer used, field ProductListCategorySuffix has been dropped from groups table
'Function GetProductListCategorySuffixForGroup(AGroup)
' OpenQuery2("SELECT * FROM groups WHERE GroupName = '" & CleanSQLStr(AGroup) & "'")
' If Not EndOfQuery2 Then
' GetProductListCategorySuffixForGroup = GetQueryValue2("ProductListCategorySuffix")
' Else
' GetProductListCategorySuffixForGroup = ""
' End If
' CloseQuery2
'End Function
' (SS,20/6/05) added following to get page text from highlights table '
' (SS,21/5/07) modified to only open database if not already open and close it if it had to be opened
' (SS,15/4/09) modified to use sitedetails table instead of highlights
Function GetWebText(AWebTextCode)
Dim LWebText
' LWebText = GetValueFromQuery("Description", "SELECT * FROM highlights WHERE ShowThis <> 0 AND ProductCode = 'WEBTEXT-" & AWebTextCode & "'")
' (SS,15/4/09) replaced above with following
' (SS,6/5/09) corrected bug after was after first "=" moved to before "Type"
' (SS,12/6/12) added CleanSQLStr before AWebTextCode to prevent SQL injection
LWebText = GetValueFromQuery("Text", "SELECT Text FROM sitedetails WHERE (Type = 'Page Content' OR Type = 'Other') AND Name = '" & CleanSQLStr(AWebTextCode) & "'")
If IsNull(LWebText) Then LWebText = ""
' (SS,15/4/09) added following token replacements
If InStr(LWebText, "{") > 0 Then ' (SS,16/6/09) added this to bypass following if "{" not found for speed
LWebText = ReplaceStr(LWebText, "{STORE_NAME}", GetStoreName)
LWebText = ReplaceStr(LWebText, "{STORE_ADDRESS}", GetStoreAddressAsHTML)
LWebText = ReplaceStr(LWebText, "{STORE_EMAIL}", GetStoreEmail)
LWebText = ReplaceStr(LWebText, "{STORE_FAX}", GetStoreFax)
LWebText = ReplaceStr(LWebText, "{STORE_TELEPHONE}", GetStoreTelephone)
LWebText = ReplaceStr(LWebText, "{VAT_REG_NO}", GetStoreVATRegNo)
LWebText = ReplaceStr(LWebText, "{VAT_RATE}", (GetVATRate * 100) & "")
' (SS,16/6/09)
LWebText = ReplaceStr(LWebText, "{WORLDPAY_INSTALL_ID}", GetWorldPayInstallID)
If InStr(LWebText, "{DELIVERY_") > 0 Then ' (SS,22/6/10) added extra check before doing the following slower replacements becaus GetDeliveryMatrix runs queries
LWebText = ReplaceStr(LWebText, "{DELIVERY_PRICE_BASED}", GetDeliveryMatrix("P", "", True, True))
LWebText = ReplaceStr(LWebText, "{DELIVERY_WEIGHT_BASED}", GetDeliveryMatrix("W", "", True, True))
' (SS,19/6/09)
LWebText = ReplaceStr(LWebText, "{DELIVERY_PRICE_BASED_EXC_VAT}", GetDeliveryMatrix("P", "", False, True))
LWebText = ReplaceStr(LWebText, "{DELIVERY_WEIGHT_BASED_EXC_VAT}", GetDeliveryMatrix("W", "", False, True))
End If
' (SS,14/5/09)
' (SS,18/5/09) because GetDeliveryCostInfo is a time consuming routine we only want to call it if {DELIVERY_COST_INFO} is in the string
If InStr(LWebText, "{DELIVERY_COST_INFO}") > 0 Then
LWebText = ReplaceStr(LWebText, "{DELIVERY_COST_INFO}", GetDeliveryCostInfo)
End If
' (SS,10/8/17) added following for Bank Transfer details from settings
If InStr(LWebText, "{BANK_TRANSFER_DETAILS") > 0 Then
' (SS,29/9/17) modified to allow two different GBP accounts
LWebText = ReplaceStr(LWebText, "{BANK_TRANSFER_DETAILS}", BankTransferDetailsToHTML(GetBankTransferDetailsForCurrency("GBP", ""))) ' GBP account for overseas customers
LWebText = ReplaceStr(LWebText, "{BANK_TRANSFER_DETAILS_GBP}", BankTransferDetailsToHTML(GetBankTransferDetailsForCurrency("GBP", "United Kingdom"))) ' (SS,29/9/17) account for UK customers
LWebText = ReplaceStr(LWebText, "{BANK_TRANSFER_DETAILS_EUR}", BankTransferDetailsToHTML(GetBankTransferDetailsForCurrency("EUR", "")))
LWebText = ReplaceStr(LWebText, "{BANK_TRANSFER_DETAILS_USD}", BankTransferDetailsToHTML(GetBankTransferDetailsForCurrency("USD", "")))
End If
' (SS,19/2/13) for sitemap replace {SITEMAP} token with sitemap, and custom version from customutils.asp
' (SS,20/2/13) added bypassing of ComposeDescription because sitemap can become very big and cause ComposeDescription to be slow, e.g. 500K takes about 60 seconds
Dim LBypassComposeDesc
LBypassComposeDesc = False
If InStr(LWebText, "{SITEMAP}") > 0 Then
LWebText = ReplaceStr(LWebText, "{SITEMAP}", GetHTMLSiteMap)
LBypassComposeDesc = True
End If
If InStr(LWebText, "{CUSTOM-SITEMAP}") > 0 Then
LWebText = ReplaceStr(LWebText, "{CUSTOM-SITEMAP}", GetCustomHTMLSitemap)
LBypassComposeDesc = True
End If
' (SS,6/6/11) added following to process tables, bullets, and tokens, first False parameters means not to convert new lines to HTML
' (SS,20/2/13) added If condition for bypassing
If Not LBypassComposeDesc Then LWebText = ComposeDescription(LWebText, False, False)
Else ' (SS,22/9/11) added following to encode special characters like pound signs etc (ComposeDescription does it of the above condition)
LWebText = HTMLEncodeSpecialChars(LWebText)
End If
GetWebText = LWebText
End Function
' (SS,5/3/13) same as GetWebText but allows a default if GetWebText returns blank
Function GetWebTextDef(AWebTextCode, ADefault)
Dim LResult
LResult = GetWebText(AWebTextCode)
If LResult = "" Then LResult = ADefault
GetWebTextDef = LResult
End Function
' (SS,4/8/15) used to get content of a given token, can be called from inc-template..
Function GetTokenText(ATokenName)
Dim LResult
LResult = GetSiteDetail("Tokens", ATokenName)
LResult = ComposeDescription(LResult, False, False)
GetTokenText = LResult
End Function
' (SS,5/6/09) gets site detail directly, quicker than using GetWebText
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
Function GetSiteDetail(AType, AName)
GetSiteDetail = NB(GetValueFromQuery("Text", "SELECT Text FROM sitedetails WHERE Type = '" & CleanSQLStr(AType) & "' AND Name = '" & CleanSQLStr(AName) & "'"))
End Function
' (SS,27/9/11) returns true if page exists in sitedetails table
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
' (SS,17/1/13) modified so that it's only called once in a page request by using FPageExists variable, to prevent repetitive calls to query
' (SS,5/2/13) changed to return True if home page i.e. APageName is ""
Function PageExists(APageName)
If FPageExists = "" Then
' (SS,5/2/13) return true if there is no page name, i.e. it's the home page
If APageName = "" Then
FPageExists = True
Else
FPageExists = GetSQLValue("SELECT COUNT(*) FROM sitedetails WHERE Type = 'Page Content' AND Name = '" & CleanSQLStr(APageName) & "'") > 0
End If
End If
PageExists = FPageExists
End Function
' (SS,27/9/11) returns Heading and Text for page content
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
Function GetPageContent(APageName, ByRef AHeading)
AHeading = GetSQLValueAsString("SELECT ExtraField2 FROM sitedetails WHERE Type = 'Page Content' AND Name = '" & CleanSQLStr(APageName) & "'")
GetPageContent = GetWebText(APageName) ' because it processes tokens etc. but could be an issue if same name exists in Page Content and Other
End Function
' (SS,9/12/10) now calls ShowHighlightsMain
Sub ShowHighlights()
ShowHighlightsMain(False) ' False means normal type, not the new grid type
End Sub
' (SS,9/12/10) for new grid format
Sub ShowHighlightsGrid()
ShowHighlightsMain(True)
End Sub
' (SS,9/12/10) changed name from ShowHighlights to ShowHighlightsMain, added AIsGrid parameter because it can be used in normal mode and new grid mode
Sub ShowHighlightsMain(AIsGrid)
Dim LCount, LRecordCount, LProductCode, LProductName, LHighlightDescription, LSalePrice, LStdPrice, LNumInStock, LCategory, LSubcategory, LShowCategoryPicture, LGroup
' (SS,20/6/05) added SUBSTRING(highlights.ProductCode FROM 1 FOR 8) <> 'WEBTEXT-' '
' (SS,27/6/05) changed LEFT to INNER JOIN removed WEBTEXT etc because INNER JOIN does it'
' because of only showing matching products. This also fixes problem where invalid code '
' in highlights table.'
'OpenQuery("SELECT * FROM highlights INNER JOIN products ON products.ProductCode = highlights.ProductCode WHERE ShowThis <> 0 ORDER BY highlights.SortOrder, highlights.ProductCode")
' (SS,15/4/09) replaced above with following, data moved from highlights to sitedetails
'OpenQuery("SELECT sitedetails.Text AS Description, products.* FROM sitedetails INNER JOIN products ON products.ProductCode = sitedetails.Name WHERE Type = 'Featured Products' AND CheckBoxField <> 0 ORDER BY sitedetails.SortOrder, products.ProductCode")
' (SS,23/6/10) replaced above with following, now linking to suppliers table to get availability
Dim LSQL
' (SS,12/12/11) added "AND NOT products.ProductDisabled" to not show disabled products, i.e. not relying on user to untick show this on edit featured products
LSQL = "SELECT sitedetails.Text AS Description, products.*, " & GetStockAvailabilityFieldList &_
" FROM sitedetails" &_
" INNER JOIN products ON products.ProductCode = sitedetails.Name" &_
" LEFT JOIN suppliers ON suppliers.SupplierID = products.SupplierID" &_
" WHERE Type = 'Featured Products' AND CheckBoxField <> 0 AND NOT products.ProductDisabled" &_
" ORDER BY sitedetails.SortOrder, products.ProductCode"
OpenQuery(LSQL)
LRecordCount = GetQueryRecordCount
LCount = 0
If Not EndOfQuery Then
' (SS,9/12/10) added If AIsGrid.. to call different header for grid format
If AIsGrid Then
ShowHighlightsHeaderForGrid
Else
ShowHighlightsHeader
End If
Do While Not EndOfQuery
LCount = LCount + 1
' (SS,26/5/07) added following to allow templates to determine whether at first or last records
SetAtFirstOrLastProduct LCount, LRecordCount
LProductCode = GetQueryValue("ProductCode")
LProductName = HTMLEncode(GetQueryValue("ProductName")) ' (SS,26/7/11) added HTMLEncode
' (SS,1/6/04) added & "" after GetFieldValue("Description") to prevent Invalid use of null error '
'LHighlightDescription = ReplaceStr(GetFieldValue("Description") & "", Chr(13) & Chr(10), "<br>")
'(SS,8/5/06) replaced above with following due to new table feature
LHighlightDescription = GetFieldValue("Description") & ""
LHighlightDescription = ComposeDescription(LHighlightDescription, True, False)
' (SS,25/5/11) following two lines removed, now in ComposeDescription itself
'LHighlightDescription = ConvertNewlinesEtcToHTML(LHighlightDescription) ' (SS,,23/5/11) replaces Replace(LHighlightDescription, Chr(13) & Chr(10), "<br>")
'LHighlightDescription = Replace(LHighlightDescription, "@@@", NL) ' (SS,8/5/06) replaces special table newline with proper newline
LSalePrice = GetQueryValue("SalePrice")
LStdPrice = GetQueryValue("StdPrice")
LNumInStock = GetQueryValue("NumInStock")
SetProductDimensions(GetFieldValue("Dimensions")) ' (SS,9/7/07)
' (SS,19/9/06)
LStdPrice = CorrectCurrencyNV(LStdPrice)
LSalePrice = CorrectCurrencyNV(LSalePrice)
' (SS,11/2/05) added LGroup '
GetFirstCatSubcatForProduct LProductCode, LGroup, LCategory, LSubcategory
LShowCategoryPicture = PictureExists("C", LCategory, "")
' (SS,24/7/07) added following because ShowHighlightsItem will get the lowest price using
' (SS,6/7/11) removed 0 parameter, added False, no need to look up options price
GetPricingFromProductRecord(False)
' (SS,23/6/10) added following to get stock availability incase it's used to show on highlights or determine whether to show buy button
' SetStockAvailability must be before SetStockInfo because SetStockInfo might use the result of SetStockAvailability
SetStockAvailabilityFromFields
SetStockInfo LNumInStock, GetFieldValue("DateStockDue")
' (SS,9/12/10) added If AIsGrid.. to call different routine for grid format
If AIsGrid Then
ShowHighlightsItemForGrid LCount, LProductCode, LProductName, LHighlightDescription, LSalePrice, LStdPrice, LNumInStock, LCategory, LSubcategory, LShowCategoryPicture
Else
ShowHighlightsItem LCount, LProductCode, LProductName, LHighlightDescription, LSalePrice, LStdPrice, LNumInStock, LCategory, LSubcategory, LShowCategoryPicture
End If
NextQueryRecord
Loop
' (SS,9/12/10) added If AIsGrid.. to call different footer for grid format
If AIsGrid Then
ShowHighlightsFooterForGrid
Else
ShowHighlightsFooter ' (SS,27/6/05) moved here from after CloseDatabase so this is only shown if header is shown
End If
End If
CloseQuery
End Sub
Sub SaveSettingsForContinue()
Session("Search") = strSearch
Session("Page") = strPage
Session("Category") = strCategory
Session("Subcategory") = strSubcategory
End Sub
Sub GetSettingsForContinue()
strSearch = Session("Search")
strPage = Session("Page")
strCategory = Session("Category")
strSubcategory = Session("Subcategory")
End Sub
Sub AddSearchToLog()
' (SS,17/12/10) modified to only log when strSearch is not blank and not
Dim LSQL
' (SS,18/1/13) added NEW_PRODUCTS_SEARCH_TEXT
If strSearch <> "" And LCase(strSearch) <> SPECIAL_OFFERS_SEARCH_TEXT And LCase(strSearch) <> NEW_PRODUCTS_SEARCH_TEXT Then
' (SS,17/12/10) removed saving of category and subcategory (", Category='" & CleanSQLStr(strCategory) & "', Subcategory='" & CleanSQLStr(strSubcategory) & "'")
' table no longer contains these fields
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
LSQL = "INSERT INTO searchlog SET SessionID='" & CleanSQLStr(FSessionID) & "', Search='" & CleanSQLStr(strSearch) & "', Page='" & CleanSQLStr(strPage) & "'"
LSQL = LSQL + ", VisitorIP='" & CleanSQLStr(Request.ServerVariables("REMOTE_ADDR")) & "', ClientWidth='" & CleanSQLStr(strClientWidth) & "', ClientHeight='" & CleanSQLStr(strClientHeight) & "'"
ExecuteQuery LSQL
'Response.Write gsADOErrors ' (SS,17/12/10) removed
End If
End Sub
' (SS,12/4/19) a new search (used by Facebook Pixel 'Search' event)
Function IsNewSearch
Dim LSearch, LPageNo
LSearch = GetSearch
LPageNo = CleanRequestQueryString("pageno")
IsNewSearch = LSearch <> "" And LCase(LSearch) <> SPECIAL_OFFERS_SEARCH_TEXT And LCase(LSearch) <> NEW_PRODUCTS_SEARCH_TEXT And LPageNo = ""
End Function
' (SS,22/9/09) returns True if given product code is found and enabled
' (SS,12/6/12) added CleanSQLStr before AProductCode to prevent SQL injection
Function CheckEnabledProductExists(AProductCode)
CheckEnabledProductExists = GetValueFromQuery("NoRecs", "SELECT COUNT(*) AS NoRecs FROM products WHERE ProductCode = '" & CleanSQLStr(AProductCode) & "' AND ProductDisabled = 0") > 0
End Function
' (SS,22/9/09) returns search phrase from search engine referrer, blank if it doesn't exist
' looks for this in the q= query string
Function GetSearchForPhraseFromReferrer
Dim LReferrer
LReferrer = LCase(Request.ServerVariables("HTTP_REFERER"))
Dim LStoreURL
LStoreURL = LCase(GetStoreURL)
GetSearchForPhraseFromReferrer = ""
' if referrer is same as this site or blank the return with blank
If LReferrer = "" Or Left(LReferrer, Len(LStoreURL)) = LStoreURL Then Exit Function
' to get the search phrase, look for ?q= or &q= until end of string or &
Dim LPos
LPos = InStr(LReferrer, "?q=")
If LPos = 0 Then LPos = InStr(LReferrer, "&q=")
If LPos = 0 Then Exit Function ' if q= not found then return blank
' get the bit after q= and before next =
LPos = LPos + 3 ' position after the =
Dim LSearchFor, LReferrerLen
LReferrerLen = Len(LReferrer)
LSearchFor = ""
Do While LPos <= LReferrerLen And Mid(LReferrer, LPos, 1) <> "&"
LSearchFor = LSearchFor + Mid(LReferrer, LPos, 1)
LPos = LPos + 1
Loop
LSearchFor = URLDecode(LSearchFor) ' decode so that + become spaces
GetSearchForPhraseFromReferrer = LSearchFor
End Function
' (SS,12/9/12) added this to allow redirection to product page
' gets called when search results in a single product
' which is better because it prevents an extra "Product Search" header from being show.
' it also helps with recently viewed items because we can get the current product code from querystring
' rather than relying on SetProductCode which can also set the global product code variable when parsing list of products
Sub SingleProductRedirect(AProductCode)
Response.Redirect GetProductLink(AProductCode)
End Sub
Sub ShowSingleProduct(AProductCode)
' (SS,15/2/12) when doing a search the query string will not contain a code, but we need to tell other routines
' which product code we've chosen to go to, because this routine might have been called by a search where single product was found
SetProductCode AProductCode
' find the product, show info if product exists '
' (SS,23/6/10) modified to add stock availability which includes added join to new suppliers table
' was OpenQuery("SELECT * FROM products WHERE ProductCode = """ & AProductCode & """ AND ProductDisabled = 0")
' (SS,12/6/12) added CleanSQLStr before AProductCode to prevent SQL injection
Dim LSQL
LSQL = "SELECT products.*, " & GetStockAvailabilityFieldList &_
" FROM products" &_
" LEFT JOIN suppliers ON suppliers.SupplierID = products.SupplierID" &_
" WHERE ProductCode = '" & CleanSQLStr(AProductCode) & "' AND ProductDisabled = 0"
OpenQuery(LSQL)
If EndOfQuery Then
CloseQuery
ShowProductDetailNotFound HTMLEncode(AProductCode) ' (SS,24/8/17) added HTMLEncode to prevent xss injection
Exit Sub
End If
' (SS,4/6/09)
AddToRecentlyViewed AProductCode
' get query values before closing query '
' (SS,30/6/11) added ProductID, this will be the primary key in future, instead of ProductCode
Dim LProductID, LProductName, LProductDesc, LDimensions, LSalePrice, LStdPrice, LNumInStock, LNew
LProductID = GetFieldValue("ProductID") ' (SS,30/6/11)
LProductName = HTMLEncode(GetFieldValue("ProductName")) ' (SS,26/7/11) added HTMLEncode
LDimensions = GetFieldValue("Dimensions")
LProductDesc = GetFieldValue("ProductDescription")
LSalePrice = GetFieldValue("SalePrice")
LStdPrice = GetFieldValue("StdPrice")
LNumInStock = GetFieldValue("NumInStock")
LNew = GetFieldValue("New") <> 0
' (SS,18/5/07) to handle 3 new price fields, discount and VATBand
' (SS,6/7/11) removed 0 parameter, added False, no need to look up options price
GetPricingFromProductRecord(False)
' (SS,23/6/10) must be before SetStockInfo because SetStockInfo might use the result of SetStockAvailability
SetStockAvailabilityFromFields
' (SS,16/11/07)
SetStockInfo LNumInStock, GetFieldValue("DateStockDue")
CloseQuery
' determine if larger picture exists, set flag if it does
Dim LShowLarge, LWidth, LHeight
' (SS,8/8/18) commented out following, now redundant, not used since RWD framework, LShowLarge, LWidth, LHeight will now have undefined values when passed to ShowTheProductDetail
'GetProductPictureSize AProductCode, LWidth, LHeight
' (SS,30/5/07) replaced LARGE_THUMBNAIL_WIDTH with FLargeThumbnailWidth and LARGE_THUMBNAIL_HEIGHT with FLargeThumbnailHeight
'LShowLarge = LWidth > LargeThumbnailWidth Or LHeight > LargeThumbnailHeight
' find the main category and subcategory of the product '
' (SS,11/2/05) added LGroup '
Dim LGroup, LCategory, LSubcategory
' GetFirstCatSubcatForProduct AProductCode, LGroup, LCategory, LSubcategory
' (SS,1/8/11) replaced above with following, because we want to show the path taken to get to product, rather then the first cat/subcat
GetBreadcrumbGroupCatSubcatForProduct AProductCode, LGroup, LCategory, LSubcategory
' show the you are here category and subcategory '
ShowProductDetailYouAreHere LGroup, LCategory, LSubcategory
' show the main details of the product '
' (SS,18/9/06) added CorrectCurrency
LStdPrice = CorrectCurrencyNV(GetStdPrice)
LSalePrice = CorrectCurrencyNV(GetSalePrice)
LProductDesc = ComposeDescription(LProductDesc, True, False) ' (SS,28/4/06), (SS,3/6/11) added False parameter
'LProductDesc = Replace(LProductDesc, Chr(13) & Chr(10), "<br>") '(SS,29/4/06) moved here due to ComposeTables added above
' (SS,25/5/11) following two lines removed, now in ComposeDescription itself
'LProductDesc = ConvertNewlinesEtcToHTML(LProductDesc) ' (SS,17/5/11) replaced above with this, which also does quotes and pounds
'LProductDesc = Replace(LProductDesc, "@@@", NL) ' (SS,29/4/06) replaces special table newline with proper newline
' (SS,18/5/09) renamed ShowProductDetail to ShowTheProductDetail
' (SS,30/6/11) added LProductID
ShowTheProductDetail LProductID, AProductCode, LProductName, LProductDesc, LDimensions, LSalePrice, LStdPrice, LNumInStock, LNew, LCategory, LSubcategory, LShowLarge, LWidth, LHeight
' (SS,13/5/11) moved ShowProductDetailViewOtherProductsHeader code from below to ShowTheProductDetail routine
End Sub
' --- START OF TABLE COMPOSING ROUTINES ---
' (SS,29/4/06) to allow creation of tables from comma separated text in description of product
' syntax:
' {table title="This is a title" class=abc cols=3 width=200,,100 align=c,l,r nowrap separator=~ option=altrow}
' #Heading
' 1,2,3
' 4,5,6
' 7,8,9
' {endtable}
'
' If no parameters are defined i.e. {table} is used it's own,
' defaults will be used. Which are 2 columns, no title,
' no widths assigned, no aligns assigned, separator is a comma,
' nowrap is not set. Classes used will be tbl-title, tbl-col1,
' tbl-col2, tbl-col3..., tbl-colhd1, tbl-colhd2, tbl-colhd3...,
' if option=altrow used, then alternative rows will use classes
' tbl-col1b, tbl-col2b, tbl-col3b...
' When there isn't enough data for all columns, the last one
' will have a colspan to fill in the missing columns.
' To use different sets of classes for diffent tables,
' class parameter can be used. E.g. class=abc will set the
' classes used by the table to be:
' tbl-abc-title, tbl-abc-col1, tbl-abc-col2, tbl-abc-colhd1,
' tbl-abc-colhd1, tbl-abc-col1b, tbl-abc-col2b, etc.
' If the first column in a row is preceded with "#" then
' it gets treated as a header row, using classes tbl-colhd1 etc.
' The title parameter creates a special title row with one column
' spanning to the last column with class tbl-title.
' The separator, e.g. comma can be used in the cell data
' if the cell data is surrounded by quotes. E.g. "This is, a cell"
' will create one cell with "This is, a cell" (quotes will be removed),
' instead of two cells, one containing "This is", second one
' containing "a cell". If the cell data in the last column
' contains a separator then this is ignored, i.e. is used as
' part of the cell data instead of creating a new column.
' (SS,2/2/12) new autohref functionality added first row containing autohref=2 where number is the column
' this automatically tries to add ahref to comma separated list of products
' (SS,8/7/07) new function to replace ComposeTables, now also composes bullet
' (SS,14/4/11) added token facility
' (SS,25/5/11) now also does the newline, and special char adjustments, also added code to handle null value
' (SS,3/6/11) had issues with too many <br />'s, NLs which shouldn't breaks, fixed
' by adding new AFromReplaceTokens parameter, which is false for main call, True when called from ReplaceTokens
' (SS,6/6/11) added AConvertNewLines parameter to keep new lines as they are when calling from GetWebText
' (SS,16/6/11) added called to RemoveHashCommentLines
Function ComposeDescription(ADesc, AConvertNewLines, AFromReplaceTokens)
Dim LConvertNewLines
LConvertNewLines = AConvertNewLines ' (SS,30/7/15) can now be changed by hash comment option in token NCNL which stands for no convert new lines
Dim LDesc
If IsNull(ADesc) Then
LDesc = ""
Else
LDesc = ADesc
End If
If LDesc <> "" Then
LDesc = ComposeDescWith(LDesc, "record", "endrecord", "GetRecordHTML") ' (SS,28/4/17) fetch values from record into session
LDesc = ComposeDescWith(LDesc, "action", "endaction", "GetActionHTML") ' (SS,13/12/18) to run a specific action e.g. rename a product see function GetActionHTML
LDesc = ComposeDescWith(LDesc, "table", "endtable", "GetTableHTML")
LDesc = ComposeDescWith(LDesc, "bullet", "endbullet", "GetBulletHTML")
' (SS,16/6/11) removes all lines beginning with #, must be after ComposeDescWith for tables, because # is a heading line in tables
' (SS,4/8/15) moved here from below LDesc = HTMLEncodeSpecialChars(LDesc), because we want to do it before ReplaceTokens, to take effect as early as possible
LDesc = RemoveHashCommentLines(LDesc)
LDesc = ReplaceTokens(LDesc) ' (SS,14/4/11) replaces all curly bracket tokens with text held in site details table
' (SS,25/5/11) following two lines moved here from after calls to ComposeDescription, don't know why they weren't here in the first place
'LDesc = ConvertNewlinesEtcToHTML(LDesc) ' (SS,17/5/11) also does quotes and pounds and other special characters
' (SS,6/6/11) replaced above with following to only convert new lines when AConvertNewLines is True
LDesc = HTMLEncodeSpecialChars(LDesc) ' (SS,22/2/13) NB. this routine will be slow if run on large amounts of text because it works char by char e.g. 2 to 5 secs on 100KB
' (SS,16/6/11) removes all lines beginning with #, must be after ComposeDescWith for tables, because # is a heading line in tables
' (SS,4/8/15) moved above, before ReplaceTokens
'LDesc = RemoveHashCommentLines(LDesc)
' (SS,30/7/15) for tokens, if NCNL found in hash comment line e.g. #OPTION NCNL then don't convert newlines into HTML BRs
' (SS,4/8/15) removed AFromReplaceTokens replaced with LConvertNewLines
If LConvertNewLines Then
If IsHashCommentLineOption("NCNL") Then LConvertNewLines = False
End If
If LConvertNewLines Then LDesc = ConvertNewlinesToHTML(LDesc)
' (SS,29/4/06) replaces special table newline with proper newline
' (SS,3/6/11) added If Not AFromReplaceTokens to ensure this is only done by the root call, otherwise we'll end up with too many <br>s, we want the newlines in bullets and tables to remain newlines
If Not AFromReplaceTokens Then
LDesc = Replace(LDesc, NEW_LINE_FIX, NL)
LDesc = RemoveBlankTableRows(LDesc) ' (SS,14/12/18) removes blank table rows, i.e. those contained special token REMOVE_BLANK_ROW
End If
End If
ComposeDescription = LDesc
End Function
' (SS,8/7/07) renamed from ComposeTables to ComposeDescWith
' made it general purpose to work with other types besides tables
' original parameter was ADesc, added AStartToken, AEndToken, AFunction
Function ComposeDescWith(ADesc, AStartToken, AEndToken, AFunction)
Dim LDesc, LNewDesc, LStart, LEnd
LDesc = ADesc
If IsNull(LDesc) Then LDesc = "" ' (SS,17/5/07) bug fix ADesc being Null was causing endless loop
LNewDesc = ""
Do
LStart = GetTokenStart(LDesc, AStartToken)
LEnd = GetTokenEnd(LDesc, AEndToken)
If (LStart > 0) And (LEnd > 0) And (LStart < LEnd) Then
' (SS,1/8/07) replaced GetTableHTML(Mid.. with Eval(AFunction + "(Mid..
' (SS,6/6/11) added AStartToken, AEndToken to call to function, used by GetBulletHTML and GetTableHTML
LNewDesc = LNewDesc + Mid(LDesc, 1, LStart - 1) + Eval(AFunction + "(Mid(LDesc, LStart, LEnd - LStart + 1), AStartToken, AEndToken)")
LDesc = Mid(LDesc, LEnd + 1)
End If
Loop Until LStart = 0 Or LEnd = 0 Or LStart > LEnd
ComposeDescWith = LNewDesc + LDesc
End Function
' (SS,16/6/11) removes all lines beginning with hash, i.e. comments used in tokens, product descriptions etc.
' (SS,22/8/14) allows special #SECTION used to separate sections, replaced with HTML comment
Function RemoveHashCommentLines(AText)
Dim LResult
LResult = AText
' (SS,19/4/12) added Not HasScriptOrStyle(AText) to not ignore # in such cases because # has a special meaning in such cases
If InStr(AText, "#") > 0 And Not HasScriptOrStyle(AText) Then
Dim i, LLines, LLastLine, LTrimLine
LLines = Split(AText, NL, -1, 0)
LResult = ""
LLastLine = UBound(LLines)
For i = LBound(LLines) to UBound(LLines)
LTrimLine = Trim(LLines(i))
If Left(LTrimLine, 1) <> "#" Then
LResult = LResult & LLines(i) & IIf(i = LLastLine, "", NL) ' only add newline if not last line to avoid extra newline
' (SS,22/8/14) added following to convert special #SECTION to HTML comment which is used by inc-template-product-details to separate description into sections
ElseIf UCase(Left(LTrimLine, 9)) = "#SECTION " Then
LResult = LResult & NEW_LINE_FIX & ReplaceStr(LTrimLine, "#SECTION ", "<!--SECTION ") & "-->" & NEW_LINE_FIX
' (SS,30/7/15) added following to allow options, converts to HTML comment
ElseIf UCase(Left(LTrimLine, 9)) = "#OPTIONS " Then
LResult = LResult & NEW_LINE_FIX & ReplaceStr(LTrimLine, "#OPTIONS ", "<!--OPTIONS ") & "-->" & NEW_LINE_FIX
SetHashCommentLineOptions Trim(UCase(Mid(LTrimLine, 10)))
End If
Next
End If
RemoveHashCommentLines = LResult
End Function
' (SS,30/7/15) sets options held in description / token text i.e. the bit after #OPTIONS
Sub SetHashCommentLineOptions(AOptions)
FHashCommentLineOptions = "," & AOptions & ","
End Sub
' (SS,30/7/15) returns options held in description / token text i.e. the bit after #OPTIONS
Function GetHashCommentLineOptions
GetHashCommentLineOptions = FHashCommentLineOptions
End Function
' (SS,30/7/15) returns true if given option exists in comma separated list
Function IsHashCommentLineOption(AOption)
IsHashCommentLineOption = InStr("," + FHashCommentLineOptions + ",", "," + UCase(AOption) + ",")
End Function
' (SS,14/12/18) removes blank table rows, i.e. those contained special token REMOVE_BLANK_ROW
Function RemoveBlankTableRows(AText)
Dim LResult, LPos
LResult = AText ' default to return whole string
LPos = InStr(AText, REMOVE_BLANK_ROW)
If LPos > 0 Then
Dim LRecStart, LRecEnd
' find the <tr> before and </tr> after
LRecStart = InStrRev(AText, "<tr>", LPos)
LRecEnd = InStr(LPos, AText, "</tr>" & NL)
' if row delimiters found then remove
If LRecStart <> 0 and LRecEnd <> 0 Then
LResult = Mid(AText, 1, LRecStart - 1) + Mid(AText, LRecEnd + 7, Len(AText))
End If
End If
' (SS,14/12/18) call recursively until no change i.e. all occurrences removed
If AText <> LResult Then
LResult = RemoveBlankTableRows(LResult)
End If
RemoveBlankTableRows = LResult
End Function
' first line and last line are tokens, beginning with {table} ending with {endtable)
' (SS,3/6/11) LNewLineFix replaced with NEW_LINE_FIX constant
' (SS,6/6/11) added AStartToken, AEndToken parameters which will contain starttable and endtable
' (SS,8/6/11) fixed issue that was introduced by allowing first line to contain a table row, this caused extra line to be added
' (SS,2/2/12) added auto href feature to automatically try to add ahref links to comma separated list of products
' (SS,22/8/14) added ability to get a custom table using CustomGetTableHTML if type=custom exists in the setting
' (SS,18/6/15) changed to work with RWD (Bootstrap) which has it's own special table classes
Function GetTableHTML(AStr, AStartToken, AEndToken)
Dim LStr, LLines, LCols, Result, LLine, LPos, LCell, LClass, r, c, n, LLen, LStart, LQuoted, LChar
Dim LHeading, LTitle, LFirstLine, LType, LClassPrefix, LWidth, LAlign, LNoWrap, LSeparator, LOption
Dim LWidths, LAligns, LNoWraps, LClassSet, LColspanSet, LWidthSet, LAlignSet, LNoWrapSet, LAlignVal, LAlternateRow, LNilClass
LStr = Replace(AStr, "{" + AEndToken + "}", "", 1, 1, 1) ' (SS,6/6/11) {endtable} is replaced with blank to allow last line to be part of table
LLines = Split(LStr, NL, -1, 0)
LFirstLine = LLines(0)
LTitle = GetTableSetting(LFirstLine, "title")
' (SS,22/8/14) added ability to get a custom table using CustomGetTableHTML
LType = LCase(GetTableSetting(LFirstLine, "type"))
If LType = "custom" Then
If FunctionExists("CustomGetTableHTML") Then
GetTableHTML = CustomGetTableHTML(LTitle)
Else
GetTableHTML = ""
End If
Exit Function
End If
LClassPrefix = GetTableSetting(LFirstLine, "class")
LNilClass = LCase(LClassPrefix) = "nil"
If LClassPrefix <> "" Then LClassPrefix = "-" + LClassPrefix
LCols = GetTableSetting(LFirstLine, "cols")
If LCols = "" Or Not IsNumeric(LCols) Then
LCols = 2
Else
LCols = CLng(LCols)
If LCols < 1 Then LCols = 1 ' don't allow less than 1 column
If LCols > 100 Then LCols = 100 ' don't allow over 100 columns
End If
LWidth = GetTableSetting(LFirstLine, "width")
If LWidth <> "" Then LWidths = Split(LWidth, ",")
LAlign = GetTableSetting(LFirstLine, "align")
If LAlign <> "" Then LAligns = Split(LAlign, ",")
LNoWrap = GetTableSetting(LFirstLine, "nowrap")
If LNoWrap <> "" Then LNoWraps = Split(LNoWrap, ",")
' (SS,2/2/12) added following to do an auto href using product names for specified column, e.g. autohref=2 to do auto hrefs for column 2
Dim LAutoHRefCol
LAutoHRefCol = GetTableSetting(LFirstLine, "autohref")
If LAutoHRefCol = "" Then
LAutoHRefCol = 0
Else
LAutoHRefCol = CInt(LAutoHRefCol)
End If
LSeparator = GetTableSetting(LFirstLine, "separator")
If LSeparator = "" Then LSeparator = ","
LOption = GetTableSetting(LFirstLine, "option")
' check for alternate row option
If InStr(1, "," + LOption + ",", ",altrow,", 1) > 0 Then
LAlternateRow = True
Else
LAlternateRow = False
End If
' (SS,18/6/15) different main and title class for RWD (i.e. Bootstrap), also th used instead of td in RWD
Dim LMainClass, LTitleClass, LTitleCellTag, LHeadingColCellTag
If IsRWD Then
' table-description is a special class that can be defined for use just in description tables
' (SS,7/6/17) removed table-striped after table-description, added table-bordered because border from bottom and left and right are not added by default, affects all site, may need to make optional ***
LMainClass = " class=""table table-description table-bordered table-condensed table-responsive"""
LTitleClass = ""
LTitleCellTag = "th"
LHeadingColCellTag = "th"
Else ' (SS,18/6/15) same as before for backward compatibility
LMainClass = Iif(LNilClass, "", " class=""tbl" + LClassPrefix + "-main""")
LTitleClass = Iif(LNilClass, "", " class=""tbl" + LClassPrefix + "-title""")
LTitleCellTag = "td"
LHeadingColCellTag = "td"
End If
' (SS,18/6/15) in following line replaced Iif(LNilClass, "", " class=""tbl" + LClassPrefix + "-main""") with LMainClass
Result = NEW_LINE_FIX + "<table" & LMainClass + ">" + NEW_LINE_FIX ' (SS,2/5/06) added new tbl-main class
If LTitle <> "" Then
' (SS,18/6/15) replaced Iif(LNilClass, "", " class=""tbl" + LClassPrefix + "-title""") with LTitleClass
' (SS,18/6/15) replaced td with LTitleCell
Result = Result + "<tr><" + LTitleCellTag + LTitleClass + " colspan=""" & LCols & """>" + LTitle + "</" & LTitleCellTag + "></tr>" + NEW_LINE_FIX
End If
' (SS,6/6/11) last line is ignored it it's blank, to allow last row to end in {endtable}
Dim LIgnoreLastLine
If Trim(LLines(UBound(LLines))) = "" Then
LIgnoreLastLine = 1
Else
LIgnoreLastLine = 0
End If
' (SS,18/6/15)
Dim LIsFirstCell, LCellTag
' (SS,6/6/11) replaced - 1 with LIgnoreLastLine to allow last row to contain {endtable}
For r = LBound(LLines) + 1 to UBound(LLines) - LIgnoreLastLine
LLine = Trim(LLines(r))
' Response.Write "[#" & r & "#" & LLine & "]" & BR ' *** debug code
Result = Result + "<tr>" + NEW_LINE_FIX
If Left(LLine, 1) = "#" Then
LHeading = True
LLine = Mid(LLine, 2) ' remove the leading #
Else
LHeading = False
End If
For c = 1 to LCols
' (SS,18/6/15) to allow first cell to be a th instead of td (in RWD)
LIsFirstCell = c = 1
If LIsFirstCell Or LHeading Then
LCellTag = LHeadingColCellTag
Else
LCellTag = "td"
End If
LPos = InStr(LLine, LSeparator)
If LPos = 0 Then
LCell = LLine
LLine = ""
Else
' ignore leading spaces
LLen = Len(LLine)
For n = 1 To LLen
If Mid(LLine, n, 1) <> " " Then Exit For
Next
LStart = n
If Mid(LLine, LStart, 1) = """" Then
LStart = LStart + 1 ' ignore leading quote
LQuoted = True
Else
LQuoted = False
End If
If c = LCols Then ' if last column take all remaining as the cell content
LCell = Trim(Mid(LLine, LStart))
' if it started with quoted then remove last quote
If LQuoted And Right(LCell, 1) = """" Then
LCell = Left(LCell, Len(LCell) - 1)
End If
Else
LCell = Mid(LLine, 1, LPos - 1)
LCell = ""
' add to string until quote or end reached
For n = LStart To LLen
LChar = Mid(LLine, n, 1)
If LQuoted And LChar = """" Then Exit For
If Not LQuoted And LChar = LSeparator Then Exit For ' if not quoted and separator then exit
LCell = LCell + LChar
Next
LLine = Trim(Mid(LLine, n + 1)) ' move to next char after separator, or quote if quoted string
If LQuoted And Mid(LLine, 1, 1) = LSeparator Then LLine = Trim(Mid(LLine, 2)) ' if was quoted then also remove the following separator
End If
End If
' (SS,2/2/12) adds href links to each comma separated item, matching with product name
If c = LAutoHRefCol Then
LCell = AddAutoProductHRefs(LCell)
End If
' set class
LClass = "tbl" + LClassPrefix
If LHeading Then
LClass = LClass + "-colhd"
Else
LClass = LClass + "-col"
End If
LClass = LClass & c
If LAlternateRow And (r Mod 2) = 0 Then LClass = LClass + "b"
' set width
LWidthSet = ""
If LWidth <> "" Then
If c <= UBound(LWidths) + 1 Then
If LWidths(c - 1) <> "" Then
LWidthSet = " width=""" + LWidths(c - 1) & """"
End If
End If
End If
' set align
LAlignSet = ""
If LAlign <> "" Then
If c <= UBound(LAligns) + 1 Then
If LAligns(c - 1) <> "" Then
Select Case UCase(LAligns(c - 1))
Case "C"
LAlignVal = "center"
Case "R"
LAlignVal = "right"
Case Else
LAlignVal = "left"
End Select
End If
' (SS,18/6/15) added IsRWD for align in Bootstrap
If IsRWD Then
LAlignSet = "text-" + LAlignVal ' sets the class to use
Else
LAlignSet = " align=""" + LAlignVal + """"
End If
End If
End If
' set nowrap
LNoWrapSet = ""
If LNoWrap <> "" Then
If c <= UBound(LNoWraps) + 1 Then
If LCase(LNoWraps(c - 1)) = "y" Then
LNoWrapSet = " nowrap"
End if
End If
End If
' set colspan for remaining columns
If LLine = "" And c < LCols Then
LColspanSet = " colspan=""" & LCols - c + 1 & """"
Else
LColspanSet = ""
End If
' if class is set to nil then don't apply the class
' (SS,18/6/15) added settings RWD, i.e. not normally, but alignment class if set
If IsRWD Then
' for align settings we use the class set in LAlignSet
If LAlignSet <> "" Then
LClassSet = " class=""" + LAlignSet + """"
LAlignSet = "" ' clear this so that it's not applied again below
Else
LClassSet = ""
End If
ElseIf LNilClass Then
LClassSet = ""
Else
LClassSet = " class=""" + LClass + """"
End If
' (SS,18/6/15) replaced "td" with LCellTag
Result = Result + "<" + LCellTag + LClassSet + LColspanSet + LWidthSet + LAlignSet + LNoWrapSet + ">" + LCell + "</" + LCellTag + ">" + NEW_LINE_FIX
If LLine = "" Then Exit For ' if nothing remaining then exit the column loop
Next
Result = Result + "</tr>" + NEW_LINE_FIX
Next
Result = Result + "</table>" + NEW_LINE_FIX
GetTableHTML = Result
End Function
Function GetTableSetting(AStr, AType)
Dim LPos, Result, i, LLen, LStart, LQuoted, LChar
LPos = InStr(1, AStr, " " + AType + "=", 1)
If LPos > 0 Then
LLen = Len(AStr)
LStart = LPos + Len(AType) + 2
' ignore leading spaces
For i = LStart To LLen
If Mid(AStr, i, 1) <> " " Then Exit For
Next
LStart = i
If Mid(AStr, LStart, 1) = """" Then
LStart = LStart + 1 ' ignore leading quote
LQuoted = True
Else
LQuoted = False
End If
Result = ""
' add to string until quote or end reached
For i = LStart To LLen
LChar = Mid(AStr, i, 1)
If LChar = """" Or LChar = "}" Then Exit For
If LChar = " " And Not LQuoted Then Exit For ' if not quoted and space then exit
Result = Result + LChar
Next
Else
Result = ""
End If
' (SS,28/4/17) if a token with square brackets is contained within this e.g. title="[FT, ProductName]" then run the ReplaceTokens function to replace the tokens e.g. fetch field value
LPos = InStr(Result, "[")
If LPos > 0 Then
' replace square brackets with curly which are understood by ReplaceTokens
Result = ReplaceStr(Result, "[", "{")
Result = ReplaceStr(Result, "]", "}")
' replace single quote with double, allows quotes within the token
Result = ReplaceStr(Result, "'", """")
' handle the token
Result = ReplaceTokens(Result)
End If
GetTableSetting = Result
End Function
' (SS,2/2/12) for new autohref in description tables
Function AddAutoProductHRefs(AList)
Dim Result, LListArray, i, LName, LNameBeforeAsterisk, LPos, LCode
Result = ""
If AList <> "" Then
LListArray = Split(AList, ",")
For i = 0 To UBound(LListArray)
LName = Trim(LListArray(i))
LPos = InStr(LName, "*")
If LPos > 0 Then
LNameBeforeAsterisk = Left(LName, LPos - 1)
Else
LNameBeforeAsterisk = LName
End If
LCode = GetProductCodeForAutoHRef(LNameBeforeAsterisk)
If LCode <> "" Then
Result = Result + "<a href=""" + GetProductLink(LCode) + """>" + LName + "</a>"
Else
Result = Result + LName
End If
If i <> UBound(LListArray) Then Result = Result + ", " ' add comma and space if not at last
Next
End If
AddAutoProductHRefs = Result
End Function
' (SS,2/2/12) for new autohref in description tables
Function GetProductCodeForAutoHRef(AValue)
' (SS,31/10/18) added GetProductsFilter to filter out subproducts
GetProductCodeForAutoHRef = GetSQLValueAsString("SELECT ProductCode FROM products WHERE ProductName LIKE '" & CleanSQLStr(AValue) & "%' AND Not ProductDisabled" + GetProductsFilter)
End Function
' (SS,31/8/12) returns product code for given product name, blank string is returned if not found
Function GetProductCodeForName(AValue)
GetProductCodeForName = GetSQLValueAsString("SELECT ProductCode FROM products WHERE ProductName = '" & CleanSQLStr(AValue) & "' AND Not ProductDisabled")
End Function
' (SS,1/8/07) first line and last line are tokens, beginning with {bullet} ending with {endbullet)
' (SS,3/6/11) NL_FIX replaced with NEW_LINE_FIX global constant
' (SS,6/6/11) added AStartToken, AEndToken, also modified to allow {endbullet} to work on same line as bullet line
' (SS,9/3/16) modified to allow embedded bullet points via token
Function GetBulletHTML(AStr, AStartToken, AEndToken)
Dim LStr, LLines, Result, r
Dim LLine, LToken, LLines2, LLine2, t ' (SS,9/3/16)
' (SS,6/6/11) remove the {bullet} and {endbullet} to allow use on same line as bullet line
LStr = Replace(AStr, "{" + AStartToken + "}", "", 1, 1, 1)
LStr = Replace(LStr, "{" + AEndToken + "}", "", 1, 1, 1)
LLines = Split(LStr, NL, -1, 0)
Result = NEW_LINE_FIX + "<ul>" + NEW_LINE_FIX
' (SS,6/6/11) changed from [LBound(LLines) + 1 to UBound(LLines) - 1] to process all lines,
' allowing {startbullet} {endbullet} on same line as the bullet point
For r = LBound(LLines) To UBound(LLines)
LLine = Trim(LLines(r))
If LLine <> "" Then ' (SS,6/6/11) only if not blank
' (SS,9/3/16) check if bullet point is a token and detokenise it (only one level, not recursive)
LToken = GetToken(LLine)
If LToken <> "" Then
LLines2 = Split(GetSiteDetail("Tokens", LToken), NL, -1, 0)
For t = LBound(LLines2) To UBound(LLines2)
LLine2 = Trim(LLines2(t))
If LLine2 <> "" Then
Result = Result + "<li>" + LLine2 + "</li>" + NEW_LINE_FIX
End If
Next
LLine = "" ' to ignore adding of the token line below because it's been detokenised
End If
If LLine <> "" Then ' (SS,9/3/16) added If to ignore if line was detokenised above
Result = Result + "<li>" + LLine + "</li>" + NEW_LINE_FIX
End If
End If
Next
Result = Result + "</ul>" + NEW_LINE_FIX
GetBulletHTML = Result
End Function
' (SS,28/4/17) for fetching record
Function GetRecordHTML(AStr, AStartToken, AEndToken)
Dim LLines, LFirstLine, LKey, LTableName, LSetting
LKey = GetProductCode ' default to current product code as the key
LLines = Split(AStr, NL, -1, 0)
LFirstLine = LLines(0)
' existing code from GetTableSetting used to get the settings, which is in held in first line in same format e.g. table="specs_servo"
LTableName = GetTableSetting(LFirstLine, "table")
LSetting = GetTableSetting(LFirstLine, "key")
If LSetting <> "" Then LKey = LSetting ' override default product for current product
If LTableName <> "" And LKey <> "" Then
FetchRecordToSession LTableName, LKey
End If
GetRecordHTML = ""
End Function
' (SS,13/12/18) additional features e.g. rename the ProductName via something like:
' {action cmd="rename" field="ProductName" value="[FT, ProductName] Servo - [FN, TorqueV3,1]Kg.cm [FN, SpeedV3,2]s [FN, Weight,1]g [FN, Thickness, 0]mm"}{endaction}
' (SS,1/5/19) added logging the product rename to log_product_renames table for debugging
Function GetActionHTML(AStr, AStartToken, AEndToken)
Dim LProductCode, LCmd, LField, LValue
LProductCode = GetProductCode ' use the current page product code
' get values from the definition
Dim LLines, LFirstLine
LLines = Split(AStr, NL, -1, 0)
LFirstLine = LLines(0)
LCmd = GetTableSetting(LFirstLine, "cmd")
' make sure command is rename, do the rename if field is ProductName and value is defined
If LCmd = "rename" Then
LField = GetTableSetting(LFirstLine, "field")
LValue = Trim(GetTableSetting(LFirstLine, "value"))
If LField = "ProductName" And LValue <> "" Then
Dim LProductName
LProductName = GetFieldForProduct(LProductCode, "ProductName")
' only update if product name is different
If LProductName <> LValue Then
' (14/12/18) added Left function to limit to 100 characters to prevent Data too long for column MySQL error
ExecuteQuery("UPDATE products SET ProductName = '" & Left(CleanSQLStr(LValue), 100) & "' WHERE ProductCode = '" & CleanSQLStr(LProductCode) & "'")
' (SS,1/5/19) log to new log_product_renames for debugging purposes
ExecuteQuery("INSERT INTO log_product_renames SET IPAddress = '" & CleanSQLStr(Request.ServerVariables("REMOTE_ADDR")) & "', ProductCode = '" & CleanSQLStr(LProductCode) & "', Message = '" & CleanSQLStr(LProductName + " => " + LValue) & "'")
End If
End If
End If
GetActionHTML = "" ' nothing is returned
End Function
' (SS,22/8/14)
' replaces tab sections with HTML comment for start of tab and HTML comment for end of tab
Function GetTabHTML(AStr, AStartToken, AEndToken)
Dim Result
Result = "<!-- THIS IS THE TAB -->"
GetTabHTML = Result
End Function
' returns start of specified token within given string
Function GetTokenStart(AStr, AToken)
Dim LPos1, LPos2
LPos1 = InStr(1, AStr, "{" + AToken + "}", 1)
LPos2 = InStr(1, AStr, "{" + AToken + " ", 1)
If (LPos1 = 0) Or ((LPos2 > 0) and (LPos2 < LPos1)) Then LPos1 = LPos2 ' get the earliest position, otherwise the one with space could match too late
GetTokenStart = LPos1
End Function
' returns end of specified token within given string
Function GetTokenEnd(AStr, AToken)
Dim LPos
LPos = InStr(1, AStr, "{" + AToken + "}", 1)
If LPos > 0 Then LPos = LPos + Len(AToken) + 1
GetTokenEnd = LPos
End Function
' (SS,1/8/07) new function which does what RemoveTable did, but works with new types like "bullet"
Function RemoveComposes(AValue)
Dim LValue
LValue = AValue
LValue = RemoveCompose(LValue, "table", "endtable")
LValue = RemoveCompose(LValue, "bullet", "endbullet")
RemoveComposes = LValue
End Function
' (SS,21/5/07) removes the special table from given string i.e. everything between {table..} and {endtable} including the tokens
' (SS,1/8/07) renamed RemoveTable to RemoveCompose
' added new parameters AStartToken and AEndToken
Function RemoveCompose(AValue, AStartToken, AEndToken)
Dim LStart, LEnd
LStart = GetTokenStart(AValue, AStartToken)
LEnd = GetTokenEnd(AValue, AEndToken)
If (LStart > 0) And (LEnd > 0) Then
RemoveCompose = Mid(AValue, 1, LStart - 1) + Mid(AValue, LEnd + 1, Len(AValue))
Else
RemoveCompose = AValue
End If
End Function
' (SS,23/5/12) returns true if given text has a script to style sheet tag
Function HasScriptOrStyle(AText)
HasScriptOrStyle = InStr(1, AText, "</script>", 1) > 0 Or InStr(1, AText, "</style>", 1) > 0
End Function
' (SS,14/4/11) replace all tokens i.e. in curly brackets { } with text from site details
' (SS,23/5/11) modified for recursion via ComposeDescription and also to pass on parameters e.g. {spinner,30,10} where spinner is token name and %1 will be replaced with 30, %2 with 10
' (SS,30/7/15) modified to replace two semicolons with single semicolon, i.e. allow semicolon be shown in text by entering it as two
Function ReplaceTokens(ADesc)
' (SS,19/4/12) if description contains script or stylesheet tags which also use the {} brackets then don't replace tokens because it'll go wrong
' (SS,4/8/15) following commented out to fix bug, fixed bug by adding "If Not HasScriptOrStyle.." to GetToken
'If HasScriptOrStyle(ADesc) Then
' ReplaceTokens = ADesc
' Exit Function
'End If
' (SS,23/5/11) used to prevent too many recursions and stack overflow
Const MAX_RECURSIONS = 100
If FReplaceTokensRecursionCount > MAX_RECURSIONS Then
ReplaceTokens = "<strong>Recursion limited to " & MAX_RECURSIONS & " to prevent stack overflow. This could be due to token content referring to itself.</strong>"
Exit Function
End If
FReplaceTokensRecursionCount = FReplaceTokensRecursionCount + 1
Dim LDesc, LToken, LCount, LTokenName ' (SS,27/4/17) added token name
LDesc = ADesc
Do
LToken = GetToken(LDesc)
If LToken <> "" Then
Dim LTokenDesc
' (SS,23/5/11) check for comma separated token parameters, and replace %1, %2 etc with values from token name
If InStr(LToken, ",") > 0 Then
' Response.Write "##" & LToken & "##" & BR
Dim LParameters, i, LParameter
LParameters = Split(LToken, ",")
' (SS,27/4/17) check for special tokens and handle them
LTokenName = UCase(Trim(LParameters(0)))
If IsSpecialToken(LTokenName) Then
LTokenDesc = HandleSpecialToken(LTokenName, LParameters)
Else ' (SS,27/4/17) as before
LTokenDesc = GetSiteDetail("Tokens", LParameters(0)) ' lookup token ignoring the comma separated parameters
' for each parameter, replace the string %1, %2, %3 etc with the parameter value
For i = 1 to UBound(LParameters)
LParameter = Trim(LParameters(i))
LParameter = ReplaceStr(LParameter, ";", ",") ' semicolons are replaced with comma to allow passing of commas in text
LParameter = ReplaceStr(LParameter, ",,", ";") ' (SS,30/7/15) two semicolons are replaced with single semicolon, two commas used because above line converts two semicolon to comma
LTokenDesc = ReplaceStr(LTokenDesc, "%" & i, LParameter)
Next
End If
Else
LTokenDesc = GetSiteDetail("Tokens", LToken)
End If
' (SS,23/5/11) this may result in recursion because ComposeDescription could call ReplaceTokens
' (SS,3/6/11) added True parameter to ComposeDescription, to ensure special newlines are not converted to <br>s until the end
LDesc = ReplaceStr(LDesc, "{" + LToken + "}", ComposeDescription(LTokenDesc, True, True))
End If
Loop While LToken <> ""
FReplaceTokensRecursionCount = FReplaceTokensRecursionCount - 1 ' (SS,23/5/11) part of recursion prevention
ReplaceTokens = LDesc
End Function
' (SS,27/4/17) quickly check whether given token is a special number
Function IsSpecialToken(ATokenName)
' (SS,14/12/18) added APH and RBR
Const SPECIAL_TOKENS = ",FR,FT,FN,FX,FV,FMM,FCM,EV,APH,RBR,"
IsSpecialToken = InStr(SPECIAL_TOKENS, "," + ATokenName + ",") > 0
End Function
' (SS,27/4/17) handle special tokens, FR, FT, FN, FX, FV, Fmm, Fcm, EV
' To fetch record need to have following before using tokens below {record table="xx" productcode="xxx"} {endrecord}
' FT - Fetch given field name as text e.g. {FT, fieldname}
' FN - Fetch given field name as number and round to x decimal places e.g. {FN, maxwidth, 1}
' FX - Fetch given field name as number, multiply by constant and round to x decimal places e.g. {FX, maxwidth, 0.03937, 2}
' FV - Fetch given field name as number, divide into constant and round to x decimal places, if either parameter blank then record blank (do not cause overflow error) e.g. {FV, maxwidth, 25.4, 2}
' Fmm - Fetch given field name as number, convert mm to inches by dividing by 25.4 e.g. if fieldx contains 50.8 then {Fmm, fieldx, 1} returns 2.0
' Fcm - Fetch given field name as number, convert cm to inches by dividing by 2.4 e.g. if fieldx contains 5.08 then {Fmm, fieldx, 1} returns 2.0
' APH - Fetch given field as text and do an add auto product href via AddAutoProductHRefs
' RBR - Remove blank row, if given field is blank then table row isn't shown
' (SS,7/12/18) modified to trap error and return "?ERROR?"
Function HandleSpecialToken(ATokenName, AParameters)
Dim LResult, LParaCount
LResult = "" ' nothing to return by default
LParaCount = UBound(AParameters)
If ATokenName = "EV" Then ' special token to call Eval function and return its value
' combine all parameters into one string to pass to Eval function
Dim LAllParas, i
LAllParas = ""
For i = 1 To LParaCount
LAllParas = LAllParas + IIf(LAllParas = "", "", ", ") + AParameters(i)
Next
' (7/12/18) added following to turn off normal error handling
On Error Resume Next
LResult = CStr(Eval(LAllParas)) ' CStr or NB is required to prevent error
' (7/12/18) added following to trap error and show "?ERROR?" instead of failing
If Err.Number <> 0 Then
LResult = "?ERROR?"
End If
On Error Goto 0 ' reset error handling back to normal
ElseIf ATokenName = "FT" Then
If LParaCount = 1 Then
LResult = NB(FetchFieldFromSession(Trim(AParameters(1))))
End If
ElseIf ATokenName = "FN" Then
If LParaCount = 2 Then
LResult = HandleSpecialTokenNumber(AParameters(1), 1, 1, AParameters(2))
End If
ElseIf ATokenName = "FX" Then
If LParaCount = 3 Then
LResult = HandleSpecialTokenNumber(AParameters(1), AParameters(2), 1, AParameters(3))
End If
ElseIf ATokenName = "FV" Then
If LParaCount = 3 Then
LResult = HandleSpecialTokenNumber(AParameters(1), 1, AParameters(2), AParameters(3))
End If
ElseIf ATokenName = "FMM" Then
If LParaCount = 2 Then
LResult = HandleSpecialTokenNumber(AParameters(1), 1, 25.4, AParameters(2))
End If
ElseIf ATokenName = "FCM" Then
If LParaCount = 2 Then
LResult = HandleSpecialTokenNumber(AParameters(1), 1, 2.54, AParameters(2))
End If
' (SS,14/12/18) added following to an AddAutoProductHRefs for given field
ElseIf ATokenName = "APH" Then ' Remove Blank Row
If LParaCount = 1 Then
LResult = AddAutoProductHRefs(NB(FetchFieldFromSession(Trim(AParameters(1)))))
End If
' (SS,14/12/18) added following to remove blank row from table, if given field is blank, later removed by call to RemoveBlankTableRows
ElseIf ATokenName = "RBR" Then ' Remove Blank Row
If LParaCount = 1 Then
LResult = IIf(NB(FetchFieldFromSession(Trim(AParameters(1)))) = "", REMOVE_BLANK_ROW, "")
End If
End If
HandleSpecialToken = LResult
End Function
' (SS,27/4/17)
Function HandleSpecialTokenNumber(AFieldName, AMultiplyBy, ADivideBy, ADecimalPlaces)
Dim LValue, LDP, LResult
LValue = NB(FetchFieldFromSession(Trim(AFieldName)))
If LValue = "" Then ' return blank string if null
LResult = ""
Else
LDP = CInt(Trim(ADecimalPlaces))
LResult = FormatNumber(LValue * AMultiplyBy / ADivideBy, LDP)
End If
HandleSpecialTokenNumber = LResult
End Function
' (SS,27/4/17) shorthand function for FetchFieldFromSession called from Eval in HandleSpecialToken
Function FF(AFieldName)
FF = FetchFieldFromSession(AFieldName)
End Function
' (SS,14/4/11) returns first token found i.e. starting with { and ending in }, blank is returned if none found, token excluding curly brackets is returned otherwise
' (SS,4/8/15) if string contains ScriptOrStyle tag then a blank is returned, i.e. no token, to fix bug where script tag was being emptied
Function GetToken(AStr)
Dim LPosStart, LPosEnd, LResult
LResult = ""
' (SS,3/8/15) added check for HasScriptOrStyle, so that script is treated as normal (blank returned to cancel detokenising), because it can contain curly brackets, subsequent styles
If Not HasScriptOrStyle(AStr) Then
LPosStart = InStr(1, AStr, "{", 1)
If LPosStart > 0 Then
LPosEnd = InStr(LPosStart, AStr, "}", 1)
If LPosEnd > 0 Then
LResult = Mid(AStr, LPosStart + 1, LPosEnd - LPosStart - 1)
End If
End If
End If
GetToken = LResult
End Function
' (SS,22/8/14) returns given section no and the section name, first section has no name
' except for first section, if there are no more sections, ASectionName is returned with ""
Function GetDescriptionSection(ADesc, ASectionNo, ByRef ASectionName)
Dim LResult, LSectionNo, LSectionName, i, LLines, LTrimLine
If ASectionNo > 1 Then ASectionName = "" ' clear section name if > 1 because "" is returned for section name if section not found
LSectionNo = 1
LLines = Split(ADesc, NL, -1, 0)
LResult = ""
For i = LBound(LLines) to UBound(LLines)
LTrimLine = Trim(LLines(i))
' if start of new section then increment the section no and set section name
If UCase(Left(LTrimLine, 12)) = "<!--SECTION " Then
LSectionNo = LSectionNo + 1
LSectionName = Replace(LTrimLine, "<!--SECTION ", "", 1, 1, 1) ' case insensitive replace
LSectionName = Trim(ReplaceStr(LSectionName, "-->", ""))
If LSectionNo = ASectionNo Then ASectionName = LSectionName ' set section name if section no matches
ElseIf ASectionNo = LSectionNo Then ' if in same section then keep this line
LResult = LResult & IIf(LResult = "", "", NL) & LLines(i) ' only add newline if not first line
ElseIf LSectionNo > ASectionNo Then ' if beyond requested section then no need to continue
Exit For
End If
Next
GetDescriptionSection = TrimAll(TrimBRs(TrimAll(LResult)))
End Function
' (SS,22/8/14) show each section from production description, i.e. split into tabs or sections of accordian
Sub ShowProductDescriptionSections(AProductDesc)
Dim LSectionName, LSectionDesc, i
LSectionName = "Description"
i = 0
Do While LSectionName <> ""
i = i + 1
LSectionDesc = GetDescriptionSection(AProductDesc, i, LSectionName)
If LSectionName <> "" Then ShowProductDetailDescriptionSection LSectionDesc, LSectionName, i = 1 ' 3rd parameter is True for first i.e. active section
Loop
End Sub
' (SS,16/3/15) returns the first section only, used by Google Shopping
Function GetProductDescriptionFirstSection(AProductDesc)
Dim LSectionName ' used as a dummy ByRef parameter to GetDescriptionSection
GetProductDescriptionFirstSection = GetDescriptionSection(AProductDesc, 1, LSectionName)
End Function
' --- END OF TABLE COMPOSING ROUTINES ---
' (SS,27/5/04) called from two places, ShowSingleProduct and GetFirstCatSubcatForProduct
Function GetCategoryListSQL(AProductCode)
' (SS,18/5/12) replaced "categories.Priority, subcategories.Priority" in ORDER BY with "productcategories.Main DESC", i.e. the new Main flag field
GetCategoryListSQL = "SELECT * FROM productcategories" &_
" INNER JOIN categories ON categories.Category = productcategories.category" &_
" INNER JOIN subcategories ON subcategories.Category = productcategories.category AND subcategories.Subcategory = productcategories.Subcategory" &_
" WHERE ProductCode = '" + CleanSQLStr(AProductCode) + "'" &_
" ORDER BY productcategories.Main DESC, categories.SortOrder, subcategories.SortOrder"
End Function
' (SS,11/2/05) added ByRef AGroup
' (SS,1/8/11) changed from function to sub
Sub GetFirstCatSubcatForProduct(AProductCode, ByRef AGroup, ByRef ACategory, ByRef ASubcategory)
' database must be open at this point with query routines free to use '
' OpenQuery2("SELECT * FROM productcategories WHERE ProductCode = """ & AProductCode & """ ORDER BY Category, Subcategory")
' (SS,27/5/04) replaced above with this new query which uses new priority field to sort by
OpenQuery2(GetCategoryListSQL(AProductCode))
If Not EndOfQuery2 Then
AGroup = GetQueryValue2("GroupName") ' (SS,11/2/05)
ACategory = GetQueryValue2("Category")
ASubcategory = GetQueryValue2("Subcategory")
Else
ACategory = ""
ASubcategory = ""
End If
CloseQuery2
End Sub
' (SS,1/8/11) returns the category/subcategory the product has for the default group, to allow correct selection when only the default group is shown on the left menu (i.e. Brands for Clifton)
' blank is returned in ACategory, ASubcategory if not found
Sub GetDefaultGroupCatSubcatForProduct(AProductCode, ByRef ACategory, ByRef ASubcategory)
ACategory = ""
ASubcategory = ""
OpenQuery2(GetCategoryListSQL(AProductCode))
If Not EndOfQuery2 Then
Dim LGroup
LGroup = GetDefaultGroup
Do While Not EndOfQuery2
If GetQueryValue2("GroupName") = LGroup Then
ACategory = GetQueryValue2("Category")
ASubcategory = GetQueryValue2("Subcategory")
Exit Do
End If
NextQueryRecord2
Loop
End If
CloseQuery2
End Sub
' (SS,1/8/11) returns the correct, group, cat, subcat for given product, i.e. path taken to get there via menu
' if no valid path found then it returns the first group/cat/subcat for the product
Sub GetBreadcrumbGroupCatSubcatForProduct(AProductCode, ByRef AGroup, ByRef ACategory, ByRef ASubcategory)
' get the category / subcategory saved by the last menu selection
ACategory = GetBreadcrumbCategory
ASubcategory = GetBreadcrumbSubcategory
If ACategory <> "" And ASubcategory <> "" Then
If IsValidCatSubcatForProduct(AProductCode, ACategory, ASubcategory) Then
AGroup = GetGroupForCategory(ACategory)
Exit Sub
End If
End If
GetFirstCatSubcatForProduct AProductCode, AGroup, ACategory, ASubcategory
End Sub
' (SS,1/8/11) returns true if given category/subcategory is valid for given product
Function IsValidCatSubcatForProduct(AProductCode, ACategory, ASubcategory)
IsValidCatSubcatForProduct = "" <> GetSQLValueAsString("SELECT ProductCode FROM productcategories WHERE ProductCode = '" & CleanSQLStr(AProductCode) & "' AND Category = '" & CleanSQLStr(ACategory) & "' AND Subcategory = '" & CleanSQLStr(ASubcategory) & "'")
End Function
' (SS,1/8/11) following four routines added for more accurate breadcrumbs
Sub SetBreadcrumbCategory
Dim LCategory
LCategory = GetCategory
If LCategory <> "" Then
Session("BreadcrumbCategory") = LCategory
End If
End Sub
Sub SetBreadcrumbSubcategory
Dim LSubcategory
LSubcategory = GetSubcategory
If LSubcategory <> "" Then
Session("BreadcrumbSubcategory") = LSubcategory
End If
End Sub
Function GetBreadcrumbCategory
GetBreadcrumbCategory = Session("BreadcrumbCategory")
End Function
Function GetBreadcrumbSubcategory
GetBreadcrumbSubcategory = Session("BreadcrumbSubcategory")
End Function
' (SS,27/3/07)
Function GetGroupForCategory(ACategory)
' (SS,11/1/13) improved by making use of GetFieldForCategory
GetGroupForCategory = GetFieldForCategory(ACategory, "GroupName")
End Function
' (SS,26/7/11) returns MenuShortName for given category, ACategory is returned if not found
' (SS,2/8/11) added check for blank ACategory to prevent query
' (SS,11/1/13) replaced GetSQLValueAsString with GetFieldForCategory
Function GetShortNameForCategory(ACategory)
Dim LResult
If ACategory = "" Then
LResult = ""
Else
LResult = GetFieldForCategory(ACategory, "MenuShortName")
If LResult = "" Then LResult = ACategory
End If
GetShortNameForCategory = LResult
End Function
' (SS,26/7/11) returns MenuShortName for given subcategory, ASubcategory is returned if not found
' (SS,2/8/11) added check for blank ASubcategory to prevent query
' (SS,11/1/13) added missing ACategory parameter, it only had ASubcategory, replaced GetSQLValueAsString with GetFieldForSubcategory
Function GetShortNameForSubcategory(ACategory, ASubcategory)
Dim LResult
If ASubcategory = "" Then
LResult = ""
Else
LResult = GetFieldForSubcategory(ACategory, ASubcategory, "MenuShortName")
If LResult = "" Then LResult = ASubcategory
End If
GetShortNameForSubcategory = LResult
End Function
' (SS,19/9/06) returns comma separated list of subcategories for given category
Function GetSubcategoryListForCategory(ACategory)
OpenQuery2("SELECT * FROM subcategories WHERE Category = '" & CleanSQLStr(ACategory) & "' ORDER BY SortOrder, Subcategory")
Dim LCategoryList
LCategoryList = ""
Do While Not EndOfQuery2
LCategoryList = LCategoryList & Iif(LCategoryList = "", "", ", ") & GetQueryValue2("Subcategory")
NextQueryRecord2
Loop
CloseQuery2
GetSubcategoryListForCategory = LCategoryList
End Function
' (SS,10/1/13) returns given field for given category
Function GetFieldForCategory(ACategory, AFieldName)
GetFieldForCategory = GetSQLValueAsString("SELECT " & AFieldName & " FROM categories WHERE Category = '" & CleanSQLStr(ACategory) & "'")
End Function
' (SS,19/9/06) returns description for given subcategory
' (SS,10/1/13) rewrote using GetSQLValueAsString for compactness, from 8 lines to 1 line
' also renamed from GetSubcategoryDescription to GetFieldForSubcategory so it can return Description, Footer, Heading or any other field from subcategories table, had to added AFieldName parameter
Function GetFieldForSubcategory(ACategory, ASubcategory, AFieldName)
GetFieldForSubcategory = GetSQLValueAsString("SELECT " & AFieldName & " FROM subcategories WHERE Category = '" & CleanSQLStr(ACategory) & "' AND Subcategory = '" & CleanSQLStr(ASubcategory) & "'")
End Function
' (SS,16/9/04) returns given field value for given product code and field name '
Function GetFieldForProduct(AProductCode, AFieldName)
' database must be open at this point with query routines free to use '
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQuery2("SELECT * FROM products WHERE ProductCode = '" & CleanSQLStr(AProductCode) & "'")
If Not EndOfQuery2 Then
GetFieldForProduct = GetQueryValue2(AFieldName)
Else
GetFieldForProduct = ""
End If
CloseQuery2
End Function
' (SS,25/6/10) added stock availability check to also allow ordering if not in stock but available to order
' (SS,30/6/11) added ProductID to shoppingbaskets, currently looked up from products
' (SS,15/4/16) following can be deleted, now replaced by new version (see Sub AddToBasket below)
' *** to be deleted
Sub AddToBasketOld_(AProductCode, AAddQty)
' (SS,1/7/11) new options feature was added, call the new option handling routine, which might redirect to the product detail page, or do its own add to basket process
If ProductOptionsEnabled Then
' (SS,14/4/16) renamed ProcessAddToBasketForOptions to AddProductToBasketIncludingOptions, and added True for AAllowAlert, added 0 for AItemID (not specified)
AddProductToBasketIncludingOptions AProductCode, AAddQty, 0, True, True, "", "" ' True for allow redirection, True to allow alert, "" for no array, i.e. take options from form
Exit Sub
End If
Dim LFound
' (SS,15/6/04) added stock checking so user cannot add to basket if not in stock '
Dim LNumInStock, LProductID
LNumInStock = 0
' OpenQuery("SELECT NumInStock FROM products WHERE ProductCode = '" + AProductCode + "'")
' (SS,25/6/10) replaced above with following to adds fields to to stock available check
' (SS,30/6/11) added ProductID, because shoppingbaskets table now requires it for new shopping_basket_options
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQuery("SELECT ProductID, NumInStock, " & GetStockAvailabilityFieldList & " FROM products " &_
"LEFT JOIN suppliers ON suppliers.SupplierID = products.SupplierID " &_
"WHERE ProductCode = '" + CleanSQLStr(AProductCode) + "'")
LFound = Not EndOfQuery
If LFound Then
LProductID = GetQueryValue("ProductID") ' (SS,30/6/11)
LNumInStock = GetQueryValue("NumInStock")
' (SS,25/6/10) added following because it decides whether product not stock can be ordered
SetStockAvailabilityFromFields
End If
CloseQuery
' If Not LFound Or Not InStock(LNumInStock) Then Exit Sub
' (SS,25/6/10) replaced above with following to not exit if IsStockAvailable is True
If Not LFound Then
Exit Sub
Else
If Not InStock(LNumInStock) And Not IsStockAvailable Then Exit Sub
End If
Dim LAddQty
LAddQty = AAddQty
' make sure it's a valid number, convert to integer, assume 1 if invalid
If IsNumeric(LAddQty) Then
LAddQty = CLng(LAddQty)
Else
LAddQty = 1
End if
If LAddQty > MAX_PRODUCT_QTY Then LAddQty = MAX_PRODUCT_QTY ' reduce to maximum '
' check if item already exists in basket '
Dim LQtyInBasket
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQuery("SELECT * FROM shoppingbaskets WHERE SessionID = '" + CleanSQLStr(FSessionID) + "' AND ProductCode = '" + CleanSQLStr(AProductCode) + "'")
LFound = Not EndOfQuery
' (SS,17/11/04) added following for new "not allow more than what is in stock" feature '
If LFound Then
LQtyInBasket = GetQueryValue("Qty")
Else
LQtyInBasket = 0
End If
CloseQuery
' (SS,17/11/04) added following to reduce the qty to the amount in stock '
Dim LNewQty
LNewQty = LQtyInBasket + LAddQty
' (SS,25/6/10) added Not IsStockAvailable
' (SS,19/7/10) removed Not AllowProductOrderIfNoStock, IsStockAvailable does this check using GetAllowOutOfStockOrdering
If Not IsStockAvailable Then
If LNewQty > LNumInStock Then
LNewQty = LNumInStock
SetAlertMessage trx("Sorry we only have <x> of this item in stock", CStr(LNumInStock)) ' (SS,19/3/10), (SS,25/6/10) added CStr()
End If
End If
If LNewQty > MAX_PRODUCT_QTY Then LNewQty = MAX_PRODUCT_QTY ' reduce to maximum '
' if not found then add it, else increase the quantity '
If Not LFound Then
' make sure product code is valid '
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQuery("SELECT * FROM products WHERE ProductCode = '" + CleanSQLStr(AProductCode) + "'")
LFound = Not EndOfQuery
CloseQuery
' if valid product code then add it to basket '
' (SS,30/6/11) added ProductID, now needs to be saved in shoppingbaskets
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
If LFound Then ExecuteQuery "INSERT INTO shoppingbaskets (SessionID, ProductID, ProductCode, Qty) VALUES ('" & CleanSQLStr(FSessionID) & "', '" & CleanSQLStr(LProductID) & "', '" & CleanSQLStr(AProductCode) & "', " & LNewQty & ");"
Else
' ExecuteQuery "UPDATE shoppingbaskets SET Qty = Qty + " & LQty & " WHERE SessionID = '" & FSessionID & "' AND ProductCode = '" & AProductCode & "'"
' (SS,17/11/04) modified above to following '
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
ExecuteQuery "UPDATE shoppingbaskets SET Qty = " & LNewQty & " WHERE SessionID = '" & CleanSQLStr(FSessionID) & "' AND ProductCode = '" & CleanSQLStr(AProductCode) & "'"
End If
' delete all the products with zero or negative quantities '
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
ExecuteQuery "DELETE FROM shoppingbaskets WHERE SessionID = '" & CleanSQLStr(FSessionID) & "' AND Qty <= 0"
' (SS,18/2/14) if item was added then either redirect to basket page at the end of script or show pop up, or neither
' (SS,20/2/14) now calls SetAddToBasketAlert instead of SetPageRedirection
If LNewQty = LQtyInBasket + LAddQty Then
SetAddToBasketAlert LAddQty, AProductCode
End If
End Sub
' (SS,25/6/10) added stock availability check to also allow ordering if not in stock but available to order
' (SS,30/6/11) added ProductID to shoppingbaskets, currently looked up from products
' (SS,15/4/16) new version which always calls AddProductToBasketIncludingOptions even if ProductOptionsEnabled is False, it handles both
Sub AddToBasket(AProductCode, AAddQty)
' (SS,1/7/11) new options feature was added, call the new option handling routine, which might redirect to the product detail page, or do its own add to basket process
' (SS,15/4/16) removed If ProductOptionsEnabled Then so that it's always called, also removed "Exit Sub" which is not longer necessary
' redundant code from below was deleted
' (SS,14/4/16) renamed ProcessAddToBasketForOptions to AddProductToBasketIncludingOptions, and added True for AAllowAlert, added 0 for AItemID (not specified)
AddProductToBasketIncludingOptions AProductCode, AAddQty, 0, True, True, "", "" ' True for allow redirection, True to allow alert, "" for no array, i.e. take options from form
End Sub
' (SS,5/7/11) replaced AProductCode with AItemID
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
' (SS,25/10/18) added RemoveSubproductsFromBasket
Sub RemoveFromBasket(AItemID)
' ExecuteQuery("DELETE FROM shoppingbaskets WHERE SessionID = '" & FSessionID & "' AND ProductCode = '" & AProductCode & "'")
' (SS,5/7/11) replaced above with following, now using ItemID instead of Product, kept SessionID for safety, to prevent rogue app on Internet from deleting other people's items
ExecuteQuery("DELETE t1, t2 FROM shoppingbaskets t1, shopping_basket_options t2 WHERE t1.ItemID = t2.ItemID AND t1.SessionID = '" & CleanSQLStr(FSessionID) & "' AND t1.ItemID = '" & CleanSQLStr(AItemID) & "'")
' (SS,5/7/11) following necessary because above only deletes record from shoppingbaskets if at least one option record exists
ExecuteQuery("DELETE FROM shoppingbaskets WHERE SessionID = '" & CleanSQLStr(FSessionID) & "' AND ItemID = '" & CleanSQLStr(AItemID) & "'")
' (SS,25/10/18)
RemoveSubproductsFromBasket(AItemID)
End Sub
' (SS,25/10/18) called from RemoveFromBasket, also from Function CustomAddSubproductsToBasket
Sub RemoveSubproductsFromBasket(AItemID)
If ProductSubproductsEnabled Then
' included FSessionID for safety (as used in RemoveFromBasket above)
ExecuteQuery "DELETE FROM shoppingbaskets WHERE SessionID = '" & CleanSQLStr(FSessionID) & "' AND SubproductItemID = '" & CleanSQLStr(AItemID) & "'"
End If
End Sub
' (SS,5/7/11) replaced AProductCode with AItemID
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
' (SS,25/10/18) Sub DeductFromBasket is now redundant, was used by old more detailed shopping status
Sub DeductFromBasket_to_be_removed(AItemID)
' ExecuteQuery("UPDATE shoppingbaskets SET Qty = Qty - 1 WHERE SessionID = '" & FSessionID & "' AND ProductCode = '" & AProductCode & "'")
' ExecuteQuery("DELETE FROM shoppingbaskets WHERE SessionID = '" & FSessionID & "' AND ProductCode = '" & AProductCode & "' AND Qty <= 0")
' (SS,5/7/11) replaced above with following
ExecuteQuery("UPDATE shoppingbaskets SET Qty = Qty - 1 WHERE SessionID = '" & CleanSQLStr(FSessionID) & "' AND ItemID = '" & CleanSQLStr(AItemID) & "'")
If GetSQLValueAsString("SELECT Qty FROM shoppingbaskets WHERE ItemID = '" & CleanSQLStr(AItemID) & "'") = "0" Then
RemoveFromBasket AItemID
End If
End Sub
' (SS,5/7/11) *** need to handle stock for options
' (SS,5/7/11) replaced Product Code with Item ID, form values QTY_ are now suffixed by ItemID
' (SS,15/1/15) changes to check voucher code
' (SS,14/4/16) new version which works correctly with options, calls AddProductToBasketIncludingOptions for each item
Sub RecalculateBasket
' (SS,16/2/12) added following to allow customer to change country in basket page to show delivery for their country
SetCountryFromBasketForm
' (SS,15/1/15)
' (SS,16/2/15) added parameter to SetVoucherCode
SetVoucherCode UCase(Trim(CleanRequestForm("VoucherCode")))
Dim LErrorMessage
If Not IsVoucherCodeValid(GetVoucherCode, LErrorMessage) Then SetAlertMessage LErrorMessage
' (SS,5/7/11) replaced LProductCodeWithPrefix with LItemIDWithPrefix
' (SS,14/4/16) added LQtyInBasket, LQtyToAdd
Dim objItem, LItemIDWithPrefix, LProductCode, LQty, LItemID, LQtyInBasket, LQtyToAdd
For Each objItem In Request.Form
LItemIDWithPrefix = objItem
LQty = CleanRequestForm(objItem)
If Left(LItemIDWithPrefix, 4) = "QTY_" Then
LItemID = CLng(Mid(LItemIDWithPrefix, 5)) ' (SS,14/4/16) added CLng to ensure integer
' LProductCode = ReplaceStr(LProductCode, "_", "-") ' convert the underscores to '-' because fields name already have fields with '-' converted to '_'
' Response.Write LProductCode & "=" & LQty & "<BR>"
' if quantity is zero or blank then delete the item, else change the quantity
If LQty = "0" Or LQty = "" Then
RemoveFromBasket LItemID
Else
' make sure it's a valid number, convert to integer, assume 1 if invalid
If IsNumeric(LQty) Then
LQty = CLng(LQty)
Else
LQty = 1
End if
' (SS,14/4/16) removed the following maximum limit
' If LQty > MAX_PRODUCT_QTY Then LQty = MAX_PRODUCT_QTY ' reduce to maximum '
' (SS,5/7/11) product code is required below, so we look it up using ItemID
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
' LProductCode = GetSQLValueAsString("SELECT ProductCode FROM shoppingbaskets WHERE ItemID = '" & CleanSQLStr(LItemID) & "'")
' (SS,14/4/16) replaced above with following to also look up qty currently in basket
If GetSQL2Values("SELECT ProductCode, Qty FROM shoppingbaskets WHERE ItemID = '" & CleanSQLStr(LItemID) & "'", LProductCode, LQtyInBasket) Then
LQtyToAdd = LQty - LQtyInBasket
' (SS,14/4/16) rewrote to call AddProductToBasketIncludingOptions for each item in basket to add/remove the different
If LQtyToAdd <> 0 Then
'If Not AddProductToBasketIncludingOptions(LProductCode, LQtyToAdd, LItemID, False, False, "", "") Then
' SetAlertMessage "Sorry, not enough of " & GetProductName(LProductCode) & " in stock"
'End If
AddProductToBasketIncludingOptions LProductCode, LQtyToAdd, LItemID, False, True, "", ""
End If
End If
End If
End If
Next
End Sub
' (SS,4/7/11) added ItemID and options for products, ItemID and OptionsList now passed to ShowBasketItem
' (SS,1/8/18) modified to additional info to options list via AddMoreOptionsInfo
Sub ShowBasket
' (SS,28/11/13) clear values for remarketing tag
' (SS,6/3/18) removed following two lines no longer used
'SetRemarketingTagTotalValue(0)
'ClearRemarketingTagProductIDs
GetSettingsForContinue ' get the settings for the continue shopping button
' (SS,18/4/16) False to make sure detail records aren't deleted
' (SS,19/4/16) now moved to ProductsMain
' ClearExistingOrderPlaced False
' (SS,18/4/16) check items in basket are in stock, display alert if not
' *** may need to move below check below, currently fails due to query being held open below
CheckBasketInStock True
' (SS,23/5/07) replaced query with GetOrderItemsQuery(FSessionID)
' (SS,26/10/18) added False parameter to GetOrderItemsQuery to exclude subproducts
OpenQuery(GetOrderItemsQuery(FSessionID, False))
' exit if there are no items '
If EndOfQuery Then
CloseQuery
ShowEmptyBasket
Exit Sub
End If
' (SS,16/2/12) following ensures something is set for delivery country, else nothing may be selected in the new delivery country combo
' due to process order routine clearing Session("DeliveryCountry") when delivery address is same as invoice
SetDeliveryCountry(GetDeliveryCountry)
' show the heading '
ShowBasketHeader
' (SS,6/7/11) removed tp_ProductCodeDashAsUnderscore added LProductID
Dim tp_ProductCode, tp_ProductName, tp_PriceEach, tp_Qty, tp_ProductTotal, tp_SubTotal, LProductID
Dim tp_VATTotal, tp_Discount, tp_Delivery, tp_GrandTotal ' (SS,23/4/09) added tp_Discount
Dim LCount, LPriceEach
LCount = 0
' (SS,4/7/11)
' (SS,9/10/14) added LobjOptionsDict and LCustomGetOrderDeliveryInfoExists, LCustomProductInBasketExists
Dim LItemID, LOptionsList, LobjOptionsDict, LCustomGetOrderDeliveryInfoExists, LCustomProductInBasketExists
LCustomGetOrderDeliveryInfoExists = FunctionExists("CustomGetOrderDeliveryInfo")
LCustomProductInBasketExists = FunctionExists("CustomProductInBasket")
SetDeliveryInfo("")
' (SS,17/2/15)
Dim LBasketDesc
LBasketDesc = ""
' (SS,23/2/15) to save shopping basket prices in dictionary for use by email basket feature
Dim LobjBasketPricesDict
Set LobjBasketPricesDict = Server.CreateObject("Scripting.Dictionary")
' (SS,9/10/14)
Set LobjOptionsDict = CreateObject("Scripting.Dictionary")
LobjOptionsDict.CompareMode = vbTextCompare ' makes the keys case insensitive
Do While Not EndOfQuery
' (SS,23/5/07)
' (SS,6/7/11) removed 0 parameter, added True, to look up options price
GetPricingFromProductRecord(True)
LCount = LCount + 1
LProductID = GetQueryValue("ProductID") ' (SS,6/7/11)
tp_ProductCode = GetFieldValue("ProductCode")
' (SS,6/3/18) removed following due to migration to gtag.js (Global Site Tag)
'AddRemarketingTagProductID(tp_ProductCode) ' (SS,28/11/13)
' replace dash with underscore to make sure + - in JavaScript work '
' (SS,6/7/11) removed following, ItemID now used
' tp_ProductCodeDashAsUnderscore = ReplaceStr(tp_ProductCode, "-", "_")
tp_Qty = NZ(GetFieldValue("Qty"))
tp_ProductName = GetFieldValue("ProductName")
LPriceEach = GetProductPrice
' (SS,4/7/11)
LItemID = GetQueryValue("ItemID")
LOptionsList = GetOptionsListForBasketItem(LItemID, "", LobjOptionsDict) ' (SS,9/10/14) added LobjOptionsDict which will be passed to a custom delivery estimation routine
' (SS,1/8/18) add additional custom info to options list
LOptionsList = AddMoreOptionsInfo(LProductID, LOptionsList)
' (SS,9/10/14) update delivery info for this product, at the end we'll have the actual one for the full order
If LCustomGetOrderDeliveryInfoExists Then SetDeliveryInfo(CustomGetOrderDeliveryInfo(GetDeliveryInfo, LProductID, LobjOptionsDict))
' (SS,10/10/14) if CustomProductInBasketExists function exists then call it to pass on product, qty and options, it may use it to recommend other products later
If LCustomProductInBasketExists Then CustomProductInBasket LProductID, tp_Qty, LobjOptionsDict
' (SS,1/4/08) added following to show VAT included prices, only for sites where entered prices don't include VAT and ShowIncVAT is true
'If Not PricesEnteredIncludeVAT And ShowIncVAT Then
' LPriceEach = Round2dp(LPriceEach + GetVATForProduct(LPriceEach))
'End If
' (SS,1/11/12) removed above, replaced CorrectCurrency in following two with CorrectCurrencyNV which removes VAT if it needs to
tp_PriceEach = CorrectCurrencyNV(LPriceEach)
tp_ProductTotal = CorrectCurrencyNV(LPriceEach * tp_Qty)
' (SS,23/2/15) added price each to dictionary
' (SS,26/2/15) causes "This key is already associated with an element of this collection" when same ProductID used same different options
' (SS,26/2/15) replaced LProductID with LItemID, because ProductID isn't unique enough
LobjBasketPricesDict.Add LItemID, LPriceEach
' (SS,17/2/15) added following for text to later send to PayPal Express
LBasketDesc = LBasketDesc + iif(LBasketDesc = "", "", ", ") + tp_ProductCode + " x " & tp_Qty
' (SS,4/7/11) added LProductID, LItemID and LOptionsList, removed tp_ProductCodeDashAsUnderscore and Server.URLEncode(tp_ProductCode)
If LCount Mod 2 = 1 Then
ShowBasketItem LItemID, LProductID, tp_ProductCode, tp_ProductName, LOptionsList, tp_Qty, tp_PriceEach, tp_ProductTotal
Else
ShowBasketItemAlternateColor LItemID, LProductID, tp_ProductCode, tp_ProductName, LOptionsList, tp_Qty, tp_PriceEach, tp_ProductTotal
End If
NextQueryRecord
Loop
Set LobjOptionsDict = Nothing ' (SS,9/10/14)
CloseQuery
' (SS,23/2/15) save basket prices dictionary to session, and free object
Set Session("BasketPricesDict") = LobjBasketPricesDict
Set LobjBasketPricesDict = Nothing
Dim LSubtotal, LDiscount, LTotalWeight, LNonWeightPrice, LVATTotal, LDelivery, LGrandTotal, LVATIncluded, LVATDeducted
' (SS,28/5/07) GetOrderTotals replaces previous code
' (SS,22/6/07) added LTotalWeight parameter
' (SS,23/4/09) added LDiscount parameter
GetOrderTotalsForBasket FSessionID, LSubtotal, LDiscount, LDelivery, LGrandTotal, LVATIncluded, LVATDeducted, LTotalWeight
Dim LIsVATDeducted
If LVATDeducted > 0 Then
tp_VATTotal = CorrectCurrency(LVATDeducted)
LIsVATDeducted = True
Else
tp_VATTotal = CorrectCurrency(LVATIncluded)
LIsVATDeducted = False
End If
SetVATLabel(LIsVATDeducted) ' (SS,28/5/07) sets label to use in basket footer
' (SS,1/4/08) added following to show VAT included prices, only for sites where entered prices don't include VAT and ShowIncVAT is true
' the inc-template-basket.asp has been adjusted to not show the VAT row unless VAT is deducted
If Not PricesEnteredIncludeVAT And ShowIncVAT Then
Dim LVATForDelivery, LVATForDiscount
LVATForDelivery = GetVATAmountToAdd(LDelivery)
LVATForDiscount = GetVATAmountToAdd(LDiscount) ' (SS,23/4/09)
' (SS,23/4/09) to following added - LVATForDiscount (remember Discount is negative)
LSubtotal = LSubtotal + LVATIncluded - LVATForDelivery - LVATForDiscount
LDiscount = LDiscount + LVATForDiscount ' (SS,23/4/09)
LDelivery = LDelivery + LVATForDelivery
' (SS,1/11/12) moved following 3 lines here from below
tp_Subtotal = CorrectCurrency(LSubtotal)
tp_Discount = CorrectCurrency(LDiscount)
tp_Delivery = CorrectCurrency(LDelivery)
Else ' added following to remove VAT from values if applicable to prevent totals from confusing buyers when VAT is deducted
tp_Subtotal = CorrectCurrencyNV(LSubtotal)
tp_Discount = CorrectCurrencyNV(LDiscount)
tp_Delivery = CorrectCurrencyNV(LDelivery)
End If
tp_GrandTotal = CorrectCurrency(LGrandTotal)
SetBasketGrandTotal(LGrandTotal) ' (SS,19/1/15) set basket grand total, can be used by PayPal Express checkout etc.
SetBasketDescription(LBasketDesc)' (SS,17/2/15) set basket description, can be used by PayPal Express checkout etc.
' (SS,23/2/15) added following for saving to basket feature
SetBasketSubtotal(LSubtotal)
SetBasketDiscount(LDiscount)
SetBasketDelivery(LDelivery)
SetBasketVAT(LVATIncluded)
' (SS,6/3/18) removed following due to migration to gtag.js (Global Site Tag)
'SetRemarketingTagTotalValue(LSubtotal) ' (SS,28/11/13) not 100% sure about being subtotal, or whether VAT is deducted as above
' show the footer '
' (SS,19/8/06) added GetCurrencyPostalArea
' (SS,23/4/09) added Session("VoucherCode"), tp_Discount
' (SS,4/6/09) added LTotalWeight
' (SS,27/1/10) changed GetCurrencyPostalArea to GetRegionCodeOrPostalArea
ShowBasketFooter GetRegionCodeOrPostalArea, tp_Subtotal, Session("VoucherCode"), tp_Discount, tp_Delivery, tp_VATTotal, LIsVATDeducted, tp_GrandTotal, LTotalWeight, GetContinueLink, GetCheckoutLink
End Sub
' (SS,17/4/07) code moved to new routine called ShowShoppingStatusMain
Sub ShowShoppingStatus
ShowShoppingStatusMain False, 0, 0, 0, False
End Sub
' (SS,17/4/07) new summary version of shopping status
Sub ShowShoppingStatusSummary
Dim LTotalQty, LItemLines, LSubtotal, LIsEmpty
ShowShoppingStatusMain True, LTotalQty, LItemLines, LSubtotal, LIsEmpty
ShowShoppingStatusSummaryVersion LTotalQty, LItemLines, CorrectCurrencyNV(LSubtotal), LIsEmpty
End Sub
' (SS,11/4/07) renamed from ShowShoppingStatusMain, added 5 parameters for use by ShowShoppingStatusSummary
' called from 2 places: ShowShoppingStatus and ShowShoppingStatusSummary
' modified to make use of new parameters
' (SS,12/9/12) modified to use ItemID instead of ProductCode for LDeleteHRef
' (SS,25/10/18) note that AIsSummary being False (i.e. more detailed) as been redundant since new Bootstrap framework
' was only used for more detailed shopping status, which isn't used any more
' (SS,26/10/18) added GetShoppingBasketFilter to query to exclude subproducts
Sub ShowShoppingStatusMain(AIsSummary, ByRef ATotalQty, ByRef AItemLines, ByRef ASubtotal, ByRef AIsEmpty)
' following ordered by ItemID, ensuring most recent item will appear last '
' OpenQuery("SELECT shoppingbaskets.*, products.ProductName, products.StdPrice, products.SalePrice FROM shoppingbaskets LEFT JOIN products ON shoppingbaskets.ProductCode = products.ProductCode WHERE SessionID = '" + FSessionID + "' ORDER By ItemID")
' (SS,21/5/07) above replaced by following
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
' (SS,26/10/18) added GetShoppingBasketFilter to filter out subproducts
OpenQuery("SELECT s.*, p.ProductName, " + GetPricingFieldsForSQL("p") + " FROM shoppingbaskets s LEFT JOIN products p ON p.ProductCode = s.ProductCode WHERE SessionID = '" + CleanSQLStr(FSessionID) + "'" + GetShoppingBasketFilter(False) + " ORDER BY ItemID")
' show the heading '
If Not AIsSummary Then ShowShoppingStatusHeader
ATotalQty = 0
AItemLines = 0
ASubtotal = 0
AIsEmpty = EndOfQuery
' if basket empty then just show the empty text else list the products '
If AIsEmpty Then
If Not AIsSummary Then ShowShoppingStatusEmpty
Else
If Not AIsSummary Then ShowShoppingStatusItemHeader
Dim LCount, LRecordCount, LProductCode, LProductName, LPrice, LQty, LProductCodeDashAsUnderscore, LDeleteHRef
LRecordCount = GetQueryRecordCount ' (SS,26/5/07)
Do While Not EndOfQuery
' (SS,26/5/07) added following to allow templates to determine whether at first or last records
SetAtFirstOrLastProduct LCount, LRecordCount
AItemLines = AItemLines + 1
LQty = NZ(GetFieldValue("Qty"))
ATotalQty = ATotalQty + LQty
LProductCode = GetFieldValue("ProductCode")
' (SS,21/5/07)
' (SS,6/7/11) removed LQty parameter, added True, to look up options price
GetPricingFromProductRecord(True)
LPrice = GetProductPrice
' (SS,1/4/08) added following to show VAT included prices, only for sites where entered prices don't include VAT and ShowIncVAT is true
If Not PricesEnteredIncludeVAT And ShowIncVAT Then
LPrice = Round2dp(LPrice + GetVATForProduct(LPrice))
End If
ASubtotal = ASubtotal + (LPrice * LQty)
If Not AIsSummary Then
' replace dash with underscore to make sure + - in JavaScript work '
LProductCodeDashAsUnderscore = ReplaceStr(LProductCode, "-", "_")
LProductName = HTMLEncode(GetFieldValue("ProductName")) ' (SS,26/7/11) added HTMLEncode
' (SS,25/10/18) NB. following is now redundant
LDeleteHRef = "javascript:itp_delete('" & GetFieldValue("ItemID") & "')" ' (SS,12/9/12) replaced LProductCode with GetFieldValue("ItemID")
ShowShoppingStatusItem LProductCode, LProductCodeDashAsUnderscore, Server.URLEncode(LProductCode), LProductName, LQty, CorrectCurrencyNV(LPrice), LDeleteHRef
End If
NextQueryRecord
Loop
End If
If Not AIsSummary Then ShowShoppingStatusFooter CorrectCurrencyNV(ASubtotal), AIsEmpty
CloseQuery
End Sub
Sub ShowSpecialOffers
Dim LProductCode, LProductCodeDashAsUnderscore
Dim LMaxSpecialOfferProducts, LSpecialOfferCount, LSQL, LWhere, LLimit, LDoneCount, LDiscountPercentage
Dim LGroup, LCategory, LSubcategory ' (SS,18/2/05) '
Dim LStdPrice, LSalePrice ' (SS,19/9/06)
ShowSpecialOffersHeader
' select products that have a sale price
' (SS,31/10/18) added GetProductsFilter to filter out subproducts
LWhere = "WHERE ProductDisabled = 0" + GetProductsFilter
' (SS,18/5/07) added FUseDiscountFieldForSpecialOffers etc to use new Discount field for special offers
If FUseDiscountFieldForSpecialOffers Then
LWhere = LWhere + " AND IFNULL(Discount, 0) > 0"
Else
LWhere = LWhere + " AND NOT ISNULL(SalePrice)"
End If
OpenQuery("SELECT COUNT(*) AS Count FROM products " + LWhere)
LSpecialOfferCount = GetQueryValue("Count")
CloseQuery
' exit if there are no items '
If LSpecialOfferCount = 0 Then
ShowSpecialOffersEmpty
Else
LMaxSpecialOfferProducts = GetProductsMaxSpecialOffer
' (SS,18/5/07) replaced "ProductCode, ProductName, StdPrice, SalePrice" with * (instead of adding Discount because some installs may not have the Discount field)
LSQL = "SELECT * FROM products " + LWhere + " ORDER BY ProductName "
LLimit = "LIMIT " & Session("SpecialOfferCount") & "," & LMaxSpecialOfferProducts
LDoneCount = 0
OpenQuery(LSQL & LLimit)
Do While Not EndOfQuery
' (SS,26/5/07)
' (SS,6/7/11) removed 0 parameter, added False, no need to look up options price
GetPricingFromProductRecord(False)
LProductCode = GetFieldValue("ProductCode")
' replace dash with underscore to make sure + - in JavaScript work '
LProductCodeDashAsUnderscore = ReplaceStr(LProductCode, "-", "_")
' (SS,18/2/05) added following to get Group and Category for product, also to the end of ShowSpecialOffersItem '
GetFirstCatSubcatForProduct LProductCode, LGroup, LCategory, LSubcategory
' (SS,18/9/06) added following two lines for multicurrency
LStdPrice = CorrectCurrency(GetStdPrice) ' (SS,28/5/07) replaced GetFieldValue("StdPrice") with GetStdPrice
LSalePrice = CorrectCurrency(GetSalePrice)' (SS,28/5/07) replaced GetFieldValue("SalePrice") with GetSalePrice
LDiscountPercentage = GetDiscountPercentage ' (SS,28/5/07) discount code moved to GetDiscountPercentage
ShowSpecialOffersItem LProductCode, LProductCodeDashAsUnderscore, Server.URLEncode(LProductCode), GetFieldValue("ProductName"), LStdPrice, LSalePrice, LDiscountPercentage, LGroup, LCategory, LSubcategory
LDoneCount = LDoneCount + 1
NextQueryRecord
Session("SpecialOfferCount") = Session("SpecialOfferCount") + 1
' if no more records then try to wrap around to the start if applicable'
If EndOfQuery And LDoneCount < LMaxSpecialOfferProducts And LSpecialOfferCount > LMaxSpecialOfferProducts Then
Session("SpecialOfferCount") = 0
CloseQuery
LLimit = "LIMIT " & Session("SpecialOfferCount") & "," & LMaxSpecialOfferProducts - LDoneCount
OpenQuery(LSQL & LLimit)
End If
Loop
' wrap around if it goes beyond last special offer record '
If Session("SpecialOfferCount") >= LSpecialOfferCount Then Session("SpecialOfferCount") = 0
CloseQuery
End If
ShowSpecialOffersFooter("javascript:itp_sosearch()")
End Sub
' (SS,27/5/10) made speed improvements, now only run once per day, held in new bestsellers table
Sub ShowBestSellers
Dim LProductCode, LProductCodeDashAsUnderscore
Dim LMaxBestSellers, LBestSellerCount, LSQL, LOrderBy, LDoneCount, LPrice
Dim LGroup, LCategory, LSubcategory ' (SS,18/2/05)
LMaxBestSellers = GetBestSellersMax
' (SS,21/10/05) added best sellers order option of by TotalValue or TotalSold (was TotalValue)
' because cufflink wanted by TotalSold (not TotalValue as it is for Clifton)
' (SS,27/5/10) moved here from below and now saving in LOrderBy
If GetBestSellersSortOrder = "TotalSold" Then
LOrderBy = "TotalSold DESC, PriceEach DESC"
Else
LOrderBy = "TotalValue DESC, TotalSold DESC, PriceEach DESC"
End If
' check best sellers table, and update table once per day, querystring also forces an update updatebs=yes
Dim LUpdateBestSellers
LSQL = "SELECT *, DateUpdated < CURDATE() AS UpdateBestSellers FROM bestsellers ORDER BY ID, " & LOrderBy ' ORDER BY ID ensures ID 0 record is first
If CleanRequestQueryString("updatebs") = "yes" Then
LUpdateBestSellers = True
Else
OpenQuery(LSQL)
If EndOfQuery Then
LUpdateBestSellers = True
Else
LUpdateBestSellers = IntToBool(GetQueryValue("UpdateBestSellers"))
End If
CloseQuery ' (SS,18/5/12) moved here from just before Application.Lock below
End If
'If LUpdateBestSellers And Application("UpdatingBestSellers") <> True Then ' <> True (instead of = False) because default value of Application variable is ""
If LUpdateBestSellers And Application("UpdatingBestSellers") <> True Then ' <> True (instead of = False) because default value of Application variable is ""
' (SS,27/5/10) ensure only one instance can update the best sellers table
Application.Lock
Application("UpdatingBestSellers") = True
Application.UnLock
' *** may be good to add an on error resume next? to ensure flag is set to flag i.e. like try..finally in Delphi
' (SS,17/7/13) added ability to ignore products with Exclude from Best Sellers
Dim LAttributeID
If ProductAttributesEnabled Then
LAttributeID = GetAttributeIDForName("Exclude from Best Sellers")
Else
LAttributeID = ""
End If
ExecuteQuery("TRUNCATE bestsellers") ' (SS,27/5/10) empty the table
ExecuteQuery("INSERT INTO bestsellers SET ID = 0, DateUpdated = CURDATE()") ' (SS,27/5/10) special record holding date updated, at least this record will always exist
' (SS,18/2/05) added "AND ProductDisabled = 0" so disabled products are not shown '
' (SS,4/4/05) modified to include TotalValue, ORDER BY changed from TotalSold DESC, PriceEach DESC to TotalValue DESC
' (SS,27/5/10) added INSERT INTO bestsellers SELECT CURDATE() AS DateUpdated, removed surplus SUM(Qty * PriceEach) AS TotalValue
' (SS,18/5/12) replaced the "LEFT JOIN" with "INNER JOIN", link to productcategories was causing product to be included more than once if it belonged to more than one category
' fixed this by adding "AND Main" to productcategories join.
' (SS,29/5/12) replaced orderdetails.ProductName with products.ProductName, to use current product name rather than historical which may change
Dim LSQL2
LSQL2 = "INSERT INTO bestsellers" &_
" SELECT 1, CURDATE() AS DateUpdated, orderdetails.ProductCode, products.ProductName, PriceEach, SUM(Qty) AS TotalSold, SUM(Qty * PriceEach) AS TotalValue, Category, Subcategory, StdPrice, SalePrice" &_
" FROM orderdetails" &_
" INNER JOIN orders ON orders.OrderNo = orderdetails.OrderNo" &_
" INNER JOIN productcategories ON productcategories.ProductCode = orderdetails.ProductCode AND Main " &_
" INNER JOIN products ON products.ProductCode = orderdetails.ProductCode"
' (SS,17/7/13) to exclude products marked with "Exclude from Best Sellers" attribute
If LAttributeID <> "" Then LSQL2 = LSQL2 + " LEFT JOIN product_attributes ON product_attributes.ProductID = products.ProductID AND product_attributes.AttributeID = " & LAttributeID
' (SS,31/10/18) added GetOrderDetailsFilter to filter out subproducts
LSQL2 = LSQL2 + " WHERE (orders.Status = 'PAYMENT RECEIVED' OR orders.Status = 'COMPLETED') AND PriceEach > 0 AND ProductDisabled = 0" &_
" AND TO_DAYS(CURDATE()) - TO_DAYS(orders.DateTimeOrdered) <= " & GetBestSellersPastDays + GetOrderDetailsFilter
' (SS,17/7/13) to exclude products marked with "Exclude from Best Sellers" attribute
If LAttributeID <> "" then LSQL2 = LSQL2 + " AND product_attributes.AttributeID IS NULL"
' (SS,18/5/12) moved the group by and order by from above query to following, because it's also used by the new per_category query below
ExecuteQuery(LSQL2 + " GROUP BY orderdetails.ProductCode ORDER BY " & LOrderBy + " LIMIT " & LMaxBestSellers) ' (SS,27/5/10) was OpenQuery(LSQL)
' (SS,18/5/12) added following to create bestsellers per category/subcategory
ExecuteQuery("CREATE TABLE IF NOT EXISTS bestsellers_per_category LIKE bestsellers")
ExecuteQuery("TRUNCATE bestsellers_per_category") ' in case table existed before create above
' same as overall query above but with different table name AND Main removed, and different group by, no order by and no limit
LSQL2 = ReplaceStr(LSQL2, "INSERT INTO bestsellers", "INSERT INTO bestsellers_per_category")
LSQL2 = ReplaceStr(LSQL2, " AND Main ", "")
ExecuteQuery(LSQL2 + " GROUP BY Category, Subcategory, orderdetails.ProductCode")
' (SS,27/5/10) clear the flag so it can be updated again
Application.Lock
Application("UpdatingBestSellers") = False
Application.UnLock
' (SS,18/5/12) removed following, open now done below
' OpenQuery(LSQL) ' open the query with new data
End If
' (SS,18/5/12) determine which table to use, bestsellers for main, if category or subcategory applicable then use bestsellers_per_category
' we're using the menu category and subcategory settings to determine which category/subcategory we're in
' (SS,22/5/12) added LSQLPre, i.e. * replaced with DISTINCT ProductCode, ProductName, StdPrice, SalePrice,
' prevents duplicates when looking at Category where product appears in more than one subcategory for same category
Dim LSQLPre
LSQLPre = "SELECT DISTINCT ProductCode, ProductName, StdPrice, SalePrice, TotalSold, TotalValue FROM "
If GetMenuCategory <> "" And GetMenuSubcategory <> "" Then
LSQL = LSQLPre + "bestsellers_per_category WHERE Category = '" & CleanSQLStr(GetMenuCategory) & "' AND Subcategory = '" & CleanSQLStr(GetMenuSubcategory) & "'"
ElseIf GetMenuCategory <> "" Then
LSQL = LSQLPre + "bestsellers_per_category WHERE Category = '" & CleanSQLStr(GetMenuCategory) & "'"
Else ' i.e. GetMenuCategory = "" And GetMenuSubcategory = ""
LSQL = LSQLPre + "bestsellers WHERE ID = 1"
End If
OpenQuery(LSQL + " ORDER BY " + LOrderBy + " LIMIT " & LMaxBestSellers) ' (SS,18/5/12)
' count the records (SS,26/5/07) improved version which uses new GetQueryRecordCount function
' (SS,28/5/10) - 1 to ignore the special ID 0 record
' (SS,18/5/12) moved here from above, no need of "- 1" because we have ID = 1 above
LBestSellerCount = GetQueryRecordCount
' exit if there are less than required number of best sellers
' (SS,18/5/12) replaced LBestSellerCount < LMaxBestSellers with LBestSellerCount = 0
If LBestSellerCount = 0 Then
ShowBestSellersEmpty
Else
ShowBestSellersHeader
LDoneCount = 0
' (SS,22/5/12) added allow TotalSold and TotalValue to show when querystring bstotals=y exists
Dim LProductName, LShowTotals
If CleanRequestQueryString("bstotals") = "y" Then
Session("bstotals") = True
ElseIf CleanRequestQueryString("bstotals") = "n" Then
Session("bstotals") = False
End If
LShowTotals = Session("bstotals") = True
Do While Not EndOfQuery
' (SS,22/5/12) removed "If GetQueryField("ID") <> 0 Then" ' ignore the ID 0 record
LDoneCount = LDoneCount + 1
' (SS,26/5/07) added following to allow templates to determine whether at first or last records
SetAtFirstOrLastProduct LDoneCount, LBestSellerCount
LProductCode = GetQueryField("ProductCode")
' replace dash with underscore to make sure + - in JavaScript work '
LProductCodeDashAsUnderscore = ReplaceStr(LProductCode, "-", "_")
If IsNull(GetQueryField("SalePrice")) Then
LPrice = GetQueryField("StdPrice")
Else
LPrice = GetQueryField("SalePrice")
End If
LPrice = CorrectCurrency(LPrice) ' (SS,18/9/06)
' (SS,18/2/05) added following to get Group and Category for product, also to the end of ShowBestSellersItem '
GetFirstCatSubcatForProduct LProductCode, LGroup, LCategory, LSubcategory
' (SS,22/5/12) add the total sold and value after the product name if enabled
LProductName = GetQueryField("ProductName")
' (SS,30/4/13) added Round because it was showing many DP, also added bold
If LShowTotals Then LProductName = LProductName & "<br />(<b>" & Round(GetQueryField("TotalValue"), 0) & " / " & GetQueryField("TotalSold") & "</b>)"
ShowBestSellersItem LDoneCount, LProductCode, LProductCodeDashAsUnderscore, Server.URLEncode(LProductCode), LProductName, LPrice, LGroup, LCategory, LSubcategory
NextQueryRecord
Loop
ShowBestSellersFooter
End If
CloseQuery
End Sub
' (SS,17/1/13)
' (SS,6/6/1/17) renamed from ShowNewProducts to ShowNewProductsOld. *** can be removed, new RWD version is below
Sub ShowNewProductsOld
Dim LNewProductsCount, LDoneCount
OpenQuery("SELECT ProductCode, ProductName FROM products WHERE DateAdded IS NOT NULL AND NOT ProductDisabled ORDER BY DateAdded DESC, SortOrder LIMIT 10")
LNewProductsCount = GetQueryRecordCount
If LNewProductsCount = 0 Then
ShowNewProductsEmpty
Else
ShowNewProductsHeader
LDoneCount = 0
Do While Not EndOfQuery
LDoneCount = LDoneCount + 1
ShowNewProductsItem LDoneCount, GetFieldValue("ProductCode"), GetFieldValue("ProductName")
NextQueryRecord
Loop
ShowNewProductsFooter
End If
CloseQuery
End Sub
' (SS,17/1/13)
' (SS,6/6/17) new version for RWD which can show a full product list, works in similar way to highlights or featured products, i.e. SalePrice, StdPrice and NumInStock
' added new parameters AMaxToShow, AExtraOptions, AExtraOptions not currently used. DoneCount counter removed.
' *** can be removed
Sub ShowNewProductsOld2(AMaxToShow, AExtraOptions)
Dim LSQL, LNewProductsCount, LStdPrice, LSalePrice
LSQL = "SELECT ProductCode, ProductName, SalePrice, StdPrice, NumInStock FROM products WHERE DateAdded IS NOT NULL AND NOT ProductDisabled"
LSQL = LSQL + " AND EXISTS(SELECT PictureID FROM pictures WHERE Type = 'P' AND Code = products.ProductCode AND Enabled)"
LSQL = LSQL + " ORDER BY DateAdded DESC, SortOrder LIMIT " & AMaxToShow
OpenQuery(LSQL)
LNewProductsCount = GetQueryRecordCount
If LNewProductsCount = 0 Then
ShowNewProductsEmpty
Else
ShowNewProductsHeader
Do While Not EndOfQuery
LStdPrice = CorrectCurrencyNV(GetFieldValue("StdPrice"))
LSalePrice = CorrectCurrencyNV(GetFieldValue("SalePrice"))
ShowNewProductsItem GetFieldValue("ProductCode"), GetFieldValue("ProductName"), LSalePrice, LStdPrice, GetFieldValue("NumInStock")
NextQueryRecord
Loop
ShowNewProductsFooter
End If
CloseQuery
End Sub
' (SS,18/7/17) replaced Sub ShowNewProductsOld2 above with this one which make use of existing ShowProducts
' where strSearch is set to NEW_PRODUCTS_SEARCH_TEXT and LMax to limit for how many to show
Sub ShowNewProducts(AMaxToShow, AExtraOptions)
' ShowProducts strGroup, strCategory, strSubcategory, strSearch, strPage, LAttributeID, LMin, LMax, LVal
ShowProducts "", "", "", NEW_PRODUCTS_SEARCH_TEXT, "", "", "", AMaxToShow, ""
End Sub
' (SS,15/2/12) original version
Sub ShowRecentReviews
ShowRecentReviewsMain 10, False
End Sub
' (SS,26/8/14) version 2
Sub ShowRecentReviewsV2(AMaxCount)
ShowRecentReviewsMain AMaxCount, True
End Sub
' (SS,15/2/12) original version
' (SS,26/8/14) code more here from ShowRecentReviews above, called from two places, i.e. ShowRecentReviewsV2
' (SS,17/8/15) modified to handle Trustpilot reviews held in trustpilot_reviews table
Sub ShowRecentReviewsMain(AMaxCount, AIsVersion2)
Dim LSQL, LReviewCount, LDoneCount, LProductCode, LReviewerName
' (SS,17/8/15) added AType "T" for Trustpilot reviews held in different trustpilot_reviews table
If ReviewTypeIsTrustpilot Then
LSQL = "SELECT *, DATE_FORMAT(ReviewDateTime, '%e %b %Y') AS ReviewDate FROM trustpilot_reviews WHERE ReviewID > 1"
Else
' (SS,26/8/14) replaced 10 with AMaxCount, added fields ReviewTitle, ProductRating, ReviewerName, ReviewText
' (SS,14/9/17) added " GROUP BY ReviewText, DATE(ReviewDateTime), ReviewerName" to prevent reviews with duplicate ReviewTexts appearing compromising SEO on home page
LSQL = "SELECT ReviewID, pr.ProductCode, DATE_FORMAT(ReviewDateTime, '%e %b %Y') AS ReviewDate, ReviewTitle, ProductRating, ReviewerName, ReviewText" &_
" FROM productreviews pr" &_
" INNER JOIN products p ON p.ProductCode = pr.ProductCode" &_
" WHERE ShowOnSite AND NOT p.ProductDisabled AND ReviewType = 'R'" &_
" GROUP BY ReviewText, DATE(ReviewDateTime), ReviewerName"
End If
LSQL = LSQL + " ORDER BY ReviewDateTime DESC LIMIT " & AMaxCount
OpenQuery(LSQL)
LReviewCount = GetQueryRecordCount
If LReviewCount = 0 Then
ShowRecentReviewsEmpty
Else
ShowRecentReviewsHeader
LDoneCount = 0
Do While Not EndOfQuery
LDoneCount = LDoneCount + 1
If ReviewTypeIsNormal Then ' (SS,17/8/15) not applicable for basic Trustpilot account
LProductCode = GetQueryField("ProductCode")
End If
If ReviewTypeIsNormal And Not AIsVersion2 Then ' original version with less fields
ShowRecentReviewsItem LDoneCount, GetQueryField("ReviewID"), LProductCode, GetProductName(LProductCode), GetQueryField("ReviewDate")
ElseIf ReviewTypeIsNormal Then ' (SS,26/8/14) version 2 with more fields
LReviewerName = GetQueryField("ReviewerName")
If LReviewerName = "" Then
LReviewerName = "Anonymous"
End If
ShowRecentReviewsItem LDoneCount, GetQueryField("ReviewID"), LProductCode, GetProductName(LProductCode), GetQueryField("ReviewDate"), GetQueryField("ReviewTitle"), GetQueryField("ProductRating"), LReviewerName, GetQueryField("ReviewText")
ElseIf ReviewTypeIsTrustpilot Then ' (SS,17/8/15) for Trustpilot, similar to "2" but with product code and name set to "", ProductRating is ReviewRating, ReviewText is ReviewBody
ShowRecentReviewsItem LDoneCount, GetQueryField("ReviewID"), "", "", GetQueryField("ReviewDate"), GetQueryField("ReviewTitle"), GetQueryField("ReviewRating"), GetQueryField("ReviewerName"), GetQueryField("ReviewBody")
End If
NextQueryRecord
Loop
ShowRecentReviewsFooter
End If
CloseQuery
End Sub
' (SS,18/09/06) moved ShowProductPrice and ShowProductPriceExcVAT here from inc-template-product-detail
' (SS,16/1/13) replaced IsCurrencyVATable with IsCountryVATable
Sub ShowProductPrice(ASalePrice, AStdPrice)
If ASalePrice <> "" Then ' (SS,18/9/06) was Not IsNull(ASalePrice)
' (SS,18/9/06) removed FormatNumber because ASalePrice and AStdPrice are now correctly formatted
%>
<span class="lst_saleprice"><%=AStdPrice%></span>
<span class="lst_price"><%=ASalePrice%></span>
<span class="lst_vat">
<%If ShowIncVAT And IsCountryVATable Then Response.Write(" inc VAT")%>
</span>
<%
Else
%>
<span class="lst_price"><%=AStdPrice%></span>
<span class="lst_vat">
<%If ShowIncVAT And IsCountryVATable Then Response.Write(" inc VAT")%>
</span>
<%
End if
End Sub
' (SS,10/8/15) modified to remove the <br> from Responsive Web Design
Sub ShowProductPriceExcVAT(ASalePrice, AStdPrice)
' (SS,19/9/06) added following
' (SS,16/1/13) replaced IsCurrencyVATable with IsCountryVATable
If Not ShowExcVAT Or Not IsCountryVATable Then
Exit Sub
End If
Dim LPrice, LVATRate
If ASalePrice <> "" Then ' (SS,18/9/06) was Not IsNull(ASalePrice)
LPrice = ASalePrice
Else
LPrice = AStdPrice
End If
LVATRate = GetVATRate
' (SS,19/9/06) a bit messy but had to be done this way
' remove the currency symbol, else following calculation will fail
If Left(LPrice, Len(GetCurrencySymbol)) = GetCurrencySymbol Then
LPrice = Mid(LPrice, Len(GetCurrencySymbol) + 1, 999)
End If
LPrice = CDbl(LPrice) * (1/(1 + LVATRate))
' (SS,10/8/15) replaced <br> with following
If not IsRWD Then
Response.Write BR
End If
%>
<span class="price_exc_vat">
<%Response.Write(GetCurrencySymbol & FormatNumber(LPrice, 2) & " exc VAT")%>
</span>
<%
End Sub
' (SS,12/5/11) New version of show product price, with boolean parameters to show out price crossed out, and actual price in large
' (SS,26/5/11) added AOptions, "saveprice" or "savepc"
Sub ShowProductPriceV2(ASalePrice, AStdPrice, AOldPriceCrossedOut, AActualPriceLarge, AOptions)
' old price crossed out
If AOldPriceCrossedOut And ASalePrice <> "" Then
%><span class="price-std-crossed-out"><%=AStdPrice%></span> <%
End If
' actual price
If AActualPriceLarge Then
%><span class="price-actual-large"><%
End If
If ASalePrice <> "" Then ' i.e. it's a sale price
%><span class="price-sale"><%=ASalePrice%></span><%
Else
%><span class="price-std"><%=AStdPrice%></span><%
End If
If AActualPriceLarge Then ' close the span open for actual price large
%></span><%
End If
' (SS,16/1/13) replaced IsCurrencyVATable with IsCountryVATable
If ShowIncVAT And IsCountryVATable Then Response.Write(" inc VAT")
' (SS,26/5/11) added following for save price/percentage
If ASalePrice <> "" Then
Dim LDoSavePrice, LDoSavePC
LDoSavePrice = InStr(AOptions, "saveprice") > 0
LDoSavePC = InStr(AOptions, "savepc") > 0
If LDoSavePrice or LDoSavePC Then Response.Write " save "
If LDoSavePrice Then
%><span class="price-save"><%=CorrectCurrencyNV(GetStdPrice - GetSalePrice)%></span><%
End If
If LDoSavePC Then
%><span class="price-save-pc"><%=IIf(LDoSavePrice," (","") & FormatNumber(GetDiscountPercentage, 0) & "%" & IIf(LDoSavePrice,")","")%></span><%
End If
End If
End Sub
' (SS,20/8/14) calls ShowProductPriceV2 above and includes VAT in the prices, assuming the price excludes VAT normally
' don't use the "saveprice" option here because the difference will be wrong if prices entered exclude VAT
' calls CorrectCurrencyWV to add VAT if country is VATable
' (SS,2/9/14) not used (as of 2/9/14) was going to be used for new Cast Iron Radiator Centre site to make inc VAT price more prominent
Sub ShowProductPriceIncVAT(ASalePrice, AStdPrice, AOldPriceCrossedOut, AActualPriceLarge, AOptions)
Dim LStdPrice, LSalePrice
LStdPrice = CorrectCurrencyWV(GetStdPrice)
LSalePrice = CorrectCurrencyWV(GetSalePrice)
ShowProductPriceV2 LSalePrice, LStdPrice, AOldPriceCrossedOut, AActualPriceLarge, AOptions
End Sub
' (SS,19/8/14) simply shows the price, no spans or classes added
' price obtained from GetProductPrice, AIncVATParameter set to True to include VAT
Sub ShowProductPriceSimple(AIncVATParameter)
Dim LPrice, LResult
LPrice = GetProductPrice
If AIncVATParameter Then
LResult = CorrectCurrencyWV(LPrice)
Else
LResult = CorrectCurrencyNV(LPrice)
End If
Response.Write LResult
End Sub
' (SS,16/2/16) replacement of ShowProductPriceSimple above but returns the value and can specify AWithCurrencySymbol parameter
Function GetProductPriceSimple(AIncVAT, AWithCurrencySymbol)
Dim LPrice, LResult
LPrice = GetProductPrice
If AIncVAT Then
LResult = CorrectCurrencyWV(LPrice)
Else
LResult = CorrectCurrencyNV(LPrice)
End If
If Not AWithCurrencySymbol Then
LResult = RemoveCurrencySymbol(LResult)
End If
GetProductPriceSimple = LResult
End Function
' (SS,22/9/17) modified version of GetProductPriceSimple without AWithCurrencySymbol parameter also removes comma (Google doesn't like commas in microdata)
Function GetProductPriceForMicrodata(AIncVAT)
Dim LResult
LResult = GetProductPriceSimple(AIncVAT, False)
GetProductPriceForMicrodata = Replace(LResult, ",", "")
End Function
' (SS,22/9/17) used for microdata in inc-template-product-detail.asp
Function GetProductConditionForMicrodata(AProductID)
Dim LResult
LResult = GetProductAttributeByName(AProductID, "Condition")
If LResult = "" Then
LResult = "NewCondition"
Else
LResult = Proper(LResult) & "Condition"
End If
GetProductConditionForMicrodata = LResult
End Function
' (SS,6/7/06)
' (SS,25/2/10) LImageCount changed to FProductImageCount global
' (SS,22/11/12) renamed from ShowProductMoreImages and added AMax parameter, called by ShowProductMoreImages above, if AExludeCount is 0 then all images are shown
' (SS,22/11/12) can also be called from inc-template-product-details.asp if we need to specify images to exclude, e.g. if magic360 or youtube video exists
' (SS,22/11/12) code moved to ShowProductMoreImagesExclude, this is called from inc-template-product-details.asp
' (SS,22/8/14) new version which doesn't use image no and makes use of new Enabled, Caption and SortOrder fields
' (SS,11/9/14) added AExcludeCount and AOptions, renamed it from ShowProductMoreImagesV2 to ShowProductMoreImages, removed previous ShowProductMoreImages and ShowProductMoreImagesExclude
Sub ShowProductMoreImages(AProductCode, AExcludeCount, AOptions)
Dim LSQL, LCount, LCaption
LSQL = "SELECT PictureID, Caption FROM pictures WHERE Type = 'P' AND Code = '" & CleanSQLStr(AProductCode) & "' AND Enabled ORDER BY SortOrder, Code2, PictureID"
OpenQuery(LSQL)
FProductImageCount = GetQueryRecordCount
' (SS,11/9/14) added following to exclude 1 or 2 pictures using AExcludeCount because we may have a magic 360 or youtube video
If AExcludeCount > 0 And FProductImageCount > (GetMaxPicturesPerProduct - AExcludeCount) Then FProductImageCount = GetMaxPicturesPerProduct - AExcludeCount
' only if more than one image exists
If FProductImageCount > 1 Then
OpenQuery(LSQL)
ShowProductMoreImagesHeader
LCount = 0
' (SS,11/9/14) added And LCount < FProductImageCount
Do While Not EndOfQuery And LCount < FProductImageCount
LCount = LCount + 1
LCaption = NB(GetQueryValue("Caption")) ' NB important to treat null as ""
' (SS,11/9/14) added tr() to Picture and of
If LCaption = "" Then LCaption = tr("Picture") & " " & LCount & " " & tr("of") & " " & FProductImageCount ' set default caption if none found
ShowProductMoreImagesItem GetQueryValue("PictureID"), LCount, FProductImageCount, LCaption
NextQueryRecord
Loop
ShowProductMoreImagesFooter
End If
CloseQuery
End Sub
' (SS,25/2/10) returns FProductImageCount set by ShowProductMoreImages to allow captions like "Image 2 of 3"
Function GetProductImageCount
GetProductImageCount = FProductImageCount
End Function
' (SS,6/7/06)
' (SS,1/11/06) added LHeaderShown
Sub ShowProductFiles(AProductCode)
If Not GetProductFilesEnabled Then Exit Sub
Dim LSQL, LFileName, LFileNameNoExt, LPos, LFileType, LFileSize, LHeaderShown
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
LSQL = "SELECT Code2, FileName, FileSize FROM files WHERE Type = 'P' AND Code = '" & CleanSQLStr(AProductCode) & "' ORDER BY Code2"
OpenQuery(LSQL)
LHeaderShown = False
Do While Not EndOfQuery
If Not LHeaderShown Then
ShowProductFilesHeader
LHeaderShown = True
End If
LFileName = Trim(GetQueryValue("FileName"))
LPos = InStrRev(LFileName, ".")
If LPos > 0 Then
LFileType = LCase(Trim(Mid(LFileName, LPos + 1, 20)))
LFileNameNoExt = Trim(Mid(LFileName, 1, LPos - 1))
Else
LFileType = "pdf" ' default to pdf it no extension
LFileNameNoExt = LFileName
End If
LFileSize = Int(GetQueryValue("FileSize") / 1024) & "kb"
ShowProductFilesItem AProductCode, LFileName, LFileNameNoExt, GetQueryValue("Code2"), LFileType, LFileSize
NextQueryRecord
Loop
CloseQuery
If LHeaderShown Then ShowProductFilesFooter
End Sub
' (SS,17/5/07)
Sub ShowProductRelatedOld(AProductCode)
' (SS,9/6/07) following line is important because when GetProductLink is called by ShowProductRelatedItem
' and get product is showing second image, we don't want all products links for related products
' to link to 2 or 3 image, it might show image not available when related product is clicked
' (SS,8/8/18) following removed
' strImageNo = ""
Dim LSQL, LHeaderShown, LRecordCount, LCount
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
LSQL = "SELECT pr.RelatedProductCode AS ProductCode, ProductName, ProductDescription, pr.SortOrder FROM productrelated pr" +_
" INNER JOIN products p ON p.ProductCode = pr.RelatedProductCode" +_
" WHERE pr.ProductCode = '" + CleanSQLStr(AProductCode) + "' AND ProductDisabled = 0" +_
" ORDER BY SortOrder, ProductCode"
OpenQuery(LSQL)
LRecordCount = GetQueryRecordCount
LHeaderShown = False
LCount = 0
Do While Not EndOfQuery
LCount = LCount + 1
SetAtFirstOrLastProduct LCount, LRecordCount
SetProductDescription(GetFieldValue("ProductDescription"))
If Not LHeaderShown Then
ShowProductRelatedHeader
LHeaderShown = True
End If
' (SS,27/7/11) added HTMLEncode
ShowProductRelatedItem GetQueryValue("ProductCode"), HTMLEncode(GetQueryValue("ProductName")), GetQueryValue("ProductDescription")
NextQueryRecord
Loop
CloseQuery
If LHeaderShown Then ShowProductRelatedFooter
End Sub
' (SS,17/5/07)
' (SS,13/5/11) renamed from ShowProductRelated to ShowRelatedProducts, added code to prevent being called twice
' because it can be called from within inc-template-product-detail for more accurate positioning
' (SS,15/5/11) added code to lookup price and stock info
' (SS,4/10/13) added SortOrder back
' (SS,30/12/14) rewritten to avoid temporary create and insert queries to prevent adding to binary log for replication, now uses a union query
Sub ShowRelatedProducts(AProductCode)
' (SS,13/5/11) prevent being called more than once
If FShowRelatedProductsCalled Then Exit Sub
FShowRelatedProductsCalled = True
' (SS,9/6/07) following line is important because when GetProductLink is called by ShowProductRelatedItem
' and get product is showing second image, we don't want all products links for related products
' to link to 2 or 3 image, it might show image not available when related product is clicked
' (SS,8/8/18) following removed
' strImageNo = ""
Dim LSQL, LHeaderShown, LRecordCount, LCount
' (SS,14/11/07) added facility to show all related products in group
' (SS,14/11/07) build temporary table with all related products from productrelated
' (SS,30/12/14) temporary table replaced with union query
LSQL = "(SELECT RelatedProductCode AS ProductCode, SortOrder FROM productrelated WHERE ProductCode = '" + CleanSQLStr(AProductCode) + "')"
If ShowRelatedProductsInGroup Then
' (SS,14/11/07) find this product used as related product for other products, and use this to add all the related products
' from these other products
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
' (SS,30/12/14) replaced insert with union
Dim LSQL2
LSQL2 = "SELECT ProductCode, SortOrder FROM productrelated WHERE RelatedProductCode = '" + CleanSQLStr(AProductCode) + "'"
OpenQuery(LSQL2)
Do While Not EndOfQuery
' (SS,30/12/14) replaced insert with union
LSQL = LSQL + NL + "UNION ALL"
LSQL = LSQL + NL + "(SELECT RelatedProductCode AS ProductCode, SortOrder FROM productrelated WHERE ProductCode = '" + CleanSQLStr(GetFieldValue("ProductCode")) + "')"
' (SS,15/11/07) also the product for which the related products exist with sort order 0
' (SS,30/12/14) replaced insert with union
LSQL = LSQL + NL + "UNION ALL"
LSQL = LSQL + NL + "(SELECT '" + CleanSQLStr(GetFieldValue("ProductCode")) + "', 0)"
NextQueryRecord
Loop
CloseQuery
Else ' (SS,v171,19/5/09) add products that also link to this product (i.e. reciprocal)
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
' (SS,30/12/14) replaced insert with union
LSQL = LSQL + NL + "UNION ALL"
LSQL = LSQL + NL + "(SELECT ProductCode, SortOrder FROM productrelated WHERE RelatedProductCode = '" + CleanSQLStr(AProductCode) + "')"
End If
' "ProductCode <> " used to not include this product i.e. linking to itself
' (SS,16/5/11) added NumInStock, DateStockDue, GetPricingFieldsForSQL("") and GetStockAvailabilityFieldList to allow price and stock info to be shown in related products list,
' also had to add join to suppliers and removed the p alias due to GetStockAvailabilityFieldList
' (SS,6/7/11) added products.ProductID
' (SS,12/1/11) removed SortOrder from ORDER BY, as requested by Neil
' (SS,18/5/12) added join to productcategories table to get main category/subcategory which is used to sort on
' ORDER BY changed from "ProductCode" to "pc.Category, pc.Subcategory, r.ProductCode"
' also added IF(COALESCE(NumInStock, 1) = 0, 0, 1) AS InStock and conditionally "InStock DESC" to order by to ensure out of stock items appear at the end
' New setting RelatedProductsOutOfStock can be Normal, End or Hide, this used to adjust the order by and where clause
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
' (SS,4/10/13) added SortOrder which was removed before
' (SS,30/12/2014) modified to use the union statements above instead of temporary table
' (SS,31/10/18) added GetProductsFilter to filter out subproducts
LSQL = "SELECT products.ProductID, r.ProductCode, ProductName, ProductDescription, MIN(r.SortOrder) AS SortOrder, NumInStock, DateStockDue, " + GetPricingFieldsForSQL("") + "," +_
" IF(COALESCE(NumInStock, 1) = 0, 0, 1) AS InStock, " +_
GetStockAvailabilityFieldList & " FROM" +_
NL + "(" + NL + LSQL + NL + ") r" + NL +_
"INNER JOIN products ON products.ProductCode = r.ProductCode" + NL +_
"INNER JOIN productcategories pc ON pc.ProductCode = products.ProductCode AND pc.Main" + NL +_
"LEFT JOIN suppliers ON suppliers.SupplierID = products.SupplierID " + NL +_
"WHERE ProductDisabled = 0" & GetProductsFilter & " AND r.ProductCode <> '" + CleanSQLStr(AProductCode) + "'" + IIf(RelatedProductsOutOfStockHide, " AND COALESCE(NumInStock, 1) > 0", "") + NL +_
"GROUP BY r.ProductCode, ProductName, ProductDescription" + NL + _
"ORDER BY " + IIf(RelatedProductsOutOfStockEnd, "InStock DESC, ", "") + "SortOrder, pc.Category, pc.Subcategory, r.ProductCode"
OpenQuery(LSQL)
LRecordCount = GetQueryRecordCount
LHeaderShown = False
LCount = 0
Do While Not EndOfQuery
LCount = LCount + 1
SetAtFirstOrLastProduct LCount, LRecordCount
SetProductDescription(GetFieldValue("ProductDescription"))
If Not LHeaderShown Then
ShowProductRelatedHeader
LHeaderShown = True
End If
' (SS,16/5/11) add following to allow price and availability info to be shown
' (SS,6/7/11) removed 0 parameter from GetPricingFromProductRecord, added False, no need to look up options price
GetPricingFromProductRecord(False)
SetStockAvailabilityFromFields
SetStockInfo GetFieldValue("NumInStock"), GetFieldValue("DateStockDue")
' (SS,27/7/11) added HTMLEncode
ShowProductRelatedItem GetQueryValue("ProductCode"), HTMLEncode(GetQueryValue("ProductName")), GetQueryValue("ProductDescription")
NextQueryRecord
Loop
CloseQuery
If LHeaderShown Then ShowProductRelatedFooter
End Sub
' (SS,3/8/04) returns true if account holder logged in '
Function AccountLoggedIn
AccountLoggedIn = Session("AccountID") <> ""
End Function
' (SS,28/5/09) returns true if customer tried to login by filling in email and password
Function AccountOptionalLoginAttempt
AccountOptionalLoginAttempt = Trim(CleanRequestForm("LoginEmail")) <> "" And Trim(CleanRequestForm("LoginPassword")) <> ""
End Function
' (SS,3/8/04) returns true if accounts enabled for this application '
Function AccountsEnabled
AccountsEnabled = FAccountsEnabled ' (SS,30/5/07) was Application("AccountsEnabled")
End Function
' (SS,27/5/09) replaced AccountsCompulsory with AccountCompulsory
Function AccountsCompulsory
AccountsCompulsory = AccountsEnabled And FAccountTypes = AT_COMPULSORY
End Function
' (SS,27/5/09)
Function AccountsOptional
AccountsOptional = AccountsEnabled And FAccountTypes = AT_OPTIONAL
End Function
' (SS,27/5/09)
Function AccountsAdmin
AccountsAdmin = AccountsEnabled And FAccountTypes = AT_ADMIN
End Function
' (SS,30/5/07)
Function UseEmailAddressForLogin
UseEmailAddressForLogin = FUseEmailAddressForLogin
End Function
' (SS,30/5/07) returns true if new account type (with Email address) are being used
Function NewAccountMethod
NewAccountMethod = AccountsEnabled And UseEmailAddressForLogin
End Function
' (SS,3/8/04) returns true if different delivery address enabled for this application '
Function AllowDifferentDeliveryAddress
AllowDifferentDeliveryAddress = FAllowDifferentDeliveryAddress
End Function
' (SS,5/8/04) returns true if different delivery address has been entered by user '
Function DeliveryAddressEntered
DeliveryAddressEntered = AllowDifferentDeliveryAddress And Not Session("DeliveryAddressSameAsInvoice")
End Function
' (SS,12/8/05) returns true if user had selected place order on account
Function IsPlaceOrderOnAccount()
IsPlaceOrderOnAccount = CleanRequestForm("options") = "OnAccount"
End Function
' (SS,2/9/05) returns true it site is secure i.e. HTTPS
Function IsSecureSite()
IsSecureSite = UCase(Request.ServerVariables("HTTPS")) = "ON"
End Function
' (SS,19/5/09) simple email validation, contains a @ and '.' and no space
' shortest a@a.cn (i.e. at least 6 chars)
Function IsValidEmailAddress(AEmail)
IsValidEmailAddress = Len(AEmail) > 5 And InStr(AEmail, "@") > 0 And InStr(AEmail, ".") > 0 And InStr(AEmail, " ") = 0
End Function
' (SS,23/3/12) returns true if given email address has placed a completed order
Function EmailAddressHasOrdered(AEmail)
EmailAddressHasOrdered = Not IsNull(GetSQLValue("SELECT OrderNo FROM orders WHERE EmailAddress = '" & CleanSQLStr(AEmail) & "' AND Status = 'COMPLETED'"))
End Function
' (SS,12/8/05) set up session values in secure site as they were in non secure site
Sub CopyMainSessionToSecureSession
Session("ReferrerAdded") = True
Session("OrderNoPlaced") = CLng(CleanRequestForm("DP_OrderNo"))
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQuery("SELECT * FROM orders WHERE OrderNo = '" & CleanSQLStr(Session("OrderNoPlaced")) & "'")
If Not EndOfQuery Then
Session("AccountID") = GetQueryValue("CustomerID")
Session("Title") = GetQueryValue("Title")
Session("FirstName") = GetQueryValue("FirstName")
Session("Surname") = GetQueryValue("Surname")
Session("CompanyName") = GetQueryValue("CompanyName")
Session("Address1") = GetQueryValue("AddressLine1")
Session("Address2") = GetQueryValue("AddressLine2")
Session("Town") = GetQueryValue("Town")
Session("County") = GetQueryValue("County")
Session("Postcode") = GetQueryValue("Postcode")
Session("Country") = GetQueryValue("Country")
Session("Telephone") = GetQueryValue("Telephone")
Session("Mobile") = GetQueryValue("Mobile") ' (SS,22/4/09)
Session("Email") = GetQueryValue("EmailAddress")
Session("PurchaseOrderNo") = GetQueryValue("PurchaseOrderNo")
Session("Message") = GetQueryValue("Message")
Session("DeliveryWillCollect") = IntToBool(GetQueryValue("DeliveryWillCollect")) ' (SS,14/11/07)
Session("DeliveryAddressSameAsInvoice") = IntToBool(GetQueryValue("DeliveryAddressSameAsInvoice"))
Session("DeliveryName") = GetQueryValue("DeliveryName")
Session("DeliveryCompanyName") = GetQueryValue("DeliveryCompanyName")
Session("DeliveryAddress1") = GetQueryValue("DeliveryAddressLine1")
Session("DeliveryAddress2") = GetQueryValue("DeliveryAddressLine2")
Session("DeliveryTown") = GetQueryValue("DeliveryTown")
Session("DeliveryCounty") = GetQueryValue("DeliveryCounty")
Session("DeliveryPostcode") = GetQueryValue("DeliveryPostcode")
Session("DeliveryCountry") = GetQueryValue("DeliveryCountry")
Session("PaymentAmount") = FormatNumber(GetFieldValue("GrandTotal"), 2)
Session("VATNumber") = GetQueryValue("VATNumber") ' (SS,16/11/07)
Session("VoucherCode") = GetVoucherCodeForVoucherID(GetQueryValue("VoucherID")) ' (SS,22/4/09), (SS,19/6/09) added GetVoucherCodeForVoucherID and corrected field "VoucherCode" to "VoucherID"
If ChooseDeliveryDate Then Session("DeliveryDate") = GetQueryValue("DeliveryDate") ' (SS,22/4/09)
End If
CloseQuery
End Sub
Sub CheckOut
Dim LCurrentStage, LNextStage, LShowNextStageButton, LShowMakePaymentButton, LShowCheckoutFormFooter, LShowPlaceOrderOnAccountButton
' LCurrentStage = Request("stage") ' (SS,14/8/05) was CleanRequestForm("stage")
' (SS,21/2/14) replaced above with following, Form takes priority over querystring
' due to stage="so" being used for cheque/bank transfer payments in URL which also has stage = "ConfirmPayment"
' stage="so" used to give a different URL for AdWord purposes (requested by Clifton AdWord company)
LCurrentStage = CleanRequestForm("stage")
If LCurrentStage = "" Then LCurrentStage = CleanRequestQueryString("stage")
LNextStage = ""
LShowNextStageButton = True
LShowMakePaymentButton = False
LShowPlaceOrderOnAccountButton = False
LShowCheckoutFormFooter = True
' (SS,19/2/14) added following to allow different URL with querystring i.e. products.asp?cmd=checkout&xcmd=filled, for Google AdWord purposes requested by Clifton
' also only set when order no already created as an extra check, to prevent redirect loop
Dim LConfirmOrderRedirect
LConfirmOrderRedirect = CleanRequestQueryString("xcmd") = "filled" And GetOrderNo <> ""
' (SS,30/5/07) if start of checkout, not logged in and using new login method then show the login page
' (SS,27/5/09) added AccountsCompulsory
If LCurrentStage = "" And Not AccountLoggedIn And NewAccountMethod And AccountsCompulsory Then
DoAccount
Exit Sub
End If
' (SS,18/11/14)
If LCurrentStage = CS_PAYPAL_EXPRESS_CHECKOUT Then
' (SS,18/4/18) added If ReturnFromPayPalExpressLaterStage and oPayPalExpress True, to handle PayPal from later stage
' i.e. the fact that there's no redirect page to handle the payment page
If ReturnFromPayPalExpressLaterStage Then
DoPayPalExpress True
Else
DoPayPalExpress False
End If
' (SS,10/12/14) if cancelled in PayPal Express checkout then go to start of checkout
ElseIf LCurrentStage = CS_PAYPAL_EXPRESS_CANCEL Then
LCurrentStage = ""
ElseIf LCurrentStage = CS_PAYPAL_EXPRESS_RETURN Then
' *** may need to do some validation before doing the following
' (SS,16/12/14) if returned from PayPal when initiated at the later normal PayPal stage then we need to complete the order
'Response.Write "### Reached here: 0###<br>"
If ReturnFromPayPalExpressLaterStage Then
'Response.Write "### Reached here: 0.1###<br>"
LNextStage = COMPLETE_ORDER
Else ' if reached here from proper PayPal Express checkout
'Response.Write "### Reached here: 0.2###<br>"
If GetDetailsFromPayPalExpress Then
LNextStage = CONFIRM_ORDER
End If
End If
' if form details just entered then save them in session
' (SS,19/11/14) added Else to combine with above PayPal Express checkout
ElseIf LCurrentStage = GET_PERSONAL_DETAILS Then
' place form values in session '
If Not AccountLoggedIn Then ' only get these if not on account '
GetPersonalDetailsFromForm ' (SS,31/5/07) moved code to GetPersonalDetailsFromForm
End If
' (SS,3/10/16)
If IsLocalMode Then
GetLocalModeDetailsFromForm
End If
Session("DeliveryDate") = CleanRequestForm("DeliveryDate") ' (SS,17/4/09)
' (SS,18/4/12) added "CollectionOnlyOrder Or" to following because disabled DeliveryWillCollect checkbox doesn't return "checked" when checkbox is disabled
Session("DeliveryWillCollect") = CollectionOnlyOrder Or (CleanRequestForm("DeliveryWillCollect") = "checked") ' (SS,14/11/07)
Session("DeliveryAddToExistingOrder") = CleanRequestForm("DeliveryAddToExistingOrder") = "checked" ' (SS,31/3/17)
Session("DeliveryAddressSameAsInvoice") = (CleanRequestForm("DeliveryAddressSameAsInvoice") = "checked") ' (SS,5/6/07) we need to ensure we get this in case GetPersonalDetailsFromForm not called above
Session("VoucherCode") = UCase(Trim(CleanRequestForm("VoucherCode"))) ' (SS,22/04/09)
' (SS,17/3/09) added following to ensure this value is set to true when DeliveryAddressSameAsInvoice checkbox isn't shown
If Not AllowDifferentDeliveryAddress Then Session("DeliveryAddressSameAsInvoice") = True
Session("PurchaseOrderNo") = CleanRequestForm("PurchaseOrderNo")
' (SS,21/8/12) added Left(.., GetMaxMessageLength) to limit length to x characters, currently 600, form also has this limit (using truncation prevents hackers/spammers from posting very long strings)
Session("Message") = Left(CleanRequestForm("Message"), GetMaxMessageLength)
' (SS,31/3/17) if Session("DeliveryAddToExistingOrder") is true then prefix with special message, or remove it
' (SS,3/5/19) code moved to separate subs AddToExistingOrderAddMessage and AddToExistingOrderRemoveMessage
If GetDeliveryAddToExistingOrder Then
AddToExistingOrderAddMessage
Else ' remove it
AddToExistingOrderRemoveMessage
End If
' if delivery details just entered then save them in session '
' (SS,19/11/14) added Else and combined with above "If LCurrentStage = GET_PERSONAL_DETAILS"
ElseIf LCurrentStage = GET_DELIVERY_ADDRESS Then
' place form values in session '
GetDeliveryDetailsFromForm ' (SS,31/5/07) moved code to GetDeliveryDetailsFromForm
End If
' (SS,1/6/07) move default code to new routine, called from two places
SetCheckoutAccountDefaults
' (SS,3/8/04) added If And AccountsEnabled And Not AccountLoggedIn '
' (SS,27/5/09) added AccountsAdmin
' (SS,19/2/14) added If LConfirmOrderRedirect, to go straight to confirm because we're going via redirect to add xcmd=filled to querystring
If LConfirmOrderRedirect Then
LNextStage = CONFIRM_ORDER ' stay at same stage
ElseIf LCurrentStage = "" And AccountsEnabled And Not AccountLoggedIn And AccountsAdmin Then
LNextStage = GET_ACCOUNT_TYPE
ElseIf LCurrentStage = "" Or LCurrentStage = GET_ACCOUNT_TYPE Then ' (SS,3/8/04) added LStage = GET_ACCOUNT_TYPE '
' (SS,4/8/04) validate the Account details
If LCurrentStage = GET_ACCOUNT_TYPE Then
' check account details if account ID or password entered
' (SS,31/5/07) added CleanRequestForm("Email") = ""
' (SS,19/6/12) removed CleanRequestForm("AccountID") = ""
If CleanRequestForm("Email") = "" And CleanRequestForm("Password") = "" Then
LNextStage = GET_PERSONAL_DETAILS
Else
' (SS,28/5/09) added 3 parameters to CheckValidAccountDetails, because routine is also called from optional accounts
' (SS,19/6/12) removed parameter CleanRequestForm("AccountID"), no longer used
If CheckValidAccountDetails(CleanRequestForm("Email"), CleanRequestForm("Password")) Then
LNextStage = GET_PERSONAL_DETAILS
End If
End If
Else
LNextStage = GET_PERSONAL_DETAILS
End If
' if first name hasn't been entered then default the DeliveryAddressSameAsInvoice to checked '
' this seemed like the only simple way of doing this '
If Session("FirstName") = "" Then
Session("DeliveryAddressSameAsInvoice") = True
End If
' (SS,18/4/12) check if there are any collection only items by checking product attributes
' if there are then set for collection only, which means ticking and disabling "Don't charge postage, I will collect", and showing alert "Please be aware that this order is for collection only"
If HasCollectionOnlyInBasket Then
SetCollectionOnlyOrder True
Session("DeliveryWillCollect") = True
SetAlertMessage "Please be aware that this order is for collection only"
Else
SetCollectionOnlyOrder False
Session("DeliveryWillCollect") = False
End If
ElseIf LCurrentStage = GET_PERSONAL_DETAILS Then
' (SS,28/5/09) for optional accounts process the log in if email and password entered
If AccountsOptional And Not AccountLoggedIn And AccountOptionalLoginAttempt Then
If CheckValidAccountDetails(Session("LoginEmail"), Session("LoginPassword")) Then ' (SS,19/6/12) removed first parameter ""
' to normal personal details form so customer can check
LNextStage = GET_PERSONAL_DETAILS
Else
' show error message if incorrect login, happens to be the same stage as above
LNextStage = GET_PERSONAL_DETAILS
' (SS,2/10/17) used to show proceed without log in (bypass) only if account already exists
If AccountExists(Session("Email")) Then
gsErrorMessage = gsErrorMessage & BR & "If you have forgotten the password then click 'Forgot Password' below to reset it." & BR & GetCheckoutAccountBypassTextAndLink
End If
End If
' validate personal details if valid then go to next stage (Delivery Address or Confirmation) '
ElseIf CheckValidPersonalDetails Then
LNextStage = CONFIRM_ORDER ' (SS,27/7/05) was CONFIRM_DETAILS '
' (SS,22/4/09) if voucher code entered then make sure it's valid
If Session("VoucherCode") <> "" Then
Dim LVoucherErrorMessage
If Not CheckVoucherCode(Session("VoucherCode"), LVoucherErrorMessage) Then
ShowCheckoutErrorMessage(LVoucherErrorMessage)
SetAlertMessage(LVoucherErrorMessage)
LNextStage = GET_PERSONAL_DETAILS ' to stay in same form and show error message '
End If
End If
' (SS,3/5/19) if customer tick "Add To Existing Order" and doesn't have an existing order then show alert asking customer to uncheck
If GetDeliveryAddToExistingOrder Then
If Not CustomerHasExistingOrder(Session("Email")) Then
Dim LExistOrderErrorMessage
LExistOrderErrorMessage = "No existing order found." & BR & "'Don't charge delivery, add to existing order' has been unticked."
ShowCheckoutErrorMessage(LExistOrderErrorMessage)
SetAlertMessage(LExistOrderErrorMessage)
SetDeliveryAddToExistingOrder False ' untick it so user doesn't need to
LNextStage = GET_PERSONAL_DETAILS ' to stay in same form and show error message '
End If
End If
' if different delivery address chosen then next stage is to ask for the delivery address '
' (SS,22/4/09) added LNextStage = CONFIRM_ORDER because error invalid VoucherCode may have been entered
If LNextStage = CONFIRM_ORDER And AllowDifferentDeliveryAddress Then
If Not Session("DeliveryAddressSameAsInvoice") Then
LNextStage = GET_DELIVERY_ADDRESS
' default the delivery address values if DeliveryName is blank '
If Session("DeliveryName") = "" Then
Session("DeliveryName") = Trim(Session("Title") + " " + Session("FirstName") + " " + Session("Surname"))
' (SS,7/2/12) removed following, requested by Neil, customer had changed the delivery address but forgot to change the postcode, decided best to leave blank
'Session("DeliveryCompanyName") = Session("CompanyName")
'Session("DeliveryAddress1") = Session("Address1")
'Session("DeliveryAddress2") = Session("Address2")
'Session("DeliveryTown") = Session("Town")
'Session("DeliveryCounty") = Session("County")
'Session("DeliveryPostcode") = Session("Postcode")
' (SS,16/2/12) added following back in and added "if" to set the same country as invoice if not already blank
If Session("DeliveryCountry") = "" Then Session("DeliveryCountry") = Session("Country")
End If
End If
End If
Else
LNextStage = GET_PERSONAL_DETAILS ' to stay in same form and show error message '
End If
ElseIf LCurrentStage = GET_DELIVERY_ADDRESS Then
If CheckValidDeliveryAddress Then
LNextStage = CONFIRM_ORDER ' (SS,27/7/05) was CONFIRM_DETAILS '
Else
LNextStage = GET_DELIVERY_ADDRESS ' to stay in same form and show error message '
End If
' (SS,27/7/05) '
ElseIf LCurrentStage = CONFIRM_ORDER Then
' (SS,23/6/09) added "If AccountLoggedIn And IsPlaceOrderOnAccount Then..." because Payment on account had stopped work for toner2desk
If AccountLoggedIn And IsPlaceOrderOnAccount Then
LNextStage = COMPLETE_ORDER ' this will occur if payment on account method used '
End If
' (SS,14/8/05)
' (SS,21/2/14) moved here from above, i.e. make sure it's after "ElseIf LCurrentStage = CONFIRM_ORDER Then"
' because
ElseIf LCurrentStage = RETURN_FROM_THIRD_PARTY_PAYMENT Then
LNextStage = COMPLETE_ORDER
' (SS,29/5/09) for cheque or bank transfer payments
' (SS,10/12/14) also for PayPal Express
ElseIf LCurrentStage = CONFIRM_PAYMENT Then
' LNextStage = PAYMENT_CONFIRMED
' (SS,19/4/12) replaced above with following to bypass the confirm payment / comfirm order stage
LNextStage = COMPLETE_ORDER
End If
DeleteDisabledProductsFromBasket
' (SS,28/5/09) just before confirm order stage, if accounts optional and not logged in
If LNextStage = CONFIRM_ORDER And AccountsOptional And Not AccountLoggedIn Then
' if password entered then try to create an account
If Session("Password") <> "" Then
' don't process order if account couldn't be create i.e. account already exists for this customer (same email)
If CreateAccount(True, 0) Then
' get the AccountID into Session("AccountID") to use for order (function is highly unlikely to fail)
' (SS,19/6/12) removed first parameter 0, i.e. AccountID, no longer used
If Not GetAccountDetails(Session("Email"), Session("Password"), "") Then
LNextStage = GET_PERSONAL_DETAILS
End If
Else ' create account failed, go back to start of checkout showing error message
LNextStage = GET_PERSONAL_DETAILS
End If
End If
End If
ShowCheckoutHeader
ShowCheckoutFormHeader(LNextStage = CONFIRM_ORDER) ' (SS,4/6/09) added LNextStage parameter
' (SS,15/8/05) added LCurrentStage <> RETURN_FROM_THIRD_PARTY_PAYMENT
' to prevent empty basket from showing when return from PayPal etc
If IsBasketEmpty And LCurrentStage <> RETURN_FROM_THIRD_PARTY_PAYMENT Then
ShowEmptyBasket
LShowNextStageButton = False
ElseIf LNextStage = GET_ACCOUNT_TYPE Then
' If UseEmailAddressForLogin Then ' (SS,31/5/07) new login system
' DoAccountLogin
' Else ' old type of login as used by toner2desk
ShowCheckoutGetAccountType gsErrorMessage
' End If
ElseIf LNextStage = GET_PERSONAL_DETAILS Then
' (SS,3/5/19) remove the special message for adding to existing order in case it was added
' tidier not to show it on the message text box in case user comes back to this stage of the checkout
AddToExistingOrderRemoveMessage
ShowCheckoutPersonalDetailsEntryForm gsErrorMessage, AllowDifferentDeliveryAddress
' (SS,3/10/16) if local mode then show the addition feature
If IsLocalMode Then
ShowCheckoutLocalModeFeatures
End If
If LCurrentStage <> GET_PERSONAL_DETAILS Then ShowCheckoutAlertMessage ' (SS,23/10/08)
ElseIf LNextStage = GET_DELIVERY_ADDRESS Then
ShowCheckoutDeliveryAddressEntryForm gsErrorMessage
ElseIf LNextStage = CONFIRM_ORDER Then ' (SS,27/8/05) was CONFIRM_DETAILS
If ProcessOrder(FSessionID) Then ' (SS,18/2/04) added FSessionID parameter, (SS,14/9/06) added If Then
'ShowCheckoutPersonalDetailsConfirmation Session("OrderNoPlaced"), AccountLoggedIn, AllowDifferentDeliveryAddress
' (SS,19/4/18) added following message for PayPal Express return
' because the "Make PayPal Payment" button is shown and might be missed by buyer
If LCurrentStage = CS_PAYPAL_EXPRESS_RETURN Or InPayPalExpress Then
SetUpPayPalExpressConfirmButton
End If
ShowOrderDetailsMaster True, Session("OrderNoPlaced")
ShowOrderDetails True, Session("OrderNoPlaced")
ShowCheckoutMessage ' (SS,11/12/07)
If AccountLoggedIn And PaymentOnAccountEnabled Then
ShowCheckoutAccountsMessage
LShowPlaceOrderOnAccountButton = True
Else
If Not InPayPalExpress Then ' (SS,24/11/14) added If to only show if not in PayPal Express
ShowCheckoutSecurePaymentMessage ' was ShowCheckoutWorldPayMessage
End If
End If
ShowCheckoutFormFooter(LNextStage) ' must be done before setting up payment forms '
' (SS,26/1/16) moved call here from Sage section in SetUpPaymentButton, and added optional setting
' (SS,5/10/16) added Not IsLocalMode to not show in local mode
If CheckoutConfirmTermAndConditions And Not IsLocalMode Then
ShowCheckoutTermsAndConditionsCheckBox
End If
' (SS,20/11/14)
' (SS,24/11/14) added "Or InPayPalExpress" because of redirect
' (SS,22/1/15) added
If LCurrentStage = CS_PAYPAL_EXPRESS_RETURN Or InPayPalExpress Then
SetUpPayPalExpressConfirmButton
Else
' (SS,20/11/08) moved SetUpPayment to REDIRECT_TO_PAYMENT_SITE, added following here
SetUpPaymentButtons
End If
LShowNextStageButton = False ' (SS,26/7/05) because SetUpPayment above will show this button
LShowCheckoutFormFooter = False ' (SS,11/8/05) to prevent form footer from being show again
' EmailOrder(Session("OrderNoPlaced")) ' for test purposes &&&
' (SS,18/2/14) to do a redirect only to ensure the URL is unique i.e. with &xcmd=filled at the end (for Clifton's AdWord campaign)
' only done when it's not already in a redirect to prevent redirect loop
' (SS,18/4/16) moved here from below, because when ProcessOrder fails because basket not in stock it needs to redirect to basket page, and following is I think only applicable if ProcessOrder returns true
If Not LConfirmOrderRedirect Then
Session("PreviousCheckoutStage") = LCurrentStage ' (SS,24/11/14) to preserve within redirect to ?cmd=checkout&xcmd=filled, so that payment methods aren't shown
SetPageRedirection GetCheckoutConfirmOrderURL
Else
Session("PreviousCheckoutStage") = "" ' if not redirecting then reset the previous stage
End If
Else ' (SS,18/4/16) depending on reason for failure, a redirect to basket page may be done here if basket not fully in stock
LShowNextStageButton = False
End If
ElseIf LNextStage = COMPLETE_ORDER Then
' Response.Write "### Reached here: 0.1.1###<br>"
If AccountLoggedIn And IsPlaceOrderOnAccount Then ' process the order on account '
ProcessOrderOnAccount Session("OrderNoPlaced"), FSessionID
' (SS,5/10/07) removed following no longer applicable, probably never gets run
' we can now have more than one payment type so we won't know which one
' ElseIf IsPayPalPayment Or IsWorldPayPayment Or IsEPDQPayment Then
' ProcessOrderThirdPartyPayment False, Session("OrderNoPlaced"), FSessionID, ""
' (SS,21/3/13) if returning from successful payment from Sage Pay
ElseIf ReturnFromPaymentProvider(ptSagePay) Then
ProcessSagePayCallback
' (SS,26/4/18) EPDQ now works similar to Payzone and SagePay, callback done via the browser
ElseIf ReturnFromPaymentProvider(ptEPDQ) Then
ProcessEPDQRedirectCallback
' (SS,30/3/18)
ElseIf ReturnFromPaymentProvider(ptPayzone) Then
ProcessPayzoneCallback
' (SS,28/5/09) added following for cheque or bank transfer payments
' (SS,6/10/16) added IsLocalMode for local mode payments, also renamed called to ProcessOrderChequeOrBankTransferOrLocalModePayment
ElseIf IsChosenPaymentCheque Or IsChosenPaymentBankTransfer Or IsLocalMode Then
ProcessOrderChequeOrBankTransferOrLocalModePayment Session("OrderNoPlaced"), FSessionID
' (SS,10/12/14) PayPal Express
ElseIf IsChosenPaymentPayPalExpress Or ReturnFromPayPalExpressLaterStage Then
'Response.Write "### Reached here: 1###<br>"
ProcessOrderPayPalExpressPayment Session("OrderNoPlaced"), FSessionID
'Response.Write "### Reached here: 2###<br>"
' (SS,10/5/12) following run when return from third party payment provider
' noticed nothing shown for ePDQ, possibly other payments too, so added simple message, to be improved in future
ElseIf LCurrentStage = RETURN_FROM_THIRD_PARTY_PAYMENT Then
' Response.Write "Return from payment provider"
' (SS,15/5/12) for now, just say thank you for your order
' Response.Write "Thank you for your order."
' (SS,24/2/14) replaced thank you for your order with following to to show thank you message with order reference etc. If session has expired it will only show thank you
If GetOrderNo <> "" Then
' (SS,31/7/17) added following to help with payment debugging, previously when checking out, payment logged wasn't saved unless using PayPal expresscheckout from basket page
' (SS,23/8/17) added "Return" and removed True
AddToPaymentLog "Return", "Return from payment provider"
ShowOrderThankYou GetOrderNo, GetStoreName
Else
Response.Write "<p>Thank you for your order.</p>"
End If
End If
' (SS,1/3/13) order would be processed at this point, so abandon the session to delete all session variables, will also empty the basket in case it hasn't already via global.asa
' requested by Neil because he would like to get rid of previous details when entering orders on behalf of customers
' (SS,8/3/13) removed because it caused an alert issue, order was being added back to stock
' I think due to PayPal not being quick enough to do a callback, or our server couldn't be contacted
' happened on two occasions, returning to site before callback was the reason
' to get around this we could detect this and not add back to stock when session is ended
' which might also fix the issue itself because it can be marked as almost completed due to the URL used from payment provider.
' Session.Abandon
LShowNextStageButton = False
'Response.Write "### Reached here: 0.1.2###<br>"
End If
If LShowNextStageButton Then ShowCheckoutNextStageButton(GetCheckoutSubmitLink)
If LShowMakePaymentButton Then ShowCheckoutMakePaymentButton(GetCheckoutSubmitLink)
If LShowPlaceOrderOnAccountButton Then ShowCheckoutPlaceOrderOnAccountButton("javascript:itp_submit_option('OnAccount')")
If LShowCheckoutFormFooter Then ShowCheckoutFormFooter(LNextStage)
ShowCheckoutFooter
End Sub
' (SS,28/5/09)
Function GetCheckoutSubmitLink
'GetCheckoutSubmitLink = "javascript:document.frmCheckout.submit()"
' (SS,6/10/11) replaced above with following, to allow VAT number validation in javascript
GetCheckoutSubmitLink = "javascript:itp_checkout_submit()"
End Function
' (SS,23/10/08)
Sub ShowCheckoutAlertMessage
' (SS,31/7/09) now gets message from FCheckoutAlertMessage setting
If FCheckoutAlertMessage <> "" Then
SetAlertMessage FCheckoutAlertMessage
End If
End Sub
' (SS,4/0/17)
Function GetCheckoutAccountBypassTextAndLink
GetCheckoutAccountBypassTextAndLink = "Alternatively, click <a href=""" & GetCheckOutJavascriptLink("bypass") & """><u>here</u></a> to proceed without using your account."
End Function
' (SS,1/6/07)
Sub SetCheckoutAccountDefaults
' if country is blank then default it to the UK '
If Session("Country") = "" Then Session("Country") = GetDefaultCountry
' (SS,8/9/06) if Title is blank then default it to default
If Session("Title") = "" And GetDefaultTitle <> "" Then Session("Title") = GetDefaultTitle
' (SS,8/11/07) default for Subscribe now set to True
' (SS,22/5/18) default now changed to False due to GDPR
If Session("Subscribe") = "" Then Session("Subscribe") = False
End Sub
' (SS,31/5/07) was in CheckOut, moved here also used by account creation
' (SS,1/6/07) added Trim
' (SS,4/6/07) added Password & Password2
' (SS,23/6/10) added Proper and CorrectPostcode
' (SS,4/10/17) added clearing of password if bypass clicked
Sub GetPersonalDetailsFromForm
Session("Title") = CleanRequestForm("Title")
Session("FirstName") = Proper(Trim(CleanRequestForm("FirstName")))
Session("Surname") = Proper(Trim(CleanRequestForm("Surname")))
Session("CompanyName") = Proper(Trim(CleanRequestForm("CompanyName")))
Session("Address1") = Proper(Trim(CleanRequestForm("Address1")))
Session("Address2") = Proper(Trim(CleanRequestForm("Address2")))
Session("Town") = Proper(Trim(CleanRequestForm("Town")))
Session("County") = Proper(Trim(CleanRequestForm("County")))
Session("Postcode") = CorrectPostcode(Trim(CleanRequestForm("Postcode")), CleanRequestForm("Country"))
Session("Country") = CleanRequestForm("Country")
Session("Telephone") = Trim(CleanRequestForm("Telephone"))
Session("Mobile") = Trim(CleanRequestForm("Mobile")) ' (SS,17/4/09)
Session("Email") = Trim(CleanRequestForm("Email"))
Session("Password") = Trim(CleanRequestForm("Password"))
Session("Password2") = Trim(CleanRequestForm("Password2"))
' (SS,18/4/12) added "CollectionOnlyOrder Or" to following because disabled DeliveryWillCollect checkbox doesn't return "checked" when checkbox is disabled
Session("DeliveryWillCollect") = CollectionOnlyOrder Or (CleanRequestForm("DeliveryWillCollect") = "checked") ' (SS,14/11/07)
Session("DeliveryAddressSameAsInvoice") = (CleanRequestForm("DeliveryAddressSameAsInvoice") = "checked")
' (SS,1/6/07)
Session("Subscribe") = (CleanRequestForm("Subscribe") = "checked")
Session("HearAboutUs") = CleanRequestForm("HearAboutUs")
Session("VATNumber") = UCase(StripSpaces(CleanRequestForm("VATNumber"))) ' (SS,16/11/07) ' (SS,17/6/09) added UCase and changed Trim to StripSpaces
' (SS,28/5/09) added following for optional accounts
Session("LoginEmail") = Trim(CleanRequestForm("LoginEmail"))
Session("LoginPassword") = Trim(CleanRequestForm("LoginPassword"))
' (SS,4/10/17) added following to clear the password to prevent attempting to log in and bypass, bypass won't work if password filled in
If CleanRequestForm("options") = "bypass" Then
Session("Password") = ""
Session("Password2") = ""
End If
End Sub
' (SS,31/5/07) was in CheckOut, moved here also used by account creation
' (SS,1/6/07) added Trim
' (SS,7/6/07) removed Delivery region
' (SS,23/6/10) added Proper and CorrectPostcode
Sub GetDeliveryDetailsFromForm
Session("DeliveryName") = Proper(Trim(CleanRequestForm("DeliveryName")))
Session("DeliveryCompanyName") = Proper(Trim(CleanRequestForm("DeliveryCompanyName")))
Session("DeliveryAddress1") = Proper(Trim(CleanRequestForm("DeliveryAddress1")))
Session("DeliveryAddress2") = Proper(Trim(CleanRequestForm("DeliveryAddress2")))
Session("DeliveryTown") = Proper(Trim(CleanRequestForm("DeliveryTown")))
Session("DeliveryCounty") = Proper(Trim(CleanRequestForm("DeliveryCounty")))
Session("DeliveryPostcode") = CorrectPostcode(Trim(CleanRequestForm("DeliveryPostcode")), CleanRequestForm("DeliveryCountry"))
Session("DeliveryCountry") = CleanRequestForm("DeliveryCountry")
End Sub
' (SS,4/6/09) added ARedirectToPayment which is true for links that redirect to payment site
' add &xmcd=pay to the URL which can help in google analytic goals
' (SS,16/2/12) replaced products.asp with GetScriptName to allow more flexiblity for development purposes e.g. products-dev.asp to be used
' (SS,27/2/13) replaced &xcmd with &xcmd
' (SS,4/9/14) added class="form-horizontal" role="form" for Bootstrap
' (SS,8/9/14) removed quotes "" from second condition of IIf there were incorrectly showing, i.e. replaced """" with ""
Sub ShowCheckoutFormHeader(ARedirectToPayment)
%>
<form name=frmCheckout class="form-horizontal" role="form" method="post" action="<%=GetScriptName%>?cmd=checkout<%=IIf(ARedirectToPayment, "&xcmd=pay","")%>">
<%
End Sub
' (SS,20/11/08) added paymenttype form field
' (SS,29/11/09) added =GetChosenPaymentType to allow chosen payment type to be passed back for cheque payments
' (SS,6/10/16) added payment ref for local mode, also change made to javascript.js to pass on this value
' (SS,21/7/17) added banktransferoption to hold which bank transfer option chosen by user via modal (only for HyperFlight currency)
' (SS,28/9/18) removed banktransferoption, not required here, it's passed on via frmBankTransferPay (SetUpBankTransferPayForm)
' Also had to make a fix in javascript.js function itp_payment_banktransfer to refer to frmBankTransferPay (not frmCheckout which was a bug)
Sub ShowCheckoutFormFooter(AStage)
%>
<input type="hidden" name="stage" value="<%=AStage%>">
<input type="hidden" name="paymenttype" value="<%=GetChosenPaymentType%>">
<input type="hidden" name="options" value="<%=CleanRequestForm("options")%>">
<%If IsLocalMode Then%><input type="hidden" name="paymentref" value="<%=GetLocalModePaymentReference%>"><%End If%>
</form>
<%
End Sub
' (SS,28/5/09) added parameters
' (SS,19/6/12) removed AAccountID parameter, no longer applicable because only email is now used to log in, removed code no longer applicable
Function CheckValidAccountDetails(AEmail, APassword)
Dim strError
strError = ""
If AEmail = "" Then strError = strError + IIf(strError = "", "", ", ") + "Email"
If APassword = "" Then strError = strError + IIf(strError = "", "", ", ") + "Password"
If strError <> "" Then
strError = "Following fields must be filled in: " + strError + "."
gsErrorMessage = strError
CheckValidAccountDetails = False
Exit Function
End If
' validate the account ID and password and get address details '
' (SS,30/5/07) added CleanRequestForm("Email")
' (SS,19/6/12) removed AAccountID parameter, no longer applicable
If Not GetAccountDetails(AEmail, APassword, "") Then
gsErrorMessage = tr("Email and/or Password is incorrect")
CheckValidAccountDetails = False
Exit Function
End If
CheckValidAccountDetails = True
End Function
' (SS,4/8/04) check account id and get address details
' (SS,30/5/07) added AEmailAddress to allow login using email address
' this routine sets Session("AccountID") to the customer ID
' (SS,22/5/12) now allows login in with special password, for internal use
' (SS,19/6/12) removed ACustomerID parameter and "If UseEmailAddressForLogin", login using CustomerID and password has been removed, setting UseEmailAddressForLogin is not longer applicable
' (SS,20/9/12) added ACustomerID, when set and others are blank it overrides the normal log in, used by auto login
Function GetAccountDetails(AEmailAddress, APassword, ACustomerID)
GetAccountDetails = False
If AEmailAddress = "" And APassword = "" And ACustomerID = "" Then Exit Function ' (SS,1/6/07)
' (SS,20/9/12) if customer ID not provided then do as before
If ACustomerID = "" Then
' (SS,12/6/12) replaced APassword with LPassword
'OpenQuery("SELECT * FROM customers WHERE EmailAddress = '" & LEmailAddress & "' AND Password = '" + LPassword + "'")
' (SS,18/6/12) replaced above with following to check encrypted password
On Error Resume Next ' (SS,19/6/12) added to prevent showing of normal SQL error, because we don't want to expose the hash/encryption method in error message
OpenQuery("SELECT * FROM customers WHERE EmailAddress = '" & CleanSQLStr(Trim(AEmailAddress)) & "' AND Password = " & GetSQLForPasswordEncryption(APassword, False))
' (SS,19/6/12) check for error, unlikely to error occur, prevent exposure of SQL if it ever does
If Err.Number <> 0 Then
LogErrorToFile("Following error occurred while in GetAccountDetails: " & Err.Description)
Response.Write("Checking account details failed. Sorry for any inconvenience caused. Please contact us.")
Exit Function
End If
On Error GoTo 0 ' cancel the error handling
' (SS,22/5/12) added following for special log in using email address and special password, for internal use,
' password is first 5 letters of store name lowercase, 123 and first 3 letters of current day, e.g. hyper123tue
If EndOfQuery Then
If APassword = LCase(Left(GetStoreName, 5) & "123" & Left(WeekDayName(DatePart("w", Now())), 3)) Then
CloseQuery
OpenQuery("SELECT * FROM customers WHERE EmailAddress = '" & CleanSQLStr(Trim(AEmailAddress)) & "'")
End If
End If
Else ' (SS,20/9/12) else find using customer ID
OpenQuery("SELECT * FROM customers WHERE CustomerID = '" & CleanSQLStr(ACustomerID) & "'")
End If
If Not EndOfQuery Then
GetAccountDetails = True
' customer has successfully logged in '
' so set account ID and get the address details
' (SS,4/6/07) if new account method then get customer ID from the record, else as before use the customer id passed to this function
' (SS,19/6/12) removed If NewAccountMethod, because old method of using AccountID to log in no longer used (was only for Toner2Desk)
Session("AccountID") = GetQueryValue("CustomerID")
Session("Title") = GetQueryValue("Title")
Session("FirstName") = GetQueryValue("FirstName")
Session("Surname") = GetQueryValue("Surname")
Session("AccountName") = GetFullName(Session("Title"), Session("FirstName"), Session("Surname"))
Session("CompanyName") = GetQueryValue("CompanyName")
Session("Address1") = GetQueryValue("AddressLine1")
Session("Address2") = GetQueryValue("AddressLine2")
Session("Town") = GetQueryValue("Town")
Session("County") = GetQueryValue("County")
Session("Postcode") = GetQueryValue("Postcode")
Session("Country") = GetQueryValue("Country")
Session("Telephone") = GetQueryValue("Telephone")
Session("Mobile") = GetQueryValue("Mobile") ' (SS,17/4/09)
Session("Subscribe") = GetQueryValue("Subscribe") ' (SS,5/6/07)
Session("HearAboutUs") = GetQueryValue("HearAboutUs") ' (SS,5/6/07)
Session("Email") = GetQueryValue("EmailAddress")
' (SS,19/6/12) following removed because password is now hashed and encrypted
'Session("Password") = GetQueryValue("Password") ' (SS,5/6/07) ' (SS,18/6/12) *** was Password
'Session("Password2") = Session("Password") ' (SS,5/6/07)
' (SS,1/6/07) removed: "If AllowDifferentDeliveryAddress Then"
Session("DeliveryAddressSameAsInvoice") = IntToBool(GetQueryValue("DeliveryAddressSameAsInvoice") <> 0)
If Not Session("DeliveryAddressSameAsInvoice") Then
Session("DeliveryName") = GetQueryValue("DeliveryName")
Session("DeliveryCompanyName") = GetQueryValue("DeliveryCompanyName")
Session("DeliveryAddress1") = GetQueryValue("DeliveryAddressLine1")
Session("DeliveryAddress2") = GetQueryValue("DeliveryAddressLine2")
Session("DeliveryTown") = GetQueryValue("DeliveryTown")
Session("DeliveryCounty") = GetQueryValue("DeliveryCounty")
Session("DeliveryPostcode") = GetQueryValue("DeliveryPostcode")
Session("DeliveryCountry") = GetQueryValue("DeliveryCountry")
End If
' (SS,1/6/07)
Session("Subscribe") = IntToBool(GetQueryValue("Subscribe"))
Session("HearAboutUs") = GetQueryValue("HearAboutUs")
Session("VATNumber") = GetQueryValue("VATNumber") ' (SS,16/11/07)
End If
CloseQuery
End Function
' (SS,28/5/09) returns true if account exists for given email address
Function AccountExists(AEmailAddress)
' (SS,19/12/12) (SS,22/2/13) added AND Password <> '', now account is assumed to only exist if both email address and password have been entered
' if password is blank then customer hasn't officially created an account
AccountExists = GetValueFromQuery("Accounts", "SELECT COUNT(*) AS Accounts FROM customers WHERE EmailAddress = '" + CleanSQLStr(AEmailAddress) + "' AND Password <> ''") > 0
End Function
' (SS,19/3/10) added tr() for translation
Function CheckValidPersonalDetails()
Dim strError
strError = ""
If Session("Title") = "" Then strError = strError + IIf(strError = "", "", ", ") + tr("Title")
If Session("FirstName") = "" Then strError = strError + IIf(strError = "", "", ", ") + tr("First Name")
If Session("Surname") = "" Then strError = strError + IIf(strError = "", "", ", ") + tr("Surname")
If Session("Address1") = "" Then strError = strError + IIf(strError = "", "", ", ") + tr("Address Line 1")
If Session("Town") = "" Then strError = strError + IIf(strError = "", "", ", ") + tr("Town/City")
' (SS,13/2/08) removed forcing of postcode, because some countries don't have it e.g. Republic of Ireland
' (SS,12/11/08) added back in
If Session("Postcode") = "" Then strError = strError + IIf(strError = "", "", ", ") + tr("Postcode/Zip")
If Session("Country") = "" Then strError = strError + IIf(strError = "", "", ", ") + tr("Country")
If Session("Telephone") = "" Then strError = strError + IIf(strError = "", "", ", ") + tr("Telephone")
If Session("Email") = "" Then strError = strError + IIf(strError = "", "", ", ") + tr("Email Address")
If strError <> "" Then strError = tr("Following fields must be filled in") + ": " + strError + "."
' check that email and confirm email matches
' (SS,28/5/09) email confirm removed, now calls IsValidEmailAddress
If strError = "" And Not IsValidEmailAddress(Session("Email")) Then strError = tr("Valid Email address must be entered") + "."
' (SS,30/11/07) make sure Country exists in countries table
' added to prevent order when Country has since been removed/disabled from Countries table and accounts are used
' this occured when Republic of Ireland was removed from gsmailorder.co.uk
If strError = "" Then
If Not IsAllowedCountry(Session("Country")) Then strError = trx("Country <x> not allowed", Session("Country"))
End If
' (SS,17/6/09) validate VATNumber
If strError = "" And Session("VATNumber") <> "" Then
If FunctionExists("CustomValidVATNumber") Then
If Not CustomValidVATNumber(Session("VATNumber")) Then
strError = tr("VAT Number must be in the correct format prefixed by the country code")
End If
Else
' (SS,15/5/10) added following new method of validating VATNumber, eventually CustomValidVATNumber above will be removed
' (SS,11/8/16) renamed IsVATNumberValid to IsVATNumberFormatValid
If Not IsVATNumberFormatValid(Session("Country"), Session("VATNumber")) Then
strError = tr("VAT Number must be in the correct format prefixed by the country code")
strError = strError + ", " + GetVATNumberExample + "." ' (SS,25/5/10)
Else ' (SS,10/8/16) validate number using external website, strError is set if error
If CheckVATNumber(Session("VATNumber"), strError) Then
' nothing to do here because strError used later to alert error message
End If
End If
End If
End If
gsErrorMessage = strError
CheckValidPersonalDetails = (strError = "")
End Function
' (SS,4/8/04)
' (SS,19/3/10) added tr() for translation
Function CheckValidDeliveryAddress()
Dim strError
strError = ""
' (SS,5/6/07) added "Delivery " to strError
If Session("DeliveryName") = "" Then strError = strError + IIf(strError = "", "", ", ") + tr("Delivery Name")
If Session("DeliveryAddress1") = "" Then strError = strError + IIf(strError = "", "", ", ") + tr("Delivery Address Line 1")
If Session("DeliveryTown") = "" Then strError = strError + IIf(strError = "", "", ", ") + tr("Delivery Town/City")
' (SS,13/2/08) removed forcing of postcode, because some countries don't have it e.g. Republic of Ireland
' (SS,12/11/08) added back in
If Session("DeliveryPostcode") = "" Then strError = strError + IIf(strError = "", "", ", ") + tr("Delivery Postcode/Zip")
If Session("DeliveryCountry") = "" Then strError = strError + IIf(strError = "", "", ", ") + tr("Delivery Country")
' (SS,30/11/07) make sure Country exists in countries table
' added to prevent order when Country has since been removed/disabled from Countries table and accounts are used
' this occured when Republic of Ireland was removed from gsmailorder.co.uk
If strError = "" Then
' (SS,19/3/10) added trx, also corrected Session("Country") to Session("DeliveryCountry") after not allowed
If Not IsAllowedCountry(Session("DeliveryCountry")) Then strError = trx("Country <x> not allowed", Session("DeliveryCountry"))
End If
If strError <> "" Then strError = "Following fields must be filled in: " + strError + "."
gsErrorMessage = strError
CheckValidDeliveryAddress = (strError = "")
End Function
Function GetFullName(ATitle, AFirstName, ASurname)
Dim LFullName
LFullName = ""
If ATitle <> "" Then LFullName = ATitle
If AFirstName <> "" Then LFullName = LFullName + Iif(LFullName = "", "", " ") + AFirstName
If ASurname <> "" Then LFullName = LFullName + Iif(LFullName = "", "", " ") + ASurname
GetFullName = LFullName
End Function
' (SS,10/8/05)
Function GetBillingAddress()
Dim LBillingAddress
LBillingAddress = ""
If Session("CompanyName") <> "" Then LBillingAddress = LBillingAddress + Session("CompanyName") + NL
If Session("Address1") <> "" Then LBillingAddress = LBillingAddress + Session("Address1") + NL
If Session("Address2") <> "" Then LBillingAddress = LBillingAddress + Session("Address2") + NL
If Session("Town") <> "" Then LBillingAddress = LBillingAddress + Session("Town") + NL
If Session("County") <> "" Then LBillingAddress = LBillingAddress + Session("County") + NL
If Session("Postcode") <> "" Then LBillingAddress = LBillingAddress + Session("Postcode") + NL
If Session("Country") <> "" Then LBillingAddress = LBillingAddress + Session("Country") + NL
GetBillingAddress = Trim(LBillingAddress)
End Function
' (SS,10/8/05) calls GetBillingAddress above but replaces newlines with <br>s
Function GetBillingAddressForHTML()
GetBillingAddressForHTML = ConvertNewlinesEtcToHTML(GetBillingAddress) ' (SS,17/5/11) changed from ConvertNewlinesToHTML to ConvertNewlinesEtcToHTML for quotes and pounds to show correctly
End Function
' (SS,14/8/05)
Function GetAddress(AName, ACompanyName, AAddress1, AAddress2, ATown, ACounty, APostcode, ACountry)
Dim LAddress
LAddress = ""
If AName <> "" Then LAddress = LAddress + AName + NL
If ACompanyName <> "" Then LAddress = LAddress + ACompanyName + NL
If AAddress1 <> "" Then LAddress = LAddress + AAddress1 + NL
If AAddress2 <> "" Then LAddress = LAddress + AAddress2 + NL
If ATown <> "" Then LAddress = LAddress + ATown + NL
If ACounty <> "" Then LAddress = LAddress + ACounty + NL
If APostcode <> "" Then LAddress = LAddress + APostcode + NL
If ACountry <> "" Then LAddress = LAddress + ACountry + NL
GetAddress = Trim(LAddress)
End Function
' (SS,14/8/05)
Function GetAddressForHTML(AName, ACompanyName, AAddress1, AAddress2, ATown, ACounty, APostcode, ACountry)
GetAddressForHTML = ConvertNewlinesEtcToHTML(GetAddress(AName, ACompanyName, AAddress1, AAddress2, ATown, ACounty, APostcode, ACountry)) ' (SS,17/5/11) changed from ConvertNewlinesToHTML to ConvertNewlinesEtcToHTML for quotes and pounds to show correctly
End Function
' returns link to be used for checkout page '
' (SS,20/2/12) replaced products.asp with GetScriptName
Function GetCheckoutLink()
GetCheckoutLink = GetScriptName + "?cmd=checkout"
End Function
' (SS,3/6/09) allows bypass for extra check for account already existing, i.e. checkout without account even though you already have one
Function GetCheckoutJavascriptLink(AValue)
GetCheckoutJavascriptLink = "javascript:itp_checkout('" & AValue & "')"
End Function
' (SS,17/1/13) we're now using this to show basket first rather than going straight to checkout, basket page has the actual checkout button via proceed to checkout
Function GetBasketLink()
GetBasketLink = GetScriptName + "?cmd=basket"
End Function
' returns link to be used to continue shopping from basket '
Function GetContinueLink()
GetContinueLink = "javascript:itp_continue('" & strSearch & "','" & strPage & "','" & strCategory & "','" & strSubcategory & "')"
End Function
' (SS,30/5/07) move here from inc-template-checkcount.asp, also called from inc-template-account.asp
Function GetTitleCombo(AEnabled)
' (SS,16/7/09) added "" new parameter, change was made to GetLookupCombo in dbfunctions.asp
GetTitleCombo = GetLookupCombo("Titles", "Title", "SortOrder", Session("Title"), True, "", AEnabled, "", "")
End Function
' (SS,15/9/06) renamed from GetCountryLookupCombo to GetCountryCombo
' (SS,11/9/07) now uses countries only (removed "SELECT DISTINCT Country FROM deliveryregions WHERE Country IS NOT NULL ORDER BY Country")
' (SS,19/3/10) added tr() for translation
' (SS,16/2/12) discovered a bug which resulted in DeliveryCountry defaulting to country held in Session("Country") rather than Session("DeliveryCountry")
' fixed by replacing Session("Country") with Session(ASelectName)
Function GetCountryCombo(ASelectName, AExtras)
' (SS,17/7/07) changed False in following to "--Please select--"
' (SS,16/7/09) added "" new parameter, change was made to GetLookupCombo in dbfunctions.asp
' (SS,16/2/12) replaced Session("Country") with Session(ASelectName)
GetCountryCombo = GetLookupCombo("countries", "Country", "SortOrder, Country", Session(ASelectName), tr("Please select..."), ASelectName, AExtras, GetCriteriaForCountryCombo, "")
End Function
' (SS,14/8/05) returns filter criteria to use for country combo
' (SS,17/4/18) modified so that it's now only based on Enabled, WorldPay, PayPal and DirectPay field are not relevant anymore
' because we also have more than one payment method and SagePay and Payzone
Function GetCriteriaForCountryCombo
' (SS,17/4/18) commented out following redundant code
'If IsPayPalPayment Then
' GetCriteriaForCountryCombo = "Enabled='Yes' AND PayPal='Yes'"
' (SS,7/9/06) added IsEPDQPayment
'ElseIf IsWorldPayPayment Or IsEPDQPayment Then
' GetCriteriaForCountryCombo = "Enabled='Yes' AND WorldPay='Yes'"
'Else
' GetCriteriaForCountryCombo = "Enabled='Yes' AND DirectPay='Yes'"
'End If
' (SS,17/4/18) replaced above with following
GetCriteriaForCountryCombo = "Enabled='Yes'"
End Function
' (SS,9/6/14)
' (SS,20/6/17) modified to add allow a customised version held in inc-template-currency.asp
' if ASelectName = "" then call the show currency routines in inc-template-currency.asp
Function GetCurrencyCombo(ASelectName, AExtras)
' (SS,20/6/17) as before
If ASelectName <> "" Then
' (SS,10/6/14) added SortOrder IS NULL so that null values appear after non-null values
GetCurrencyCombo = GetLookupCombo("currencies", "CurrencyName", "SortOrder IS NULL, SortOrder, Currency", Session(ASelectName), tr("Please select..."), ASelectName, AExtras, "", "Currency")
Else
Dim LSQL, LCurrencyCode, LCurrencyName, LIsSelected
LSQL = "SELECT * FROM currencies ORDER BY SortOrder IS NULL, SortOrder, Currency"
ShowCurrencyComboHeader(Session("CurrencyCode"))
OpenQuery(LSQL)
Do While Not EndOfQuery
LCurrencyCode = GetQueryValue("Currency")
LCurrencyName = GetQueryValue("CurrencyName")
LIsSelected = LCurrencyCode = Session("CurrencyCode")
ShowCurrencyComboItem LCurrencyCode, LCurrencyName, LIsSelected
NextQueryRecord
Loop
CloseQuery
ShowCurrencyComboFooter
GetCurrencyCombo = "" ' return nothing, not need because above functions do the rendering
End If
End Function
Function GetHearAboutUsCombo
' (SS,17/7/07) added dashes
' (SS,19/3/10) added tr() for translation
' (SS,6/9/14) changed default i.e. placeholder from tr("Please select...") to "» " & tr("Select how you heard about us")
GetHearAboutUsCombo = GetListCombo("HearAboutUs", FHearAboutUsList, Session("HearAboutUs"), "» " & tr("Select how you heard about us"))
End Function
' (SS,17/4/09) to pass to ShowOrderDetailsMaster in inc-template-checkout.asp
Function GetMobile
GetMobile = Session("Mobile")
End Function
' (SS,17/4/09) to pass to ShowOrderDetailsMaster in inc-template-checkout.asp
Function GetDeliveryDate
GetDeliveryDate = Session("DeliveryDate")
End Function
' (SS,14/8/05)
Sub ShowOrderDetailsMaster(AInCheckout, AOrderNo)
' (SS,12/5/10) replaced '%D %M %Y' with GetLanguageDateFormat(False)
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQuery("SELECT *, DATE_FORMAT(DateTimeOrdered, '" & GetLanguageDateFormat(False) & "') AS DateOfOrderLong FROM orders WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'")
If Not EndOfQuery Then
' (SS,1/8/17) if not in checkout then we're displaying old orders, so get the currency code and exchange rate from this order
' fixes issue where old non-GBP orders would shown in current exchange rate and currency code
If Not AInCheckout Then
SetCurrencyForOrder GetQueryValue("CurrencyCode"), GetQueryValue("ExchangeRate")
End If
Dim LDateOfOrder, LStatus, LAccountID, LFullName, LDeliveryAddressSameAsInvoice, LBillingAddress, LDeliveryAddress, LTelephone, LEmailAddress, LPurchaseOrderNo, LMessage
LDateOfOrder = GetQueryValue("DateOfOrderLong")
LStatus = GetQueryValue("Status")
LAccountID = GetQueryValue("CustomerID")
LDeliveryAddressSameAsInvoice = IntToBool(GetQueryValue("DeliveryAddressSameAsInvoice"))
LBillingAddress = GetAddressForHTML(GetFullName(GetQueryValue("Title"), GetQueryValue("FirstName"), GetQueryValue("Surname")), GetQueryValue("CompanyName"), GetQueryValue("AddressLine1"), GetQueryValue("AddressLine2"), GetQueryValue("Town"), GetQueryValue("County"), GetQueryValue("Postcode"), GetQueryValue("Country"))
LDeliveryAddress = GetAddressForHTML(GetQueryValue("DeliveryName"), GetQueryValue("DeliveryCompanyName"), GetQueryValue("DeliveryAddressLine1"), GetQueryValue("DeliveryAddressLine2"), GetQueryValue("DeliveryTown"), GetQueryValue("DeliveryCounty"), GetQueryValue("DeliveryPostcode"), GetQueryValue("DeliveryCountry"))
LTelephone = GetQueryValue("Telephone")
LEmailAddress = GetQueryValue("EmailAddress")
LPurchaseOrderNo = GetQueryValue("PurchaseOrderNo")
LMessage = ReplaceStr(GetQueryValue("Message"), Chr(13) + Chr(10), "<br>")
' (SS,3/6/09)
Dim LPaymentMethod, LPaymentReference, LCourier, LTrackingNo, LMessageToCustomer
LPaymentMethod = GetQueryValue("PaymentMethod")
LPaymentReference = GetQueryValue("PaymentReference")
LCourier = GetQueryValue("Courier")
LTrackingNo = GetQueryValue("TrackingNo")
LMessageToCustomer = GetQueryValue("MessageToCustomer")
' (SS,3/6/09) added LPaymentMethod, LPaymentReference, LCourier, LTrackingNo, LMessageToCustomer
' (SS,8/3/13) added StatusForCustomer to convert COMPLETED to DESPATCHED
ShowCheckoutOrderDetailsMaster AInCheckout, AOrderNo, LDateOfOrder, StatusForCustomer(LStatus), LAccountID, LDeliveryAddressSameAsInvoice, LBillingAddress, LDeliveryAddress, LTelephone, LEmailAddress, LPurchaseOrderNo, LMessage, LPaymentMethod, LPaymentReference, LCourier, LTrackingNo, LMessageToCustomer
End If
CloseQuery
End Sub
' (SS,1/8/17) modified to fix issue where orders shown in accounts was always show in current currency and exchange rate
' fixed by checking for AInCheckout not True and setting the currency code, exchange rate and symbol from the original order, then restoring it later
Sub ShowOrderDetails(AInCheckout, AOrderNo)
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
' (SS,31/10/18) added GetOrderDetailsFilter to filter out subproducts
OpenQuery("SELECT * FROM orderdetails WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'" & GetOrderDetailsFilter & " ORDER BY OrderDetailID")
ShowCheckoutOrderDetailsHeader AInCheckout
' (SS,23/4/09) added tp_Discount
Dim tp_ProductCode, tp_ProductName, tp_Price, tp_Qty, tp_ProductTotal, tp_Subtotal, tp_Discount
Dim tp_VATIncluded, tp_VATDeducted, tp_Delivery, tp_GrandTotal
Dim LCount, LPrice, LQty, LPaymentDesc
LCount = 0
LPaymentDesc = ""
Do While Not EndOfQuery
LCount = LCount + 1
tp_ProductCode = GetFieldValue("ProductCode")
LQty = GetFieldValue("Qty")
tp_Qty = LQty
tp_ProductName = GetFieldValue("ProductName")
LPrice = GetFieldValue("PriceEach")
tp_price = CorrectCurrencyNV(LPrice) ' (SS,1/11/12) changed CorrectCurrency to CorrectCurrencyNV
tp_ProductTotal = CorrectCurrencyNV(CDbl(LPrice) * CInt(LQty)) ' (SS,1/11/12) changed CorrectCurrency to CorrectCurrencyNV
' (SS,6/7/11) added OptionsList
ShowCheckoutOrderDetailsItem AInCheckout, tp_ProductCode, tp_ProductName, GetFieldValue("OptionsList"), tp_Qty, tp_price, tp_ProductTotal
LPaymentDesc = LPaymentDesc + iif(LPaymentDesc = "", "", ", ") + tp_ProductCode + " x " & tp_Qty
NextQueryRecord
Loop
CloseQuery
' get the total values from orders
' OpenQuery("SELECT * FROM orders WHERE OrderNo = " & AOrderNo)
' (SS,23/4/09) replaced above with following to also lookup voucher code, although I could get it instead from the session variable
OpenQuery("SELECT orders.*, vouchers.VoucherCode FROM orders LEFT JOIN vouchers ON vouchers.VoucherID = orders.VoucherID WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'")
DIM LVATIncluded, LVATDeducted, LVoucherCode
tp_Subtotal = CorrectCurrencyNV(GetFieldValue("Subtotal")) ' (SS,1/11/12) changed CorrectCurrency to CorrectCurrencyNV
tp_Discount = CorrectCurrencyNV(GetFieldValue("Discount")) ' (SS,23/4/09) ' (SS,1/11/12) changed CorrectCurrency to CorrectCurrencyNV
tp_Delivery = CorrectCurrencyNV(GetFieldValue("Delivery")) ' (SS,1/11/12) changed CorrectCurrency to CorrectCurrencyNV
LVATIncluded = NZ(GetFieldValue("VATIncluded"))
LVATDeducted = NZ(GetFieldValue("VATDeducted"))
tp_VATIncluded = CorrectCurrency(LVATIncluded)
tp_VATDeducted = CorrectCurrency(LVATDeducted)
tp_GrandTotal = CorrectCurrency(GetFieldValue("GrandTotal"))
LVoucherCode = GetFieldValue("VoucherCode") ' (SS,23/4/09)
If NZD(GetFieldValue("Discount")) = 0 Then LVoucherCode = LVoucherCode + " - not valid" ' (SS,8/5/09) as requested by Neil
Session("PaymentDescription") = LPaymentDesc
Session("PaymentAmount") = Round2dp(CorrectCurrencyNF(GetFieldValue("GrandTotal")))
Session("PaymentAmountGBP") = GetFieldValue("GrandTotal") ' (SS,17/2/12) payment in GBP pounds, for passing on to payment providers only dealing with GBP when multi-currency used
Session("PaymentAmountFormatted") = CorrectCurrency(GetFieldValue("GrandTotal")) ' (SS,17/2/12) formatted and with currency symbol, for displaying on cheque/bank transfer info
Session("BankTransferPaymentLine") = GetBankTransferPaymentLine ' (SS,19/12/14) payment amounts shown in bank transfer details
Session("BankTransferDetails") = GetBankTransferDetailsAsHTML ' (SS,19/12/14) bank transfer details to be shown
' (SS,16/11/07) added following to show intra-community supply message
Dim LExtraMessage
If IntToBool(GetFieldValue("IntraCommunitySupply")) Then
LExtraMessage = tr(INTRA_COMMUNITY_SUPPLY_MESSAGE) ' (SS,19/3/10) added tr
Else
LExtraMessage = ""
End If
' (SS,4/6/09) show message to customer, i.e. out of stock message
Dim LMessageToCustomer
LMessageToCustomer = ReplaceNewLinesWithBR(GetFieldValue("MessageToCustomer"))
SetAlertMessage LMessageToCustomer
' (SS,9/10/14) to pass on delivery into to ShowCheckoutOrderDetailsFooter routine
SetDeliveryInfo GetFieldValue("DeliveryInfo")
CloseQuery
' (SS,23/4/09) added LVoucherCode and tp_Discount
ShowCheckoutOrderDetailsSubtotal tp_Subtotal, LVoucherCode, tp_Discount, tp_Delivery
' (SS,28/5/07) VAT totals now only shown if VAT Registered
' function ShowCheckoutOrderDetailsVAT is called if it exists, may not exist in old version of inc-template-checkout.asp
If FunctionExists("ShowCheckoutOrderDetailsVAT") Then
SetVATLabel(LVATDeducted > 0)
' (SS,1/11/12) added "If LVATIncluded > 0 Then" so that VAT Deducted is no longer shown because VAT has now been removed from values shown above, to prevent customer confusion
If LVATIncluded > 0 Then
ShowCheckoutOrderDetailsVAT GetVATLabel, IIf(LVATIncluded > 0, tp_VATIncluded, tp_VATDeducted)
End If
Else ' old version
If LVATIncluded > 0 Then ShowCheckoutOrderDetailsVATIncluded tp_VATIncluded
' (SS,1/11/12) removed following VAT Deducted no longer shown because VAT has now been removed from values shown above, to prevent customer confusion
'If LVATDeducted > 0 Then ShowCheckoutOrderDetailsVATDeducted tp_VATDeducted
End If
ShowCheckoutOrderDetailsGrandTotal tp_GrandTotal
' (SS,14/6/07) added AInCheckout and AOrderNo parameters
' (SS,16/11/07) added message parameter
' (SS,4/6/09) added LMessageToCustomer parameter
ShowCheckoutOrderDetailsFooter AInCheckout, AOrderNo, LExtraMessage, LMessageToCustomer
End Sub
' (SS,18/2/04) added basket session ID parameter because now called from secure site with different session id '
' (SS,25/6/10) changed ProcessOrder variable to Result to avoid confusion/recursion
' (SS,7/3/13) added code to send an alert email if there's a failure while processing order, also removed contractprices reference
' (SS,2/11/16) fix to return False when there's a failure, e.g. delivery couldn't be calculated
Function ProcessOrder(ABasketSessionID)
Dim Result, LErrorMsg, LBody ' (SS,7/3/13) added LErrorMsg, LBody to help with alert email
Result = True
' following is a bit like a try and finally in delphi
' if ProcessTheOrder fails the tables will still get unlocked
On Error Resume Next
'gbDebugOn = True
' lock the tables
' (SS,5/8/04) added "contractprices READ" because GetProductPrice now used which may use contractprices table
' (SS,14/9/06) added "deliveryregions READ, deliverycosts READ" now used by delivery calculation
' (SS,21/5/07) now uses LSQL and contractprices only added if enabled
' (SS,23/4/09) added vouchers WRITE
' (SS,8/5/09) added productcategories READ
' (SS,13/5/09) added voucherproducts READ
' (SS,25/6/10) added suppliers READ
' (SS,6/7/11) added shopping_basket_options READ and product_option_values WRITE for calculating options price and weight, and updating stock
' (SS,6/7/11) order_detail_options WRITE for adding options to order
' (SS,5/10/11) added product_attributes READ, attributes READ, due to new GetAttributeInBasketCount which is used by delivery calc i.e. covdem
' (SS,8/9/14) added product_options READ, due to new function ProductOptionExists used by castironradcen
' (SS,18/12/14) added currencies READ due to default payment currency code and rate lookup, used by hyperflight
Dim LSQL
LSQL = "LOCK TABLES settings WRITE, products WRITE, shoppingbaskets WRITE, orders WRITE, orderdetails WRITE, countries READ, deliveryregions READ, deliverycosts READ, " &_
"vouchers WRITE, productcategories READ, voucherproducts READ, suppliers READ, " &_
"shopping_basket_options READ, product_option_values WRITE, order_detail_options WRITE, " &_
"product_attributes READ, attributes READ, product_options READ, currencies READ"
' (SS,16/1/15) added following, table used when UseDeliveryPostcodes setting is true
If UseDeliveryPostcodes Then LSQL = LSQL & ", delivery_postcodes READ"
ExecuteQuery(LSQL)
' (SS,18/4/16)
' Result = False ' (SS,2/11/16) redundant
LErrorMsg = ""
Result = TryProcessOrder(ABasketSessionID) ' (SS,18/4/16) TryProcessOrder is now a function, returns False when basket not in stock
If Err.Number <> 0 Then
' (SS,25/6/10) changed DebugLog to LogErrorToFile
LErrorMsg = "Following error occurred while processing the order: " & Err.Description
Result = False ' (SS,2/11/16) because TryProcessOrder will have returned True and this is an error condition which should return False
' (SS,22/10/08) if delivery calculation failed then don't progress any further, alert message will be shown to user
ElseIf FDeliveryCalculationFailure Then
LErrorMsg = "Delivery Calculation Failure occurred while processing the order"
Result = False ' (SS,2/11/16) because TryProcessOrder will have returned True and this is an error condition which should return False
End If
' unlock the tables
ExecuteQuery("UNLOCK TABLES")
' (SS,22/10/08) added following error message
' (SS,19/4/16) replaced Not Result with LErrorMsg <> "" because False can be returned now when basket not in stock
If LErrorMsg <> "" Then
LogErrorToFile(LErrorMsg)
ShowCheckoutErrorMessage("Processing of order failed. Sorry for any inconvenience caused. Please contact us.")
' (SS,7/3/13) added emailing of error message for alert purposes, together with session variables and basket
LBody = LErrorMsg & NL
LBody = LBody & NL & "SessionID = " & ABasketSessionID
LBody = LBody & NL & "IPAddress = " & Request.ServerVariables("REMOTE_ADDR")
LBody = LBody & NL & "DateTimeOrdered = " & Now
LBody = LBody & NL & SessionVarList("OrderNoPlaced,AccountID,Title,FirstName,Surname,CompanyName,Address1,Address2,Town,County,Postcode,Country,VATNumber,Telephone,Email,VoucherCode,Subscribe,HearAboutUs" &_
",Message,DeliveryDate,DeliveryWillCollect,DeliveryAddressSameAsInvoice,DeliveryName,DeliveryCompanyName,DeliveryAddress1,DeliveryAddress2,DeliveryTown,DeliveryCounty,DeliveryPostcode,DeliveryCountry")
' items in basket
LBody = LBody & NL & "Basket:"
' (SS,26/10/18) added False parameter to GetOrderItemsQuery to exclude subproducts
OpenQuery(GetOrderItemsQuery(ABasketSessionID, False))
Do While Not EndOfQuery
LBody = LBody & NL & GetQueryValue("Qty") & ", " & GetQueryValue("ProductCode") & ", " & GetQueryValue("ProductName")
NextQueryRecord
Loop
CloseQuery
SendAlertEmail "Failure while processing order", LBody, False
End If
ProcessOrder = Result
End Function
' (SS,15/6/07) moved this code from TryProcessOrder to here, used elsewhere too
Sub ClearDeliveryDetailsIfSameAsInvoice
' If delivery address same as invoice then clear the delivery address values
If Session("DeliveryAddressSameAsInvoice") Then
Session("DeliveryName") = ""
Session("DeliveryCompanyName") = ""
Session("DeliveryAddress1") = ""
Session("DeliveryAddress2") = ""
Session("DeliveryTown") = ""
Session("DeliveryCounty") = ""
Session("DeliveryPostcode") = ""
Session("DeliveryCountry") = ""
End If
End Sub
' (SS,18/2/04) added basket session ID parameter because now called from secure site with different session id '
' (SS,6/10/11) added NoteVATValidation
' (SS,4/3/15) fixed issue where delivery price not calculated as on basket page, due to missing call to CustomProductInBasket
' (SS,12/4/16) changes to call ClearExistingOrderPlaced
' (SS,18/4/16) added CheckBasketInStock to check basket in stock, also changed from Sub to Function to return False if early exit due to basket not in stock
' (SS,20/4/16) slight change to getting next order no
' (SS,1/12/16) added code to always set priority and get it from CustomGetPriority
' (SS,18/4/18) change to preserve payment log which was being lost when this routine called again for same order
' (SS,29/10/18) change to allow new SubproductOrderDetailID field to set for products that are subproducts
Function TryProcessOrder(ABasketSessionID)
TryProcessOrder = False ' (SS,18/4/16) now a function
' (SS,12/4/16) code from here moved to separate routine which is called elsewhere, add stock previous taken from stock back to stock and delete the existing order detail records
' (SS,18/4/16) this must be called before CheckBasketInStock below
ClearExistingOrderPlaced True ' True to make sure order detail records are deleted
' (SS,18/4/16) make sure products in basket are in stock, exit sub and redirect to basket page if not in stock
If Not CheckBasketInStock(False) Then
SetPageRedirection GetBasketPageURL
Exit Function ' False is returned
End If
Dim LOrderNo, LSQL
'LOrderNo = GetOrderNo
'' if no order no from above then get next available one, else reuse it
'If LOrderNo = "" Then
' LOrderNo = GetNextOrderNo
'End If
' (SS,20/4/16) replaced above with following because GetNextOrderNo will try to reuse the same OrderNo if possible until order processed (i.e. email sent)
' i.e. it will either return the same order no or next available one
LOrderNo = GetNextOrderNo
ClearDeliveryDetailsIfSameAsInvoice ' (SS,15/6/07) placed code here into separate sub
' create the order header record, replacing if it already exists
' (SS,19/2/04) added new DeliveryName & Address and Credit Card fields
Dim LDeliveryAddressSameAsInvoice
If Session("DeliveryAddressSameAsInvoice") Then
LDeliveryAddressSameAsInvoice = 1 ' i.e. True '
Else
LDeliveryAddressSameAsInvoice = 0 ' i.e. False '
End If
' (SS,18/12/14) get the default payment currency from currencies table where applicable, mainly to determine amount to show for bank transfers
Dim LDefaultPaymentCurrencyCode, LDefaultPaymentCurrencyRate
GetDefaultPaymentCurrency GetCurrencyCode, LDefaultPaymentCurrencyCode, LDefaultPaymentCurrencyRate
' (SS,18/4/18) added following to ensure PaymentLog is preserved because REPLACE orders SQL statement will delete it
' we are now looking up the current value and saving it in the REPLACE command below, if order doesn't exist then blank will be returned
Dim LPaymentLog
LPaymentLog = NB(GetOrderFieldValue(LOrderNo, "PaymentLog"))
' (SS,14/9/06) added DeliveryRegion
' (SS,19/9/06) added CurrencyCode and ExchangeRate
' (SS,22/6/07) removed DeliveryRegion
' (SS,14/9/07) added Subscribe and HearAboutUs
' (SS,14/11/07) added DeliveryWillCollect
' (SS,16/11/07) added VATNumber and IntraCommunitySupply
' (SS,17/4/09) added Mobile and DeliveryDate
' (SS,23/4/09) added VoucherID
' (SS,8/5/09) removed quotes from DeliveryDate so Null can also be assigned
' ConvertUKToMySQLDate in dbfunction changed to return date without '-'
' (SS,7/3/13) corrected bug introduced yesterday due to CustomerID changing from VARCHAR to INT, was setting to '' and causing failure, when it should be NULL
' i.e. replaced: {", CustomerID='" & CleanSQLStr(Session("AccountID")) & "'" &_} with
' {", CustomerID=" & IIf(Session("AccountID") = "", "NULL", "'" & CleanSQLStr(Session("AccountID")) & "'" &_}
' (SS,18/12/14) added DefaultPaymentCurrencyCode and DefaultPaymentCurrencyRate
' (SS,18/4/18) added PaymentLog to preserve it, previous values were being overwritten for same order
' (SS,28/6/18) added DeliveryOption
LSQL = "REPLACE orders SET OrderNo=" & LOrderNo &_
", SessionID='" & ABasketSessionID & "'" &_
", IPAddress='" & CleanSQLStr(Request.ServerVariables("REMOTE_ADDR")) & "'" &_
", CustomerID=" & IIf(Session("AccountID") = "", "NULL", "'" & CleanSQLStr(Session("AccountID")) & "'") &_
", Title='" & CleanSQLStr(Session("Title")) & "'" &_
", FirstName='" & CleanSQLStr(Session("FirstName")) & "'" &_
", Surname='" & CleanSQLStr(Session("Surname")) & "'" &_
", CompanyName='" & CleanSQLStr(Session("CompanyName")) & "'" &_
", AddressLine1='" & CleanSQLStr(Session("Address1")) & "'" &_
", AddressLine2='" & CleanSQLStr(Session("Address2")) & "'" &_
", Town='" & CleanSQLStr(Session("Town")) & "'" &_
", County='" & CleanSQLStr(Session("County")) & "'" &_
", Postcode='" & CleanSQLStr(Session("Postcode")) & "'" &_
", Country='" & CleanSQLStr(Session("Country")) & "'" &_
", Telephone='" & CleanSQLStr(Session("Telephone")) & "'" &_
", Mobile='" & CleanSQLStr(Session("Mobile")) & "'" &_
", EmailAddress='" & CleanSQLStr(Session("Email")) & "'" &_
", Subscribe= " & BoolToInt(Session("Subscribe")) &_
", HearAboutUs= '" & CleanSQLStr(Session("HearAboutUs")) & "'" &_
", PurchaseOrderNo='" & CleanSQLStr(Session("PurchaseOrderNo")) & "'" &_
", Message='" & CleanSQLStr(Session("Message")) & "'" &_
", DateTimeOrdered=NOW()" &_
", DeliveryDate=" & ConvertUKToMySQLDate(Session("DeliveryDate")) &_
", DeliveryWillCollect=" & BoolToInt(Session("DeliveryWillCollect")) &_
", DeliveryAddressSameAsInvoice=" & LDeliveryAddressSameAsInvoice &_
", DeliveryName='" & CleanSQLStr(Session("DeliveryName")) & "'" &_
", DeliveryCompanyName='" & CleanSQLStr(Session("DeliveryCompanyName")) & "'" &_
", DeliveryAddressLine1='" & CleanSQLStr(Session("DeliveryAddress1")) & "'" &_
", DeliveryAddressLine2='" & CleanSQLStr(Session("DeliveryAddress2")) & "'" &_
", DeliveryTown='" & CleanSQLStr(Session("DeliveryTown")) & "'" &_
", DeliveryCounty='" & CleanSQLStr(Session("DeliveryCounty")) & "'" &_
", DeliveryPostcode='" & CleanSQLStr(Session("DeliveryPostcode")) & "'" &_
", DeliveryCountry='" & CleanSQLStr(Session("DeliveryCountry")) & "'" &_
", Status='" & ORDER_PLACED_STATUS & "'" &_
", CurrencyCode='" & CleanSQLStr(GetCurrencyCode) & "'" &_
", ExchangeRate=" & GetCurrencyRate &_
", DefaultPaymentCurrencyCode='" & CleanSQLStr(LDefaultPaymentCurrencyCode) & "'" &_
", DefaultPaymentCurrencyRate='" & CleanSQLStr(LDefaultPaymentCurrencyRate) & "'" &_
", VATNumber='" & CleanSQLStr(Session("VATNumber")) & "'" &_
", IntraCommunitySupply=" & BoolToInt(IsIntraCommunitySupply) &_
", VoucherID=" & GetVoucherIDForVoucherCode(Session("VoucherCode")) &_
", PaymentLog='" & CleanSQLStr(LPaymentLog) & "'" &_
", DeliveryOption='" & CleanSQLStr(GetDeliveryOption) & "'"
' (SS,6/10/16) also save the extra local mode values
If IsLocalMode Then
LSQL = LSQL &_
", Notes='" & CleanSQLStr(Session("Notes")) & "'" &_
", LocalMode=1" &_
", Exchange=" & BoolToInt(Session("Exchange")) &_
", ExchangeReason='" & CleanSQLStr(Session("ExchangeReason")) & "'"
End If
' (SS,1/12/16) now priority is always set, because HyperFlight overrides it depending on items ordered and delivery address
' moved here from within "If IsLocalMode Then" above
' also added CustomGetPriority used to set priority via CustomGetDelivery earlier
If FunctionExists("CustomGetPriority") Then SetPriority CustomGetPriority
LSQL = LSQL & ", Priority='" & CleanSQLStr(GetPriority) & "'"
ExecuteQuery(LSQL)
' (SS,16/1/15) added following to save paymentlog to orders table (for PayPal Express)
' (SS,23/8/17) replaced SavePaymentLog with following to force saving of PaymentLog from Session
AddToPaymentLog "", ""
' (SS,6/10/11) to note VAT Number validation
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
NoteVATValidation "orders", "OrderNo = '" & CleanSQLStr(LOrderNo) & "'"
' create the order detail records from shoppingbasket, decrement the non blank stock values in products
' (SS,23/5/07) replaced "products.StdPrice, products.SalePrice" in following with GetPricingFieldsForSQL("products")
' (SS,28/5/07) replaced with GetOrderItemsQuery(ABasketSessionID)
' (SS,26/10/18) added True parameter to GetOrderItemsQuery to include subproducts where applicable
OpenQuery(GetOrderItemsQuery(ABasketSessionID, True))
Dim LPriceEach, LSubtotal, LOrderDetailID, LItemID, LProductID
' (SS,9/10/14) added following to get the delivery info
Dim LobjOptionsDict, LCustomGetOrderDeliveryInfoExists, LCustomProductInBasketExists, LQty
LCustomGetOrderDeliveryInfoExists = FunctionExists("CustomGetOrderDeliveryInfo")
LCustomProductInBasketExists = FunctionExists("CustomProductInBasket") ' (SS,4/3/15) missed from before
SetDeliveryInfo("")
Set LobjOptionsDict = CreateObject("Scripting.Dictionary")
LobjOptionsDict.CompareMode = vbTextCompare ' makes the keys case insensitive
' (SS,29/10/18) to allow new SubproductOrderDetailID field to be set correctly, assumes correct sequence of ItemID in shoppingbaskets
Dim LIsSubproduct, LLastMainOrderDetailID
LSubtotal = 0
Do While Not EndOfQuery
' (SS,23/5/07)
' (SS,6/7/11) removed GetFieldValue("Qty") parameter, added True, to look up options price
GetPricingFromProductRecord(True)
LProductID = GetFieldValue("ProductID") ' (SS,9/10/14)
LQty = GetFieldValue("Qty") ' (SS,4/3/15) now saving in LQty, now used in at least 3 places
LPriceEach = GetProductPrice
LSubtotal = LSubtotal + (LPriceEach * LQty)
' (SS,29/10/18)
LIsSubproduct = Not IsNull(GetFieldValue("SubproductItemID"))
' (SS,6/7/11) added ProductID and OptionsList
' (SS,30/5/12) added NumInStock to keep historical record of number in stock
' (SS,1/6/12) renamed NumInStock to PrevStock due to ambigious error in queries i.e. Stock Required report
' (SS,13/6/12) for PrevStock, replaced NB with NNULL, because it was failing when PrevStock was blank, tried '' but this fails with incorrect integer value because '' isn't NULL
' (SS,9/10/14) added LobjOptionsDict
LSQL = "INSERT INTO orderdetails SET OrderNo=" & LOrderNo &_
", ProductID=" & GetFieldValue("ProductID") &_
", ProductCode='" & CleanSQLStr(GetFieldValue("ProductCode")) & "'" &_
", ProductName='" & CleanSQLStr(GetFieldValue("ProductName")) & "'" &_
", OptionsList='" & CleanSQLStr(GetOptionsListForBasketItem(GetFieldValue("ItemID"), "", LobjOptionsDict)) & "'" &_
", Qty=" & LQty &_
", PriceEach=" & LPriceEach &_
", PrevStock=" & NNULL(GetFieldValue("NumInStock"))
' (SS,29/10/18) set the new SubproductOrderDetailID with OrdedDetailID of parent product
If LIsSubproduct Then LSQL = LSQL & ", SubproductOrderDetailID=" & LLastMainOrderDetailID
ExecuteQuery(LSQL)
' (SS,29/10/18) if this product is a main product i.e. with subproducts then get the last insert id to use as SubproductOrderDetailID for subsequent subproduct records
If Not LIsSubproduct Then LLastMainOrderDetailID = GetSQLLastInsertID
' (SS,9/10/14) update delivery info for this product, at the end we'll have the actual one for the full order
If LCustomGetOrderDeliveryInfoExists Then SetDeliveryInfo(CustomGetOrderDeliveryInfo(GetDeliveryInfo, LProductID, LobjOptionsDict))
' (SS,4/3/15) missed from update made on (SS,10/10/14) if CustomProductInBasketExists function exists then call it to pass on product, qty and options, it may use it to recommend other products later
If LCustomProductInBasketExists Then CustomProductInBasket LProductID, LQty, LobjOptionsDict
' (SS,6/7/11) add the order detail option records from shopping basket options
If ProductOptionsEnabled Then
LOrderDetailID = GetSQLLastInsertID
LItemID = GetQueryValue("ItemID")
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
ExecuteQuery("INSERT INTO order_detail_options SELECT NULL, '" & CleanSQLStr(LOrderNo) & "', " & LOrderDetailID & ", ProductOptionID, ProductOptionValueID, OptionName, OptionValue FROM shopping_basket_options WHERE ItemID = " & LItemID)
End If
NextQueryRecord
Loop
Set LobjOptionsDict = Nothing
CloseQuery ' (SS,27/7/05) added because it was missing
' (SS,23/4/09) added LDiscount
Dim LDiscount, LDelivery, LGrandTotal, LVATIncluded, LVATDeducted, LTotalWeight
' (SS,27/7/05) added following line and moved code from here to new GetOrderTotals '
' (SS,22/6/07) added LTotalWeight parameter
' (SS,23/4/09) added LDiscount parameter
GetOrderTotalsForBasket ABasketSessionID, LSubtotal, LDiscount, LDelivery, LGrandTotal, LVATIncluded, LVATDeducted, LTotalWeight
' check if products out of stock and set MessageToCustomer
Dim LOutOfStockDetails
' LOutOfStockDetails = GetOutOfStockDetails(ABasketSessionID)
' (SS,18/4/16) replaced above with following, no longer used because customer is prevented from ordering if not in stock
LOutOfStockDetails = ""
' place subtotal, vat total, delivery cost etc in order
' (SS,22/6/07) added TotalWeight
' (SS,23/4/09) added Discount
' (SS,4/6/09) added MessageToCustomer
' (SS,21/7/10) added CleanSQLStr to LOutOfStockDetails to fix bug
' (SS,12/6/12) added CleanSQLStr before LOrderNo to prevent SQL injection
' (SS,9/10/14) added DeliveryInfo
LSQL = "UPDATE orders SET Subtotal=" & LSubtotal & ", Discount=" & LDiscount & ", Delivery=" & LDelivery &_
", VATIncluded=" & LVATIncluded & ", VATDeducted=" & LVATDeducted & ", GrandTotal=" & LGrandTotal &_
", TotalWeight=" & LTotalWeight &_
", MessageToCustomer='" & CleanSQLStr(LOutOfStockDetails) & "'" &_
", DeliveryInfo='" & CleanSQLStr(GetDeliveryInfo) & "'" &_
" WHERE OrderNo = '" & CleanSQLStr(LOrderNo) & "'"
ExecuteQuery(LSQL)
' take the products out of stock (only if stock qty has a value) '
'LSQL = "UPDATE products, shoppingbaskets SET NumInStock = NumInStock - shoppingbaskets.Qty" &_
' " WHERE shoppingbaskets.SessionID = '" & ABasketSessionID & "' AND products.ProductCode = shoppingbaskets.ProductCode"
'ExecuteQuery(LSQL)
' (SS,7/7/11) above replaced with following, which works correctly when same product appears more than once
TakeProductsFromStock LOrderNo, True
TryProcessOrder = True ' (SS,18/4/16) now a function
End Function
' (SS,12/4/16) clears existing order already placed, putting items back into stock and deleting the order detail and option records
' moved code here from Sub TryProcessOrder (with slight improvements), also called from basket routines to simplify stock handling
' and also releases the stock for others to buy while this use continues to make changes to their basket
' (SS,20/4/16) fix to ensure it's only down if OrderNoCanBeReused
Sub ClearExistingOrderPlaced(ADeleteOrderDetailRecords)
Dim LOrderNo
LOrderNo = GetOrderNo
' only do if order already placed and in 'ORDER PLACED' status, and detail records exist
' (SS,20/4/16) added OrderNoCanBeReused to ensure it's only done if order no hasn't been processed i.e. can be reused, otherwise there's chance that previous order can be put back in stock and lines deleted
' (SS,21/9/16) replaced [LOrderNo <> "" And OrderNoCanBeReused] with [OrderNoCanBeReused(LOrderNo)]
' Function OrderNoCanBeReused which now does a check for ORDER_PLACED_STATUS or ORDER_CANCELLED_STATUS and prevents order details being deleted from previous order
If OrderNoCanBeReused(LOrderNo) Then
' If GetValueFromQuery("Status", "SELECT Status FROM orders WHERE OrderNo = '" & CleanSQLStr(LOrderNo) & "'") = ORDER_PLACED_STATUS Then
' (SS,21/9/16) replaced above with following If, does the same thing
If CheckOrderInStatus(LOrderNo, ORDER_PLACED_STATUS) Then
' add the items back in stock from orderdetails
TakeProductsFromStock LOrderNo, False
' mark order as cancelled if at ORDER PLACED status (just in case it gets changed above while doing TakeProductsFromStock and TakeProductOptionsFromStock)
ExecuteQuery("UPDATE orders SET Status = '" & ORDER_CANCELLED_STATUS & "' WHERE OrderNo = '" & CleanSQLStr(LOrderNo) & "' AND Status = '" & ORDER_PLACED_STATUS & "'")
End If
' delete records if requested, normally from TryProcessOrder
' (SS,21/9/16) order detail records were being deleted from previous order when same customer tried to place another order (or went as far as order records being created in checkout)
' New OrderNoCanBeReused function should prevent this because it checks for order being in ORDER_PLACED_STATUS or ORDER_CANCELLED_STATUS before doing this
If ADeleteOrderDetailRecords Then
' delete the order detail records
ExecuteQuery("DELETE FROM orderdetails WHERE OrderNo = '" & CleanSQLStr(LOrderNo) & "'")
' (SS,6/7/11) delete the order detail option records
If ProductOptionsEnabled Then
ExecuteQuery("DELETE FROM order_detail_options WHERE OrderNo = '" & CleanSQLStr(LOrderNo) & "'")
End If
End If
End If
End Sub
' (SS,4/6/09) called from TryProcessOrder, returns list of items that are out of stock
' (SS,25/6/10) modified to use new stock availability feature
' (SS,21/7/10) remove stock availability feature, replaced with new ShowOutOfStockMessage setting
' which only calls this function when this setting is true
' (SS,18/4/16) *** no longer used, redundant code, can be removed
Function GetOutOfStockDetails(ASessionID)
Dim LSQL, LResult
LResult = ""
If ShowOutOfStockMessage Then
' select all products in basket that are potentially out of stock
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
LSQL = "SELECT shoppingbaskets.ProductCode, ProductName, shoppingbaskets.Qty - GREATEST(products.NumInStock, 0) AS QtyOutOfStock " &_
"FROM shoppingbaskets " &_
"INNER JOIN products ON products.ProductCode = shoppingbaskets.ProductCode " &_
"LEFT JOIN suppliers ON suppliers.SupplierID = products.SupplierID " &_
"WHERE SessionID = '" & CleanSQLStr(ASessionID) & "' AND products.NumInStock IS NOT NULL AND products.NumInStock < shoppingbaskets.Qty " &_
"ORDER BY ItemID"
OpenQuery(LSQL)
Do While Not EndOfQuery
If LResult <> "" Then LResult = LResult & NL
LResult = LResult & GetQueryValue("QtyOutOfStock") & " x " & GetQueryValue("ProductName")
NextQueryRecord
Loop
If LResult <> "" Then LResult = tr("Following items are currently out of stock") & ":" & NL & LResult
CloseQuery
End If
GetOutOfStockDetails = LResult
End Function
' (SS,14/9/06)
' (SS,26/10/18) added AIncludeSubproducts, which is passed onto GetShoppingBasketFilter to include subproducts or not
Function GetOrderItemsQuery(ASessionID, AIncludeSubproducts)
' following ordered by ItemID, ensuring most recent item will appear last
' (SS,26/5/07) replaced products.StdPrice, products.SalePrice with GetPricingFieldsForSQL("products")
' (SS,22/10/08) added products.CustomFlag
' (SS,30/5/12) added products.NumInStock, used to hold a historical value
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
' (SS,26/10/18) added GetShoppingBasketFilter to ignore subproducts
GetOrderItemsQuery = "SELECT shoppingbaskets.*, products.ProductName, " + GetPricingFieldsForSQL("products") + ", products.PostalWeight, products.CustomFlag, products.NumInStock FROM shoppingbaskets LEFT JOIN products ON shoppingbaskets.ProductCode = products.ProductCode WHERE SessionID = '" + CleanSQLStr(ASessionID) + "'" + GetShoppingBasketFilter(AIncludeSubproducts) + " ORDER By ItemID"
End Function
' (SS,26/10/18) called from shopping basket queries to not show subproducts
Function GetShoppingBasketFilter(AIncludeSubproducts)
If Not ProductSubproductsEnabled Or AIncludeSubproducts Then
GetShoppingBasketFilter = ""
Else
GetShoppingBasketFilter = " AND SubproductItemID IS NULL"
End If
End Function
' (SS,31/10/18) similar to GetShoppingBasketFilter above but works with order details, i.e. don't show subproduct records
Function GetOrderDetailsFilter
If ProductSubproductsEnabled Then
GetOrderDetailsFilter = " AND SubproductOrderDetailID IS NULL"
Else
GetOrderDetailsFilter = ""
End If
End Function
' (SS,31/10/18) similar to GetShoppingBasketFilter and GetOrderDetailsFilter above with works with products, i.e. don't show subproduct records
Function GetProductsFilter
If ProductSubproductsEnabled Then
GetProductsFilter = " AND NOT IsSubproduct"
Else
GetProductsFilter = ""
End If
End Function
' (SS,27/7/05) moved here from TryProcessOrder
' (SS,12/9/06) now calculates delivery using weight too
' (SS,28/5/07) modified for prices not including VAT, VAT Bands, also called from ShowBasket
' (SS,22/6/07) added ATotalWeight parameter weight is now saved in Order Record
' (SS,23/4/09) added ByRef ADiscount parameter, voucher code used to determine this
' (SS,15/5/09) renamed from GetOrderTotals to GetOrderTotalsForBasket
' (SS,19/3/20) modified to allow voucher to set free delivery
Sub GetOrderTotalsForBasket(ABasketSessionID, ByRef ASubtotal, ByRef ADiscount, ByRef ADelivery, ByRef AGrandTotal, ByRef AVATIncluded, ByRef AVATDeducted, ByRef ATotalWeight)
Dim LPriceEach, LTotalWeight, LNonWeightPrice, LWeight, LQty, LVATTotal
ASubtotal = 0
ADiscount = 0 ' (SS,23/4/09)
LTotalWeight = 0
LNonWeightPrice = 0
LVATTotal = 0
' (SS,22/10/08)
Dim LFlaggedWeight, LNonFlaggedWeight
LFlaggedWeight = 0
LNonFlaggedWeight = 0
' go through basket calculating the subtotal '
' (SS,12/9/06) added PostalWeight
' (SS,26/10/18) added False parameter to GetOrderItemsQuery to exclude subproducts
OpenQuery(GetOrderItemsQuery(ABasketSessionID, False))
Do While Not EndOfQuery
LQty = GetFieldValue("Qty")
' (SS,26/5/07)
' (SS,6/7/11) removed LQty parameter, added True, to look up options price
GetPricingFromProductRecord(True)
LPriceEach = GetProductPrice
ASubtotal = ASubtotal + (LPriceEach * LQty)
LWeight = NZ(GetFieldValue("PostalWeight")) + FOptionsWeight ' (SS,6/7/11) added FOptionsWeight
LTotalWeight = LTotalWeight + (LWeight * LQty)
If LWeight = 0 Then LNonWeightPrice = LNonWeightPrice + (LPriceEach * LQty) ' (SS,14/9/06)
' (SS,22/10/08)
If IntToBool(GetFieldValue("CustomFlag")) Then
LFlaggedWeight = LFlaggedWeight + (LWeight * LQty)
Else
LNonFlaggedWeight = LNonFlaggedWeight + (LWeight * LQty)
End If
' (SS,27/5/07) calculate VAT and add to VAT total, also takes VAT Band into account
' if PricesEnteredIncludeVAT then it's the VAT content of the Price
LVATTotal = LVATTotal + GetVATForProduct(LPriceEach * LQty)
NextQueryRecord
Loop
CloseQuery
ATotalWeight = LTotalWeight ' (SS,22/6/07)
ASubtotal = Round2dp(ASubtotal) ' (SS,28/5/07) make sure it's rounded to 2dp
' (SS,28/5/07) modified to copy with prices not including VAT and VAT bands
' (SS,22/10/08) added LFlaggedWeight and LNonFlaggedWeight to GetDeliveryCost
ADelivery = GetDeliveryCost(ASubtotal, LNonWeightPrice, LTotalWeight, LFlaggedWeight, LNonFlaggedWeight, LVATTotal) ' will include VAT if PricesEnteredIncludeVAT
' Dim LVoucherErrorMessage ' required to call IsVoucherCodeValid
' If IsVoucherCodeValid(Session("VoucherCode"), LVoucherErrorMessage) Then
' ADiscount = GetDiscountUsingVoucherCode(Session("VoucherCode"), ABasketSessionID, ASubtotal)
' End If
' (SS,23/4/09) added following to determine discount amount if there is a voucher
' (SS,16/2/15) replaced above with following
Dim LVoucherErrorMessage, LVoucherCode ' LVoucherErrorMessage required to call IsVoucherCodeValid
LVoucherCode = GetVoucherCode
' (SS,4/10/16) added discount override when using local mode
If LVoucherCode = DISCOUNT_OVERRIDE_VOUCHER_CODE Then ' clear the internal voucher, will get set below if there is an override discount value
SetVoucherCode ""
End If
If IsDiscountOverride Then
ADiscount = GetDiscountOverride(ASubtotal) ' (SS,23/3/17) added ASubtotal
SetVoucherCode DISCOUNT_OVERRIDE_VOUCHER_CODE ' because it's held in a session variable
LVoucherCode = GetVoucherCode
Else ' (SS,4/10/16) as before
If IsVoucherCodeValid(LVoucherCode, LVoucherErrorMessage) Or LVoucherCode = "" Then
Dim LFreeDelivery ' (SS,19/3/20)
LFreeDelivery = False ' (SS,19/3/20) default to no free delivery
If LVoucherCode = "" Or IsVoucherCodeAuto(LVoucherCode) Then
' (SS,19/3/20) added ADelivery, LFreeDelivery parameter
ADiscount = GetDiscountUsingAutoVoucherCode(ABasketSessionID, ASubtotal, ADelivery, LFreeDelivery) ' also sets the voucher code if one was found
Else
' (SS,19/3/20) added ADelivery, LFreeDelivery parameter
ADiscount = GetDiscountUsingVoucherCode(LVoucherCode, ABasketSessionID, ASubtotal, ADelivery, LFreeDelivery)
End If
' (SS,19/3/20) if voucher indicates free delivery then set delivery to 0
If LFreeDelivery Then
ADelivery = 0
End If
End If
End If
If PricesEnteredIncludeVAT Then
' (SS,28/5/07) add the VAT content of delivery to VAT Total, working backwards
' (SS,23/4/09) added GetVATAmount(ADiscount) working backwards
LVATTotal = Round2dp(Round2dp(LVATTotal) + GetVATAmount(ADelivery) + GetVATAmount(ADiscount))
' get VAT that is included for UK and EU orders
AVATIncluded = GetVATIncludedAmount(LVATTotal) ' (SS,28/5/07) replaced LGrandTotal with LVATTotal
' get VAT to deduct from GrandTotal for Rest of the World orders
AVATDeducted = GetVATDeductedAmount(LVATTotal) ' (SS,28/5/07) replaced LGrandTotal with LVATTotal
' (SS,23/4/09) added " + ADiscount" which is a negative amount
AGrandTotal = Round2dp(ASubtotal + ADiscount + ADelivery - AVATDeducted) ' also deduct the VAT for countries not VATable because prices include VAT
Else
' (SS,23/4/09) added following to calculate VAT for discounts this can get complicated if some products are non vatable
' because we're giving an overall discount off the subtotal
Dim LVATForDiscount
LVATForDiscount = GetVATAmountToAddNR(ADiscount)
' don't allow the VAT for discount to be greater than Subtotal's VAT
If LVATForDiscount > LVATTotal Then LVATForDiscount = LVATTotal
' (SS,28/5/07)
' (SS,23/4/09) added "+ LVATForDiscount"
LVATTotal = Round2dp(LVATTotal + GetVATAmountToAddNR(ADelivery) + LVATForDiscount) ' also add the VAT of delivery to VAT Total
AVATIncluded = GetVATIncludedAmount(LVATTotal) ' only adds VAT if country is VATable
AVATDeducted = 0
' (SS,23/4/09) added " + ADiscount" which is a negative amount
AGrandTotal = Round2dp(ASubtotal + ADiscount + ADelivery + AVATIncluded) ' also add the VAT because prices don't include VAT
End If
End Sub
' (SS,19/1/15) used by PayPal Express checkout, to send approx total
Sub SetBasketGrandTotal(AGrandTotal)
Session("BasketGrandTotal") = AGrandTotal
End Sub
' (SS,19/1/15) used by PayPal Express checkout, to send approx total
Function GetBasketGrandTotal
GetBasketGrandTotal = Session("BasketGrandTotal")
End Function
' (SS,17/2/15) used by PayPal Express checkout, to send summary description
Sub SetBasketDescription(ADescription)
Session("BasketDescription") = ADescription
End Sub
' (SS,17/2/15) used by PayPal Express checkout, to send summary description
Function GetBasketDescription
GetBasketDescription = Left(Session("BasketDescription"), 127) ' max of 127 chars
End Function
' (SS,23/2/15) added following subs and functions for email/save basket
Sub SetBasketSubtotal(ASubtotal)
Session("BasketSubtotal") = ASubtotal
End Sub
Sub SetBasketDiscount(ADiscount)
Session("BasketDiscount") = ADiscount
End Sub
Sub SetBasketDelivery(ADelivery)
Session("BasketDelivery") = ADelivery
End Sub
Sub SetBasketVAT(AVAT)
Session("BasketVAT") = AVAT
End Sub
Function GetBasketSubtotal
GetBasketSubtotal = Session("BasketSubtotal")
End Function
Function GetBasketDiscount
GetBasketDiscount = Session("BasketDiscount")
End Function
Function GetBasketDelivery
GetBasketDelivery = Session("BasketDelivery")
End Function
Function GetBasketVAT
GetBasketVAT = Session("BasketVAT")
End Function
' get next order no from setting or orders table
' (SS,20/4/16) changed to return same order no if it can be reused, i.e. order not fully processed
Function GetNextOrderNo
Dim LOrderNo, LOrderNoFromSettings, LHighestOrderNoUsed, LNextOrderNo
' (SS,27/6/16)
LOrderNo = GetOrderNo
LNextOrderNo = ""
' (SS,20/4/16) added if to reuse same order no if not blank and reuse flag is true
' (SS,27/6/16) modified to all check that order is in order placed or cancelled status, because in CIRC orders paid for, completed with confirmation emails sent were being overwritten
'If LOrderNo <> "" And OrderNoCanBeReused Then
' If CheckOrderInStatus(LOrderNo, ORDER_PLACED_STATUS) Or CheckOrderInStatus(LOrderNo, ORDER_CANCELLED_STATUS) Then
' LNextOrderNo = LOrderNo
' End If
'End If
' (SS,21/9/16) replaced above with following
If OrderNoCanBeReused(LOrderNo) Then
LNextOrderNo = LOrderNo
End If
If LNextOrderNo = "" Then
LOrderNoFromSettings = GetSetting("Application", "NextOrderNo") ' GetSetting always returns a string value or null if not found
If IsNull(LOrderNoFromSettings) Then
LOrderNoFromSettings = 1
Else
' (SS,9/11/07) in following replaced CInt with CLng because CInt only allows -32,768 to 32,767
' it was causing overflow when NextOrderNo was 50001
LOrderNoFromSettings = CLng(LOrderNoFromSettings)
If LOrderNoFromSettings <= 0 Then LOrderNoFromSettings = 1
End If
LHighestOrderNoUsed = GetValueFromQuery("HighestOrderNoUsed", "SELECT MAX(OrderNo) AS HighestOrderNoUsed FROM orders")
If IsNull(LHighestOrderNoUsed) Then LHighestOrderNoUsed = 0
If LHighestOrderNoUsed > LOrderNoFromSettings Then
LNextOrderNo = LHighestOrderNoUsed + 1
Else
LNextOrderNo = LOrderNoFromSettings
End If
' set next order no to be used in settings table
SetSetting "Application", "NextOrderNo", LNextOrderNo + 1
' (SS,12/4/15) following moved here from TryProcessOrder (after call to this GetNextOrderNo function)
' store the order no in session
' Session("OrderNoPlaced") = LNextOrderNo
' (SS,20/4/16) replaced above with following, which places in session and sets flag to reuse
SetOrderNo LNextOrderNo
End If
' return the next order no
GetNextOrderNo = LNextOrderNo
End Function
' (SS,8/6/07) returns order no in current session
Function GetOrderNo
GetOrderNo = Session("OrderNoPlaced")
End Function
' (SS,20/4/16) places in session and sets reuse flag
Sub SetOrderNo(AOrderNo)
Session("OrderNoPlaced") = AOrderNo
Session("OrderNoReuse") = True
End Sub
' (SS,20/4/16) returns True if existing order no can be reused, i.e hasn't been cleared using ClearOrderNo
' (SS,21/9/16) modified to add parameter AOrderNo and do a status check for order also checks for "" value,
' True is only returned if it isn't "" and Session("OrderNoReuse") is True and the order is in order placed or cancelled status
Function OrderNoCanBeReused(AOrderNo)
Dim LResult
If AOrderNo <> "" And Session("OrderNoReuse") <> False Then ' N.B. Session("OrderNoReuse") can be "" Or True i.e. <> False
LResult = CheckOrderInStatus(AOrderNo, ORDER_PLACED_STATUS) Or CheckOrderInStatus(AOrderNo, ORDER_CANCELLED_STATUS)
Else
LResult = False
End If
OrderNoCanBeReused = LResult
End Function
' (SS,18/4/16) clears order no from session to prevent it being used again
' (SS,20/4/16) renamed from ClearOrderNo to StopOrderNoReuse to prevent same order being reused via Session("OrderNoReuse") flag
Sub StopOrderNoReuse
' Session("OrderNoPlaced") = ""
' (SS,20/4/16) rather than clearing a flag is set to prevent same number being reused when order is next created
' this overcomes issue when there's a redirect in later stage PayPal express PayPal to ensure specific where OrderNo was being lost for Thank You display
' using Session("OrderNoReuse") flag was a more elegant solution and keeps order no as long as possible
Session("OrderNoReuse") = False
End Sub
' (SS,8/6/07)
Function GetSessionID
GetSessionID = FSessionID
End Function
' (SS,8/6/07)
Function GetIPAddress
GetIPAddress = Request.ServerVariables("REMOTE_ADDR")
End Function
' (SS,14/6/07) moved code here from EmailOrder to create separate routine called from more than one place
' (SS,29/5/09) renamed from GetOrderConfirmationURL to GetOrderDetailsURL
Function GetOrderDetailsURL(AOrderNo)
Dim LSessionID, LIPAddress
' (SS,10/6/07) get credentials for showing order
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQuery2("SELECT SessionID, IPAddress FROM orders WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'")
LSessionID = GetQueryValue2("SessionID")
LIPAddress = GetQueryValue2("IPAddress")
CloseQuery2
' (SS,29/5/09) renamed order-confirmation.asp to order-details.asp
GetOrderDetailsURL = GetStoreURL + "/order-details.asp?orderno=" & AOrderNo & "&sid=" & LSessionID & "&ip=" & LIPAddress
End Function
' (SS,1/6/09) added following to use a slightly different URL for email
' allowing the HTML email to be slightly different, most email clients don't load the email logo, so email looks untidy
' but fine for browsing on screen
Function GetOrderDetailsURLForEmail(AOrderNo)
GetOrderDetailsURLForEmail = GetOrderDetailsURL(AOrderNo) & "&cmd=foremail"
End Function
' (SS,29/5/09) same as GetOrderDetailsURL but with cmd=paymentslip at the end
Function GetPaymentSlipURL(AOrderNo)
GetPaymentSlipURL = GetOrderDetailsURL(AOrderNo) & "&cmd=paymentslip"
End Function
' (SS,22/5/09) returns field value for given order and field
Function GetOrderFieldValue(AOrderNo, AField)
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
GetOrderFieldValue = GetValueFromQuery(AField, "SELECT " & AField & " FROM orders WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'")
End Function
' (SS,25/4/19) returns up to 5 given order field values, needs to be called with a minimum of 1 field (use GetOrderFieldValue above for 1 field)
Function GetOrderFieldValues(AOrderNo, AField1, ByRef AValue1, AField2, ByRef AValue2, AField3, ByRef AValue3, AField4, ByRef AValue4, AField5, ByRef AValue5)
Dim LSQL
LSQL = "SELECT " & AField1
If AField2 <> "" Then LSQL = LSQL & ", " & AField2
If AField3 <> "" Then LSQL = LSQL & ", " & AField3
If AField4 <> "" Then LSQL = LSQL & ", " & AField4
If AField5 <> "" Then LSQL = LSQL & ", " & AField5
LSQL = LSQL & " FROM orders WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'"
GetOrderFieldValues = GetSQL5Values(LSQL, AValue1, AValue2, AValue3, AValue4, AValue5)
End Function
' (SS,15/2/11) added order number to end of subject
' (SS,11/8/16) change to VAT alert email to include more details via new check, new function EmailOrderVATAlertAddFields added
' (SS,23/1/17) change to subject, now store name, followed by Order Confirmation (& VAT Invoice) / Despatched / Cancelled then order no (no hyphen)
' (SS,12/3/18) modified to send the email body content directly, rather than via URL and SendEmailByITPEmailer
' To fix issue where some email client e.g. Outlook 2016 were ending up with an empty attached file
Sub EmailOrder(AOrderNo)
' (SS,7/1/16) removed Dim LBody and Const USE_DUNDAS and SEND_VIA_URL
'Const USE_DUNDAS = False
' (SS,29/6/07) set this to false if the hanging bug occurs again, via url has the advantage of embedding the logo
' (SS,23/7/07) same thing happened to order 2164 (previously 1955), service pack 2 was installed a couple of weeks before
' decided to set to False, i.e. CDO still used but image not embedded
' but order 2164 worked okay when I did it manually using "view order in browser" and adding &cmd=email
' (SS,15/8/07) CDO_SEND_VIA_URL = False failed on Order 2398, (True also failed, Dundas also failed)
' restarting IIS fixed the problem. Did notice earlier today when I logged in that, a IIS worker process error.
' (SS,4/9/07) another failure (order: 2623), even USE_DUNDAS = False would hang, but the plain text email worked fine
' so it could be caused by the GetWebPageContents routine, building the HTML, rather the getting the webpage
' might resolve the problem.
'Const SEND_VIA_URL = True ' (SS,30/1/08) renamed from CDO_SEND_VIA_URL to SEND_VIA_URL, also changed to True
' (SS,12/3/18) following not longer applicable
' LURL = GetOrderDetailsURLForEmail(AOrderNo) ' (SS,10/6/07) get URL for order confirmation web page
' (SS,10/6/07) following was only necessary for Dundas, not required for CDO
' (SS,29/6/07) modified to use Dundas if the USE_DUNDAS constant is True
' did this to investigate emailing with CDO hanging with Galleon Order No 1955
' (SS,7/1/16) moved following no longer used
'If USE_DUNDAS Or Not SEND_VIA_URL Then
' LBody = GetWebPageContents(LURL)
'Else
' LBody = ""
'End If
' (SS,10/6/07) SendEmail i.e. "Dundas.Mailer" replaced by CDO, because Dundas cannot accurately send HTML
' it insert newlines every 1000 or so characters and breaks HTML tags to cause corrupt HTML
' CDO also has a useful create from web page feature, so don't need to use GetWebPageContents
' (SS,29/6/07) added If USE_DUNDAS to force sending using Dundas, also added CDO_SEND_VIA_URL flag to send without URL
' (SS,7/1/16) USE_DUNDAS removed
'If USE_DUNDAS Then
' SendEmail LEmailAddress, LBcc1, LBcc2, LFrom, LSubject, LBody, HTMLEmailsEnabled
'Else
' If Not SEND_VIA_URL Then LURL = ""
' If Not SendEmailByCDO(LEmailAddress, LBcc1, LBcc2, LFrom, LSubject, LBody, "", HTMLEmailsEnabled, LURL) Then
' (SS,30/1/08) replaced above CDO method with new hopefully more reliable ITP method
'End If
' (SS,7/1/16) replaced LBody with "" and HTMLEmailsEnabled with True
'If Not SendEmailByITPEmailer(LEmailAddress, LBcc1, LBcc2, LFrom, LSubject, "", True, LURL) Then
' Response.Write("<br>!!! Email failure !!!<br>")
'End If
' (SS,7/1/15) removed "If HTMLEmailsEnabled" because now always HTML
' (SS,12/3/18) removed LURL, no longer used
' added LBody and LTitle, also moved others here form below
Dim LEmailAddress, LBcc1, LBcc2, LFrom, LSubject
Dim LBody, LTitle, LStatus, LIsPaymentSlip, LFullName
LBcc1 = FEmailOrderConfirmationBcc1
LBcc2 = FEmailOrderConfirmationBcc2
LFrom = FEmailOrderConfirmationFrom
LIsPaymentSlip = CleanRequestQueryString("cmd") = "paymentslip" ' (SS,12/3/18)
' (SS,12/3/18) added following to fetch the body (before we were fetching from URL)
' Values for LEmailAddress, LFullName, LStatus, LTitle are returned
' removed duplicated code that was creating the subject from order status, returned LTitle used instead
' 3rd parameter AForEmail is True
LBody = GetEmailBody(AOrderNo, LIsPaymentSlip, True, LEmailAddress, LFullName, LStatus, LTitle)
' (SS,23/1/17) replaced above with following
' (SS,12/3/18) replaced tr(LSubject) in following with tr(LTitle)
LSubject = GetStoreName + " " + tr(LTitle) & " " & AOrderNo
' (SS,12/3/18) replaced above SendEmailByITPEmailer with following SendEmailByCDO
' we're no longer getting from webpage, but building from returned strings,
' SendEmailByITPEmailer has an issue with emails looking like they have an empty attachment in Outlook 2016, and worse in some other email clients
SendEmailByCDO LEmailAddress, LBcc1, LBcc2, LFrom, LSubject, LBody, "", True, "", ""
' (SS,11/8/16) code moved to separate routine
SendVATNumberAlertEmail AOrderNo, LStatus
' (SS,18/5/17) if completed status and ReviewTypeIsTrustpilot and AFS email address specified then send the Trustpilot AFS email
' (SS,12/3/18) moved following here from after EmailOrder in ShowEmailConfirmationHTML
If LStatus = ORDER_COMPLETED_STATUS And ReviewTypeIsTrustpilot And GetTrustpilotAFSEmailAddress <> "" Then
SendTrustpilotAFSEmail LFullName, AOrderNo, LEmailAddress
End If
' (SS,20/3/18) send SMS if applicable for order status
If LStatus = PAYMENT_RECEIVED_STATUS Or LStatus = AWAITING_PAYMENT_STATUS Or LStatus = ORDER_COMPLETED_STATUS Then
Dim LSMSType
' i.e. ORDER_COMPLETED_STATUS then send order despatched SMS
If LStatus = ORDER_COMPLETED_STATUS Then
LSMSType = ST_ORDER_DESPATCHED
Else ' for PAYMENT_RECEIVED_STATUS or AWAITING_PAYMENT_STATUS send the order confirmation SMS
LSMSType = ST_ORDER_CONFIRMATION
End If
SendSMS AOrderNo, LSMSType
End If
End Sub
' (SS,16/4/19) returns True if OrderNo, Session ID and IP address provided in query string are valid, also returns the order no itself
' Code moved here from ShowEmailConfirmationHTML, so it can be called from new cancelled order feature
' Also returns OrderNo set in AOrderNo
Function AuthenticateOrderNo(ByRef AOrderNo)
Dim LSessionID, LIPAddress, LResult
AOrderNo = CleanRequestQueryString("orderno")
If AOrderNo = "" Then AOrderNo = GetOrderNo
LSessionID = CleanRequestQueryString("sid")
If LSessionID = "" Then LSessionID = GetSessionID
LIPAddress = CleanRequestQueryString("ip")
If LIPAddress = "" Then LIPAddress = GetIPAddress
If AOrderNo = "" Or LSessionID = "" Or LIPAddress = "" Then
Response.Write("Sorry, not enough info to show order details<br>")
LResult = False
Else
LResult = True
End If
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
If LResult Then
If IsNull(GetValueFromQuery("OrderNo", "SELECT OrderNo FROM orders WHERE OrderNo = '" + CleanSQLStr(AOrderNo) + "' AND SessionID = '" + LSessionID + "' AND IPAddress = '" + LIPAddress + "'")) Then
Response.Write("Sorry, not enough credentials to show order details<br>")
LResult = False
End If
End If
AuthenticateOrderNo = LResult
End Function
' (SS,8/6/07) shows HTML version of confirmation for emailing
' (SS,12/3/18) modified version which gets string from GetEmailBody which is rendered using Response.Write
' This routine is only called from order-details.asp to show order details on a page.
' order-details.asp is no longer fetched to get the content to send via email.
' Change was made to improve reliability and prevent blank attachments that were resulting in Outlook 2016
' (SS,16/4/19) modified to call new function AuthenticateOrderNo
Sub ShowEmailConfirmationHTML
Dim LOrderNo
' (SS,16/4/19) replaced code moved from above to AuthenticateOrderNo, LOrderNo is returned from AuthenticateOrderNo
If Not AuthenticateOrderNo(LOrderNo) Then
Exit Sub
End If
' (SS,29/5/09) added code for checking for payment slip
' (SS,18/5/17) added LFullName, LStatus
' (SS,12/3/18) added LTitle
Dim LEmailAddress, LCmd, LFullName, LStatus, LTitle
LCmd = CleanRequestQueryString("cmd")
' (SS,29/5/09) added LCmd = "paymentslip"
' nothing is returned but HTML response is done
' (SS,1/6/09) added LCmd = "foremail", if cmd is foremail then we must return the email version
' (SS,7/1/16) removed True for AIsHTML because it's now always HTML
' GetEmailBody LOrderNo, LCmd = "paymentslip", LEmailAddress, LCmd = "foremail"
' (SS,18/5/17) swapped parameters LEmailAddress and LCmd = "foremail", added LFullName to return full name, and LStatus to return status
' to be used by Trustpilot review (AFS) email
' (SS,12/3/18) removed 3rd parameter (LCmd = "foremail") doesn't appear to be used for anything, add new LTitle parameter
' also added Response.Write because GetEmailBody is now a function which returns a string instead of rendering directly
' 3rd parameter AForEmail is False
Response.Write GetEmailBody(LOrderNo, LCmd = "paymentslip", False, LEmailAddress, LFullName, LStatus, LTitle)
' (SS,29/6/07) added following to send an email when querystring has cmd="email"
If LCmd = "email" Then
EmailOrder(LOrderNo)
End If
End Sub
' (SS,8/6/07) returns Body, also sets AEmailAddress to the email address to use
' (SS,29/5/09) added AIsPaymentSlip parameter
' (SS,1/6/09) added AForEmail
' (SS,19/3/10) added translation using function tr for HTML only,
' (SS,16/2/11) added tr to INTRA_COMMUNITY_SUPPLY_MESSAGE and added text to status message for English version
' (SS,3/10/12) added SetInvoiceCountry for correct VAT deducted figures for overseas orders
' (SS,23/10/15) modified to not show order details when status is order completed, now text is taken from message to customer field
' (SS,26/10/15) modified to use custom confirmation email text from GetSiteDetail("Other", "ConfirmationEmailTemplate")
' (SS,7/1/16) cleaned up code, was becoming hard to maintain, split to new routines: GetEmailBodyOrderDetails, GetEmailBodyAddressDetails, GetEmailBodyOrderItems, GetEmailBodyMessage
' removed Dim NL now defined globally, cleaned up dated comments
' changed to Sub because no value is returned, calls made to inc-template-email-html.asp to output straight to HTML i.e. Response.Write
' references to strBody removed, old plain text version
' 2nd parameter AIsHTML removed, because it's always HTML
' EmailOrderConfirmationFooter in inc-template-messages.asp no longer used (was only used by plain text)
' Also changed from Function to Sub
' (SS,18/5/17) parameters changed from AOrderNo, AIsPaymentSlip, ByRef AEmailAddress, AForEmail
' to AOrderNo, AIsPaymentSlip, AForEmail, ByRef AEmailAddress, ByRef AFullName, ByRef AStatus
' (SS,12/3/18) changed from Sub to Function which returns a string rather than render directly
' Also replaced ShowEmailHeader with GetOrderEmailHeader
' Added ByRef ATitle as a parameter to return it back to caller to prevent duplication i.e. used in subject of email
' LTitle renamed to ATitle in function
Function GetEmailBody(AOrderNo, AIsPaymentSlip, AForEmail, ByRef AEmailAddress, ByRef AFullName, ByRef AStatus, ByRef ATitle)
' (SS,12/3/18) LResult holds the returned email HTML content
Dim LResult
LResult = ""
' open order, looking up voucher code and nicely formatted date/time
OpenQuery("SELECT orders.*, DATE_FORMAT(orders.DateTimeOrdered, '" & GetLanguageDateFormat(True) & "') AS FormattedDateTime, vouchers.VoucherCode FROM orders LEFT JOIN vouchers ON vouchers.VoucherID = orders.VoucherID WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'")
Dim LStatus, LFullName
LStatus = GetFieldValue("Status")
LFullName = GetFullName(GetFieldValue("Title"), GetFieldValue("FirstName"), GetFieldValue("Surname"))
' header, determine title depending on order status
' Dim LTitle ' (SS,12/3/18) replaced by ATitle in parameter of this function
If AIsPaymentSlip Then ' (SS,29/5/09)
ATitle = "Payment Slip"
ElseIf LStatus = ORDER_COMPLETED_STATUS Then
ATitle = "Order Despatched"
ElseIf LStatus = ORDER_CANCELLED_STATUS Then
ATitle = "Order Cancelled"
Else
' (SS,14/6/10) if email confirmation is also receipt then set title to "Invoice" followed by last 2 letters for domain and order no
If EmailConfirmationIsVATReceipt Then
' LTitle = tr("Invoice") & " " & UCase(Right(GetStoreURL, 2)) & AOrderNo
' (SS,25/11/16) replaced above with following
ATitle = "Order Confirmation & VAT Invoice"
Else
ATitle = "Order Confirmation"
End If
End If
' (SS,1/6/09) added AForEmail, (SS,19/3/10) added tr for translation
' (SS,12/3/18) replaced ShowEmailHeader with GetOrderEmailHeader, and added LResult = LResult &
LResult = LResult & GetOrderEmailHeader(tr(ATitle), AForEmail)
' (SS,23/10/15) if not order completed status, i.e. not order despatched, only then include the order details
If LStatus <> ORDER_COMPLETED_STATUS Then
' (SS,12/3/18) GetEmailBodyOrderDetails is now a function which returns a value (also added LResult = LResult &)
LResult = LResult & GetEmailBodyOrderDetails(AOrderNo, AIsPaymentSlip, LFullName)
End If
' the main message of email
' (SS,29/5/09) added AIsPaymentSlip to bring up in print preview for payment slip
' (SS,4/4/17) added IsLocalMode which also needs to show print preview
' (SS,12/3/18) replaced ShowEmailFooter with GetOrderEmailFooter, and added LResult = LResult &
' (SS,13/12/18) added AOrderNo to GetEmailBodyMessage
LResult = LResult & GetOrderEmailFooter(LFullName, GetEmailBodyMessage(AOrderNo, LStatus, LFullName), AIsPaymentSlip Or IsLocalMode)
' (SS,15/4/16) if complete/despatched email place details at the bottom
If LStatus = ORDER_COMPLETED_STATUS Then
' (SS,12/3/18) GetEmailBodyOrderDetails is now a function which returns a value (also added LResult = LResult &)
LResult = LResult & GetEmailBodyOrderDetails(AOrderNo, AIsPaymentSlip, LFullName)
End If
AEmailAddress = GetFieldValue("EmailAddress") ' return email address
' (SS,18/5/17) added following to now also return full name and status (ByRef)
AFullName = LFullName
AStatus = LStatus
CloseQuery ' close orders table
' (SS,12/3/18) now returns a string value
GetEmailBody = LResult
End Function
' (SS,7/1/15) moved code here from GetEmailBody, assumes access to orders record using GetFieldValue
' AExtraMessage returns extra text to add to email message
' (SS,8/8/17) change to call to ShowEmailOrderHeader to also pass on GBP Grand Total
' (SS,12/3/18) changed from Sub to Function which returns a string rather than render directly
Function GetEmailBodyOrderDetails(AOrderNo, AIsPaymentSlip, AFullName)
' (SS,12/3/18) LResult holds the returned email HTML content
Dim LResult
LResult = ""
Dim LStatus
LStatus = GetFieldValue("Status") ' (SS,22/5/09)
' (SS,12/3/18) added "LResult = LResult & ", GetEmailBodyAddressDetails is now a function
LResult = LResult & GetEmailBodyAddressDetails(AIsPaymentSlip, AFullName) ' (SS,7/1/16) code moved to this routine
' (SS,19/9/06) set the currency code, rate, symbols for calls to CorrectCurrencyNV etc to work correctly
' (SS,1/8/17) moved code to separate SetCurrencyForOrder routine
SetCurrencyForOrder GetFieldValue("CurrencyCode"), GetFieldValue("ExchangeRate")
' (SS,8/11/07) added GetFieldValue("DateTimeOrdered")
' (SS,16/11/07) added GetFieldValue("VATNumber")
' (SS,20/4/09) added GetFieldValue("Mobile"), GetFieldValue("DeliveryDate"), LVoucherCode
' (SS,29/5/09) added LStatus
' (SS,13/5/10) replaced DateTimeOrdered with FormattedDateTime
' (SS,8/3/13) added StatusForCustomer to convert COMPLETED to DESPATCHED
' (SS,8/8/17) added new parameter GBPGrandTotal ( FormatNumber(GetFieldValue("GrandTotal", 2)) ) to the end to be show in multicurrency sites
' (SS,12/3/18) added "LResult = LResult & and replaced ShowEmailOrderHeader with GetOrderEmailHeaderDetails which is now a function
LResult = LResult & GetOrderEmailHeaderDetails(AOrderNo, GetFieldValue("FormattedDateTime"), StatusForCustomer(LStatus), GetFieldValue("CustomerID"), GetFieldValue("PurchaseOrderNo"), GetFieldValue("Message"), GetFieldValue("Telephone"), GetFieldValue("Mobile"), GetFieldValue("EmailAddress"), GetFieldValue("VATNumber"), GetFieldValue("DeliveryDate"), NB(GetFieldValue("VoucherCode")), GetFieldValue("PaymentMethod"), GetFieldValue("PaymentReference"), FormatNumber(GetFieldValue("GrandTotal"), 2))
' (SS,12/3/18) added "LResult = LResult & ", GetEmailBodyOrderItems is now a function
LResult = LResult & GetEmailBodyOrderItems(AOrderNo) ' (SS,7/1/16) moved code to separate routine
Dim LVATLabel, LVATAmount ' (SS,8/6/07)
LVATLabel = ""
LVATAmount = 0
' (SS,28/5/07) added If IsVATRegistered to not show VAT if not registered
If IsVATRegistered Then
' (SS,8/6/07) improvements to VAT
LVATAmount = NZ(GetFieldValue("VATIncluded"))
If PricesEnteredIncludeVAT Then
If NZ(GetFieldValue("VATDeducted")) > 0 Then
LVATLabel = "" ' (SS,2/11/12) was "VAT Deducted" changed to "" to ensure it's no longer shown, no need to because all prices shown now have VAT deducted
LVATAmount = NZ(GetFieldValue("VATDeducted"))
Else
LVATLabel = "VAT Included"
End If
Else
LVATLabel = "VAT"
End If
LVATLabel = tr(LVATLabel) ' (SS,19/3/10) for translation
End If
' (SS,10/6/07) moved email address and message higher up
' (SS,22/6/07) added following to show weight in HTML email
Dim LTotalWeight
If GetShowWeightInEmail Then
LTotalWeight = Format2dp(NZ(GetFieldValue("TotalWeight"))) & " " & GetWeightUnits
LTotalWeight = CorrectCommasAndDP(LTotalWeight) ' (SS,13/5/10) corrects commas and dp for language
Else
LTotalWeight = Null
End If
' (SS,8/6/07)
' (SS,20/4/09) added GetFieldValue("Discount") <> 0 and CorrectCurrency(GetFieldValue("Discount"))
' (SS,2/11/09) replaced "VAT" with LVATLabel
' (SS,2/11/12) replaced CorrectCurrency with CorrectCurrencyNV for Subtotal, Discount and Delivery, to ensure VAT is removed for countries who aren't charged VAT, to prevent confusion
' (SS,12/3/18) added "LResult = LResult & ", renamed ShowEmailOrderFooter to GetOrderEmailFooterDetails which is now a function
LResult = LResult & GetOrderEmailFooterDetails(CorrectCurrencyNV(GetFieldValue("Subtotal")), GetFieldValue("Discount") <> 0, CorrectCurrencyNV(GetFieldValue("Discount")), CorrectCurrencyNV(GetFieldValue("Delivery")), LVATLabel, CorrectCurrency(LVATAmount), CorrectCurrency(GetFieldValue("GrandTotal")), LTotalWeight)
' (SS,12/3/18) now returns a string containing the content
GetEmailBodyOrderDetails = LResult
End Function
' (SS,7/1/16) code moved here from GetEmailBody, assumes access to orders record using GetFieldValue
' (SS,12/3/18) changed from Sub to Function which returns a string rather than render directly
Function GetEmailBodyAddressDetails(AIsPaymentSlip, AFullName)
Dim LInvoiceAddress, LDeliveryAddress
LInvoiceAddress = AFullName & NL
LInvoiceAddress = LInvoiceAddress + iif(GetFieldValue("CompanyName") = "", "", GetFieldValue("CompanyName") & NL)
LInvoiceAddress = LInvoiceAddress + iif(GetFieldValue("AddressLine1") = "", "", GetFieldValue("AddressLine1") & NL)
LInvoiceAddress = LInvoiceAddress + iif(GetFieldValue("AddressLine2") = "", "", GetFieldValue("AddressLine2") & NL)
LInvoiceAddress = LInvoiceAddress + iif(GetFieldValue("Town") = "", "", GetFieldValue("Town") & NL)
LInvoiceAddress = LInvoiceAddress + iif(GetFieldValue("County") = "", "", GetFieldValue("County") & NL)
LInvoiceAddress = LInvoiceAddress + iif(GetFieldValue("Postcode") = "", "", GetFieldValue("Postcode") & NL)
LInvoiceAddress = LInvoiceAddress + iif(GetFieldValue("Country") = "", "", GetFieldValue("Country") & NL)
' (SS,3/11/12) added following to get Country for CorrectCurrencyNV which may deduct VAT depending on Invoice Country, emails are sent from outside of a Session
' this was resulting in emails with VAT not deducted from total figures, add this solved the problem because it puts the country into session variable so that GetInvoiceCountry returns it for CorrectCurrencyNV
SetInvoiceCountry GetFieldValue("Country")
' (SS,12/12/12) added following to VATNumber into session for CorrectCurrencyNV to work correctly to deduct VAT for Intra-Community supply
SetVATNumberForIntraCommunity GetFieldValue("VATNumber")
' (SS,16/11/07) added AllowCollection to show collection text instead
If AllowCollection And IntToBool(GetFieldValue("DeliveryWillCollect")) Then
LDeliveryAddress = "There will be no delivery charge." & NL & "Please contact us to arrange a time to collect your goods."
ElseIf IntToBool(GetFieldValue("DeliveryAddressSameAsInvoice")) Then
LDeliveryAddress = LInvoiceAddress
Else
LDeliveryAddress = iif(GetFieldValue("DeliveryName") = "", "", GetFieldValue("DeliveryName") & NL)
LDeliveryAddress = LDeliveryAddress + iif(GetFieldValue("DeliveryCompanyName") = "", "", GetFieldValue("DeliveryCompanyName") & NL)
LDeliveryAddress = LDeliveryAddress + iif(GetFieldValue("DeliveryAddressLine1") = "", "", GetFieldValue("DeliveryAddressLine1") & NL)
LDeliveryAddress = LDeliveryAddress + iif(GetFieldValue("DeliveryAddressLine2") = "", "", GetFieldValue("DeliveryAddressLine2") & NL)
LDeliveryAddress = LDeliveryAddress + iif(GetFieldValue("DeliveryTown") = "", "", GetFieldValue("DeliveryTown") & NL)
LDeliveryAddress = LDeliveryAddress + iif(GetFieldValue("DeliveryCounty") = "", "", GetFieldValue("DeliveryCounty") & NL)
LDeliveryAddress = LDeliveryAddress + iif(GetFieldValue("DeliveryPostcode") = "", "", GetFieldValue("DeliveryPostcode") & NL)
LDeliveryAddress = LDeliveryAddress + iif(GetFieldValue("DeliveryCountry") = "", "", GetFieldValue("DeliveryCountry") & NL)
End If
SetDeliveryInfo GetFieldValue("DeliveryInfo") ' (SS,9/10/14)
' (SS,29/5/09) modified to use store address when payment slip
' (SS,19/3/10) added tr for translation
' (SS,12/3/18) renamed from ShowEmailAddressDetails and also now returns a string i.e. GetEmailBodyAddressDetails =
GetEmailBodyAddressDetails = GetOrderEmailAddressDetails(tr(iif(AIsPaymentSlip, "To", "Delivery Address")), iif(AIsPaymentSlip, GetStoreAddressAsHTML, ReplaceNewLinesWithBR(LDeliveryAddress)), tr(iif(AIsPaymentSlip, "From", "Billing Address")), ReplaceNewLinesWithBR(LInvoiceAddress))
End Function
' (SS,7/1/16) code moved here from GetEmailBody, also modified to use OpenQuery2 instead of OpenQuery to prevent clash issue
' (SS,12/3/18) changed from Sub to Function which returns a string rather than render directly
' (SS,1/8/18) modified to add additional info to options list (for CIRC)
Function GetEmailBodyOrderItems(AOrderNo)
' (SS,12/3/18) LResult holds the returned email HTML content
Dim LResult
LResult = ""
' (SS,1/8/18) added following to allow additional info to be added for an option
Dim LOptionsList
' (SS,31/10/18) added GetOrderDetailsFilter to filter out subproducts
OpenQuery2("SELECT * FROM orderdetails WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'" & GetOrderDetailsFilter & " ORDER BY OrderDetailID")
Dim tp_ProductCode, tp_ProductName, tp_Price, tp_Qty, tp_ProductTotal
Dim LPrice, LQty
Do While Not EndOfQuery2
tp_ProductCode = GetQueryValue2("ProductCode")
LQty = NZ(GetQueryValue2("Qty"))
tp_Qty = LQty
tp_ProductName = GetQueryValue2("ProductName")
LPrice = NZ(GetQueryValue2("PriceEach"))
tp_Price = FormatNumber(CorrectCurrencyNF(LPrice), 2)
tp_ProductTotal = FormatNumber(CorrectCurrencyNF(LPrice * LQty), 2)
' (SS,1/8/18) add additional custom info to options list
LOptionsList = AddMoreOptionsInfo(GetQueryValue2("ProductID"), GetQueryValue2("OptionsList"))
' (SS,6/7/11) added ProductID and OptionsList
' (SS,2/11/12) changed CorrectCurrency to CorrectCurrencyNV to show prices without VAT for countries that aren't charged VAT
' (SS,12/3/18) renamed from ShowEmailOrderItem, now a function which returns a string, added LResult = LResult &
' (SS,1/8/18) replaced GetQueryValue2("OptionsList") with LOptionsList to allow additional info to be added above
LResult = LResult & GetOrderEmailProductItem(GetQueryValue2("ProductID"), tp_ProductCode, tp_ProductName, LOptionsList, tp_Qty, CorrectCurrencyNV(LPrice), CorrectCurrencyNV(LPrice * LQty))
NextQueryRecord2
Loop
CloseQuery2
' (SS,12/3/18) now returns a string instead of direct rendering
GetEmailBodyOrderItems = LResult
End Function
' (SS,7/1/16) code from GetEmailBody to create just the message
' (SS,28/1/16) fix to prevent MessageToCustomer from being added twice when EmailUseMessageToCustomerForDespatch is True, via LMessageToCustomerAlreadyUsed
' (SS,3/3/16) change to show date on despatch email
' (SS,10/5/16) change to add the text "Tracking may not start working for a few hours"
' (SS,13/12/18) added AOrderNo parameter for use by CancelOrderAllowed and GetCancelOrderURL
' (SS,28/6/19) added additional message for completed order email
Function GetEmailBodyMessage(AOrderNo, AStatus, AFullName)
' (SS,29/5/09) added LPaymentMethod
Dim LPaymentMethod
LPaymentMethod = GetFieldValue("PaymentMethod") ' (SS,29/5/09)
Dim LConfirmationEmailTemplateUsed ' (SS,30/10/15)
LConfirmationEmailTemplateUsed = False ' (SS,7/1/15)
' (SS,28/1/16) added following to prevent message to customer from being added twice
Dim LMessageToCustomerAlreadyUsed
LMessageToCustomerAlreadyUsed = False
' (SS,22/5/09) added code to allow different message for cancelled and completed order
Dim LStatusMessage, LCourier
LCourier = NB(GetFieldValue("Courier"))
If AStatus = ORDER_COMPLETED_STATUS Then
' (SS,23/10/15) if MessageToCustomer is not blank then use this, else obey the previous standard
Dim LMessageToCustomer
If EmailUseMessageToCustomerForDespatch Then
LMessageToCustomerAlreadyUsed = True ' (SS,28/1/16) to prevent MessageToCustomer from being added again later
LMessageToCustomer = Trim(NB(GetFieldValue("MessageToCustomer")))
' (SS,30/10/15) if nothing set then get the default
If LMessageToCustomer = "" Then
LMessageToCustomer = GetSiteDetail("Delivery Agents", "Default")
End If
Else
LMessageToCustomer = ""
End If
If LMessageToCustomer <> "" Then
LMessageToCustomer = ReplaceStr(LMessageToCustomer, "{STORE_NAME}", GetStoreName)
LMessageToCustomer = ReplaceStr(LMessageToCustomer, "{STORE_EMAIL}", GetStoreEmail)
LStatusMessage = ConvertNewlinesToHTML(LMessageToCustomer)
Else
If LCourier <> "" Then LCourier = "via " & LCourier & " "
' LStatusMessage = "Your order has been despatched for delivery " & LCourier & "and should be with you shortly."
' (SS,19/3/10) replaced above with following for translation
'LStatusMessage = trx("Your order has been despatched for delivery <x>and should be with you shortly", LCourier) & "."
' (SS,3/3/16) replaced above with following to add the date on despatch email
LCourier = "on " & FormatDateTime(GetFieldValue("DateTimeCompleted"), 1) & " " & LCourier
' (SS,24/3/16) replaced "Your order was despatched for delivery" with "Your order was despatched"
LStatusMessage = trx("Your order was despatched <x>and should be with you shortly", LCourier) & "."
End If
ElseIf AStatus = ORDER_CANCELLED_STATUS Then
LStatusMessage = tr("Your order has been cancelled") & "." ' (SS,19/3/10) added tr
Else ' email confirmation
' (SS,26/10/15) get from template, override the default if it exists
LStatusMessage = GetSiteDetail("Other", "ConfirmationEmailTemplate")
LConfirmationEmailTemplateUsed = LStatusMessage <> ""
If LStatusMessage = "" Then
LStatusMessage = trx("Thank you for ordering from <x>", GetStoreName) & "."
' (SS,16/2/11) added following for English version only
If IsEnglish Then LStatusMessage = LStatusMessage & " This email is to confirm that your order has been received and we will dispatch the goods as soon as possible."
Else ' (SS,26/10/15)
LStatusMessage = ReplaceStr(LStatusMessage, "{STORE_NAME}", GetStoreName)
LStatusMessage = ReplaceStr(LStatusMessage, "{STORE_EMAIL}", GetStoreEmail)
End If
End If
Dim LEmailMessage ' (SS,7/1/16)
' (SS,16/11/07) added following IntraCommunity Supply message
If IntToBool(GetFieldValue("IntraCommunitySupply")) Then
If LEmailMessage <> "" Then LEmailMessage = LEmailMessage & NL & NL
LEmailMessage = LEmailMessage & tr(INTRA_COMMUNITY_SUPPLY_MESSAGE)
End If
' (SS,22/5/09) message to customer and tracking details
' (SS,13/3/13) tidied the code slightly
Dim LMsg, LTrackingNo
LMsg = ""
LTrackingNo = Trim(GetFieldValue("TrackingNo"))
If LTrackingNo <> "" Then
If IsURL(LTrackingNo) Then
LMsg = "The Tracking URL is <a href=""" & LTrackingNo & """>" & LTrackingNo & "</a>." ' (SS,10/5/16) added the fullstop
Else
LMsg = "The Tracking Number is "
' (SS,13/3/13) added following to add URL to tracking number
Dim LTrackingURL, LTrackingPostcode
' (SS,16/5/13) added Postcode because some courier sites require it
LTrackingPostcode = NB(GetQueryValue("DeliveryPostcode"))
If LTrackingPostcode = "" Then LTrackingPostcode = NB(GetQueryValue("Postcode"))
LTrackingURL = GetTrackingURLForCourier(GetQueryValue("DeliveryAgentName"), GetQueryValue("Courier"), GetFieldValue("TrackingNo"), LTrackingPostcode)
If LTrackingURL <> "" Then
' LTrackingNo = "<a href=""" & LTrackingURL & """>" & LTrackingNo & "</a>"
' (SS,16/5/13) replaced above with following
LTrackingNo = LTrackingNo & ". Please click <a href=""" & LTrackingURL & """>here</a> to track"
End If
LMsg = LMsg & LTrackingNo & "."
End If
LMsg = LMsg & " Tracking may not start working for a few hours." ' (SS,10/5/16) added this, requested by Neil
If LEmailMessage <> "" Then LEmailMessage = LEmailMessage & NL & NL
LEmailMessage = LEmailMessage & LMsg
End If
' (SS,28/1/16) added "If" to prevent MessageToCustomer from being added twice because it may have already been added above
If Not LMessageToCustomerAlreadyUsed Then
LMsg = GetFieldValue("MessageToCustomer")
If LMsg <> "" Then
If LEmailMessage <> "" Then LEmailMessage = LEmailMessage & NL & NL
LEmailMessage = LEmailMessage & LMsg
End If
End If
' (SS,29/5/09) added payment details for cheque or bank transfer
If AStatus = AWAITING_PAYMENT_STATUS Then
If LEmailMessage <> "" Then LEmailMessage = LEmailMessage & NL & NL
If LPaymentMethod = ptCheque Then
LEmailMessage = LEmailMessage & "You have chosen to pay by cheque. Please send a cheque for the full amount payable to " & GetChequesPayableTo & " to the following address: " & NL & NL & TrimAll(GetStoreAddress(False, False))
ElseIf IsPaymentBankTransfer(LPaymentMethod) Then ' (SS,4/8/17) replaced LPaymentMethod = ptBankTransfer with IsPaymentBankTransfer(LPaymentMethod)
' LExtraMessage = LExtraMessage & "You have chosen to pay by bank transfer. Please make a full payment to the following account:" & NL & NL & GetBankTransferDetails
' (SS,19/12/14) replaced above with following
' (SS,9/8/17) replaced GetBankTransferDetails with GetBankTransferDetailsAsHTML
LEmailMessage = LEmailMessage & "You have chosen to pay by bank transfer. " & GetBankTransferPaymentLine & NL & NL & GetBankTransferDetailsAsHTML
End If
' (SS,13/12/18) added following to allow order to be cancelled by URL
If CancelOrderAllowed(AOrderNo) Then
LEmailMessage = LEmailMessage & NL & NL & "If you would like to cancel this order please click <a href=""" + GetCancelOrderURL(AOrderNo) + """>here</a>."
End If
End If
If LEmailMessage <> "" Then
LEmailMessage = ConvertNewlinesToHTML(LEmailMessage)
LEmailMessage = LStatusMessage & "<br><br>" & LEmailMessage
Else
LEmailMessage = LStatusMessage
End If
LEmailMessage = tr("Dear") & " " & AFullName & "," & BR & BR & ConvertNewlinesToHTML(LEmailMessage) & BR & BR
' (SS,16/2/11) if English then use the new improved message
' (SS,23/10/15) if not using message to customer for this or message is blank then assume the previous default
' (SS,30/10/15) moved here from inc-template-email-html.asp, also added Not LConfirmationEmailTemplateUsed, to only add if ConfirmationEmailTemplateUsed hasn't been used
If Not LConfirmationEmailTemplateUsed And (Not EmailUseMessageToCustomerForDespatch Or LStatusMessage = "") Then
Dim LEmailLink ' (SS,16/2/11) added because now in two places
LEmailLink = "<a href=""mailto:" + GetStoreEmail + """>" + GetStoreEmail + "</a>"
If IsEnglish Then
LEmailMessage = LEmailMessage & "If you have any queries please email us on " & LEmailLink & " quoting the order reference." & BR & BR
LEmailMessage = LEmailMessage & "We very much appreciate your custom and hope you are pleased with our products."
Else ' (SS,16/2/11) for non-English carry on using previous version so that it translates correctly
LEmailMessage = LEmailMessage & trx("If you have any queries in relation to the above order please visit our web site at <x>", "<a href=""" & GetStoreURL & """>" & GetStoreURL & "</a>")
End If
LEmailMessage = LEmailMessage & BR & BR
End If
LEmailMessage = LEmailMessage & Iif(IsEnglish, "Kindest regards", tr("Regards")) & "," & BR & BR & tr(GetStoreRegards)
' (SS,28/6/19) added additional message for completed order emails (VDK)
If AStatus = ORDER_COMPLETED_STATUS Then
Dim LAdditionalMessage
LAdditionalMessage = GetSiteDetail("Other", "CompletedOrderEmailAdditionalInfo")
If LAdditionalMessage <> "" Then
LEmailMessage = LEmailMessage & BR & LAdditionalMessage
End If
End If
GetEmailBodyMessage = LEmailMessage ' (SS,7/1/15) value returned from this routine
End Function
' (SS,20/11/08)
' (SS,20/4/18) added header and footer to tidy up buttons, bank transfer appears higher up for HyperFlight
Sub SetUpPaymentButtons
ShowCheckoutPaymentButtonHeader ' (SS,20/4/18)
' (SS,05/10/16) if local mode then show payment method combo and payment reference
If IsLocalMode Then
ShowCheckoutLocalModePayment
SetUpPaymentButton "LOCALMODE"
Else ' (SS,05/10/16) as before
SetUpPaymentButton GetPaymentType
' (SS,20/4/18) for HyperFlight show Bank Transfer before second method i.e. PayPal
If IsHyperFlight And AllowBankTransfers Then SetUpPaymentButton ptBankTransfer
SetUpPaymentButton GetPaymentType2
SetUpPaymentButton GetPaymentType3 ' (SS,4/6/09)
' (SS,28/5/09) added following to allow cheque payments and bank transfers
' (SS,20/4/18) move cheque below bank transfer, added If Not IsHyperFlight to allow Bank Transfer to be shown earlier for HF
If Not IsHyperFlight And AllowBankTransfers Then SetUpPaymentButton ptBankTransfer
If AllowCheques Then SetUpPaymentButton ptCheque
End If
ShowCheckoutPaymentButtonFooter ' (SS,20/4/18)
End Sub
' (SS,20/11/14)
Sub SetUpPayPalExpressConfirmButton
SetUpPaymentButton ptPayPalExpress
End Sub
' (SS,8/9/06)
Function GetReturnFromPaymentURL
GetReturnFromPaymentURL = GetStoreURL + "/products.asp?cmd=checkout&stage=" & RETURN_FROM_THIRD_PARTY_PAYMENT
End Function
' (SS,20/4/18) used to show a normally disabled payment method if main credit card payment failed (i.e. Payzone for HF)
Sub SetCreditCardPaymentProviderFailure
Session("CreditCardPaymentProviderFailure") = True
End Sub
' (SS,20/4/18)
Function IsCreditCardPaymentProviderFailure
IsCreditCardPaymentProviderFailure = Session("CreditCardPaymentProviderFailure") = True
End Function
' (SS,20/11/08) new routine with code from SetUpPayment, now handles two payment types
' (SS,19/1/15) added extra parameter to ShowCheckoutPaymentButton for image, second parameter is now always the caption for the image
' (SS,28/3/18) added Payzone
' (SS,17/4/18) removed ptDirect and ptGoogleCheckout (now redundant)
Sub SetUpPaymentButton(APaymentType)
Dim LPaymentLink
LPaymentLink = GetPaymentSubmitJavascriptLink(APaymentType)
If IsLocalMode Then
' (SS,6/10/16) added local mode
ShowCheckoutPaymentButton LPaymentLink, "Make payment", ""
' (SS,28/5/09) added cheque payment
ElseIf APaymentType = ptCheque Then
' (SS,20/9/11) changed "images/pay-by-cheque.gif" to "Pay by Cheque", now using button instead of gif
' (SS,5/2/15) added images/make-payment-cheque.png
' (SS,20/4/18) removed image "images/make-payment-cheque.png"
ShowCheckoutPaymentButton LPaymentLink, "Pay by Cheque", ""
' (SS,28/5/09) added bank transfer payment
ElseIf APaymentType = ptBankTransfer Then
' *** (SS,20/7/17) added following for custom bank transfer dialog
If FunctionExists("CustomShowBankTransferModal") Then
CustomShowBankTransferModal(Session("Country")) ' (SS,29/9/17) modified to pass on the billing country
Else ' (SS,20/7/17) as before
' (SS,20/9/11) changed "images/pay-by-bank-transfer.gif" to "Pay by Bank Transfer", now using button instead of gif
' (SS,19/1/15) added images/make-payment-bacs.png image
' (SS,20/4/18) removed image "images/make-payment-bacs.png"
ShowCheckoutPaymentButton LPaymentLink, "Pay by Bank Transfer", ""
End If
ElseIf APaymentType = ptWorldPay Then
If GetWorldPayInstallID = "" Then
ShowCheckoutMakePaymentButton "javascript:alert('WorldPay facility not yet enabled')"
Else
'If AccountLoggedIn Then
' ShowCheckoutPayNowWithCreditCardButton LPaymentLink ' replaced "javascript:document.frmWorldPay.submit()" with GetPaymentSubmitJavascriptLink("frmWorldPay")
'Else
'End If
' (SS,16/6/09) removed the above, replaced with check for more than one payment type
If GetPaymentType1 = ptWorldPay And PaymentTypeCount = 1 Then
ShowCheckoutMakePaymentButton LPaymentLink ' replaced "javascript:document.frmWorldPay.submit()" with GetPaymentSubmitJavascriptLink("frmWorldPay")
Else ' because there's more than one payment type we need to show the world pay logo
' (SS,19/1/15) added "Pay by WorldPay" caption
' (SS,10/3/17) changed from .gif to .png
ShowCheckoutPaymentButton LPaymentLink, "Pay by WorldPay", "images/make-payment-worldpay.png"
End If
End If
' (SS,19/1/15) "Pay by PayPal" caption, and replaced make-payment-paypal.gif with make-payment-paypal.png
ElseIf APaymentType = ptPayPal Then
' (SS,9/4/18) added following "If" so that when GetPayPalUKOnlyCheckout is True then PayPal button only shown for UK
' on the normal checkout stage, for PayPal Express Checkout it's still available for all regions
' (SS,20/4/18) added IsCreditCardPaymentProviderFailure to show PayPal if main credit card option failed
If Not GetPayPalUKOnlyCheckout Or IsUK(GetInvoiceCountry) Or IsCreditCardPaymentProviderFailure Then
If GetPayPalBusiness = "" Then
ShowCheckoutPaymentButton "javascript:alert('PayPal facility not yet enabled')", "Pay by PayPal", "images/make-payment-paypal.gif"
Else
' (SS,8/6/18) added surcharge text if surcharge applicable
Dim LPayPalCaption
LPayPalCaption = "Pay by PayPal"
If IsPayPalSurcharge Then LPayPalCaption = LPayPalCaption & "<br><small>" & GetPayPalSurchargePercentage & "% surcharge</small>"
ShowCheckoutPaymentButton LPaymentLink, LPayPalCaption, "images/make-payment-paypal.png"
End If
End If
ElseIf APaymentType = ptEPDQ Then ' (SS,7/9/06)
' (SS,10/5/12) modified to work correctly
If GetEPDQClientID = "" Then
ShowCheckoutMakePaymentButton "javascript:alert('ePDQ facility not yet enabled')"
Else
' (SS,19/1/15) added "Pay by ePDQ" caption, later changed to "Pay by Credit or Debit Card"
ShowCheckoutPaymentButton LPaymentLink, "Pay by credit or debit card", "images/make-payment-epdq.png"
End If
' (SS,20/3/13) added Sage Pay
ElseIf APaymentType = ptSagePay Then
' (SS,22/3/13) enable SagePay for Sage themselves to test, if in test mode and if website was called using pptest=true
If Not IsSagePayTestMode Or IsPaymentProviderTestModeActivated Then
If GetSagePayVendorName = "" Then
' (SS,19/1/15) added "Pay by SagePay" caption
ShowCheckoutPaymentButton "javascript:alert('SagePay facility not yet enabled')", "Pay by SagePay", "images/make-payment-sage-pay.gif"
Else
' ShowCheckoutPaymentButton LPaymentLink, "images/make-payment-sage-pay.gif"
ShowCheckoutMakePaymentButton LPaymentLink
End If
End If
' (SS,20/11/14)
ElseIf APaymentType = ptPayPalExpress Then
' (SS,6/2/15)
'ShowCheckoutMakePayPalPaymentText
' (SS,19/1/15) added "" for no image, new third parameter
'ShowCheckoutPaymentButton LPaymentLink, "Make PayPal Payment", ""
' (SS,19/4/18) following replaces ShowCheckoutMakePayPalPaymentText and ShowCheckoutPaymentButton above
ShowCheckoutMakePayPalPaymentButton LPaymentLink
' (SS,28/3/18) added Payzone
ElseIf APaymentType = ptPayzone Then
' (SS,5/4/18) added If GetPaymentAllowedIP for testing on our local IP address only
If GetPaymentAllowedIP Then
If GetPayzoneMerchantID = "" Then
ShowCheckoutMakePaymentButton "javascript:alert('Payzone facility not yet enabled')"
Else
'ShowCheckoutPaymentButton LPaymentLink, "Pay by credit or debit card", "images/make-payment-payzone.png"
' (SS,20/4/18) replaced above with following
ShowCheckoutPaymentButton LPaymentLink, "Pay by Visa or MasterCard<br><small>Preferred :-)</small>", "images/make-payment-payzone.png"
End If
End If
End If
' (SS,16/4/18) redirection payment stage/form (with 2 sec delay) now removed, the hidden payment form is shown here
' hidden and just below the button, by call to SetUpPayForm
SetUpPayForm APaymentType, GetOrderNo
End Sub
' (SS,28/9/07) new routine with code from SetUpPayment, now handles two payment types
' (SS,20/11/08) button code moved to SetUpPaymentButtons
' (SS,28/3/18) added Payzone
' (SS,17/4/18) removed ptDirect, SetUpDirectPayForm and ptGoogleCheckout
' (SS,18/4/18) also removed ptPayPalExpress which is not necessary here
' (SS,19/4/18) added ptPayPalExpress back in, was required for final stage when using PayPal checkout button from basket page
Sub SetUpPayForm(APaymentType, AOrderNo)
' (SS,5/10/16) added SetUpLocalModePayForm for local mode
If IsLocalMode Then
SetUpLocalModePayForm(AOrderNo)
ElseIf APaymentType = ptWorldPay Then
SetUpWorldPayForm(AOrderNo)
ElseIf APaymentType = ptPayPal Then
' (SS,15/12/14) added If UsePayPalExpress (later GetPayPalExpressEnabled) to use PayPal Express instead
If GetPayPalExpressEnabled Then
'DoPayPalExpress True
' (SS,16/4/18) replaced above with following (i.e. added back in) because we're no longer redirectly for any payment form
' we're now setting up for every payment method in one page, so we can't call DoPayPalExpress
' when this routine is called, because it'll go to PayPal immediately
' (SS,19/4/18) renamed from SetUpPayPalExpressForm to SetUpPayPalExpressFormFromLaterStage
SetUpPayPalExpressFormFromLaterStage(AOrderNo)
Else
SetUpPayPalForm(AOrderNo)
End If
' (SS,10/12/14) added following for PayPalExpress
ElseIf APaymentType = ptPayPalExpress Then
SetUpPayPalExpressForm(AOrderNo) ' (SS,19/4/18) renamed from SetUpPayPalExpressPayForm to SetUpPayPalExpressForm
ElseIf APaymentType = ptEPDQ Then ' (SS,7/9/06)
SetUpEPDQForm(AOrderNo)
ElseIf APaymentType = ptSagePay Then ' (SS,20/3/13)
SetUpSagePayForm(AOrderNo)
' (SS,16/4/18) replaced SetUpChequeOrBankTransferPayForm with following separate cheque and bank transfer
ElseIf APaymentType = ptCheque Then
SetUpChequePayForm(AOrderNo)
ElseIf APaymentType = ptBankTransfer Then
SetUpBankTransferPayForm(AOrderNo)
' (SS,28/3/18) added Payzone
ElseIf APaymentType = ptPayzone Then
SetUpPayzoneForm(AOrderNo)
End If
End Sub
' (SS,28/5/09)
' (SS,16/4/18) replaced SetUpChequeOrBankTransferPayForm with new separate SetUpChequePayForm and SetUpBankTransferPayForm
Sub SetUpChequePayForm(AOrderNo)
' (SS,21/2/14) replaced GetCheckoutLink with GetReturnFromPaymentURL, which is the same with stage=co at the end
' for unique URL for AdWord purposes (Clifton)
' (SS,21/7/17) added banktransferoption
%>
<form name="frmChequePay" action="<%=GetReturnFromPaymentURL%>" method="post" class="nospace">
<input type="hidden" name="stage" value="<%=CONFIRM_PAYMENT%>">
<input type="hidden" name="paymenttype" value="<%=ptCheque%>">
</form>
<%
End Sub
' (SS,16/4/18) replaced SetUpChequeOrBankTransferPayForm with new separate SetUpChequePayForm and SetUpBankTransferPayForm
Sub SetUpBankTransferPayForm(AOrderNo)
' (SS,21/2/14) replaced GetCheckoutLink with GetReturnFromPaymentURL, which is the same with stage=co at the end
' for unique URL for AdWord purposes (Clifton)
' (SS,21/7/17) added banktransferoption
%>
<form name="frmBankTransferPay" action="<%=GetReturnFromPaymentURL%>" method="post" class="nospace">
<input type="hidden" name="stage" value="<%=CONFIRM_PAYMENT%>">
<input type="hidden" name="paymenttype" value="<%=ptBankTransfer%>">
<input type="hidden" name="banktransferoption" value="<%=CleanRequestForm("banktransferoption")%>">
</form>
<%
End Sub
' (SS,5/10/16) used by local mode
Sub SetUpLocalModePayForm(AOrderNo)
%>
<form name="frmLocalModePay" action="<%=GetReturnFromPaymentURL%>" method="post" class="nospace">
<input type="hidden" name="stage" value="<%=CONFIRM_PAYMENT%>">
<input type="hidden" name="paymenttype" value="<%=GetChosenPaymentType%>">
<input type="hidden" name="paymentref" value="<%=GetLocalModePaymentReference%>">
</form>
<%
End Sub
' --------------------------
' Start of WorldPay routines
' --------------------------
' (SS,6/7/07) added HTMLEncode to some of the values to allow quotes etc
' (SS,30/4/10) added language
Sub SetUpWorldPayForm(AOrderNo)
Dim wpInstID, wpCartID, wpDesc, wpAmount, wpCurrency
Dim wpTestMode, wpName, wpAddress, wpPostcode, wpCountry, wpEmail, wpTel, wpLang
wpLang = GetWorldPayLanguageCode ' (SS,30/4/10)
wpInstID = GetWorldPayInstallID
wpCartID = AOrderNo
wpDesc = GetDescriptionForWorldPay
' (SS,14/11/12) added FormatNumber(x, 2) to ensure two decimal digits
' (SS,20/6/13) replaced FormatNumber(x, 2) with Format2dpnc(x) to remove commas, which was causing transactions above �1000 to fail
wpAmount = Format2dpnc(Session("PaymentAmount"))
wpCurrency = GetCurrencyCode ' (SS,19/9/06) was "GBP"
wpTestMode = GetWorldPayTestMode ' for normal mode this should be "0", test mode it should be "100"
wpName = GetNameForWorldPay
wpAddress = GetAddressForWorldPay
wpPostcode = GetPostcodeForWorldPay
wpCountry = GetCountryISOCode(GetCountryForWorldPay)
wpEmail = Session("Email")
wpTel = Session("Telephone")
' (SS,14/11/12) added MD5 for extra security to prevent sabotage of form values
Dim wpMD5SecretKey, wpSignature
wpMD5SecretKey = GetWorldPayMD5SecretKey ' (SS,14/11/12)
If wpMD5SecretKey = "" Then
wpSignature = ""
Else
' the following fields are used md5:amount:currency:cartId:testMode:instId
' the value set in WorldPay Signature fields is: amount:currency:cartId:testMode:instId (excluding the md5)
wpSignature = GetSQLValueAsString("SELECT CAST(MD5('" & wpMD5SecretKey & ":" & wpAmount & ":" & wpCurrency & ":" & wpCartID & ":" & wpTestMode & ":" & wpInstID & "') AS CHAR(32))")
End If
' (SS,27/01/10) replaced old URL which wasn't redirecting https://select.worldpay.com/wcc/purchase
' with new one https://secure.wp3.rbsworldpay.com/wcc/purchase
' (SS,11/08/11) replaced rbsworldpay URL with following:
' https://secure.worldpay.com/wcc/purchase
' as mentioned here:
' http://www.worldpay.com/media/index.php?page=archive&sub=worldpay-url-changes&c=WW
%>
<form name="frmWorldPay" action="https://secure.worldpay.com/wcc/purchase" method="post" class="nospace">
<input type="hidden" name="lang" value="<%=wpLang%>">
<%
' (SS,21/5/10) added following to hide currency selection
If GetPaymentHideCurrency Then
%>
<input type="hidden" name="hideCurrency" value="true">
<%
End If
%>
<input type="hidden" name="instId" value="<%=wpInstID%>">
<input type="hidden" name="cartId" value="<%=wpCartID%>">
<input type="hidden" name="desc" value="<%=HTMLEncode(wpDesc)%>">
<input type="hidden" name="amount" value="<%=wpAmount%>">
<input type="hidden" name="currency" value="<%=wpCurrency%>">
<input type="hidden" name="testMode" value="<%=wpTestMode%>">
<input type="hidden" name="name" value="<%=HTMLEncode(wpName)%>">
<input type="hidden" name="address" value="<%=HTMLEncode(wpAddress)%>">
<input type="hidden" name="postcode" value="<%=HTMLEncode(wpPostcode)%>">
<input type="hidden" name="country" value="<%=wpCountry%>">
<input type="hidden" name="email" value="<%=HTMLEncode(wpEmail)%>">
<input type="hidden" name="tel" value="<%=HTMLEncode(wpTel)%>">
<input type="hidden" name="M_OrderNo" value="<%=AOrderNo%>">
<input type="hidden" name="M_SessionID" value="<%=HTMLEncode(FSessionID)%>">
<input type="hidden" name="M_Name" value="<%=HTMLEncode(wpName)%>">
<input type="hidden" name="M_Email" value="<%=HTMLEncode(wpEmail)%>">
<input type="hidden" name="M_Desc" value="<%=HTMLEncode(wpDesc)%>">
<input type="hidden" name="fixContact" value="">
<%If wpSignature <> "" Then%><input type="hidden" name="signature" value="<%=wpSignature%>"><%End If%>
</form>
<%
' (SS,20/11/08) moved button from here to SetUpPaymentButton
End Sub
Function GetNameForWorldPay()
' (SS,13/8/05) removed If DeliveryAddressEntered, don't know why it was here, DeliveryName is relevant for WorldPay
'If DeliveryAddressEntered Then
' GetNameForWorldPay = Session("DeliveryName")
'Else
GetNameForWorldPay = GetFullName(Session("Title"), Session("FirstName"), Session("Surname"))
'End If
End Function
Function GetAddressForWorldPay()
Dim NL
NL = " " ' HTML line feed '
Dim LAddress
LAddress = LAddress + iif(Session("Address1") = "", "", Session("Address1") & NL)
LAddress = LAddress + iif(Session("Address2") = "", "", Session("Address2") & NL)
LAddress = LAddress + iif(Session("Town") = "", "", Session("Town") & NL)
LAddress = LAddress + iif(Session("County") = "", "", Session("County"))
GetAddressForWorldPay = Left(LAddress, 255) ' make sure its maximum of 255 characters '
End Function
Function GetPostcodeForWorldPay()
GetPostcodeForWorldPay = Session("Postcode")
End Function
Function GetCountryForWorldPay()
GetCountryForWorldPay = Session("Country")
End Function
Function GetDescriptionForWorldPay()
GetDescriptionForWorldPay = Left(Session("PaymentDescription"), 255) ' max of 255 chars '
End Function
' --------------------------
' End of WorldPay routines
' --------------------------
' --------------------------
' Start of PayPal routines
' --------------------------
' (SS,10/12/14)
' (SS,17/4/18) removed separate Sub SetUpPayPalExpressPayFormFromLaterStage, which was identical to this and not called anywhere
' (SS,17/4/18) replaced CONFIRM_PAYMENT with CS_PAYPAL_EXPRESS_CHECKOUT, and GetChosenPaymentType with ptPayPalExpress
' (SS,17/4/18) renamed from SetUpPayPalExpressPayForm to SetUpPayPalExpressForm, form name changed from "frmPayPalExpressPay" to "frmPayPal" so that it gets submitted by same PayPal button as normal PayPal
' (SS,18/4/18) replaced GetReturnFromPaymentURL with LURL i.e. products.asp?cmd=checkout&stage=" & CS_PAYPAL_EXPRESS_CHECKOUT
' Removed hidden stage and paymenttype form fields because it's in the querystring
' (SS,19/4/18) renamed this SetUpPayPalExpressForm to SetUpPayPalExpressFormFromLaterStage because had to have separate one for normal PayPal express checkout
Sub SetUpPayPalExpressFormFromLaterStage(AOrderNo)
Dim LURL
' xcmd=paypallaterstage indicates that we're using a later stage
LURL = "/products.asp?cmd=checkout&stage=" & CS_PAYPAL_EXPRESS_CHECKOUT & "&xcmd=paypallaterstage"
%>
<form name="frmPayPal" action="<%=LURL%>" method="post" class="nospace">
<input type="hidden" name="paymenttype" value="<%=ptPayPalExpress%>">
</form>
<%
End Sub
' (SS,10/12/14)
' (SS,19/4/18) This is for normal PayPal Express checkout from basket page (i.e. not from later stage)
' Form renamed from frmPayPalExpressPay to frmPayPalExpress, replaced GetChosenPaymentType with ptPayPalExpress
Sub SetUpPayPalExpressForm(AOrderNo)
%>
<form name="frmPayPalExpress" action="<%=GetReturnFromPaymentURL%>" method="post" class="nospace">
<input type="hidden" name="stage" value="<%=CONFIRM_PAYMENT%>">
<input type="hidden" name="paymenttype" value="<%=ptPayPalExpress%>">
</form>
<%
End Sub
' (SS,6/7/07) added HTMLEncode to some of the values to allow quotes etc
' (SS,16/6/09) make sure paypalcallbacks table exists in database
' (SS,10/9/14) added LCallbackURL i.e. notify_url, to allow more than one site to be used by the same PayPal account
Sub SetUpPayPalForm(AOrderNo)
Dim LBusiness, LReturnURL, LCallbackURL, LCancelURL, LDescription, LAmount, LCurrency, LShippingAddressFlag
Dim LEmail, LFirstName, LLastName, LAddress1, LAddress2, LCity, LCounty, LPostcode, LCountry
LBusiness = GetPayPalBusiness
LReturnURL = GetStoreURL + "/products.asp?cmd=checkout&stage=" & RETURN_FROM_THIRD_PARTY_PAYMENT
LCallbackURL = GetStoreURL + "/paypal-callback.asp" ' (SS,10/9/14)
LCancelURL = GetStoreURL + "/products.asp"
LDescription = GetDescriptionForPayPal
LAmount = Session("PaymentAmountGBP") ' (SS,17/2/12) replaced PaymentAmount with PaymentAmountGBP because we're paying using GBP even if another currency has been chosen
LCurrency = "GBP"
LShippingAddressFlag = GetPayPalShippingAddressFlag ' (SS,22/6/07) also changed "1" to =LShippingAddressFlag
LEmail = Left(Session("Email"), 127)
LFirstName = Left(Session("FirstName"), 32)
LLastName = Left(Session("Surname"), 64)
LAddress1 = Left(Session("Address1"), 100)
LAddress2 = Left(Session("Address2"), 100)
LCity = Left(Session("Town"), 40)
LCounty = Left(Session("County"), 32)
LPostcode = Left(Session("Postcode"), 32)
LCountry = GetCountryISOCode(Session("Country"))
' (SS,22/6/07) added following to pass delivery address instead to PayPal to allow printing of PayPal delivery slips
' this will end up being used as default for billing address, but user can override it. Neil preferred to have delivery address
' for printing of delivery labels with postage. This is only done when Shipping flag isn't "1"
' We can't use delivery name because PayPal uses separate FirstName and Surname, but it's most likely to be the same.
' (SS,2/7/13) removed the following because it was causing problems with PayPal for some customers with delivery address different to billing address
' PayPal delivery labels are not printed by Neil, haven't been for a very long time.
'If LShippingAddressFlag <> "1" And Not Session("DeliveryAddressSameAsInvoice") Then
' LAddress1 = Left(Session("DeliveryAddress1"), 100)
' LAddress2 = Left(Session("DeliveryAddress2"), 100)
' LCity = Left(Session("DeliveryTown"), 40)
' LCounty = Left(Session("DeliveryCounty"), 32)
' LPostcode = Left(Session("DeliveryPostcode"), 32)
' LCountry = GetCountryISOCode(Session("DeliveryCountry"))
'End If
Dim LActionURLPrefix
If GetPayPalTestMode Then
LActionURLPrefix = "https://www.sandbox.paypal.com"
Else
LActionURLPrefix = "https://www.paypal.com"
End If
%>
<form name="frmPayPal" action="<%=LActionURLPrefix%>/cgi-bin/webscr" method="post" class="nospace">
<input type="hidden" name="cmd" value="_xclick">
<input type="hidden" name="business" value="<%=LBusiness%>">
<input type="hidden" name="return" value="<%=LReturnURL%>">
<input type="hidden" name="notify_url" value="<%=LCallbackURL%>">
<input type="hidden" name="item_name" value="<%=HTMLEncode(LDescription)%>">
<input type="hidden" name="amount" value="<%=LAmount%>">
<input type="hidden" name="invoice" value="<%=AOrderNo%>">
<input type="hidden" name="custom" value="<%=HTMLEncode(FSessionID)%>">
<input type="hidden" name="currency_code" value="<%=LCurrency%>">
<input type="hidden" name="no_shipping" value="<%=LShippingAddressFlag%>">
<input type="hidden" name="cancel_return" value="<%=LCancelURL%>">
<input type="hidden" name="no_note" value="1">
<input type="hidden" name="rm" value="2">
<input type="hidden" name="email" value="<%=HTMLEncode(LEmail)%>">
<input type="hidden" name="first_name" value="<%=HTMLEncode(LFirstName)%>">
<input type="hidden" name="last_name" value="<%=HTMLEncode(LLastName)%>">
<input type="hidden" name="address1" value="<%=HTMLEncode(LAddress1)%>">
<input type="hidden" name="address2" value="<%=HTMLEncode(LAddress2)%>">
<input type="hidden" name="city" value="<%=HTMLEncode(LCity)%>">
<input type="hidden" name="state" value="<%=HTMLEncode(LCounty)%>">
<input type="hidden" name="zip" value="<%=HTMLEncode(LPostcode)%>">
<input type="hidden" name="country" value="<%=LCountry%>">
</form>
<%
' (SS,28/9/07) removed following, added PayPal image to form above
' (SS,10/11/08) went back to this method, image removed from above
' <input alt="" src="images/make-payment-paypal.gif" type="image"/>
' (SS,20/11/08) moved button from here to SetUpPaymentButton
End Sub
' (SS,24/11/14)
Function InPayPalExpress
InPayPalExpress = Session("PreviousCheckoutStage") = CS_PAYPAL_EXPRESS_RETURN
End Function
' (SS,18/11/14) following relies on <!-- #include file ="paypalfunctions.asp" -->
' (SS,15/12/14) added ALaterStage which is True if called from a later stage after user logged in or entered address details
' (SS,12/2/15) added Description to send to PayPal
Sub DoPayPalExpress(ALaterStage)
' (SS,18/11/14) added following to get it to work
' (SS,12/2/15) added LDescription
Dim LPaymentAmount, LDescription, LCurrencyCodeType, LPaymentType, LReturnURL, LCancelURL, LResArray, LAck
' ==================================
' PayPal Express Checkout Module
' ==================================
' (SS,18/11/14) removed following
' On Error Resume Next
'------------------------------------
' The paymentAmount is the total value of
' the shopping cart, that was set
' earlier in a session variable
' by the shopping cart page
'------------------------------------
' (SS,17/2/15) added "if" and move some code here
If ALaterStage Then
LPaymentAmount = Session("PaymentAmountGBP")
LDescription = GetDescriptionForPayPal ' (SS,12/2/15)
AddToPaymentLog "PayPal", "DoPayPalExpress called with ALaterStage = True" ' (SS,18/4/18)
Else
LPaymentAmount = GetBasketGrandTotal ' (SS,19/1/15) added GetBasketGrandTotal, replacing Session("Payment_Amount") or 1000 default
LDescription = GetBasketDescription ' (SS,17/2/15)
AddToPaymentLog "PayPal", "DoPayPalExpress called with ALaterStage = False" ' (SS,18/4/18)
End If
' (SS,8/6/18) apply PayPal surcharge if applicable
LPaymentAmount = PayPalAmountWithSurcharge(LPaymentAmount)
'------------------------------------
' The currencyCodeType and paymentType
' are set to the selections made on the Integration Assistant
'------------------------------------
LCurrencyCodeType = "GBP"
LPaymentType = "Sale"
'------------------------------------
' The returnURL is the location where buyers return to when a
' payment has been succesfully authorized.
'
' This is set to the value entered on the Integration Assistant
'------------------------------------
' returnURL = GetStoreURL & "/products.asp?cmd=checkout&xcmd=paypal_express_return"
'------------------------------------
' The cancelURL is the location buyers are sent to when they click the
' return to XXXX site where XXX is the merhcant store name
' during payment review on PayPal
'
' This is set to the value entered on the Integration Assistant
'------------------------------------
'cancelURL = GetStoreURL & "/products.asp?cmd=checkout&stage=" & CS_PAYPAL_EXPRESS_CANCEL
' (SS,11/12/14) replaced above with following because check out with PayPal is on the basket page
' (SS,16/12/14) added If ALaterStage and different URL to return to correct payment method selection / confirm order stage, i.e. not the basket
LReturnURL = GetStoreURL & "/products.asp?cmd=checkout&stage=" & CS_PAYPAL_EXPRESS_RETURN
If ALaterStage Then
' (SS,16/12/14) added following to return to a different URL if from a later stage, so that payment can be processed immediately
LReturnURL = LReturnURL & "&xcmd=paypallaterstage"
LCancelURL = GetStoreURL & "/" & GetCheckoutConfirmOrderURL
Else
LCancelURL = GetStoreURL & "/products.asp?cmd=basket&stage=" & CS_PAYPAL_EXPRESS_CANCEL
End If
'
'------------------------------------
' Calls the SetExpressCheckout API call
'
' The CallShortcutExpressCheckout function is defined in the file PayPalFunctions.asp,
' it is included at the top of this file.
'-------------------------------------------------
' (SS,15/12/14) added If Not
If Not ALaterStage Then
' (SS,12/2/15) added LDescription
Set LResArray = PP_CallShortcutExpressCheckout(LPaymentAmount, LDescription, LCurrencyCodeType, LPaymentType, LReturnURL, LCancelURL)
Else ' (SS,15/12/14)
Dim LShipToName, LShipToStreet, LShipToStreet2, LShipToCity, LShipToState, LShipToCountryCode, LShipToZip, LPhoneNum, LEmail
LShipToName = Left(Session("FirstName"), 32) + " " + Left(Session("Surname"), 64)
LShipToStreet = Left(Session("Address1"), 100)
LShipToStreet2 = Left(Session("Address2"), 100)
LShipToCity = Left(Session("Town"), 40)
LShipToState = Left(Session("County"), 32)
LShipToCountryCode = GetCountryISOCode(Session("Country"))
LShipToZip = Left(Session("Postcode"), 32)
LPhoneNum = Session("Telephone")
LEmail = Left(Session("Email"), 127)
'LShippingAddressFlag = GetPayPalShippingAddressFlag ' (SS,22/6/07) also changed "1" to =LShippingAddressFlag
'LFirstName = Left(Session("FirstName"), 32)
'LLastName = Left(Session("Surname"), 64)
'LAddress1 = Left(Session("Address1"), 100)
'LAddress2 = Left(Session("Address2"), 100)
'LCity = Left(Session("Town"), 40)
'LCounty = Left(Session("County"), 32)
'LPostcode = Left(Session("Postcode"), 32)
'LCountry = GetCountryISOCode(Session("Country"))
' (SS,12/2/15) added LDescription
' (SS,17/2/15) tidied variable names and moved LShipToStreet2 from before LPhoneNum to after LShipToStreet
Set LResArray = PP_CallMarkExpressCheckout(LPaymentAmount, LDescription, LCurrencyCodeType, LPaymentType, LReturnURL, LCancelURL, LShipToName, LShipToStreet, LShipToStreet2, LShipToCity, LShipToState, LShipToCountryCode, LShipToZip, LPhoneNum, LEmail)
End If
LAck = UCase(LResArray("ACK"))
If LAck = "SUCCESS" Then
' Redirect to paypal.com
PP_ReDirectURL LResArray("TOKEN"), ALaterStage ' (SS,16/12/14) added ALaterStage
Else
'Display a user friendly Error on the page using any of the following error information returned by PayPal
' (SS,3/2/15) replaced with ShowPayPalExpressError
ShowPayPalExpressError LResArray
End If
End Sub
' (SS,19/11/14), returns False if invalid, True if okay
Function GetDetailsFromPayPalExpress
Dim LToken
LToken = CleanRequest("token")
Session("PayPalExpressToken") = LToken ' (SS,24/11/14) save for next stage
If LToken = "" Then
GetDetailsFromPayPalExpress = False
Exit Function
End If
Dim LResultsDict
Set LResultsDict = PP_GetShippingDetails(LToken)
If UCase(LResultsDict("ACK")) <> "SUCCESS" Then
ShowPayPalExpressError LResultsDict
GetDetailsFromPayPalExpress = False
Exit Function
End If
' (SS,19/11/14) log all values to log file
Dim i, LKeys, LItems, LList
LKeys = LResultsDict.Keys
LItems = LResultsDict.Items
LList = NL
For i = 0 To LResultsDict.Count - 1
' Response.Write LKeys(i) & " = " & LItems(i) & "<BR>"
LList = LList & LKeys(i) & " = " & LItems(i) & NL
Next
DebugLog -1, LList
' (SS,17/2/15)
' (SS,23/8/17) added "PayPal" and removed False
AddToPaymentLog "PayPal", "---GetDetailsFromPayPalExpress---" & NL & LList
GetDetailsFromPayPalExpress = True
Dim LCountry, LDeliveryCountry
Session("Title") = Left(LResultsDict("SALUTATION"), 15)
' middle name not used
' Session("FirstName") = Left(Proper(Trim(LResultsDict("FIRSTNAME") & " " & LResultsDict("MIDDLENAME"))), 100)
Session("FirstName") = Left(Proper(Trim(LResultsDict("FIRSTNAME"))), 100)
Session("Surname") = Left(Proper(Trim(LResultsDict("LASTNAME"))), 100)
Session("CompanyName") = Left(Proper(Trim(LResultsDict("BUSINESS"))), 100)
Session("Address1") = Left(Proper(Trim(LResultsDict("STREET"))), 100)
Session("Address2") = Left(Proper(Trim(LResultsDict("STREET2"))), 100)
Session("Town") = Left(Proper(Trim(LResultsDict("CITY"))), 100)
Session("County") = Left(Proper(Trim(LResultsDict("STATE"))), 100)
LCountry = GetCountryNameForCode(LResultsDict("COUNTRYCODE"))
Session("Postcode") = Left(CorrectPostcode(LResultsDict("ZIP"), LCountry), 20)
Session("Country") = LCountry
Session("Telephone") = Left(Trim(LResultsDict("PHONENUM")), 100)
' (SS,16/2/15) replace "+44 " with 0 (for UK, requested by Deborah)
If Left(Session("Telephone"), 4) = "+44 " Then Session("Telephone") = "0" + Mid(Session("Telephone"), 5)
'Session("Mobile") = Trim(CleanRequestForm("Mobile"))
Session("Email") = Left(Trim(LResultsDict("EMAIL")), 100)
'Session("Password") = Trim(CleanRequestForm("Password"))
'Session("Password2") = Trim(CleanRequestForm("Password2"))
' (SS,18/4/12) added "CollectionOnlyOrder Or" to following because disabled DeliveryWillCollect checkbox doesn't return "checked" when checkbox is disabled
Session("DeliveryWillCollect") = CollectionOnlyOrder Or (CleanRequestForm("DeliveryWillCollect") = "checked") ' (SS,14/11/07)
'Session("DeliveryAddressSameAsInvoice") = (CleanRequestForm("DeliveryAddressSameAsInvoice") = "checked")
' (SS,1/6/07)
'* Session("Subscribe") = (CleanRequestForm("Subscribe") = "checked")
'* Session("HearAboutUs") = CleanRequestForm("HearAboutUs")
' Session("DeliveryDate") = CleanRequestForm("DeliveryDate") ' (SS,17/4/09)
' Session("VoucherCode") = UCase(Trim(CleanRequestForm("VoucherCode"))) ' (SS,22/04/09)
' (SS,17/3/09) added following to ensure this value is set to true when DeliveryAddressSameAsInvoice checkbox isn't shown
' If Not AllowDifferentDeliveryAddress Then Session("DeliveryAddressSameAsInvoice") = True
' Session("PurchaseOrderNo") = CleanRequestForm("PurchaseOrderNo")
' (SS,21/8/12) added Left(.., GetMaxMessageLength) to limit length to x characters, currently 600, form also has this limit (using truncation prevents hackers/spammers from posting very long strings)
Session("Message") = Left(Proper(Trim(LResultsDict("PAYMENTREQUEST_0_NOTETEXT"))), GetMaxMessageLength)
Session("DeliveryName") = Left(Proper(Trim(LResultsDict("PAYMENTREQUEST_0_SHIPTONAME"))), 100)
Session("DeliveryCompanyName") = Left(Proper(Trim(LResultsDict("BUSINESS"))), 100)
Session("DeliveryAddress1") = Left(Proper(Trim(LResultsDict("PAYMENTREQUEST_0_SHIPTOSTREET"))), 100)
Session("DeliveryAddress2") = Left(Proper(Trim(LResultsDict("PAYMENTREQUEST_0_SHIPTOSTREET2"))), 100)
Session("DeliveryTown") = Left(Proper(Trim(LResultsDict("PAYMENTREQUEST_0_SHIPTOCITY"))), 100)
Session("DeliveryCounty") = Left(Proper(Trim(LResultsDict("PAYMENTREQUEST_0_SHIPTOSTATE"))), 100)
LDeliveryCountry = GetCountryNameForCode(LResultsDict("PAYMENTREQUEST_0_SHIPTOCOUNTRYCODE"))
Session("DeliveryPostcode") = Left(CorrectPostcode(LResultsDict("PAYMENTREQUEST_0_SHIPTOZIP"), LDeliveryCountry), 20)
Session("DeliveryCountry") = LDeliveryCountry
' (SS,22/1/15) compare addresses and set DeliveryAddressSameAsInvoice flag
If Session("FirstName") & " " & Session("Surname") = Session("DeliveryName") And Session("CompanyName") = Session("DeliveryCompanyName") And Session("Address1") = Session("DeliveryAddress1") And Session("Address2") = Session("DeliveryAddress2") And Session("Town") = Session("DeliveryTown") And Session("County") = Session("DeliveryCounty") And Session("Postcode") = Session("DeliveryPostcode") And Session("Country") = Session("DeliveryCountry") Then
Session("DeliveryAddressSameAsInvoice") = True
Else
Session("DeliveryAddressSameAsInvoice") = False
End If
' (SS,22/1/15) check countries, make sure they're in the list of allowed countries
If Not IsValidCountry(LDeliveryCountry) Then
GetDetailsFromPayPalExpress = False
SetAlertMessage "Sorry we don't ship to '" & LDeliveryCountry & "'"
ElseIf Not IsValidCountry(LCountry) Then
GetDetailsFromPayPalExpress = False
SetAlertMessage "Sorry we don't invoice to '" & LCountry & "'"
End If
'email = resArray("EMAIL") ' Email address of payer.
'* payerId = resArray("PAYERID") ' Unique PayPal customer account identification number.
'* payerStatus = resArray("PAYERSTATUS") ' Status of payer. Character length and limitations: 10 single-byte alphabetic characters.
'salutation = resArray("SALUTATION") ' Payer's salutation.
'firstName = resArray("FIRSTNAME") ' Payer's first name.
'middleName = resArray("MIDDLENAME") ' Payer's middle name.
'lastName = resArray("LASTNAME") ' Payer's last name.
'*suffix = resArray("SUFFIX") ' Payer's suffix.
'cntryCode = resArray("COUNTRYCODE") ' Payer's country of residence in the form of ISO standard 3166 two-character country codes.
'business = resArray("BUSINESS") ' Payer's business name.
'shipToName = resArray("PAYMENTREQUEST_0_SHIPTONAME") ' Person's name associated with this address.
'shipToStreet = resArray("PAYMENTREQUEST_0_SHIPTOSTREET") ' First street address.
'shipToStreet2 = resArray("PAYMENTREQUEST_0_SHIPTOSTREET2") ' Second street address.
'shipToCity = resArray("PAYMENTREQUEST_0_SHIPTOCITY") ' Name of city.
'shipToState = resArray("PAYMENTREQUEST_0_SHIPTOSTATE") ' State or province
'shipToCntryCode = resArray("PAYMENTREQUEST_0_SHIPTOCOUNTRYCODE") ' Country code.
'shipToZip = resArray("PAYMENTREQUEST_0_SHIPTOZIP") ' U.S. Zip code or other country-specific postal code.
'* addressStatus = resArray("ADDRESSSTATUS") ' Status of street address on file with PayPal
'* invoiceNumber = resArray("INVNUM") ' Your own invoice or tracking number, as set by you in the element of the same name in SetExpressCheckout request .
'phonNumber = resArray("PHONENUM") ' Payer's contact telephone number. Note: PayPal returns a contact telephone number only if your Merchant account profile settings require that the buyer enter one.
End Function
' (SS,24/11/14)
' (SS,10/12/14) returns True if successful
' (SS,20/1/15) added AOrderStatus, APaymentReference and APaymentInfo parameters
' (SS,8/6/18) added APaymentAmountReceived parameter, due to new surcharge feature
' (SS,25/6/18) added APaymentProviderSurcharge and APaymentProviderFee
' (SS,26/6/18) renamed APaymentAmountReceived to APaymentReceivedAmount, added APaymentReceivedCurrencyCode
Function PayPalExpressConfirmPayment(ByRef AOrderStatus, ByRef APaymentReference, ByRef APaymentInfo, ByRef APaymentReceivedAmount, ByRef APaymentReceivedCurrencyCode, ByRef APaymentProviderSurcharge, ByRef APaymentProviderFee)
' Response.Write "### PayPalExpressConfirmPayment 1 ###<br>"
Dim LToken, LFinalPaymentAmount
LToken = SESSION("PP_token") ' Session("PayPalExpressToken")
'------------------------------------
' The paymentAmount is the total value of
' the shopping cart, that was set
' earlier in a session variable
' by the shopping cart page
'------------------------------------
LFinalPaymentAmount = Session("PaymentAmountGBP") ' (SS,6/2/15) replaced PaymentAmount with PaymentAmountGBP
' (SS,8/6/18) apply PayPal surcharge if applicable
LFinalPaymentAmount = PayPalAmountWithSurcharge(LFinalPaymentAmount)
' (SS,25/6/18) calculate the extra charge added i.e. surcharge
APaymentProviderSurcharge = Round2dp(LFinalPaymentAmount - Session("PaymentAmountGBP"))
'------------------------------------
' Calls the GetExpressCheckoutDetails API call
'
' The GetShippingDetails function is defined in PayPalFunctions.asp
' included at the top of this file.
'-------------------------------------------------
'Response.Write "### Reached here: 10 (TOKEN:" & SESSION("PayPalExpressToken") & ")###<br>"
Dim LResultsDict, LErrorCode
'Response.Write "### Reached here: 11 (PP_PAYERID:" & SESSION("PP_PAYERID") & ")###<br>"
Set LResultsDict = PP_GetShippingDetails( LToken )
If UCase(LResultsDict("ACK")) <> "SUCCESS" Then
'Display a user friendly Error on the page using any of the following error information returned by PayPal
ShowPayPalExpressError LResultsDict
PayPalExpressConfirmPayment = False
Exit Function
End If
'Response.Write "### Reached here: 12 (PP_PAYERID:" & SESSION("PP_PAYERID") & ")###<br>"
Set LResultsDict = PP_ConfirmPayment( LFinalPaymentAmount )
'Response.Write "### Reached here: 13 (PP_PAYERID:" & SESSION("PP_PAYERID") & ")###<br>"
If UCase(LResultsDict("ACK")) <> "SUCCESS" Then
'Display a user friendly Error on the page using any of the following error information returned by PayPal
ShowPayPalExpressError LResultsDict
' (SS,17/2/15) if error code is 10486 then redirect back to PayPal as recommended by PayPal
LErrorCode = PP_URLDecode(LResultsDict("L_ERRORCODE0"))
If LErrorCode = "10486" Then
PP_ReDirectURL LToken, ReturnFromPayPalExpressLaterStage
End If
'Response.Write "###This point reached###" & BR
PayPalExpressConfirmPayment = False
Exit Function
End If
'Response.Write "### PayPalExpressConfirmPayment 2 ###<br>"
' '********************************************************************************************************************
' '
' ' THE PARTNER SHOULD SAVE THE KEY TRANSACTION RELATED INFORMATION LIKE
' ' transactionId & orderTime
' ' IN THEIR OWN DATABASE
' ' AND THE REST OF THE INFORMATION CAN BE USED TO UNDERSTAND THE STATUS OF THE PAYMENT
' '
' '********************************************************************************************************************
' token = resArray("TOKEN") ' The timestamped token value that was returned by SetExpressCheckout response and passed on GetExpressCheckoutDetails request.
' transactionId = resArray("PAYMENTINFO_0_TRANSACTIONID") ' Unique transaction ID of the payment. Note: If the PaymentAction of the request was Authorization or Order, this value is your AuthorizationID for use with the Authorization & Capture APIs.
' transactionType = resArray("PAYMENTINFO_0_TRANSACTIONTYPE") ' The type of transaction Possible values: l cart l express-checkout
' paymentType = resArray("PAYMENTINFO_0_PAYMENTTYPE") ' Indicates whether the payment is instant or delayed. Possible values: l none l echeck l instant
' orderTime = resArray("PAYMENTINFO_0_ORDERTIME") ' Time/date stamp of payment
' amt = resArray("PAYMENTINFO_0_AMT") ' The final amount charged, including any shipping and taxes from your Merchant Profile.
' currencyCode = resArray("PAYMENTINFO_0_CURRENCYCODE") ' A three-character currency code for one of the currencies listed in PayPay-Supported Transactional Currencies. Default: USD.
' 'feeAmt = resArray("PAYMENTINFO_0_FEEAMT") ' PayPal fee amount charged for the transaction
' 'settleAmt = resArray("PAYMENTINFO_0_SETTLEAMT") ' Amount deposited in your PayPal account after a currency conversion.
' taxAmt = resArray("PAYMENTINFO_0_TAXAMT") ' Tax charged on the transaction.
' 'exchangeRate = resArray("PAYMENTINFO_0_EXCHANGERATE") ' Exchange rate if a currency conversion occurred. Relevant only if your are billing in their non-primary currency. If the customer chooses to pay with a currency other than the non-primary currency, the conversion occurs in the customer�s account.
' ' Status of the payment:
' 'Completed: The payment has been completed, and the funds have been added successfully to your account balance.
' 'Pending: The payment is pending. See the PendingReason element for more information.
' paymentStatus = resArray("PAYMENTINFO_0_PAYMENTSTATUS")
' 'The reason the payment is pending:
' ' none: No pending reason
' ' address: The payment is pending because your customer did not include a confirmed shipping address and your Payment Receiving Preferences is set such that you want to manually accept or deny each of these payments. To change your preference, go to the Preferences section of your Profile.
' ' echeck: The payment is pending because it was made by an eCheck that has not yet cleared.
' ' intl: The payment is pending because you hold a non-U.S. account and do not have a withdrawal mechanism. You must manually accept or deny this payment from your Account Overview.
' ' multi-currency: You do not have a balance in the currency sent, and you do not have your Payment Receiving Preferences set to automatically convert and accept this payment. You must manually accept or deny this payment.
' ' verify: The payment is pending because you are not yet verified. You must verify your account before you can accept this payment.
' ' other: The payment is pending for a reason other than those listed above. For more information, contact PayPal customer service.
' pendingReason = resArray("PAYMENTINFO_0_PENDINGREASON")
' 'The reason for a reversal if TransactionType is reversal:
' ' none: No reason code
' ' chargeback: A reversal has occurred on this transaction due to a chargeback by your customer.
' ' guarantee: A reversal has occurred on this transaction due to your customer triggering a money-back guarantee.
' ' buyer-complaint: A reversal has occurred on this transaction due to a complaint about the transaction from your customer.
' ' refund: A reversal has occurred on this transaction because you have given the customer a refund.
' ' other: A reversal has occurred on this transaction due to a reason not listed above.
' reasonCode = resArray("PAYMENTINFO_0_REASONCODE")
Dim LOrderNo, LTransactionID, LTransactionType, LPaymentType, LOrderTime, LAmount, LCurrencyCode, LFeeAmount, LSettleAmount, LTaxAmount, LExchangeRate, LPaymentStatus, LPendingReason, LReasonCode
LOrderNo = Session("OrderNoPlaced")
LTransactionID = CleanSQLStr(LResultsDict("PAYMENTINFO_0_TRANSACTIONID"))
LTransactionType = CleanSQLStr(LResultsDict("PAYMENTINFO_0_TRANSACTIONTYPE"))
LPaymentType = CleanSQLStr(LResultsDict("PAYMENTINFO_0_PAYMENTTYPE"))
LOrderTime = CleanSQLStr(LResultsDict("PAYMENTINFO_0_ORDERTIME"))
LAmount = CleanSQLStr(LResultsDict("PAYMENTINFO_0_AMT"))
LCurrencyCode = CleanSQLStr(LResultsDict("PAYMENTINFO_0_CURRENCYCODE"))
LFeeAmount = CleanSQLStr(LResultsDict("PAYMENTINFO_0_FEEAMT"))
LSettleAmount = CleanSQLStr(LResultsDict("PAYMENTINFO_0_SETTLEAMT"))
LTaxAmount = CleanSQLStr(LResultsDict("PAYMENTINFO_0_TAXAMT"))
LExchangeRate = CleanSQLStr(LResultsDict("PAYMENTINFO_0_EXCHANGERATE"))
LPaymentStatus = CleanSQLStr(LResultsDict("PAYMENTINFO_0_PAYMENTSTATUS"))
LPendingReason = CleanSQLStr(LResultsDict("PAYMENTINFO_0_PENDINGREASON"))
LReasonCode = CleanSQLStr(LResultsDict("PAYMENTINFO_0_REASONCODE"))
' insert the record
Dim LSQL
LSQL = "INSERT INTO paypal_express_Log SET OrderNo=" & LOrderNo &_
", Token='" & CleanSQLStr(LToken) & "'" &_
", TransactionID='" & LTransactionID & "'" &_
", TransactionType='" & LTransactionType & "'" &_
", PaymentType='" & LPaymentType & "'" &_
", OrderTime='" & LOrderTime & "'" &_
", Amount='" & LAmount & "'" &_
", CurrencyCode='" & LCurrencyCode & "'" &_
", FeeAmount='" & LFeeAmount & "'" &_
", SettleAmount='" & LSettleAmount & "'" &_
", TaxAmount='" & LTaxAmount & "'" &_
", ExchangeRate='" & LExchangeRate & "'" &_
", PaymentStatus='" & LPaymentStatus & "'" &_
", PendingReason='" & LPendingReason & "'" &_
", ReasonCode='" & LReasonCode & "'"
ExecuteQuery(LSQL)
' (SS,20/1/14) set order status and payment reference
If UCase(LPaymentStatus) = "COMPLETED" Then
AOrderStatus = PAYMENT_RECEIVED_STATUS
Else ' i.e. Pending
AOrderStatus = AWAITING_PAYMENT_STATUS
End If
APaymentReference = LTransactionID
APaymentInfo = LTransactionType & " " & LPaymentType & " " & LPaymentStatus
APaymentReceivedAmount = LAmount ' (SS,8/6/18) amount is now passed back, could be higher than order amount due to possible surcharge
APaymentReceivedCurrencyCode = Left(Trim(LCurrencyCode), 3) ' (SS,26/6/18) currency code is also passed back
If IsChosenPaymentPayPalExpress Then APaymentInfo = APaymentInfo & NL & "Checkout with PayPal used" ' (SS,22/1/15) to indicate tha checkout was used from the basket page
PayPalExpressConfirmPayment = True
' Response.Write "### PayPalExpressConfirmPayment 3 ###<br>"
' (SS,25/6/18) returns the fee amount
APaymentProviderFee = ParseFloat(LFeeAmount)
End Function
' (SS,19/1/15) shows paypal error and adds to log
Sub ShowPayPalExpressError(AResultsDict)
Dim LErrorCode, LErrorShortMsg, LErrorLongMsg, LErrorSeverityCode
LErrorCode = PP_URLDecode(AResultsDict("L_ERRORCODE0"))
LErrorShortMsg = PP_URLDecode(AResultsDict("L_SHORTMESSAGE0"))
LErrorLongMsg = PP_URLDecode(AResultsDict("L_LONGMESSAGE0"))
LErrorSeverityCode = PP_URLDecode(AResultsDict("L_SEVERITYCODE0"))
' *** to be improved, perhaps log to file
Response.Write "<p style=""color: red; font-weight: bold"">"
Response.Write "ERROR!" & BR
Response.Write "Error Code: " & LErrorCode & BR
Response.Write "Error Short Msg: " & LErrorShortMsg & BR
Response.Write "Error Long Msg: " & LErrorLongMsg & BR
Response.Write "Error Severity Code: " & LErrorSeverityCode & BR
Response.Write "</p>"
' add to log and save
AppUtilsAddToPaymentLog "ERROR!", "Error Code: " & LErrorCode, "Error Short Msg: " & LErrorShortMsg & NL & "Error Long Msg: " & LErrorLongMsg & NL & "Error Severity Code: " & LErrorSeverityCode
' SavePaymentLog ' (SS,23/8/17) removed, no longer required
End Sub
' --------------------------
' End of PayPal routines
' --------------------------
' (SS,16/1/15) Session used because log is added before an order number is created
' called from paypalfunctions.asp Function PP_hash_call
' (SS,12/2/15) modified to call AddToPaymentLog
Sub AppUtilsAddToPaymentLog(AMethodName, ANvpStr, AResponseText)
'If Session("PaymentLog") <> "" Then Session("PaymentLog") = Session("PaymentLog") & NL ' extra newline for separation purposes
'Session("PaymentLog") = Session("PaymentLog") & AMethodName & NL & ANvpStr & NL & AResponseText & NL
' (SS,12/2/15) above replaced with following
' (SS,23/8/17) added "PayPal" removed last False parameter
AddToPaymentLog "PayPal", AMethodName & NL & ANvpStr & NL & AResponseText
End Sub
' (SS,12/2/15) called from AppUtilsAddToPaymentLog, added directly from AppUtils when adding to PaymentLog required e.g. Bank Transfer details
' (SS,31/7/17) added date/time (now)
' (SS,23/8/17) new version without AForceSave but with AType to separate types which can be VAT No Check, Bank Transfer details, PayPal etc.
' SavePaymentLog sub removed because AddToPaymentLog will save when it can i.e. Order record exists, otherwise add to session
' if AType is "" then nothing is added Session("PaymentLog") is saved to order if order exists ie. AddToPaymentLog "", ""
Sub AddToPaymentLog(AType, AMessage)
Dim LNewMessage, LOrderNo
If AType <> "" Then
LNewMessage = GetPaymentLogSeparator(AType, True) & NL
LNewMessage = LNewMessage & Now() & NL
LNewMessage = LNewMessage & AMessage & NL
LNewMessage = LNewMessage & GetPaymentLogSeparator(AType, False) & NL & NL
Else
LNewMessage = ""
End If
' save to session first
Session("PaymentLog") = Session("PaymentLog") & LNewMessage
' ### (SS,18/4/18) for debugging
' SendAlertEmail "Payment Log", "AddToPaymentLog called with Type: " & AType & ", Message: " & AMessage, False
' if order no exists then save from session to the order record and clear session
LOrderNo = GetOrderNo
If LOrderNo <> "" And Session("PaymentLog") <> "" Then
ExecuteQuery("UPDATE orders SET PaymentLog = CONCAT(COALESCE(PaymentLog, ''), '" & CleanSQLStr(Session("PaymentLog")) & "') WHERE OrderNo = '" & CleanSQLStr(LOrderNo) & "'")
Session("PaymentLog") = "" ' clear the paymentlog in session because we're now adding to the log and don't want to add the same message multiple times
End If
End Sub
' (SS,23/8/17) returns separator string used to separate messages in payment log field
Function GetPaymentLogSeparator(AType, AStart)
Dim Result
Result = "=== " + iif(AStart, "", "End of ") + AType + ": "
Result = Result + String(50 - Len(Result), "=")
GetPaymentLogSeparator = Result
End Function
' (SS,23/8/17) returns the most recent message section from given log
Function GetPaymentLogSection(APaymentLog, AType)
' put into array
' work backwards until end separater found
' continue until start found
Dim Result
Result = ""
If APaymentLog <> "" Then
Dim LLines, LLine, i, LStart, LEndSeparator, LStartSeparator
LLines = Split(APaymentLog, NL, -1, 0)
LStartSeparator = GetPaymentLogSeparator(AType, True)
LEndSeparator = GetPaymentLogSeparator(AType, False)
LStart = False
For i = UBound(LLines) to LBound(LLines) Step -1
LLine = LLines(i)
If Not LStart And LLine = LEndSeparator Then
LStart = True
End If
If LStart Then
Result = LLine & NL & Result
If LLine = LStartSeparator Then Exit For
End If
Next
End If
GetPaymentLogSection = Result
End Function
' (SS,21/3/13) certain payment provides e.g. SagePay, require a unique number, i.e. same order can be used again for same purchase,
' e.g. if buyer goes back to site and then back to SagePay then it'll complain, we get around this by added an extra number to the end to keep it unique
Function GetUniquePaymentProviderSuffix
If Session("PaymentProviderSuffix") = "" Then
Session("PaymentProviderSuffix") = 1
Else
Session("PaymentProviderSuffix") = Session("PaymentProviderSuffix") + 1
End If
GetUniquePaymentProviderSuffix = Session("PaymentProviderSuffix")
End Function
' --------------------------
' Start of Sage Pay routines
' --------------------------
' (SS,19/3/13) new SagePay payment method
' (SS,23/7/15) modified for v3.00 (migration from v2.23), version number changed from 2.23 to 3.00
Sub SetUpSagePayForm(AOrderNo)
Dim LVPSProtocol, LMode, LSagePayURL
' current version is 3.00, simulator said it wants 2.23
' (SS,23/7/15) changed from version 2.23 to 3.00
LVPSProtocol = "3.00"
LMode = GetSagePayMode ' can be LIVE, TEST or "SIM"
' set the URL to use depending on mode
If LMode = "LIVE" then
LSagePayURL = "https://live.sagepay.com/gateway/service/vspform-register.vsp"
ElseIf LMode = "TEST" then
LSagePayURL = "https://test.sagepay.com/gateway/service/vspform-register.vsp"
Else
LSagePayURL = "https://test.sagepay.com/simulator/vspformgateway.asp"
End If
' build the form Crypt field and encrypt it, &xx=yyy, first one without &
FSagePayCrypt = ""
AddToSagePayCrypt "VendorTxCode", AOrderNo & "-" & GetUniquePaymentProviderSuffix ' unique number, even for same session
AddToSagePayCrypt "Amount", Session("PaymentAmount")
AddToSagePayCrypt "Currency", GetCurrencyCode
AddToSagePayCrypt "Description", GetDescriptionForPaymentProvider(100) ' upto 100 chars
' (SS,23/3/13) added URLEncode around ptSagePay because SAGEPAY was changed to SAGE PAY, and can't have space
AddToSagePayCrypt "SuccessURL", GetReturnFromPaymentURL & "&pt=" & URLEncode(ptSagePay) & "&success=true" ' SagePay will send values back using &crypt
AddToSagePayCrypt "FailureURL", GetReturnFromPaymentURL & "&pt=" & URLEncode(ptSagePay) & "&success=false" ' SagePay will send values back using &crypt
AddToSagePayCrypt "CustomerName", Left(GetNameForPaymentProvider, 100) ' optional
AddToSagePayCrypt "CustomerEMail", Left(Session("Email"), 255) ' optional
AddToSagePayCrypt "VendorEMail", GetStoreEmail ' optional
AddToSagePayCrypt "SendEMail", 1 ' optional, 1 means send both customer and vendor emails
AddToSagePayCrypt "eMailMessage", "Thank you so very much for your order." ' optional
' billing address details
AddToSagePayCrypt "BillingFirstnames", Left(Session("FirstName"), 20)
AddToSagePayCrypt "BillingSurname", Left(Session("Surname"), 20)
AddToSagePayCrypt "BillingAddress1", Left(Session("Address1"), 100)
If Session("Address2") <> "" Then AddToSagePayCrypt "BillingAddress2", Left(Session("Address2"), 100)
AddToSagePayCrypt "BillingCity", Left(Session("Town"), 100)
AddToSagePayCrypt "BillingPostCode", Left(Session("Postcode"), 10)
AddToSagePayCrypt "BillingCountry", GetCountryISOCode(Session("Country"))
' AddToSagePayCrypt "BillingState, "") ' optional, US customers only, 2 letter state, which we don't have *** we could convert the county to a 2 letter state
' BillingPhone is also optional
' delivery address details
' We can't use delivery name because PayPal uses separate FirstName and Surname, but it's most likely to be the same.
If Session("DeliveryAddressSameAsInvoice") Then
AddToSagePayCrypt "DeliveryFirstNames", Left(Session("FirstName"), 20)
AddToSagePayCrypt "DeliverySurname", Left(Session("Surname"), 20)
AddToSagePayCrypt "DeliveryAddress1", Left(Session("Address1"), 100)
If Session("Address2") <> "" Then AddToSagePayCrypt "DeliveryAddress2", Left(Session("Address2"), 100)
AddToSagePayCrypt "DeliveryCity", Left(Session("Town"), 100)
AddToSagePayCrypt "DeliveryPostCode", Left(Session("Postcode"), 10)
AddToSagePayCrypt "DeliveryCountry", GetCountryISOCode(Session("Country"))
' AddToSagePayCrypt "DeliveryState, "") ' optional, US customers only, 2 letter state, which we don't have *** we could convert the county to a 2 letter state
' DeliveryPhone is also optional
Else ' i.e. different delivery address
' split the name across two fields because we don't have separate first and surnames for delivery name
Dim LDeliveryName, LDeliveryFirstName, LDeliverySurname, LPos
LDeliveryName = Trim(Session("DeliveryName"))
LPos = InStrRev(LDeliveryName, " ") ' in reverse, first space before the surname because there may be a title
If LPos > 0 Then
LDeliveryFirstName = Trim(Left(LDeliveryName, LPos - 1))
LDeliverySurname = Trim(Mid(LDeliveryName, LPos + 1))
ElseIf Len(LDeliveryFirstName) > 20 Then
LDeliveryFirstName = Left(LDeliveryName, 20)
LDeliverySurname = Mid(LDeliveryName, 21)
Else
LDeliveryFirstName = LDeliveryName
LDeliverySurname = ""
End If
AddToSagePayCrypt "DeliveryFirstNames", Left(LDeliveryFirstName, 20)
AddToSagePayCrypt "DeliverySurname", Left(LDeliverySurname, 20)
AddToSagePayCrypt "DeliveryAddress1", Left(Session("DeliveryAddress1"), 100)
If Session("DeliveryAddress2") <> "" Then AddToSagePayCrypt "DeliveryAddress2", Left(Session("DeliveryAddress2"), 100)
AddToSagePayCrypt "DeliveryCity", Left(Session("DeliveryTown"), 100)
AddToSagePayCrypt "DeliveryPostCode", Left(Session("DeliveryPostcode"), 10)
AddToSagePayCrypt "DeliveryCountry", GetCountryISOCode(Session("DeliveryCountry"))
' AddToSagePayCrypt "DeliveryState, "") ' optional, US customers only, 2 letter state, which we don't have *** we could convert the county to a 2 letter state
' DeliveryPhone is also optional
End If
' following is debug code, currently commented out
' If IsPaymentProviderTestModeActivated Then Response.Write "#" & FSagePayCrypt & "#<br>"
' encrypt the string before including in the hidden field
'Response.Write "#" & FSagePayCrypt & "#<br>"
'Dim LSQL
'LSQL = "SELECT HEX(AES_ENCRYPT('" & CleanSQLStr(FSagePayCrypt) & "', '" & CleanSQLStr(GetSagePayEncryptionKey) & "'))"
'FSagePayCrypt = "@" & GetSQLValueAsString(LSQL) ' @ indicates AES encryption used
' (SS,20/3/13) tried above using MySQL but Sage didn't like it, might be because MySQL uses 128bit encryption, used AESEncrypt instead provide by SagePay in rijndael.asp
FSagePayCrypt = "@" & AESEncrypt(FSagePayCrypt, GetSagePayEncryptionKey)
%>
<form name="frmSagePay" action="<%=LSagePayURL%>" method="post" class="nospace">
<input type="hidden" name="VPSProtocol" value="<%=LVPSProtocol%>">
<input type="hidden" name="TxType" value="PAYMENT">
<input type="hidden" name="Vendor" value="<%=GetSagePayVendorName%>">
<input type="hidden" name="Crypt" value="<%=FSagePayCrypt%>">
</form>
<%
End Sub
' (SS,20/3/13)
Sub AddToSagePayCrypt(AFieldName, AFieldValue)
If FSagePayCrypt <> "" Then FSagePayCrypt = FSagePayCrypt + "&"
FSagePayCrypt = FSagePayCrypt & AFieldName & "=" & AFieldValue ' need to URL or HTML encode AFieldValue HTMLEncode
End Sub
' (SS,21/3/13)
' not actually a direct call back like PayPal and WorldPay, but done via a return to the site via a special URL
' (SS,23/7/15) modified for v3.00 (migration from v2.23), 5 new fields added to sagepaycallbacks table
Sub ProcessSagePayCallback
Dim LCrypt, LDecoded
LCrypt = "@" & AESEncrypt(FSagePayCrypt, GetSagePayEncryptionKey)
LCrypt = Request.QueryString("Crypt")
If LCrypt <> "" Then
If Left(LCrypt, 1) = "@" Then
LDecoded = AESDecrypt(Mid(LCrypt, 2), GetSagePayEncryptionKey)
Else
Response.Write(BR & BR & "Incorrect data received from SagePay." & BR)
Exit Sub
End If
Else ' nothing passed back from Sage then we need to show a message to redirect to another part of the site
Response.Write(BR & BR & "Nothing received from Sage Pay." & BR)
Exit Sub
End If
' Response.Write("#" & LDecoded & "#<br>")
' get the decrypted values from crypt into separate variables
Dim LOrderNo, LPos
Dim LStatus, LStatusDetail, LVendorTxCode, LVPSTxId, LTxAuthNo, LAmount, LAVSCV2, LAddressResult, LPostCodeResult, LCV2Result
Dim LGiftAid, L3DSecureStatus, LCAVV, LCardType, LLast4Digits, LAddressStatus, LPayerStatus
Dim LExpiryDate, LBankAuthCode, LDeclineCode, LFraudResponse, LSurcharge ' (SS,23/7/15)
LStatus = CleanSQLStr(GetSagePayToken(LDecoded, "Status"))
LStatusDetail = CleanSQLStr(GetSagePayToken(LDecoded, "StatusDetail"))
LVendorTxCode = CleanSQLStr(GetSagePayToken(LDecoded, "VendorTxCode"))
LVPSTxId = CleanSQLStr(GetSagePayToken(LDecoded, "VPSTxId"))
LTxAuthNo = CleanSQLStr(GetSagePayToken(LDecoded, "TxAuthNo"))
LAmount = CleanSQLStr(GetSagePayToken(LDecoded, "Amount"))
LAVSCV2 = CleanSQLStr(GetSagePayToken(LDecoded, "AVSCV2"))
LAddressResult = CleanSQLStr(GetSagePayToken(LDecoded, "AddressResult"))
LPostCodeResult = CleanSQLStr(GetSagePayToken(LDecoded, "PostCodeResult"))
LCV2Result = CleanSQLStr(GetSagePayToken(LDecoded, "CV2Result"))
LGiftAid = CleanSQLStr(GetSagePayToken(LDecoded, "GiftAid"))
L3DSecureStatus = CleanSQLStr(GetSagePayToken(LDecoded, "3DSecureStatus"))
LCAVV = CleanSQLStr(GetSagePayToken(LDecoded, "CAVV"))
LCardType = CleanSQLStr(GetSagePayToken(LDecoded, "CardType"))
LLast4Digits = CleanSQLStr(GetSagePayToken(LDecoded, "Last4Digits"))
LAddressStatus = CleanSQLStr(GetSagePayToken(LDecoded, "AddressStatus")) '** PayPal transactions only
LPayerStatus = CleanSQLStr(GetSagePayToken(LDecoded, "PayerStatus")) '** PayPal transactions only
' (SS,23/7/15) added following fields for version 3.00
LExpiryDate = Left(CleanSQLStr(GetSagePayToken(LDecoded, "ExpiryDate")), 4)
LBankAuthCode = Left(CleanSQLStr(GetSagePayToken(LDecoded, "BankAuthCode")), 6)
LDeclineCode = Left(CleanSQLStr(GetSagePayToken(LDecoded, "DeclineCode")), 2)
LFraudResponse = Left(CleanSQLStr(GetSagePayToken(LDecoded, "FraudResponse")), 10)
LSurcharge = Left(CleanSQLStr(GetSagePayToken(LDecoded, "Surcharge")), 20)
' get the order number from LVendorTxCode before the "-" and proceed only if it exists
LPos = InStr(LVendorTxCode, "-")
LOrderNo = Left(LVendorTxCode, LPos - 1)
If LOrderNo = "" Then
Response.Write(BR & BR & "Order No must be specified." & BR)
Exit Sub
End If
' insert the record
' (SS,23/7/15) added ExpiryDate, BankAuthCode, DeclineCode, FraudResponse, Surcharge
Dim LSQL
LSQL = "INSERT INTO sagepaycallbacks SET OrderNo=" & LOrderNo &_
", SessionID='" & GetSessionID & "'" &_
", Status='" & LStatus & "'" &_
", DateTimeReceived=NOW()" &_
", StatusDetail='" & LStatusDetail & "'" &_
", VendorTxCode='" & LVendorTxCode & "'" &_
", VPSTxId='" & LVPSTxId & "'" &_
", TxAuthNo='" & LTxAuthNo & "'" &_
", Amount='" & LAmount & "'" &_
", AVSCV2='" & LAVSCV2 & "'" &_
", AddressResult='" & LAddressResult & "'" &_
", PostCodeResult='" & LPostCodeResult & "'" &_
", CV2Result='" & LCV2Result & "'" &_
", GiftAid='" & LGiftAid & "'" &_
", 3DSecureStatus='" & L3DSecureStatus & "'" &_
", CAVV='" & LCAVV & "'" &_
", CardType='" & LCardType & "'" &_
", Last4Digits='" & LLast4Digits & "'" &_
", AddressStatus='" & LAddressStatus & "'" &_
", PayerStatus='" & LPayerStatus & "'" &_
", ExpiryDate='" & LExpiryDate & "'" &_
", BankAuthCode='" & LBankAuthCode & "'" &_
", DeclineCode='" & LDeclineCode & "'" &_
", FraudResponse='" & LFraudResponse & "'" &_
", Surcharge='" & LSurcharge & "'"
ExecuteQuery(LSQL)
' if paid then mark the order as paid and send email
If LStatus = "OK" And LOrderNo <> "" Then
' following will update the status, send email and empty basket
' (SS,21/1/15) added "" parameter for PaymentInfo
' (SS,25/6/18) added new PaymentProviderFee parameter i.e. 0 to the end
' (SS,26/6/18) added three more parameters, PaymentReceivedAmount, PaymentReceivedCurrencyCode and PaymentProviderSurcharge
' only PaymentAmountReceived is set a value, i.e. ParseFloat(LAmount). The other two are 0. Currency Code is set to "GBP"
ProcessOrderThirdPartyPayment True, LOrderNo, GetSessionID, ptSagePay, LVPSTxId, PAYMENT_RECEIVED_STATUS, "", ParseFloat(LAmount), "GBP", 0, 0
Else
Response.Write("Payment not successful." & BR)
End If
End Sub
'------------------------------------------------------
' (SS,21/3/13) following is exact copy of function from Sage Pay to get value of given token
' I renamed it from getToken to GetSagePayToken
' (SS,23/7/15) changes for version 3.00 (from 2.23) added extra fields
'** The getToken function. **
'** NOTE: A function of convenience that extracts the value from the "name=value&name2=value2..." reply string **
'** Does not use the split function because returned values can contain & or =, so it looks for known names and **
'** removes those, leaving only the field selected **
public function GetSagePayToken(strList,strRequired)
dim strTokenValue, iIndex ' (SS,21/3/13) had to add this to prevent Variable is undefined error
dim arrTokens
' (SS,23/7/15) added for version 3.00 ExpiryDate, BankAuthCode, DeclineCode, FraudResponse, Surcharge
arrTokens = array("Status","StatusDetail","VendorTxCode","VPSTxId", _
"TxAuthNo","Amount","AVSCV2","AddressResult","PostCodeResult", _
"CV2Result","GiftAid","3DSecureStatus","CAVV","AddressStatus", _
"CardType","Last4Digits","PayerStatus", _
"ExpiryDate","BankAuthCode","DeclineCode", _
"FraudResponse","Surcharge")
'** If the toekn we're after isn't in the list, return nothing **
if instr(strList,strRequired+"=")=0 then
GetSagePayToken=""
exit function
else
'** The token is present, so ignore everything before it in the list **
strTokenValue=mid(strList,instr(strList,strRequired)+len(strRequired)+1)
'** Strip off all remaining tokens if they are present **
iIndex = LBound(arrTokens)
do while iIndex <= UBound(arrTokens)
if arrTokens(iIndex)<>strRequired then
if instr(strTokenValue,"&"+arrTokens(iIndex))<>0 then
strTokenValue=left(strTokenValue,instr(strTokenValue,"&"+arrTokens(iIndex))-1)
end if
end if
iIndex = iIndex + 1
loop
GetSagePayToken=strTokenValue
end if
end function
'------------------------------------------------------
' ------------------------
' End of Sage Pay routines
' ------------------------
' --------------------------
' Start of ePDQ routines
' --------------------------
' (SS,7/9/06)
' (SS,6/7/07) added HTMLEncode to some of the values to allow quotes etc
' (SS,2/10/14) added PARAMVAR to allow more than one site to use same account via virtual directory e.g. https://secure.itpartnership.com/<PARAMVAR>/epdq-callback.asp
' (SS,7/3/18) added hidden ACCEPTURL to redirect back to site automatically, but wasn't working
' (SS,26/4/18) replaced (this old migrated CPI service) with new version see Sub SetUpEPDQForm below
Sub SetUpEPDQFormOld(AOrderNo)
Dim LClientID, LPassphrase, LChargeType, LCurrencyCode, LTotal
Dim LePDQData, LReturnURL, LMerchantDisplayName
LClientID = GetEPDQClientID
LPassphrase = GetEPDQPassphrase
LChargeType = GetEPDQChargeType
LCurrencyCode = "826"
LTotal = Session("PaymentAmount")
' LReturnURL = GetReturnFromPaymentURL
' (SS,8/9/06) above doesn't work so following because the cmd and stage parameters are
' removed by ePDQ, replaced by oid, so used following instead to redirect
LReturnURL = GetStoreURL + "/epdq-return.asp"
LMerchantDisplayName = GetEPDQMerchantDisplayName ' (SS,10/5/12) was GetStoreURLNoHttp
LePDQData = GetEncryptedEPDQData(LClientID, AOrderNo, LPassphrase, LChargeType, LCurrencyCode, LTotal)
Dim Lbaddr1, Lbaddr2, Lbcity, Lbcountyprovince, Lbcountry, Lbpostalcode, Lbstate, Lbtelephonenumber, Lemail
Dim Lsaddr1, Lsaddr2, Lscity, Lscountyprovince, Lscountry, Lspostalcode, Lsstate, Lstelephonenumber
' extra fields
Lbaddr1 = Left(Session("Address1"), 60)
Lbaddr2 = Left(Session("Address2"), 60)
Lbcity = Left(Session("Town"), 25)
Lbcountyprovince = Left(Session("County"), 25)
Lbcountry = GetCountryISOCode(Session("Country"))
Lbpostalcode = Left(Session("Postcode"), 9)
' if USA then use first two letters of postcode/zip as two letter state code
If Lbcountry = "US" Then
Lbstate = Left(Trim(Lbpostalcode), 2)
Else
Lbstate = ""
End If
Lbtelephonenumber = Left(Session("Telephone"), 30)
Lemail = Left(Session("Email"), 64)
Lsaddr1 = Left(Session("DeliveryAddress1"), 60)
Lsaddr2 = Left(Session("DeliveryAddress2"), 60)
Lscity = Left(Session("DeliveryTown"), 25)
Lscountyprovince = Left(Session("DeliveryCounty"), 25)
Lscountry = GetCountryISOCode(Session("DeliveryCountry"))
Lspostalcode = Left(Session("DeliveryPostcode"), 9)
' if USA then use first two letters of postcode/zip as two letter state code
If Lscountry = "US" Then
Lsstate = Left(Trim(Lspostalcode), 2)
Else
Lsstate = ""
End If
Lstelephonenumber = Left(Session("Telephone"), 30)
' (SS,7/3/18) added hidden ACCEPTURL to return straight to website (to GetReturnFromPaymentURL) successful payment, so that conversion tracking is done without customer having to press a button to return
%>
<form name="frmEPDQ" action="https://secure2.epdq.co.uk/cgi-bin/CcxBarclaysEpdq.e" method="post" class="nospace">
<%=LePDQData%>
<input type="hidden" name="returnurl" value="<%=LReturnURL%>">
<input type="hidden" name="merchantdisplayname" value="<%=LMerchantDisplayName%>">
<input type="hidden" name="baddr1" value="<%=HTMLEncode(Lbaddr1)%>">
<input type="hidden" name="baddr2" value="<%=HTMLEncode(Lbaddr2)%>">
<input type="hidden" name="bcity" value="<%=HTMLEncode(Lbcity)%>">
<input type="hidden" name="bcountyprovince" value="<%=HTMLEncode(Lbcountyprovince)%>">
<input type="hidden" name="bcountry" value="<%=Lbcountry%>">
<input type="hidden" name="bpostalcode" value="<%=HTMLEncode(Lbpostalcode)%>">
<input type="hidden" name="bstate" value="<%=HTMLEncode(Lbstate)%>">
<input type="hidden" name="btelephonenumber" value="<%=HTMLEncode(Lbtelephonenumber)%>">
<input type="hidden" name="email" value="<%=HTMLEncode(Lemail)%>">
<input type="hidden" name="collectdeliveryaddress" value="0">
<input type="hidden" name="PARAMVAR" value="<%=HTMLEncode(GetEPDQPARAMVAR)%>">
<input type="hidden" name="ACCEPTURL" value="<%=HTMLEncode(GetReturnFromPaymentURL & "&accept=1")%>">
</form>
<%
' (SS,10/5/12) shipping address removed from above, also added hidden collectdeliveryaddress to notify ePDQ that delivery address not applicable
'<input type="hidden" name="saddr1" value="<~=HTMLEncode(Lsaddr1)~>">
'<input type="hidden" name="saddr2" value="<~=HTMLEncode(Lsaddr2)~>">
'<input type="hidden" name="scity" value="<~=HTMLEncode(Lscity)~>">
'<input type="hidden" name="scountyprovince" value="<~=HTMLEncode(Lscountyprovince)~>">
'<input type="hidden" name="scountry" value="<~=HTMLEncode(Lscountry)~>">
'<input type="hidden" name="spostalcode" value="<~=HTMLEncode(Lspostalcode)~>">
'<input type="hidden" name="sstate" value="<~=HTMLEncode(Lsstate)~>">
'<input type="hidden" name="stelephonenumber" value="<~=HTMLEncode(Lstelephonenumber)~>">
' removed following doesn't look nice, takes too much spaces
' <input type="hidden" name="cpi_logo" value="https://secure.itpartnership.com/period-products.jpg">
' (SS,20/11/08) moved button from here to SetUpPaymentButton
End Sub
' (SS,7/9/06) taken from ePDQ example, made into function
' uses ASPTear object that has to be installed
' (SS,25/4/18) modified to use GetWebPageViaPOST instead of SOFTWING.ASPtear
Function GetEncryptedEPDQData(AClientID, AOID, APassword, AChargeType, ACurrencyCode, ATotal)
Dim strEPDQ, LSite, LQuery
LSite = "https://secure2.epdq.co.uk/cgi-bin/CcxBarclaysEpdqEncTool.e"
' (SS,10/5/12) added "&mandatecsc=1" to force security code to be mandatory
LQuery = "clientid=" & AClientID & "&oid=" & AOID & "&password=" & APassword & "&chargetype=" & AChargeType & "¤cycode=" & ACurrencyCode & "&total=" & ATotal & "&mandatecsc=1"
' (SS,25/4/18) replaced Set objTear = CreateObject("SOFTWING.ASPtear") with GetWebPageViaPOST
strEPDQ = GetWebPageViaPOST(LSite & "?" & LQuery)
GetEncryptedEPDQData = strEPDQ
End Function
' (SS,26/4/18) new version
Sub SetUpEPDQForm(AOrderNo)
' clear the hash string
ClearEPDQStringToHash
Dim LTotal, LISOCurrencyCode
' amount is always GBP
LISOCurrencyCode = "GBP" ' in ISO alpha (not numeric like Payzone)
LTotal = Session("PaymentAmountGBP") ' amount in GBP
LTotal = Round(CDbl(LTotal) * 100) ' x 100 because it's in pence
Dim LActionURL
If GetEPDQTestMode Then
LActionURL = "https://mdepayments.epdq.co.uk/ncol/test/orderstandard.asp"
Else
LActionURL = "https://payments.epdq.co.uk/ncol/prod/orderstandard.asp"
End If
%>
<form name="frmEPDQ" action="<%=LActionURL%>" method="post" class="nospace">
<%
Dim LCustomerName
LCustomerName = GetNameForPaymentProvider
' order has to be alphabetic on name for the hash string
AddEPDQField "ACCEPTURL", GetReturnFromPaymentURL & "&pt=" & ptEPDQ, 0, True
AddEPDQField "AMOUNT", LTotal, 0, True
AddEPDQField "CN", LCustomerName, 100, True
AddEPDQField "CURRENCY", LISOCurrencyCode, 0, True
AddEPDQField "EMAIL", Session("Email"), 64, True
' following logo needs to be uploaded, removed for now, perhaps use store name followed by .PNG
'AddEPDQField "LOGO", GetStoreURL & "/images/logo.png" , 64, True
AddEPDQField "ORDERID", AOrderNo, 50, True
AddEPDQField "OWNERADDRESS", Session("Address1"), 60, True
AddEPDQField "OWNERCTY", GetCountryISOCode(Session("Country")), 0, True
AddEPDQField "OWNERTELNO", Session("Telephone"), 30, True
AddEPDQField "OWNERTOWN", Session("Town"), 25, True
AddEPDQField "OWNERZIP", Session("Postcode"), 9, True
AddEPDQField "PSPID", GetEPDQClientID, 0, True
AddEPDQField "TITLE", GetStoreName, 0, True
' AddEPDQField "LANGUAGE", "en_GB", 0, True ' (SS,26/4/18) language of the customer not passed on for now
'AddEPDQField "OWNERADDRESS2", Session("Address2"), 60, True
'AddEPDQField "TITLE", GetEPDQMerchantDisplayName, 0, True
' AddEPDQField "PARAMVAR", GetEPDQPARAMVAR, 0, True
'AddPayzoneField "OrderDescription", GetDescriptionForPaymentProvider(256), 256, True
%><input type="hidden" name="SHASign" value="<%=GetEPDQHashDigest%>">
</form>
<%
End Sub
' (SS,26/4/18) outputs the input field and adds to FEPDQStringToHash (implementation similar to Payzone's)
' If AIncludeInForm is true then is including in both form and digest, if false then digest only
Sub AddEPDQField(AFieldName, AFieldValue, AMaxLength, AIncludeInForm)
Dim LFieldValue, LFieldValueEncoded
If AMaxLength = 0 Then
LFieldValue = AFieldValue
Else
LFieldValue = Left(AFieldValue, AMaxLength)
End If
' replace ampersand with + (their system currently can't handle special characters)
' not encoded yet, not sure if necessary
LFieldValueEncoded = LFieldValue
' add the hidden variable to form
If AIncludeInForm Then
Response.Write "<input type=""hidden"" name=""" & AFieldName & """ value=""" & LFieldValueEncoded & """>" & NL
End If
AddEPDQStringToHash AFieldName, LFieldValueEncoded
End Sub
' (SS,26/4/18)
Sub ClearEPDQStringToHash
' Global FEPDQStringToHash
FEPDQStringToHash = ""
End Sub
' (SS,26/4/18) notice it's different to Payzone, "&" not used to separate the values, and passphrase added to the end of each value
Sub AddEPDQStringToHash(AFieldName, AFieldValue)
FEPDQStringToHash = FEPDQStringToHash & AFieldName & "=" & AFieldValue & GetEPDQPassphrase
End Sub
' (SS,27/4/18) similar to AddEPDQStringToHash above, but gets value from form field and returns a value, calls AddEPDQStringToHash
Function GetEPDQFormFieldToHash(AFieldName)
Dim LFieldValue
LFieldValue = CleanRequestQueryString(AFieldName)
FEPDQStringToHash = FEPDQStringToHash & UCase(AFieldName) & "=" & LFieldValue & GetEPDQPassphrase ' NB. name has to be uppercase
GetEPDQFormFieldToHash = LFieldValue
End Function
' (SS,28/3/18)
Function GetEPDQHashDigest
Dim LResult
' use SHA1 function of MySQL to create the hash
' (SS,5/4/18) added CAST(AS CHAR) for MySQL server 5.1.x and old ODBC driver, otherwise it returns ????, fine in MySQL 5.7.x on test server
LResult = GetSQLValueAsString("SELECT CAST(SHA1(""" & CleanSQLStr(FEPDQStringToHash) & """) AS CHAR)")
GetEPDQHashDigest = LResult
End Function
' this routine gets called by epdq-callback.asp which is normally called by ePDQ callback when payment has been made
' (SS,27/4/18) old version, for new version see Sub ProcessEPDQRedirectCallback below
' Kept this to continue processing callbacks the old way as well
Sub ProcessEPDQCallback
' (SS,26/4/18) !!! debug
SendWebLogEmail "ProcessEPDQCallback"
Dim LSQL, LOrderNo
Dim Ltransactionstatus, Ltotal, LClientID, Lchargetype, Ldatetime, Lecistatus, Lcardprefix
' get values from query forms etc (passed on values) '
LOrderNo = CleanSQLStr(Trim(CleanRequest("oid")))
Ltransactionstatus = CleanSQLStr(CleanRequest("transactionstatus"))
Ltotal = CleanSQLStr(CleanRequest("total"))
LClientID = CleanSQLStr(CleanRequest("ClientID"))
Lchargetype = CleanSQLStr(CleanRequest("chargetype"))
Ldatetime = CleanSQLStr(CleanRequest("datetime"))
Lecistatus = CleanSQLStr(CleanRequest("ecistatus"))
Lcardprefix = CleanSQLStr(CleanRequest("cardprefix"))
If LOrderNo = "" Then
Response.Write("<br><br>Order No must be specified.<br>")
Exit Sub
End If
' insert the record '
LSQL = "INSERT INTO epdqcallbacks SET OrderNo=" & LOrderNo &_
", transactionstatus='" & Ltransactionstatus & "'" &_
", total='" & Ltotal & "'" &_
", ClientID='" & LClientID & "'" &_
", chargetype='" & Lchargetype & "'" &_
", datetime='" & Ldatetime & "'" &_
", ecistatus='" & Lecistatus & "'" &_
", cardprefix='" & Lcardprefix & "'"
ExecuteQuery(LSQL)
' if paid then mark the order as paid and send email
If UCase(Ltransactionstatus) = "SUCCESS" And LOrderNo <> "" Then
' we need to get SessionID because it's not passed on from ePDQ
Dim LSessionID
LSessionID = GetSessionIDForOrder(LOrderNo)
' following will update the status, send email and empty basket
' OrderNo is used as PaymentReference because ePDQ doesn't pass it's own ref back
' (SS,5/10/07) added ptEPDQ
' (SS,16/5/12) added PAYMENT_RECEIVED_STATUS
' (SS,21/1/15) added "" for new PaymentInfo parameter
' (SS,25/6/18) added new PaymentProviderFee parameter i.e. 0 to the end
' (SS,26/6/18) added two more parameters, PaymentAmountReceived and PaymentProviderSurcharge, only PaymentAmountReceived is sent a value, i.e. ParseFloat(LAmount). The other two are 0.
' (SS,26/6/18) also added PaymentReceivedCurrencyCode, set this to "GBP"
ProcessOrderThirdPartyPayment True, LOrderNo, LSessionID, ptEPDQ, LOrderNo, PAYMENT_RECEIVED_STATUS, "", ParseFloat(Ltotal), "GBP", 0, 0
End If
End Sub
' (SS,27/4/18) new version for callbacks via browser redirect
Sub ProcessEPDQRedirectCallback
Dim LIPCTY, LECI, LCardNo, LPAYIDSUB, LOrderNo, LAmount, LCurrency, LED, LCCCTY
Dim LCVCCheck, LAAVCheck, LSHASIGN, LPM, LPAYID, LVC, LACCEPTANCE, LBRAND, LIP, LSTATUS, LNCERROR, LCN, LTRXDATE
Dim LSHASIGNCalculated
' (SS,26/4/18) !!! debug
'SendWebLogEmail "ProcessEPDQRedirectCallback"
ClearEPDQStringToHash
' following have to be in this alphabetical order
' (SS,30/4/18) removed BRAND and ED (Expiry Date) due to sensitive info that we don't wanted in address bar, even though most of card no is replaced with X's
' Also removed, AAVCheck, CCCTY, CVCCheck, ECI, ED, PAYIDSUB, VC
'LAAVCheck = GetEPDQFormFieldToHash("AAVCheck")
LACCEPTANCE = GetEPDQFormFieldToHash("ACCEPTANCE")
LAmount = GetEPDQFormFieldToHash("amount")
LBRAND = GetEPDQFormFieldToHash("BRAND")
'LCardNo = GetEPDQFormFieldToHash("CARDNO")
'LCCCTY = GetEPDQFormFieldToHash("CCCTY")
LCN = GetEPDQFormFieldToHash("CN")
LCurrency = GetEPDQFormFieldToHash("currency")
'LCVCCheck = GetEPDQFormFieldToHash("CVCCheck")
'LECI = GetEPDQFormFieldToHash("ECI")
'LED = GetEPDQFormFieldToHash("ED")
LIP = GetEPDQFormFieldToHash("IP")
'LIPCTY = GetEPDQFormFieldToHash("IPCTY")
LNCERROR = GetEPDQFormFieldToHash("NCERROR")
LOrderNo = GetEPDQFormFieldToHash("orderID")
LPAYID = GetEPDQFormFieldToHash("PAYID")
'LPAYIDSUB = GetEPDQFormFieldToHash("PAYIDSUB")
LPM = GetEPDQFormFieldToHash("PM")
LSTATUS = GetEPDQFormFieldToHash("STATUS")
LTRXDATE = GetEPDQFormFieldToHash("TRXDATE")
'LVC = GetEPDQFormFieldToHash("VC")
LSHASIGN = CleanRequestQueryString("SHASIGN")
' show message and abort if no order no specified
If LOrderNo = "" Then
Response.Write("Order No must be specified" & BR & NL)
Exit Sub
End If
LSHASIGNCalculated = UCase(GetEPDQHashDigest) ' UCase used because they return it in upper case
'Response.Write BR & BR & "SHASIGN Actual: " & LSHASIGN & BR
'Response.Write BR & BR & "SHASIGN Calculated: " & LSHASIGNCalculated & BR
'Response.Write BR & BR & "FEPDQStringToHash: " & HTMLEncode(FEPDQStringToHash) & BR
' add to new epdq_redirect_callbacks log
' !!! to be completed
Dim LSQL
LSQL = "INSERT INTO epdq_redirect_callbacks SET OrderNo=" & CleanSQLStr(LOrderNo) &_
", SessionID='" & CleanSQLStr(GetSessionID) & "'" &_
", DateTimeReceived=NOW()" &_
", Acceptance='" & CleanSQLStr(LACCEPTANCE) & "'" &_
", Amount='" & CleanSQLStr(LAmount) & "'" &_
", Brand='" & CleanSQLStr(LBRAND) & "'" &_
", CN='" & CleanSQLStr(LCN) & "'" &_
", Currency='" & CleanSQLStr(LCurrency) & "'" &_
", IP='" & CleanSQLStr(LIP) & "'" &_
", NCError='" & LNCERROR & "'" &_
", PayID='" & LPAYID & "'" &_
", PM='" & LPM & "'" &_
", Status='" & LSTATUS & "'" &_
", TRXDate='" & LTRXDATE & "'" &_
", SHASIGN='" & LSHASIGN & "'"
ExecuteQuery(LSQL)
' (SS,18/4/18)
AddToPaymentLog "EPDQ", "Status = " & LSTATUS & NL & "NCERROR = " & LNCERROR & NL & "ACCEPTANCE = " & LACCEPTANCE
' if paid then mark the order as paid and send email
' STATUS "5" means authorised
' (SS,1/5/18) replaced LSTATUS = "5" with EPDQSuccessfulPayment, because it can be 5 or 9
If LSHASIGN = LSHASIGNCalculated And EPDQSuccessfulPayment And LOrderNo <> "" Then
' following will update the status, send email and empty basket
' (SS,25/6/18) added new PaymentProviderFee parameter i.e. 0 to the end
' (SS,26/6/18) added two more parameters, PaymentAmountReceived and PaymentProviderSurcharge, only PaymentAmountReceived is sent a value, i.e. ParseFloat(LAmount). The other two are 0.
' (SS,26/6/18) also added PaymentReceivedCurrencyCode, set this to LCurrency
ProcessOrderThirdPartyPayment True, LOrderNo, GetSessionID, ptEPDQ, LPAYID, PAYMENT_RECEIVED_STATUS, "", ParseFloat(LAmount), LCurrency, 0, 0
Else
'Response.Write("Payment not successful" & BR & NL)
'Response.Write("Status Code is " & LStatusCode & BR & NL)
'If LSHASIGN = LSHASIGNCalculated Then
' Response.Write("SHASIGN mismatch" & BR & NL)
'End If
' (SS,6/4/18) show error message and retry button
ShowPaymentFailureAlert ""
SetCreditCardPaymentProviderFailure ' (SS,20/4/18) flag the error
End If
End Sub
' (SS,30/4/18) used by conversion via IsSuccessfulReturnFromPaymentProviderPage to check for successful payment when returning from payment provider
Function EPDQSuccessfulPayment
' (SS,1/5/18) added CleanRequestQueryString("STATUS") = "9", 5 is for Authorised and 9 for Payment requested
EPDQSuccessfulPayment = CleanRequestQueryString("STATUS") = "5" Or CleanRequestQueryString("STATUS") = "9"
End Function
' --------------------------
' End of ePDQ routines
' --------------------------
Function GetDescriptionForPayPal()
GetDescriptionForPayPal = Left(Session("PaymentDescription"), 127) ' max of 127 chars '
End Function
' --------------------------
' Start of Payzone routines
' --------------------------
' (SS,23/3/18) is Payzone payment
Function IsPayzonePayment
IsPayzonePayment = GetPaymentType = ptPayzone
End Function
' (SS,26/3/18)
Function GetPayzoneMerchantID
GetPayzoneMerchantID = FPayzoneMerchantID
End Function
' (SS,26/3/18)
Function GetPayzoneGatewayAccountPassword
GetPayzoneGatewayAccountPassword = FPayzoneGatewayAccountPassword
End Function
' (SS,26/3/18)
Function GetPayzonePreSharedKey
GetPayzonePreSharedKey = FPayzonePreSharedKey
End Function
' (SS,5/4/18) for testing purposes, returns true if valid IP
Function GetPaymentAllowedIP
Const IP_ALLOWED = "ALL"
'Const IP_ALLOWED = "80.229.231.82,86.2.247.155"
Dim LClientIPAddress, LResult
LClientIPAddress = Request.ServerVariables("REMOTE_ADDR")
If IP_ALLOWED = "ALL" Then
LResult = True
Else
LResult = InStr("," & IP_ALLOWED & ",", "," & LClientIPAddress & ",") > 0
End If
GetPaymentAllowedIP = LResult
End Function
' (SS,23/3/18)
' (SS,17/4/18) renamed from SetUpPayzonePayForm to SetUpPayzoneForm
' (SS,19/12/18) added EmailAddress
Sub SetUpPayzoneForm(AOrderNo)
' (SS,28/3/18) clear globals used here
ClearPayzoneStringToHash
' (SS,6/4/18) added LISOCurrencyCode
Dim LTotal, LISOCurrencyCode
LTotal = Session("PaymentAmount") ' amount in selected currency
If IsCurrencyUSD Then
LISOCurrencyCode = "840"
ElseIf IsCurrencyEUR Then
LISOCurrencyCode = "978"
Else ' GBP or other
LISOCurrencyCode = "826"
LTotal = Session("PaymentAmountGBP") ' amount in GBP
End If
' (SS,6/4/18) USD and EUR not set up or working currently so adding the following to default all to GBP, need to comment out the following once USD and EUR working
LISOCurrencyCode = "826"
LTotal = Session("PaymentAmountGBP") ' amount in GBP
LTotal = Round(CDbl(LTotal) * 100) ' x 100 because it's in pence
' date time ordered in required UTC format, TIMEDIFF(NOW(), UTC_TIMESTAMP()) used to determine the difference to UTC
Dim LTransactionDateTimeUTC
'LTransactionDateTimeUTC = GetSQLValueAsString("SELECT CONCAT(DATE_FORMAT(DateTimeOrdered, '%Y-%m-%d %H:%i:%s'), ' +', LEFT(TIMEDIFF(NOW(), UTC_TIMESTAMP()), 5)) AS TransactionDateTimeUTC FROM orders WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'")
' (SS,5/4/18) added CAST(AS CHAR) for MySQL server 5.1.x and old ODBC driver, otherwise it returns ????, fine in MySQL 5.7.x on test server
LTransactionDateTimeUTC = GetSQLValueAsString("SELECT CAST(CONCAT(DATE_FORMAT(DateTimeOrdered, '%Y-%m-%d %H:%i:%s'), ' +', LEFT(TIMEDIFF(NOW(), UTC_TIMESTAMP()), 5)) AS CHAR) AS TransactionDateTimeUTC FROM orders WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'")
' *** to add CallbackURL, check encoding for OrderDescription, CustomerName and size check (before or after encode)
' better name for GetCountryForWorldPay e.g. GetCountryForPaymentProvider
' GetReturnFromPaymentURL
%>
<form name="frmPayzone" action="https://mms.payzoneonlinepayments.com/Pages/PublicPages/PaymentForm.aspx" method="post" class="nospace">
<%
AddPayzoneField "PreSharedKey", GetPayzonePreSharedKey, 0, False
AddPayzoneField "MerchantID", GetPayzoneMerchantID, 0, True
AddPayzoneField "Password", GetPayzoneGatewayAccountPassword, 0, False
AddPayzoneField "Amount", LTotal, 0, True
AddPayzoneField "CurrencyCode", LISOCurrencyCode, 0, True
AddPayzoneField "OrderID", AOrderNo, 50, True
AddPayzoneField "TransactionType", "SALE", 0, True
AddPayzoneField "TransactionDateTime", LTransactionDateTimeUTC, 0, True
'AddPayzoneField "CallbackURL", GetStoreURL, 0, True
AddPayzoneField "CallbackURL", GetReturnFromPaymentURL & "&pt=" & ptPayzone, 0, True
'AddPayzoneField "CallbackURL", GetStoreURL & "/checkout/callback/payzone", 0, True
AddPayzoneField "OrderDescription", GetDescriptionForPaymentProvider(256), 256, True
' (SS,12/4/18) added IsPayzoneSafeString to ensure name has valid chars for Payzone, it doesn't like accented chars, or specials chars like &, ", ', <, >
' if not safe then a blank name is passed to Payzone
Dim LCustomerName
LCustomerName = GetNameForPaymentProvider
If Not IsPayzoneSafeString(LCustomerName) Then LCustomerName = ""
AddPayzoneField "CustomerName", LCustomerName, 100, True
AddPayzoneField "DisplayBillingAddress", False, 0, True
'AddPayzoneField "DisplayBillingAddress", True, 0, True
' (SS,12/4/18) removed address lines, kept postcode
'AddPayzoneField "Address1", Session("Address1"), 100, True
'AddPayzoneField "Address2", Session("Address2"), 50, True
'AddPayzoneField "Address3", "", 50, False
'AddPayzoneField "Address4", "", 50, False
'AddPayzoneField "City", Session("Town"), 50, True
'AddPayzoneField "State", Session("County"), 50, True
AddPayzoneField "Address1", "", 100, True
AddPayzoneField "Address2", "", 50, True
AddPayzoneField "Address3", "", 50, False
AddPayzoneField "Address4", "", 50, False
AddPayzoneField "City", "", 50, True
AddPayzoneField "State", "", 50, True
AddPayzoneField "PostCode", Session("Postcode"), 50, True
AddPayzoneField "CountryCode", GetCountryCodeNumeric(Session("Country")), 0, True
AddPayzoneField "EmailAddress", Session("Email"), 100, True ' (SS,19/12/18)
'AddToSagePayCrypt "SuccessURL", GetReturnFromPaymentURL & "&pt=" & URLEncode(ptSagePay) & "&success=true" ' SagePay will send values back using &crypt
'AddToSagePayCrypt "FailureURL", GetReturnFromPaymentURL & "&pt=" & URLEncode(ptSagePay) & "&success=false" ' SagePay will send values back using &crypt
'AddToSagePayCrypt "CustomerName", Left(GetNameForPaymentProvider, 100) ' optional
'AddToSagePayCrypt "CustomerEMail", Left(Session("Email"), 255) ' optional
AddPayzoneField "ResultDeliveryMethod", "POST", 0, True
%><input type="hidden" name="HashDigest" value="<%=GetPayzoneHashDigest%>">
</form>
<%
' Response.Write "###" & FPayzoneStringToHash & "###" & BR
End Sub
' (SS,12/4/18) returns true if string only contains following characters which we found safe to use in Payzone
Function IsPayzoneSafeString(AStr)
Dim LValid, i, LChar, LCharCode
' from https://www.w3schools.com/html/html_charset.asp
' only allow space, 0 to 9, A to Z, a to z
' also allow dash, comma and fullstop
LValid = True
For i = 1 To Len(AStr)
LChar = Mid(AStr, i, 1)
If LChar = "" Then
LCharCode = 0
Else
LCharCode = Asc(UCase(Left(LChar, 1)))
End If
LValid = (LCharCode >= 48 And LCharCode <= 57) Or (LCharCode >= 65 And LCharCode <= 90) Or LCharCode = 32 Or LCharCode = 44 Or LCharCode = 45 Or LCharCode = 46
If Not LValid Then Exit For
Next
IsPayzoneSafeString = LValid
End Function
' (SS,28/3/18) outputs the input field and adds to FPayzoneStringToHash
' If AIncludeInForm is true then is including in both form and digest, if false then digest only
Sub AddPayzoneField(AFieldName, AFieldValue, AMaxLength, AIncludeInForm)
Dim LFieldValue, LFieldValueEncoded
If AMaxLength = 0 Then
LFieldValue = AFieldValue
Else
LFieldValue = Left(AFieldValue, AMaxLength)
End If
' add to string to hash
'If AFieldName = "CallbackURL" Then
' LFieldValueEncoded = ReplaceStr(LFieldValue, "&", "%26")
'Else
' LFieldValueEncoded = HTMLEncode(LFieldValue)
'End If
' LFieldValueEncoded = HTMLEncode(AFieldValue) ' use this once their issue is resolved
' replace ampersand with + (their system currently can't handle special characters)
LFieldValueEncoded = LFieldValue
'LFieldValueEncoded = ReplaceStr(LFieldValue, "&", "+")
'LFieldValueEncoded = ReplaceStr(LFieldValueEncoded, "<", "")
'LFieldValueEncoded = ReplaceStr(LFieldValueEncoded, ">", "")
'LFieldValueEncoded = ReplaceStr(LFieldValueEncoded, """", "")
'LFieldValueEncoded = ReplaceStr(LFieldValueEncoded, "'", "")
' remove special HTML chars (until Payzone correct the issue)
'LFieldValueEncoded = ReplaceStr(LFieldValue, "&", "+")
'LFieldValueEncoded = HTMLEncode(LFieldValueEncoded)
'LFieldValueEncoded = ReplaceStr(LFieldValue, """", """)
'LFieldValueEncoded = AFieldValue
' add the hidden variable to form
If AIncludeInForm Then
Response.Write "<input type=""hidden"" name=""" & AFieldName & """ value=""" & LFieldValueEncoded & """>" & NL
End If
'AddPayzoneStringToHash AFieldName, HTMLEncode(LFieldValue)
AddPayzoneStringToHash AFieldName, LFieldValueEncoded
End Sub
' (4/4/18)
Sub ClearPayzoneStringToHash
' Global FPayzoneStringToHash
FPayzoneStringToHash = ""
End Sub
' (4/4/18)
Sub AddPayzoneStringToHash(AFieldName, AFieldValue)
FPayzoneStringToHash = FPayzoneStringToHash & IIf(FPayzoneStringToHash = "", "", "&") & AFieldName & "=" & AFieldValue
End Sub
' (SS,28/3/18)
Function GetPayzoneHashDigest
Dim LResult
' use SHA1 function of MySQL to create the hash
' (SS,5/4/18) added CAST(AS CHAR) for MySQL server 5.1.x and old ODBC driver, otherwise it returns ????, fine in MySQL 5.7.x on test server
LResult = GetSQLValueAsString("SELECT CAST(SHA1(""" & CleanSQLStr(FPayzoneStringToHash) & """) AS CHAR)")
GetPayzoneHashDigest = LResult
End Function
' (SS,19/12/18) added EmailAddress
Sub ProcessPayzoneCallback
ClearPayzoneStringToHash
Dim LStatusCode, LPreviousStatusCode, LMessage, LPreviousMessage, LCrossReference, LAmount, LCurrencyCode
Dim LOrderNo, LTransactionType, LTransactionDateTime, LOrderDescription
Dim LCustomerName, LAddress1, LAddress2, LAddress3, LAddress4, LCity, LState, LPostCode, LCountryCode, LEmailAddress
Dim LHashDigest, LHashDigestCalculated
LStatusCode = CleanRequestForm("StatusCode")
LMessage = CleanRequestForm("Message")
LPreviousStatusCode = CleanRequestForm("PreviousStatusCode")
LPreviousMessage = CleanRequestForm("PreviousMessage")
LCrossReference = CleanRequestForm("CrossReference")
LAmount = CleanRequestForm("Amount")
LCurrencyCode = CleanRequestForm("CurrencyCode")
LOrderNo = CleanRequestForm("OrderID")
LTransactionType = CleanRequestForm("TransactionType")
LTransactionDateTime = CleanRequestForm("TransactionDateTime")
LOrderDescription = CleanRequestForm("OrderDescription")
LCustomerName = CleanRequestForm("CustomerName")
LAddress1 = CleanRequestForm("Address1")
LAddress2 = CleanRequestForm("Address2")
LAddress3 = CleanRequestForm("Address3")
LAddress4 = CleanRequestForm("Address4")
LCity = CleanRequestForm("City")
LState = CleanRequestForm("State")
LPostCode = CleanRequestForm("PostCode")
LCountryCode = CleanRequestForm("CountryCode")
LEmailAddress = CleanRequestForm("EmailAddress") ' (SS,19/12/18)
LHashDigest = CleanRequestForm("HashDigest")
' show message and abort if no order no specified
If LOrderNo = "" Then
Response.Write("Order No must be specified" & BR & NL)
Exit Sub
End If
AddPayzoneStringToHash "PreSharedKey", GetPayzonePreSharedKey
AddPayzoneStringToHash "MerchantID", GetPayzoneMerchantID
AddPayzoneStringToHash "Password", GetPayzoneGatewayAccountPassword
AddPayzoneStringToHash "StatusCode", LStatusCode
AddPayzoneStringToHash "Message", LMessage
AddPayzoneStringToHash "PreviousStatusCode", LPreviousStatusCode
AddPayzoneStringToHash "PreviousMessage", LPreviousMessage
AddPayzoneStringToHash "CrossReference", LCrossReference
AddPayzoneStringToHash "Amount", LAmount
AddPayzoneStringToHash "CurrencyCode", LCurrencyCode
AddPayzoneStringToHash "OrderID", LOrderNo
AddPayzoneStringToHash "TransactionType", LTransactionType
AddPayzoneStringToHash "TransactionDateTime", LTransactionDateTime
AddPayzoneStringToHash "OrderDescription", LOrderDescription
AddPayzoneStringToHash "CustomerName", LCustomerName
AddPayzoneStringToHash "Address1", LAddress1
AddPayzoneStringToHash "Address2", LAddress2
AddPayzoneStringToHash "Address3", LAddress3
AddPayzoneStringToHash "Address4", LAddress4
AddPayzoneStringToHash "City", LCity
AddPayzoneStringToHash "State", LState
AddPayzoneStringToHash "PostCode", LPostCode
AddPayzoneStringToHash "CountryCode", LCountryCode
AddPayzoneStringToHash "EmailAddress", LEmailAddress ' (SS,19/12/18)
LHashDigestCalculated = GetPayzoneHashDigest
' Response.Write BR & BR & "HashDigest Calculated: " & LHashDigestCalculated & BR & NL
' add to payzonecallbacks log
Dim LSQL
LSQL = "INSERT INTO payzonecallbacks SET OrderNo=" & LOrderNo &_
", SessionID='" & GetSessionID & "'" &_
", DateTimeReceived=NOW()" &_
", StatusCode='" & LStatusCode & "'" &_
", Message='" & Left(CleanSQLStr(LMessage), 255) & "'" &_
", PreviousStatusCode='" & Left(CleanSQLStr(LPreviousStatusCode), 255) & "'" &_
", PreviousMessage='" & Left(CleanSQLStr(LPreviousMessage), 255) & "'" &_
", CrossReference='" & Left(CleanSQLStr(LCrossReference), 255) & "'" &_
", Amount='" & Left(CleanSQLStr(LAmount), 20) & "'" &_
", CurrencyCode='" & Left(CleanSQLStr(LCurrencyCode), 3) & "'" &_
", TransactionType='" & Left(CleanSQLStr(LTransactionType), 10) & "'" &_
", TransactionDateTime='" & Left(CleanSQLStr(LTransactionDateTime), 20) & "'" &_
", OrderDescription='" & Left(CleanSQLStr(LOrderDescription), 256) & "'" &_
", CustomerName='" & Left(CleanSQLStr(LCustomerName), 100) & "'" &_
", Address1='" & Left(CleanSQLStr(LAddress1), 100) & "'" &_
", Address2='" & Left(CleanSQLStr(LAddress2), 100) & "'" &_
", Address3='" & Left(CleanSQLStr(LAddress3), 100) & "'" &_
", Address4='" & Left(CleanSQLStr(LAddress4), 100) & "'" &_
", City='" & Left(CleanSQLStr(LCity), 100) & "'" &_
", State='" & Left(CleanSQLStr(LState), 100) & "'" &_
", PostCode='" & Left(CleanSQLStr(LPostCode), 100) & "'" &_
", CountryCode='" & Left(CleanSQLStr(LCountryCode), 3) & "'" &_
", HashDigestReturned='" & Left(CleanSQLStr(LHashDigest), 100) & "'" &_
", HashDigestCalculated='" & Left(CleanSQLStr(LHashDigestCalculated), 100) & "'"
ExecuteQuery(LSQL)
' (SS,18/4/18)
AddToPaymentLog "Payzone", "Status = " & LStatusCode & NL & "Message = " & LMessage
' if paid then mark the order as paid and send email
If LHashDigest = LHashDigestCalculated And LStatusCode = "0" And LOrderNo <> "" Then
' following will update the status, send email and empty basket
' (SS,25/6/18) calculate the Fee on the amount, 1.25% + 5p
Dim LPaymentProviderFee
LAmount = Round2dp(ParseFloat(LAmount) / 100) ' (SS,26/6/18) amount is in pence, so divde by 100 to convert to pounds
LPaymentProviderFee = Round2dp(Round2dp(LAmount * 0.0125) + 0.05)
' (SS,25/6/18) added new LPaymentProviderFee parameter
' (SS,26/6/18) added two more parameters, PaymentAmountReceived and PaymentProviderSurcharge. PaymentAmountReceived is also sent a value, i.e. ParseFloat(LAmount). The other one (surcharge) is 0.
' (SS,26/6/18) also added PaymentReceivedCurrencyCode, set this to LCurrencyCode
If LCurrencyCode = "826" Then LCurrencyCode = "GBP" ' convert from code to alpha for GBP, note that amount is
ProcessOrderThirdPartyPayment True, LOrderNo, GetSessionID, ptPayzone, LCrossReference, PAYMENT_RECEIVED_STATUS, "", ParseFloat(LAmount), LCurrencyCode, 0, LPaymentProviderFee
Else
'Response.Write("Payment not successful" & BR & NL)
'Response.Write("Status Code is " & LStatusCode & BR & NL)
'If LHashDigest <> LHashDigestCalculated Then
' Response.Write("Hash Digest mismatch" & BR & NL)
'End If
' (SS,6/4/18) show error message and retry button
ShowPaymentFailureAlert ""
SetCreditCardPaymentProviderFailure ' (SS,20/4/18) flag the error
End If
End Sub
' (SS,30/4/18) used by conversion via IsSuccessfulReturnFromPaymentProviderPage to check for successful payment when returning from payment provider
Function PayzoneSuccessfulPayment
PayzoneSuccessfulPayment = CleanRequestForm("StatusCode") = "0"
End Function
' --------------------------
' End of Payzone routines
' --------------------------
' (SS,19/3/13) new for Sage Pay, similar to previous need to specify maximum length
Function GetDescriptionForPaymentProvider(AMaxLength)
GetDescriptionForPaymentProvider = Left(Session("PaymentDescription"), AMaxLength) ' max of x chars '
End Function
' (SS,19/3/13) same as GetNameForWorldPay, i.e. Title, First Name and Surname
Function GetNameForPaymentProvider
GetNameForPaymentProvider = GetNameForWorldPay
End Function
' (SS,15/5/10) rewritten using GetValueFromQuery (1 line of code instead of 6)
Function GetCountryISOCode(ACountry)
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
GetCountryISOCode = NB(GetValueFromQuery("CodeA2", "SELECT * FROM countries WHERE Country = '" + CleanSQLStr(ACountry) + "'"))
End Function
' (SS,28/3/18) returns country code in numeric e.g. "826" for UK
Function GetCountryCodeNumeric(ACountry)
GetCountryCodeNumeric = NB(GetValueFromQuery("CodeNumeric", "SELECT * FROM countries WHERE Country = '" + CleanSQLStr(ACountry) + "'"))
End Function
' (SS,16/1/15) returns True if country is UK, uses the ISO code from countries table to determine this
Function IsUK(ACountry)
IsUK = GetCountryISOCode(ACountry) = "GB"
End Function
' (SS,16/1/15) to use a setting, for now it returns True
Function UseDeliveryPostcodes
UseDeliveryPostcodes = FUseDeliveryPostcodes ' (SS,10/4/15) replaced True with FUseDeliveryPostcodes
End Function
' (SS,20/11/14) returns Country Name for given 2 letter ISO Code
Function GetCountryNameForCode(ACountryCode)
GetCountryNameForCode = NB(GetValueFromQuery("Country", "SELECT * FROM countries WHERE CodeA2 = '" + CleanSQLStr(ACountryCode) + "' ORDER BY SortOrder"))
End Function
' (SS,19/12/13) returns EU country code for VAT number checking purposes
' EU has two exception, Greece ISO "GR" is "EL", United Kingdom ISO "UK" is "GB"
Function GetEUCountryCode(ACountry)
Dim LISO
LISO = GetCountryISOCode(ACountry)
If LISO = "UK" Then
GetEUCountryCode = "GB"
ElseIf LISO = "GR" Then
GetEUCountryCode = "EL"
Else
GetEUCountryCode = LISO
End If
End Function
' (SS,15/5/10)
Function GetCountryVATNumberFormat(ACountry)
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
GetCountryVATNumberFormat = NB(GetValueFromQuery("VATNumberFormat", "SELECT * FROM Countries WHERE Country = '" + CleanSQLStr(ACountry) + "'"))
End Function
' (SS,20/2/12) returns ChargeVAT flag from countries table for given country, True is returned if country not found
Function GetCountryChargeVATFlag(ACountry)
Dim LChargeVAT
If ACountry = "" Then
LChargeVAT = True
Else
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
LChargeVAT = GetValueFromQuery("ChargeVAT", "SELECT ChargeVAT FROM Countries WHERE Country = '" + CleanSQLStr(ACountry) + "'")
' (SS,26/2/15) replaced LChargeVAT = Null with IsNull(LChargeVAT) to fix bug where country wasn't found and LChargeVAT = Null still returned false
If IsNull(LChargeVAT) Then
LChargeVAT = True
Else
LChargeVAT = IntToBool(LChargeVAT)
End If
End If
GetCountryChargeVATFlag = LChargeVAT
End Function
' (SS,15/5/10) validate VAT number according to http://customs.hmrc.gov.uk/channelsPortalWebApp/channelsPortalWebApp.portal?_nfpb=true&_pageLabel=pageVAT_RatesCodesTools&propertyType=document&columns=1&id=HMCE_CL_001721#P100_1122
' (SS,11/8/16) renamed from IsVATNumberValid to IsVATNumberFormatValid
Function IsVATNumberFormatValid(ACountry, ANumberToValidate)
Dim i, LLen, LChar, LCharFormat, LValid, LNumberToValidate, LCountryCode, LAllowedFormats
' (SS,11/11/10) if VAT Number validation is not enabled then assume valid
' (SS,20/12/13) added "Not IsIntraCommunitySupply" to not validate VAT number if not applicable, e.g. people in the UK may enter a VAT number which we don't want to validate
' (SS,16/10/14) added special VAT check bypass where "[NV]" is added to end if VAT number, because on rare occasions it might be valid by checking site says it isn't
' (SS,10/08/16) removed Or Not IsIntraCommunitySupply because this relies on DeliveryCountry which isn't set in CheckValidPersonalDetails when checking out, i.e. different delivery address
' therefore we need to force the VAT check no matter what the delivery country
If Not ValidateVATNumber Or Right(ANumberToValidate, 4) = "[NV]" Then
IsVATNumberFormatValid = True
Exit Function
End If
LCountryCode = GetEUCountryCode(ACountry) ' (SS,19/12/13) replaced GetCountryISOCode with GetEUCountryCode because Greece uses "EL" nor "GR" GetEUCountryCode does this correct
LAllowedFormats = GetCountryVATNumberFormat(ACountry)
If ACountry = "" Or LAllowedFormats = "" Then
IsVATNumberFormatValid = False
SetVATNumberExample "" ' (SS,25/5/10)
Exit Function
End If
LLen = Len(ANumberToValidate)
LNumberToValidate = Mid(ANumberToValidate, 3, LLen - 2)
LLen = LLen - 2 ' reduce by 2 because we're now ignoring country prefix
LValid = Mid(ANumberToValidate, 1, 2) = LCountryCode ' valid if first 2 characters match the country code
' Response.Write("<br>Formats: " & LAllowedFormats & "<br>")
Dim LVATFormats, LVATFormat
LVATFormats = Split(LAllowedFormats, ",")
If LValid Then
For Each LVATFormat In LVATFormats
LValid = LLen = Len(LVATFormat) ' default to True if length is the same
'Response.Write("<br>LNumberToValidate: " & LNumberToValidate & "<br>")
'Response.Write("<br>Format: " & LVATFormat & "<br>")
i = 1
Do While LValid And i <= LLen
LCharFormat = Mid(LVATFormat, i, 1)
LChar = Mid(LNumberToValidate, i, 1)
If LCharFormat = "X" Then ' any letter
LValid = IsCharAlpha(LChar)
ElseIf LCharFormat = "Y" Then ' any letter except O and I
LValid = IsCharAlpha(LChar) And LChar <> "O" And LChar <> "I"
ElseIf IsCharNumeric(LCharFormat) Then ' 0 to 9
LValid = IsCharNumeric(LChar)
Else ' i.e. any letter besides X and Y
LValid = LChar = LCharFormat
End If
i = i + 1
Loop
If LValid Then Exit For ' if valid here then it's valid so no need to compare any more
Next
End If
' (SS,25/5/10)
If Not LValid Then
' determine the examples to show in error message prefixed with country code
Dim LExampleVATFormats
LExampleVATFormats = ""
For Each LVATFormat In LVATFormats
LExampleVATFormats = LExampleVATFormats + iif(LExampleVATFormats = "", "", ", ") + LCountryCode + ReplaceStr(LVATFormat, "Y", "X") ' "Y" is also replaced with "X"
Next
SetVATNumberExample tr("e.g.") + " " + LExampleVATFormats
End If
IsVATNumberFormatValid = LValid
End Function
' (SS,25/5/10)
Sub SetVATNumberExample(AVATNumberExample)
FVATNumberExample = AVATNumberExample
End Sub
' (SS,25/5/10)
Function GetVATNumberExample
GetVATNumberExample = FVATNumberExample
End Function
' (SS,6/10/11) added a hidden field and message used for VAT validation in account and checkout forms
Sub AddVATNumberValidationHTML
If IsVATZeroForIntraCommunity And ValidateVATNumber Then
%>
<input name="VATNumberValidated" type="hidden" value="">
<script type="text/javaScript">
ITP_InvalidVATNoMessage = "<%=tr("Following VAT Number is not valid")%>"
</script>
<%
End If
End Sub
' delete all the products form basket for this session that have since been disabled '
Sub DeleteDisabledProductsFromBasket()
Dim LSQL
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
LSQL = "DELETE FROM shoppingbaskets USING shoppingbaskets, products "
LSQL = LSQL + "WHERE shoppingbaskets.SessionID = '" & CleanSQLStr(FSessionID) & "' AND shoppingbaskets.ProductCode = products.ProductCode AND products.ProductDisabled <> 0"
ExecuteQuery LSQL
End Sub
' returns true if basket is empty
' (SS,17/2/04) now calls IsBasketSessionEmpty
' (SS,5/6/07) now calls returns FIsBasketEmpty (whichs is set from initialise using GetIsBasketEmpty)
Function IsBasketEmpty
IsBasketEmpty = FIsBasketEmpty
End Function
' returns true if basket is empty '
' (SS,17/2/04) modified version of IsBasketEmpty with ASessionID as parameter '
' (SS,5/6/07) was IsBasketSessionEmpty, renamed to GetIsBasketEmpty and removed ASession parameter
Function GetIsBasketEmpty
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
GetIsBasketEmpty = NZ(GetValueFromQuery("Count", "SELECT Count(*) As Count FROM shoppingbaskets WHERE SessionID = '" & CleanSQLStr(FSessionID) & "'")) = 0
End Function
' (SS,21/5/07) returns total number of items in basket
' (SS,12/5/18) added GetShoppingBasketFilter(False) to ignore subproducts (missed from before)
Function GetTotalItemsInBasket
' If IsEmpty used to ensure this query is run maximum of once per session
If IsEmpty(FTotalItemsInBasket) Then
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
FTotalItemsInBasket = NZL(GetValueFromQuery("Items", "SELECT SUM(Qty) As Items FROM shoppingbaskets WHERE SessionID = '" & CleanSQLStr(FSessionID) & "'"& GetShoppingBasketFilter(False)))
End If
GetTotalItemsInBasket = FTotalItemsInBasket
End Function
' (SS,28/1/08) added following to count how many times a product name token is used, this can be used by customutils.asp to calc delivery
Function GetProductNameTokensInBasket(AToken)
' If IsEmpty used to ensure this query is run maximum of once per session
If IsEmpty(FProductNameTokensInBasket) Then
If AToken = "" Then
FProductNameTokensInBasket = 0
Else
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
FProductNameTokensInBasket = NZL(GetValueFromQuery("Tokens", "SELECT SUM(Qty) As Tokens FROM shoppingbaskets LEFT JOIN products ON shoppingbaskets.ProductCode = products.ProductCode WHERE SessionID = '" & CleanSQLStr(FSessionID) & "' AND INSTR(products.ProductName, '" + CleanSQLStr(AToken) + "')"))
End If
End If
GetProductNameTokensInBasket = FProductNameTokensInBasket
End Function
' (SS,1/2/13) added following to count how many times a product code prefix is used, e.g. "RMP-" for ready made parcels this can be used by customutils.asp to calc delivery
Function GetProductCodePrefixesInBasket(APrefix)
' If IsEmpty used to ensure this query is run maximum of once per session, prevents running of same query more than once
If IsEmpty(FProductCodePrefixesInBasket) Or FProductCodePrefixesPrefix <> APrefix Then
FProductCodePrefixesPrefix = APrefix
If APrefix = "" Then
FProductCodePrefixesInBasket = 0
Else
FProductCodePrefixesInBasket = NZL(GetSQLValue("SELECT SUM(Qty) FROM shoppingbaskets WHERE SessionID = '" & CleanSQLStr(FSessionID) & "' AND LEFT(ProductCode, " & Len(APrefix) & ") = '" + CleanSQLStr(APrefix) + "'"))
End If
End If
GetProductCodePrefixesInBasket = FProductCodePrefixesInBasket
End Function
' (SS,30/10/08) added following to count number of flagged products in basket, this can be used by customutils.asp to calc delivery
' AFlagged should be True to count Flagged products, False to count Unflagged (i.e. the value of the CustomFlag field)
Function GetFlaggedItemsInBasket(AFlagged)
' (SS,18/3/15) added "If InGoogleShoppingDataFeedthis" to handle when doing this check for Google Shopping delivery costs, i.e. there is no basket
If InGoogleShoppingDataFeed Then
GetFlaggedItemsInBasket = NZL(GetValueFromQuery("Count", "SELECT COUNT(*) AS Count FROM products WHERE ProductID = " & GetGoogleShoppingDataFeedProductID & " AND CustomFlag = " + Iif(AFlagged, "TRUE", "FALSE")))
Else
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
' (SS,31/10/18) added GetProductsFilter to filter out subproducts
GetFlaggedItemsInBasket = NZL(GetValueFromQuery("Count", "SELECT SUM(Qty) As Count FROM shoppingbaskets LEFT JOIN products ON shoppingbaskets.ProductCode = products.ProductCode WHERE SessionID = '" & CleanSQLStr(FSessionID) & "' AND products.CustomFlag = " + Iif(AFlagged, "TRUE", "FALSE") + GetProductsFilter))
End If
End Function
' (SS,20/2/12)
Sub SetDeliveryCountryCookie
If CleanRequest("cmd") = "recalculate" Then
Dim LSelectedDeliveryCountry
LSelectedDeliveryCountry = CleanRequestForm("DeliveryCountry")
If LSelectedDeliveryCountry <> "" Then
SetCookie "DeliveryCountry", LSelectedDeliveryCountry
End If
End If
End Sub
' (SS,20/2/12) used to change the delivery and invoice country from basket form
' to show the correct delivery and also include/exclude VAT if country is vatable/not vatable
' this routine is also called from GetCurrencyExchangeRate
' invoice country is also changed if it had been the same as delivery country
' this saves user from having to re-enter in the checkout stage, also shows the correct prices inc/exc VAT depending on whether country is vatable
' (SS,28/6/18) added TrackedDeliverySelected
' (SS,29/6/18) minor change (see below)
Sub SetCountryFromBasketForm
If CleanRequest("cmd") = "recalculate" Then ' only do it in basket recalculate
Dim LDeliveryCountrySameAsInvoice, LSelectedDeliveryCountry
LDeliveryCountrySameAsInvoice = Session("Country") = Session("DeliveryCountry")
LSelectedDeliveryCountry = CleanRequestForm("DeliveryCountry")
If LSelectedDeliveryCountry <> "" Then
SetDeliveryCountry(LSelectedDeliveryCountry)
If LDeliveryCountrySameAsInvoice Then
SetInvoiceCountry(LSelectedDeliveryCountry)
End If
End If
' (SS,28/6/18) added following to also allow delivery option (tracking) to be selected
' (SS<29/6/18) changed from TrackedDeliverySelected to DeliveryMethod, and "checked" to "tracked"
If TrackedDeliveryOptionEnabled Then
SetTrackedDeliverySelected(CleanRequestForm("DeliveryMethod") = "tracked")
End If
End If
End Sub
' (SS,27/5/07) replaced GetCurrencyCountry with GetDeliveryCountry
Function GetDeliveryCountry
Dim LCountry
If Session("DeliveryCountry") = "" Then
LCountry = Session("Country")
Else
LCountry = Session("DeliveryCountry")
End If
If LCountry = "" Then
LCountry = GetCookie("DeliveryCountry") ' (SS,20/2/12)
' (SS,19/1/15) if country from cookie is no longer valid then ignore it
If LCountry <> "" Then ' two separate ifs used due to ASP's limitation of always calling all condition sections if even previous ones are FALSE in an AND
If Not IsValidCountry(LCountry) Then
LCountry = ""
DeleteCookie "DeliveryCountry" ' (SS,26/2/15) delete invalid cookie, no point keeping it
End If
End If
End If
If LCountry = "" Then LCountry = FCurrencyCountry
If LCountry = "" Then LCountry = GetDefaultCountry ' (SS,16/2/12)
GetDeliveryCountry = LCountry
End Function
' (SS,19/1/15) Checks that country is valid (exists in countries table) need because countries may have changed e.g. CovDem/CIRC, UK (England) etc. were dropped, and cookie for existing
' customers may hold the old value which no longer exists in the countries table
' (SS,22/1/15) added Enabled = 'Yes'
Function IsValidCountry(ACountry)
IsValidCountry = GetSQLRecordExists("SELECT Country FROM countries WHERE Country = '" & CleanSQLStr(ACountry) & "' AND Enabled = 'Yes'")
End Function
' (SS,16/2/12) sets delivery country in session to given, used in basket page to allow user to calculate delivery for their region without going to checkout stage
Function SetDeliveryCountry(ADeliveryCountry)
If Session("Country") = "" Then Session("Country") = ADeliveryCountry ' default the invoice country as well if not already set
Session("DeliveryCountry") = ADeliveryCountry
End Function
' (SS,27/5/07) replaced GetCurrencyCountry with GetInvoiceCountry
Function GetInvoiceCountry
Dim LCountry
LCountry = Session("Country")
' (SS,2/11/12) added following
' (SS,26/2/15) replaced GetCookie("DeliveryCountry") in following with GetDeliveryCountry which may also call GetCookie("DeliveryCountry")
If LCountry = "" Then LCountry = GetDeliveryCountry
If LCountry = "" Then LCountry = FCurrencyCountry
If LCountry = "" Then LCountry = GetDefaultCountry ' (SS,20/2/12)
GetInvoiceCountry = LCountry
End Function
' (SS,20/2/12) sets invoice country in session to given, used in basket page to allow user to set invoice country if they change delivery country
' (SS,12/12/12) corrected to Sub from Function
Sub SetInvoiceCountry(AInvoiceCountry)
Session("Country") = AInvoiceCountry
End Sub
' (SS,9/6/14) get delivery country from session, cookie, GeoIP, save to cookie if necessary
Sub SetCurrencyAndDeliveryCountryCookieFromGeoIP
' don't do a Geo IP lookup if it's not the main products.asp script
' this prevents it running in order-details.asp and other scripts
If Not ScriptIsProducts Or Not GeoIPEnabled Then Exit Sub
Dim LGeoIPCountry, LIPFromQueryString
LGeoIPCountry = ""
LIPFromQueryString = CleanRequestQueryString("ip") ' used for testing purposes, to override the remote IP
' if currency code or delivery country cookie is not set then lookup from GeoIP and save to cookie
If Session("DeliveryCountry") = "" Or Session("CurrencyCode") = "" Then
If Session("DeliveryCountry") = "" Then
Session("DeliveryCountry") = GetCookie("DeliveryCountry")
' (SS,6/2/15) if country from cookie is no longer valid then ignore it
If Session("DeliveryCountry") <> "" Then ' two separate ifs used due to ASP's limitation of always calling all condition sections if even previous ones are FALSE in an AND
If Not IsValidCountry(Session("DeliveryCountry")) Then Session("DeliveryCountry") = ""
End If
If Session("DeliveryCountry") = "" Then
LGeoIPCountry = GetGeoIPCountry(LIPFromQueryString)
Session("DeliveryCountry") = LGeoIPCountry
SetCookie "DeliveryCountry", Session("DeliveryCountry")
End If
End If
If Session("CurrencyCode") = "" Then
Session("CurrencyCode") = GetCookie("CurrencyCode")
If Session("CurrencyCode") = "" Then
If LGeoIPCountry = "" Then LGeoIPCountry = GetGeoIPCountry(LIPFromQueryString) ' if not already looked up above
Session("CurrencyCode") = GetCurrencyCodeForCountry(LGeoIPCountry)
SetCookie "CurrencyCode", Session("CurrencyCode")
End If
End If
End If
End Sub
' (SS,5/6/14) returns country name using the remote IP address, looking up in geo_ip_country table
Function GetGeoIPCountry(AIPAddress)
Dim LIPAddress, LIPAddressInt, LCountryCode, LCountryName
' if IP supplied (used for testing) then use that else use the one from server variables
If AIPAddress = "" Then
LIPAddress = Request.ServerVariables("REMOTE_ADDR")
Else
LIPAddress = AIPAddress
End If
' lookup the country code in geo_ip_country table
LIPAddressInt = IPAddressToInt(LIPAddress)
LCountryCode = ""
If LIPAddressInt <> 0 Then
OpenDatabaseCommon
OpenQueryCommon("SELECT * FROM geo_ip_country WHERE IPStartInteger <= " & LIPAddressInt & " AND IPEndInteger >= " & LIPAddressInt)
If Not EndOfQueryCommon Then
LCountryCode = GetQueryValueCommon("CountryISOCode")
End If
CloseQueryCommon
CloseDatabaseCommon
End If
' lookup the country code in the countries table to get the country name, some codes may have more than one, in which case get the first (sorted by SortOrder)
' if country code is blank or not found then assume default country name
Dim LResult
If LCountryCode = "" Then
LResult = ""
Else
' SortOrderISOCode allows the preferred default to be set when there are more than one
LResult = GetSQLValueAsString("SELECT Country FROM countries WHERE CodeA2 = '" & CleanSQLStr(LCountryCode) & "' ORDER BY SortOrderISOCode, Country")
End If
If LResult = "" Then LResult = GetDefaultCountry
GetGeoIPCountry = LResult
End Function
' (SS,16/11/07)
Function GetVATNumberForIntraCommunity
GetVATNumberForIntraCommunity = Session("VATNumber")
End Function
' (SS,12/12/12) because order confirmation is called outside of session we need to put VAT Number into session for functions like CorrectCurrencyNV to work correctly
Sub SetVATNumberForIntraCommunity(AVATNumber)
Session("VATNumber") = AVATNumber
End Sub
' (SS,23/6/09) help message used to display on create/edit accounts form
Function GetVATNumberHelpMessage
If FunctionExists("CustomVATNumberHelpMessage") Then
GetVATNumberHelpMessage = CustomVATNumberHelpMessage
Else
GetVATNumberHelpMessage = ""
End If
End Function
' (SS,15/1/15) gets voucher from posted form and places in session variable
' added because voucher code on basket page
' (SS,16/2/15) added AVoucherCode parameter
Sub SetVoucherCode(AVoucherCode)
Session("VoucherCode") = AVoucherCode
End Sub
' (SS,15/1/15)
Function GetVoucherCode
GetVoucherCode = Session("VoucherCode")
End Function
' (SS,10/10/16)
Sub ClearVoucherCodeForLocalMode
If IsLocalMode And GetVoucherCode = DISCOUNT_OVERRIDE_VOUCHER_CODE Then
SetVoucherCode ""
End If
End Sub
' (SS,16/1/15) used to get delivery region using given UK postcode
' due to use of PayPal Express, only United Kingdom can be used for UK, no longer can use UK (England), UK (Mainland) etc.
' new table delivery_postcodes holds list of mappings
' returns blank if region couldn't be determined, or postcode is blank
' (SS,6/2/15) new improved version
' (SS,10/2/15) added new AGroupName parameter parameter, which can be blank for normal or main group
' (SS,16/3/15) modified to use OpenQuery2 instead of OpenQuery to prevent clash due to recursive use
' (SS,19/01/15) slight mod to allow 0, e.g. CR0 9DW, some areas can have a 0
' also modified to allow all postcodes, default to DEFAULT_DELIVERY_REGION_UK if not found, also send alert email if an invalid postcode is entered
Function GetDeliveryRegionFromPostcode(APostcode, AGroupName)
' (SS,10/2/15) complete list of postcodes now including here to check if text "ALL" in PostcodeList
Dim LAllPostcodeAreas
LAllPostcodeAreas = "AL,CB,CM,CO,EN,IG,IP,LU,MK,NR,PE,RM,SG,SS,WD," &_
"B,CV,DE,DY,LE,NG,NN,ST,WS,WV," &_
"BD,DH,DL,DN,HD,HG,HU,HX,LN,LS,NE,S,SR,TS,WF,YO," &_
"BB,BL,CA,CW,FY,L,LA,M,OL,PR,SK,SY,TF,WA,WN,CH," &_
"E,EC,N,NW,SE,SW,W,WC," &_
"GU,HA,HP,OX,PO,RG,SL,SN,SO,SP,UB," &_
"BN,BR,CR,CT,DA,KT,ME,RH,SM,TN,TW,BA," &_
"BH,BS,DT,EX,GL,HR,PL,TA,TQ,TR,WR," &_
"CF,LD,LL,NP,SA," &_
"BT," &_
"AB,DD,DG,EH,FK,G,HS,IV,KA,KW,KY,ML,PA,PH,TD,ZE"
Dim LResult, LPostcode, LPrefix, LOutwardNo, LInwardNo, LSuffix
LResult = ""
LPostcode = CorrectPostcodeForUK(APostcode)
' (SS,18/2/15) added CorrectPostcodeForUK which removes extra spaces and hyphens
If SplitPostcode(LPostcode, LPrefix, LOutwardNo, LInwardNo, LSuffix) Then
Dim LPostcodeList, i, LPostcodeItem
Dim LPostcodeArea, LStart, LEnd, LPos, LWhere
' (SS,10/2/15) filter by new GroupName field
LWhere = " WHERE GroupName = '" & CleanSQLStr(AGroupName) & "'"
If AGroupName = "" Then
LWhere = LWhere & " OR GroupName IS NULL OR GroupName = 'Main'"
End If
OpenQuery2("SELECT * FROM delivery_postcodes" & LWhere & " ORDER BY SortOrder, ID")
Do While Not EndOfQuery2
' (SS,10/2/15) check for ALL and replace with full postcode list
LPostcodeList = Trim(GetQueryValue2("PostcodeList"))
If UCase(LPostcodeList) = "ALL" Then LPostcodeList = LAllPostcodeAreas
LPostcodeList = Split(LPostcodeList, ",")
' for each item in postcode list
For i = LBound(LPostcodeList) to UBound(LPostcodeList)
LPostcodeItem = Trim(UCase(LPostcodeList(i))) ' (SS,5/2/15) added Trim to remove spaces, and UCase to convert to uppercase
' determine start and end, assume 0 if no start or end
LStart = ParseFirstInt(LPostcodeItem)
LPos = InStr(LPostcodeItem, "-")
If LPos = 0 Then
LEnd = LStart
Else
LEnd = ParseInt(Mid(LPostcodeItem, LPos + 1))
End If
' (SS,19/1/15) removed following because some areas have 0 e.g. CR0 9DW
' If LStart = 0 Then LStart = 1 ' because 0 isn't a valid number
If LEnd = 0 Then LEnd = 99 ' if LEnd is 0 i.e. not specified then assume maximum
' determine postcode area which can be 1 or 2 letters
LPostcodeArea = ""
If IsCharAlpha(Mid(LPostcodeItem, 1, 1)) Then
LPostcodeArea = Mid(LPostcodeItem, 1, 1)
If IsCharAlpha(Mid(LPostcodeItem, 2, 1)) Then LPostcodeArea = LPostcodeArea + Mid(LPostcodeItem, 2, 1)
End If
' if postcode area matches then match the outward range, if within range then it's a match
' (SS,18/2/15) replaced CInt with ParseInt because LOutwardNo can contain a letter, e.g. EC2M 2RH, SW1W 0NY, E1W 1AB
If LPostcodeArea <> "" And LPostcodeArea = LPrefix Then
'Response.Write("OutwardNo 1:" & LOutwardNo & BR)
If ParseInt(LOutwardNo) >= LStart And ParseInt(LOutwardNo) <= LEnd Then
'Response.Write ("Start: " & LStart & ", End: " & LEnd & ", OutwardNo: " & ParseInt(LOutwardNo) & BR)
LResult = GetQueryValue2("DeliveryRegionCode")
Exit Do
End If
End If
Next
NextQueryRecord2
Loop
CloseQuery2
End If
' (SS,19/3/15) remove ElseIf LPostcode = "" from above and added this to default to the default region, if region not found in delivery postcodes
' if a postcode was entered then an alert email is sent, but customer is allowed to continue to place the order
If LResult = "" Then
LResult = DEFAULT_DELIVERY_REGION_UK
If LPostcode <> "" And Not InGoogleShoppingDataFeed Then ' don't send emails if doing Google Shopping Data Feed
Dim LBody
' added emailing of error message for alert purposes, together with session variables
LBody = LBody & "SessionID = " & GetSessionID
LBody = LBody & NL & "IPAddress = " & Request.ServerVariables("REMOTE_ADDR")
LBody = LBody & NL & "DateTime = " & Now
LBody = LBody & NL & SessionVarList("OrderNoPlaced,AccountID,Title,FirstName,Surname,CompanyName,Address1,Address2,Town,County,Postcode,Country,VATNumber,Telephone,Email,VoucherCode,Subscribe,HearAboutUs" &_
",Message,DeliveryDate,DeliveryWillCollect,DeliveryAddressSameAsInvoice,DeliveryName,DeliveryCompanyName,DeliveryAddress1,DeliveryAddress2,DeliveryTown,DeliveryCounty,DeliveryPostcode,DeliveryCountry")
SendAlertEmail "Invalid postcode entered. Default UK delivery region assumed.", LBody, False
End If
End If
GetDeliveryRegionFromPostcode = LResult
End Function
' (SS,13/9/06)
' (SS,22/10/08) renamed ARemaining parameter to AValue, we're now alerting the user instead of calculating delivery using multiples
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
' (SS,16/3/15) modified to use Query2 to prevent recursive issue, i.e. OpenQuery replaced with OpenQuery2, GetQueryValue with GetQueryValue2 etc.
Function GetDeliveryFromTable(ARegionCode, AIsSpecialRegion, AType, AValue)
GetDeliveryFromTable = 0 ' (SS,22/10/08) moved here from 2 different places below
Dim LSQL, LRegionNo
LSQL = "SELECT * FROM deliveryregions WHERE Type = '" + CleanSQLStr(AType) + "'"
If AIsSpecialRegion And Session("DeliveryRegion") <> "" Then
LSQL = LSQL + " AND RegionName = '" + CleanSQLStr(Session("DeliveryRegion")) + "'"
Else
LSQL = LSQL + " AND RegionCode = '" + CleanSQLStr(ARegionCode) + "'"
End If
OpenQuery2(LSQL)
If Not EndOfQuery2 Then
LRegionNo = GetQueryValue2("RegionNo")
Else
LRegionNo = ""
End If
CloseQuery2
' flag error and exit if LRegionNo is not found
If LRegionNo = "" Then
FDeliveryCalculationFailure = True
Exit Function
End If
Dim LRegionField
LRegionField = "Region" & LRegionNo
LSQL = "SELECT * FROM deliverycosts WHERE Type = '" + CleanSQLStr(AType) + "' AND " + LRegionField + " IS NOT NULL ORDER BY Value"
OpenQuery2(LSQL)
' flag error and exit if no records found
If EndOfQuery2 Then
FDeliveryCalculationFailure = True
CloseQuery2
Exit Function
End If
' (SS,22/10/08) added LFound
Dim LFound
LFound = False
Do While Not EndOfQuery2
If CDbl(GetQueryValue2("Value")) >= AValue Then
LFound = True ' (SS,22/10/08)
Exit Do
End If
NextQueryRecord2
Loop
' (SS,22/10/08) signal failure if not found
If LFound Then
GetDeliveryFromTable = CDbl(GetQueryValue2(LRegionField))
Else
FDeliveryCalculationFailure = True
End If
CloseQuery2
End Function
' (SS,18/2/04) added application delivery values previously was hard code '
' (SS,4/11/04) modified to use GetPostalArea and GetDeliveryCostForPostalArea '
' also renamed from GetDeliveryCost to GetDeliveryCostForCountry
' (SS,12/9/06) added AWeightOfGoods
' (SS,27/5/07) was GetDeliveryCostForCountry, also removed ACountry parameter
' (SS,28/5/07) added AVATContent which is the VAT content of goods, if PricesEnteredIncludeVAT then ATotalPriceOfGoods includes VAT
' (SS,21/10/08) amendments for custom delivery to pass on postcode and to work in conjunction with normal by price/weight calculations
' (SS,22/10/08) added AFlaggedWeight and ANonFlaggedWeight, removed the looping facility where it was looping around if price/weight
' was too big, user is now alert if value/weight is too high
Function GetDeliveryCost(ATotalPriceOfGoods, APriceOfNonWeightedGoods, AWeightOfGoods, AFlaggedWeight, ANonFlaggedWeight, AVATContent)
GetDeliveryCost = 0
' (SS,14/11/07) if collection allow and customer has chosen to collect then no delivery charge
' (SS,1/12/16) replaced AllowCollection And Session("DeliveryWillCollect") with GetDeliveryWillCollect
' (SS,31/3/17) added GetDeliveryAddToExistingOrder
' (SS,2/10/17) removed Or GetDeliveryAddToExistingOrder, move below to do a check to make sure customer has an existing order not yet completed
If GetDeliveryWillCollect Then Exit Function
' (SS,2/10/17)
If GetDeliveryAddToExistingOrder And Session("Email") <> "" Then
If CustomerHasExistingOrder(Session("Email")) Then
Exit Function ' i.e. free delivery
End If
End If
Dim LCountry
LCountry = GetDeliveryCountry
'Response.Write ("###Total price:" & ATotalPriceOfGoods - GetVATAmount(ATotalPriceOfGoods) & "####<br>")
'Response.Write ("###Total priced goods:" & APriceOfNonWeightedGoods - GetVATAmount(APriceOfNonWeightedGoods) & "####<br>")
'Response.Write ("###Total weight:" & AWeightOfGoods & "####<br>")
FDeliveryCalculationFailure = False
Dim LDeliveryCost
LDeliveryCost = 0
' (SS,26/5/07) removed following this is now done in GetSettings in the initialisation process
' GetDeliverySettings
Dim LRegionCode
' (SS,5/6/07) modified to get new region code field, GetPostalArea is used if GetRegionCode returns blank
LRegionCode = GetRegionCode(LCountry)
If LRegionCode = "" Then LRegionCode = GetPostalArea(LCountry)
' (SS,16/1/15) moved following here from below, renamed from LPostcode to LDeliveryPostcode
Dim LDeliveryPostcode
LDeliveryPostcode = Session("DeliveryPostcode")
If LDeliveryPostcode = "" Then LDeliveryPostcode = Session("Postcode")
' (SS,16/1/15) if UseDeliveryPostcodes enabled in UK then we determine region from postcode
If UseDeliveryPostcodes And IsUK(LCountry) Then
LRegionCode = GetDeliveryRegionFromPostcode(LDeliveryPostcode, "")
'Response.Write "###" & LRegionCode & "###<br>"
If LRegionCode = "" Then
FDeliveryCalculationFailure = True
SetAlertMessage "Your postcode might be wrong. Please check."
End If
End If
' (SS,13/9/07) replaced IsNull(FDeliveryType) Or FDeliveryType = "" Or FDeliveryType = DT_ORIGINAL with GetDeliveryType
If GetDeliveryType = DT_ORIGINAL Then
GetDeliveryCost = GetDeliveryCostForPostalArea(LRegionCode, ATotalPriceOfGoods)
Exit Function
End If
Dim LByPriceDeliveryCost, LByWeightDeliveryCost
LByPriceDeliveryCost = 0
LByWeightDeliveryCost = 0
' (SS,25/11/08) fixed bug by adding APriceOfNonWeightedGoods > 0 because it was adding delivery when APriceOfNonWeightedGoods was 0
If FIsDeliveryByPrice And APriceOfNonWeightedGoods > 0 Then
' (SS,22/10/08) modified to no longer loop around to calculate using increments, if price too much then shopper is alerted
Dim LTotalPrice
LTotalPrice = APriceOfNonWeightedGoods
' (SS,27/5/07) remove the VATContent if prices include VAT because table contains prices excluding VAT
If PricesEnteredIncludeVAT Then LTotalPrice = LTotalPrice - AVATContent ' use the total price without VAT
' GetDeliveryFromTable will set FDeliveryCalculationFailure to True if it fails
LByPriceDeliveryCost = GetDeliveryFromTable(LRegionCode, FIsDeliverySpecialRegionByPrice, "P", LTotalPrice)
End If
If Not FDeliveryCalculationFailure Then
If FIsDeliveryByWeight Then
' (SS,22/10/08) modified to no longer loop around to calculate using increments, if weight too much then shopper is alerted
Dim LTotalWeight
If FIsDeliveryByWeightFlagged Then
LTotalWeight = AFlaggedWeight
ElseIf FIsDeliveryByWeightNonFlagged Then
LTotalWeight = ANonFlaggedWeight
Else
LTotalWeight = AWeightOfGoods
End If
' GetDeliveryFromTable will set FDeliveryCalculationFailure to True if it fails
' (SS,25/11/08) added "If LTotalWeight > 0 Then" to only do when LTotalWeight isn't zero
If LTotalWeight > 0 Then LByWeightDeliveryCost = GetDeliveryFromTable(LRegionCode, FIsDeliverySpecialRegionByWeight, "W", LTotalWeight)
End If
End If
If Not FDeliveryCalculationFailure Then
' (SS,21/10/08) fixed bug, i.e. 2nd FIsDeliveryByPrice changed to FIsDeliveryByWeight
If FIsDeliveryByPrice And FIsDeliveryByWeight Then
If GetDeliveryType = DT_HIGHEST Then
LDeliveryCost = Iif(LByPriceDeliveryCost > LByWeightDeliveryCost, LByPriceDeliveryCost, LByWeightDeliveryCost)
ElseIf GetDeliveryType = DT_LOWEST Then
LDeliveryCost = Iif(LByPriceDeliveryCost < LByWeightDeliveryCost, LByPriceDeliveryCost, LByWeightDeliveryCost)
Else ' i.e. GetDeliveryType = DT_TOTAL (also if customer delivery is enabled i.e. DT_CUSTOM)
LDeliveryCost = LByPriceDeliveryCost + LByWeightDeliveryCost
End If
ElseIf FIsDeliveryByPrice Then
LDeliveryCost = LByPriceDeliveryCost
Else ' i.e. FIsDeliveryByWeight
LDeliveryCost = LByWeightDeliveryCost
End If
End If
' (SS,22/10/08)
If FDeliveryCalculationFailure Then
SetAlertMessage "Delivery cost could not be determined, please contact us."
End If
' (SS,27/5/07) added following custom delivery code
' (SS,21/10/08) custom delivery calculation moved here from above, to still allow normal delivery by price or delivery by weight calculation to work if enabled
' (SS,21/10/08) modified to pass on postcode and delivery cost calculated above
If Not FDeliveryCalculationFailure And CustomDeliveryEnabled Then
Dim LTotalItems
LTotalItems = GetTotalItemsInBasket
' (SS,21/10/08) added 2 more parameters, LPostcode and LDeliveryCost
' (SS,9/12/14) added LByPriceDeliveryCost, LByWeightDeliveryCost parameters to CustomGetDelivery
' (SS,16/1/15) move postcode code to above and replaced LPostcode with LDeliveryPostcode
LDeliveryCost = CustomGetDelivery(ATotalPriceOfGoods, APriceOfNonWeightedGoods, AWeightOfGoods, AVATContent, LTotalItems, LDeliveryCost, LByPriceDeliveryCost, LByWeightDeliveryCost, LDeliveryPostcode, LRegionCode, Session("DeliveryRegion"), LCountry)
' (SS,22/10/08) if CustomGetDelivery returns -1 then it means it's a delivery calculation failure, so flag it as such
' (SS,19/10/18) added Not IsLocalMode to allow such deliveries in local mode
' i.e. CIRC wanted to deliver to France using a VAT code to not pay VAT, this change allowed it,
' before it was giving a failure while processing error.
If Not IsLocalMode And LDeliveryCost = -1 Then FDeliveryCalculationFailure = True
End If
' (SS,18/10/06) changed GetVATAmount to GetVATAmountToAdd
' (SS,26/5/07) added If PricesEnteredIncludeVAT to only add VAT to delivery if PricesEnteredIncludeVAT
' (SS,20/2/12) replaced GetVATAmountToAdd with GetVATAmountToAddNC, because GetVATAmountToAdd only adds if country is VAT, here we always want to add if PricesEnteredIncludeVAT
' fixes bug which was causing delivery cost to be lower than it show be for RW orders where VAT isn't charged, here we want to add VAT to delivery because it's deducted later
If PricesEnteredIncludeVAT Then LDeliveryCost = LDeliveryCost + GetVATAmountToAddNC(LDeliveryCost)
' (SS,4/10/16) if overriden using local mode then set to overriding amount
If IsDeliveryCostOverride Then
LDeliveryCost = GetDeliveryCostOverride(LDeliveryCost) ' (SS,24/3/17) added LDeliveryCost to allow amount to be factored if applicable
End If
' (SS,21/2/12) added Round2dp to make sure it's rounded to 2 dp, had noticed some orders in clifton where it hadn't been rounded
GetDeliveryCost = Round2dp(LDeliveryCost)
' (SS,22/10/08) removed following asp error message, shopper is now alerted instead
'If FDeliveryCalculationFailure Then
' Err.Raise 8, "AppUtils", "Delivery Calculation Failure" ' raise a user-defined error
'End If
End Function
' (SS,14/9/06)
' (SS,07/6/07) to be removed, will no longer be used
Function ShowSpecialDeliveryRegion
ShowSpecialDeliveryRegion = False
Dim LHasDeliveryByPriceItems, LHasDeliveryByWeightItems
' determine whether by price or by weight items are in basket
' (SS,26/10/18) added False parameter to GetOrderItemsQuery to exclude subproducts
OpenQuery(GetOrderItemsQuery(Session.SessionID, False))
Do While Not EndOfQuery
If NZ(GetQueryValue("PostalWeight")) = 0 Then
LHasDeliveryByPriceItems = True
Else
LHasDeliveryByWeightItems = True
End If
NextQueryRecord
Loop
CloseQuery
If Not (FIsDeliverySpecialRegionByPrice Or FIsDeliverySpecialRegionByWeight) Then
Exit Function
End If
If (FIsDeliverySpecialRegionByPrice And LHasDeliveryByPriceItems) Or (FIsDeliverySpecialRegionByWeight And LHasDeliveryByWeightItems) Then
ShowSpecialDeliveryRegion = True
End If
End Function
' (SS,14/9/06)
Function GetCriteriaForDeliveryRegionCombo
If FIsDeliverySpecialRegionByPrice Then
GetCriteriaForDeliveryRegionCombo = "Type='P'"
Else
GetCriteriaForDeliveryRegionCombo = "Type='W'"
End If
End Function
' (SS,14/5/09) now calls GetDeliveryCostInfo, code from here to GetDeliveryCostInfo
Sub ShowDeliveryCostInfo()
Response.Write(GetDeliveryCostInfo)
End Sub
' (SS,4/11/04) for showing in customer info '
' (SS,24/6/05) improved by not showing duplicates if single charge, i.e. not below or above '
' (SS,6/7/05) added Iif(Application("UKText") etc to override the default country names '
' (SS,11/6/07) replaced Application variables with functions
' (SS,14/5/09) added this due to customerinfo being held in sitedetails to return as a string
' code taken from ShowDeliveryCostInfo, which now calls this
' Response.Write replaced with LResult = LResult &
' ShowDeliveryCostForArea replaced with LResult = LResult & GetDeliveryCostForArea, ShowDeliveryMatrix with GetDeliveryMatrix
Function GetDeliveryCostInfo()
Dim LResult
LResult = ""
If GetDeliveryType = DT_ORIGINAL Then
LResult = LResult & GetDeliveryCostForArea("UK", Iif(DeliveryUKText = "", "UK Mainland", DeliveryUKText))
LResult = LResult & GetDeliveryCostForArea("EU", Iif(DeliveryEUText = "", "Europe", DeliveryEUText))
LResult = LResult & GetDeliveryCostForArea("RW", Iif(DeliveryRWText = "", "Rest of the World", DeliveryRWText))
LResult = LResult & "<br>"
Else
If FIsDeliveryByPrice Then LResult = LResult & GetDeliveryMatrix("P", "", True, True)
If FIsDeliveryByWeight Then LResult = LResult & GetDeliveryMatrix("W", "", True, True)
End If
GetDeliveryCostInfo = LResult
End Function
' (SS,17/10/06)
' (SS,21/10/08) added "& NL" to end of table rows to make code more readable
' (SS,14/5/09) now calls GetDeliveryMatrix
Sub ShowDeliveryMatrix(AType, AValueHeading, AIncVat, AZeroAsFree)
Response.Write(GetDeliveryMatrix(AType, AValueHeading, AIncVat, AZeroAsFree))
End Sub
' (SS,14/5/09) added this due to customerinfo being held in sitedetails to return as a string
' code taken from ShowDeliveryMatrix, which now calls this
' Response.Write replaced with LResult = LResult &
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
Function GetDeliveryMatrix(AType, AValueHeading, AIncVat, AZeroAsFree)
' show the region headings
Dim LResult, i, LRegionCount, LValue
OpenQuery("SELECT * FROM deliveryregions WHERE Type = '" + CleanSQLStr(AType) + "' ORDER BY Type, RegionNo")
If AValueHeading = "" Then
AValueHeading = Iif(AType="P" , "Total Price", "Total Weight")
End If
' (SS,10/9/14) added following to use single class for table in Bootstrap called "table"
' also replace td in heading row with th so that Bootstrap applies the correct style
' also added right align for row, and th cell because it was working for td but not th
Dim LTableClass, LTableRowHeadingClass, LTableRowClass
If IsRWD Then
LTableClass = " class=""table table-condensed"" style=""text-align: right"""
LTableRowHeadingClass = " style=""text-align: right"""
LTableRowClass = ""
Else
LTableClass = " class=""delivery_charge"""
LTableRowHeadingClass = " class=""delivery_charge_ttl"""
LTableRowClass = " class=""delivery_charge_row"""
End If
LResult = NL & "<table" & LTableClass & "><tr" & LTableRowHeadingClass & "><th style=""text-align:right"">" & AValueHeading & "</th>"
LRegionCount = 0
Do While Not EndOfQuery
LRegionCount = LRegionCount + 1
LResult = LResult & "<th style=""text-align:right"">" & GetQueryValue("RegionName") & "</th>"
NextQueryRecord
Loop
CloseQuery
LResult = LResult & "</tr>" & NL
' show the delivery costs
OpenQuery("SELECT * FROM deliverycosts WHERE Type = '" + CleanSQLStr(AType) + "' ORDER BY Value")
Do While Not EndOfQuery
LResult = LResult & "<tr" & LTableRowClass & "><td>"
LValue = GetQueryValue("ValueText")
If NB(LValue) <> "" Then
LResult = LResult & LValue
Else
LValue = GetQueryValue("Value")
If AType = "P" Then
If AIncVAT Then
LResult = LResult & CorrectCurrencyWV(LValue)
Else
LResult = LResult & CorrectCurrency(LValue)
End If
Else
LResult = LResult & LValue
End If
End If
LResult = LResult & "</td>"
For i = 1 To LRegionCount
LResult = LResult & "<td>"
LValue = GetQueryValue("Region" & i)
If NB(LValue) = "" Then
LResult = LResult & " "
ElseIf AZeroAsFree And CDbl(LValue) = 0 Then
LResult = LResult & "Free"
Else
If AIncVAT Then
LResult = LResult & CorrectCurrencyWV(LValue)
Else
LResult = LResult & CorrectCurrency(LValue)
End If
End If
LResult = LResult & "</td>"
Next
LResult = LResult & "</tr>" & NL
NextQueryRecord
Loop
CloseQuery
LResult = LResult & "</table>" & NL
LResult = LResult & "<br>" & NL
GetDeliveryMatrix = LResult
End Function
' (SS,24/6/05) '
' (SS,11/6/07) modified to use settings from table instead of Application values from global.asa
' (SS,14/5/09) now calls GetDeliveryCostForArea
Sub ShowDeliveryCostForArea(APostalArea, APostalDesc)
Response.Write(GetDeliveryCostForArea(APostalArea, APostalDesc))
End Sub
' (SS,14/5/09) added this due to customerinfo being held in sitedetails to return as a string
' code taken from ShowDeliveryCostForArea, which now calls this
Function GetDeliveryCostForArea(APostalArea, APostalDesc)
Dim LResult, LBelowDel, LAboveDel
LResult = ""
' NB. LBelowDel & LAboveDel contain Currency Symbol and 0 is set as "Free"
LBelowDel = GetDeliveryCostForInfo(APostalArea, 0)
Dim LLimit
If APostalArea = "UK" Then
LLimit = DeliveryUKLimit
ElseIf APostalArea = "EU" Then
LLimit = DeliveryEULimit
Else
LLimit = DeliveryRWLimit
End If
LAboveDel = GetDeliveryCostForInfo(APostalArea, LLimit)
If LBelowDel = LAboveDel Then
LResult = APostalDesc & ": " & LBelowDel & "<br>"
Else
LResult = APostalDesc & " orders below the value of " & CorrectCurrency(LLimit) & ": " & LBelowDel & "<br>"
LResult = LResult & APostalDesc & " orders above the value of " & CorrectCurrency(LLimit) & ": " & LAboveDel & "<br>"
End If
GetDeliveryCostForArea = LResult
End Function
' (SS,4/11/04) for showing in customer info '
' returns a string with pound sign, or "free" '
Function GetDeliveryCostForInfo(APostalArea, APriceOfGoods)
Dim LDeliveryCost
LDeliveryCost = GetDeliveryCostForPostalArea(APostalArea, APriceOfGoods)
If LDeliveryCost = 0 Then
GetDeliveryCostForInfo = "<strong>Free</strong>"
Else
GetDeliveryCostForInfo = "<strong>" & CorrectCurrency(LDeliveryCost) & "</strong>"
End If
End Function
' (SS,4/11/04) '
' (SS,11/6/07) no longer using Application variables
Function GetDeliveryCostForPostalArea(APostalArea, APriceOfGoods)
Dim LDelivery
If APostalArea = "UK" Or APostalArea = "" Then
If APriceOfGoods >= DeliveryUKLimit Then
LDelivery = DeliveryUKAbove
Else
LDelivery = DeliveryUKBelow
End If
ElseIf APostalArea = "EU" Then
If APriceOfGoods >= DeliveryEULimit Then
LDelivery = DeliveryEUAbove
Else
LDelivery = DeliveryEUBelow
End If
Else ' i.e. RW
If APriceOfGoods >= DeliveryRWLimit Then
LDelivery = DeliveryRWAbove
Else
LDelivery = DeliveryRWBelow
End If
End If
GetDeliveryCostForPostalArea = LDelivery
End Function
' get postal area, UK, EU or RW
Function GetPostalArea(ACountry)
Dim LPostalArea
If ACountry = "" Then
LPostalArea = "UK"
Else
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
'OpenQuery("SELECT PostalArea FROM countries WHERE Country = '" + CleanSQLStr(ACountry) + "'")
'If Not EndOfQuery Then
' LPostalArea = GetFieldValue("PostalArea")
'Else
' LPostalArea = ""
'End If
'CloseQuery
' (SS,16/11/12) replaced with following to prevent clash with main query oRS used by OpenQuery, "" is return if country not found
LPostalArea = GetSQLValueAsString("SELECT PostalArea FROM countries WHERE Country = '" + CleanSQLStr(ACountry) + "'")
If LPostalArea = "" Then LPostalArea = "UK"
End If
GetPostalArea = LPostalArea
End Function
' (SS,5/6/07) returns new RegionCode field from Countries table, blank is returned if not found
Function GetRegionCode(ACountry)
Dim LRegionCode
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
GetRegionCode = NB(GetValueFromQuery("RegionCode", "SELECT RegionCode FROM countries WHERE Country = '" + CleanSQLStr(ACountry) + "'"))
End Function
' (SS,13/3/06) get flag from country table, country is blank or not found then True is returned
' (SS,27/5/07) renamed from IsVATable to IsCountryVATable, also removed ACountry parameter, added LCountry = GetInvoiceCountry
' (SS,11/8/16) delivery country now used to decide whether VAT is charged
Function IsCountryVATable
Dim LCountry, LChargeVAT
' (SS,14/10/13) added If IsSalesPostalAreaInvoice to use delivery country if e.g. delivery to UK and invoice is RW
' most of the time invoice country is used, but on rare occasions e.g. billing address is RW and delivery is UK then we need to charge VAT
'
'If IsSalesPostalAreaInvoice Then
' LCountry = GetInvoiceCountry
'Else
' LCountry = GetDeliveryCountry
'End If
' (SS,11/8/16) replaced above with following, i.e. delivery country is used to decide whether VAT is charged, IsSalesPostalAreaInvoice no longer called
LCountry = GetDeliveryCountry
' if UseVATFlagFromCountries is true then use VAT flag from country else use the old from postal area method
If UseVATFlagFromCountries Then ' (SS,27/5/07) UseVATFlagFromCountries replaces LCase(GetAppSetting("Application", "UseVATFlagFromCountries") = "true")
'If LCountry = "" Then
' LChargeVAT = True
'Else
'OpenQuery("SELECT ChargeVAT FROM Countries WHERE Country = '" + LCountry + "'")
'If Not EndOfQuery Then
' LChargeVAT = GetFieldValue("ChargeVAT") = "Yes"
' (SS,11/5/09) replaced above with following, ChargeVAT field is now TINYINT(1) instead of CHAR(1)
' LChargeVAT = IntToBool(GetFieldValue("ChargeVAT"))
'Else
' LChargeVAT = True
' End If
' CloseQuery
'End If
' (SS,20/12/12) replaced above with new function GetCountryChargeVATFlag
LChargeVAT = GetCountryChargeVATFlag(LCountry)
Else
Dim LPostalArea
If LCountry = "" Then
LPostalArea = "UK"
Else
LPostalArea = GetPostalArea(LCountry)
End If
If Instr("," + VATABLE_REGIONCODES + ",", "," + LPostalArea + ",") = 0 Then
LChargeVAT = False
Else
LChargeVAT = True
End If
End If
IsCountryVATable = LChargeVAT
End Function
' (SS,30/11/07)
Function IsAllowedCountry(ACountry)
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQuery("SELECT Country FROM Countries WHERE Country = '" + CleanSQLStr(ACountry) + "' AND Enabled='Yes'")
If Not EndOfQuery Then
IsAllowedCountry = True
Else
IsAllowedCountry = False
End If
CloseQuery
End Function
' (SS,14/10/13) uses invoice and delivery country to lookup using the new sales_postal_area_lookup table
' decided in the end not to include the actual table because the logic is quite simple, and there's always an overhead when adding a new table
' INSERT INTO `sales_postal_area_lookup` (`InvoicePostalArea`, `DeliveryPostalArea`, `SalesPostalAreaIsInvoice`) VALUES
' ('UK', 'UK', 1),
' ('EU', 'EU', 1),
' ('RW', 'RW', 1),
' ('UK', 'EU', 1),
' ('EU', 'UK', 1),
' ('RW', 'UK', 0),
' ('RW', 'EU', 0),
' ('UK', 'RW', 1),
' ('EU', 'RW', 1);
' LResult = GetSQLValue("SELECT SalesPostalAreaIsInvoice FROM sales_postal_area_lookup WHERE InvoicePostalArea = '" + CleanSQLStr(LInvoicePostalArea) + "' AND DeliveryPostalArea = '" + CleanSQLStr(LDeliveryPostalArea) + "'")
' If LResult = Null Then
' LResult = 1
' End If
' IsSalesPostalAreaInvoice = IntToBool(LResult)
' (SS,11/8/16) modified to use new method where the delivery country decides whether vatable, see tables below from email of 5/8/16
'Invoice Country Delivery Country Charge VAT Current Charge VAT New
'UK UK Yes Y
'UK EU Yes Y
'UK RW Yes N
'EU EU Yes Y
'EU UK Yes Y
'EU RW Yes N
'RW RW No N
'RW UK Yes Y
'RW EU Yes Y
'When invoicing to EU and VAT No entered:
'Invoice Country Delivery Country VAT No Entered Charge VAT Current Charge VAT New
'EU EU No Yes Y
'EU UK No Yes Y
'EU RW No Yes N
'EU EU Yes No N
'EU UK Yes Yes Y
'EU RW Yes Yes N
' (SS,11/8/16) routine no longer used, VAT is decided by delivery country, routine now redundant, not called
Function IsSalesPostalAreaInvoice
Dim LInvoicePostalArea, LDeliveryPostalArea, LResult
LInvoicePostalArea = GetPostalArea(GetInvoiceCountry)
LDeliveryPostalArea = GetPostalArea(GetDeliveryCountry)
If LInvoicePostalArea = "RW" And (LDeliveryPostalArea = "UK" Or LDeliveryPostalArea = "EU") Then
IsSalesPostalAreaInvoice = False
Else
IsSalesPostalAreaInvoice = True
End If
End Function
' (SS,28/5/07)
Function IsVATRegistered
IsVATRegistered = FVATRegistered
End Function
' (SS,16/11/07)
Function IsVATZeroForIntraCommunity
IsVATZeroForIntraCommunity = FVATZeroForIntraCommunity
End Function
' (SS,17/11/07)
Function GetVATIntraCommunityCountries
GetVATIntraCommunityCountries = FVATIntraCommunityCountries
End Function
' (SS,11/11/10)
Function ValidateVATNumber
ValidateVATNumber = FValidateVATNumber
End Function
' (SS,12/12/12)
Function VATIntraCommunityEmailAlertEnabled
VATIntraCommunityEmailAlertEnabled = FVATIntraCommunityEmailAlertEnabled
End Function
' this VAT rate figure is used when VATBands aren't enabled, or it's used for Delivery Cost if VAT bands are enabled
Function GetVATRate
If FVATRegistered Then
GetVATRate = FVATRate ' (SS,26/5/07) was GetAppSetting("Application", "VATRate")
Else
GetVATRate = 0
End If
End Function
' uses VATRate in settings table to calculate the VAT '
' (SS,16/9/04) discovered error in calculation '
' Price of Goods contains VAT already '
' This requires a different calculation '
Function GetVATAmount(APrice)
' GetVATAmount = CDbl(FormatNumber(APriceOfGoods * GetVATRate, 2)) '
' (SS,16/9/04) above replaced with following '
' (SS,28/5/07) replaced CDbl(FormatNumber..., 2) with Round2dp
GetVATAmount = Round2dp(APrice * (1 - (1 / (1 + GetVATRate))))
End Function
' (SS,17/10/06) similar to GetVATAmount above but adds VAT onto price that
' does not have VAT, (GetVATAmount returns the amount of VAT where given price already includes VAT)
Function GetVATAmountToAdd(APrice)
' (SS,28/5/07) replaced CDbl(FormatNumber..., 2) with Round2dp
GetVATAmountToAdd = Round2dp(GetVATAmountToAddNR(APrice))
End Function
' (SS,22/6/10) added this to do the same as GetVATAmountToAdd with no country check
Function GetVATAmountToAddNC(APrice)
GetVATAmountToAddNC = Round2dp(APrice * GetVATRate)
End Function
' (SS,28/5/07) non rounding version of GetVATAmountToAdd also called from GetVATAmountToAdd
' (SS,22/6/10) now takes IsCountryVATable and IsIntraCommunitySupply into account (mainly to show correct total values on showbasket page when country is not VATable)
Function GetVATAmountToAddNR(APrice)
' GetVATAmountToAddNR = APrice * GetVATRate
' (SS,22/6/10) replaced above with following
If IsCountryVATable And Not IsIntraCommunitySupply Then
GetVATAmountToAddNR = APrice * GetVATRate
Else
GetVATAmountToAddNR = 0
End If
End Function
' (SS,27/5/07) removed ACountry parameter, calls GetInvoiceCountry to get country
' (SS,28/5/07) replaced APriceOfGoods with AVATAmount
' (SS,16/11/07) added Intra Community not VAT feature
Function GetVATIncludedAmount(AVATAmount)
Dim LVATIncludedAmount
If IsCountryVATable Then
LVATIncludedAmount = AVATAmount ' (SS,28/5/07) was GetVATAmount(APriceOfGoods)
' (SS,16/11/07) no VAT if IsVATZeroForIntraCommunity enabled, country is in VATIntraCommunityCountries list and VATNumber has been provided
If IsIntraCommunitySupply Then
LVATIncludedAmount = 0
End If
Else
LVATIncludedAmount = 0
End If
GetVATIncludedAmount = LVATIncludedAmount
End Function
' (SS,16/11/07) returns true if IsVATZeroForIntraCommunity enabled, country is in VATIntraCommunityCountries list and VATNumber has been provided
Function IsIntraCommunitySupply
IsIntraCommunitySupply = False
If IsVATZeroForIntraCommunity And GetVATNumberForIntraCommunity <> "" Then
' (SS,30/10/08) modified to use countries table if GetVATIntraCommunityCountries is "USE TABLE"
If GetVATIntraCommunityCountries = "USE TABLE" Then
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
If IntToBool(GetValueFromQuery("IntraCommunitySupply", "SELECT IntraCommunitySupply FROM countries WHERE Country = '" & CleanSQLStr(GetDeliveryCountry) & "'")) Then
IsIntraCommunitySupply = True
End If
ElseIf InStr("," + GetVATIntraCommunityCountries + ",", "," + GetDeliveryCountry + ",") > 0 Then
IsIntraCommunitySupply = True
End If
End If
End Function
' (SS,27/5/07) removed ACountry parameter, calls GetInvoiceCountry to get country
' (SS,28/5/07) replaced APriceOfGoods with AVATAmount
' (SS,12/12/12) correction to deduct VAT for intracommunity supply for sites where prices include VAT
Function GetVATDeductedAmount(AVATAmount)
Dim LVATDeductedAmount
' (SS,12/12/12) added "And Not IsIntraCommunitySupply" so that VAT is deducted for EU sales where VAT Number is entered for sites that have Prices Entered Inc VAT e.g. HyperFlight
If IsCountryVATable And Not IsIntraCommunitySupply Then
LVATDeductedAmount = 0
Else
LVATDeductedAmount = AVATAmount ' (SS,28/5/07) was GetVATAmount(APriceOfGoods)
End If
GetVATDeductedAmount = LVATDeductedAmount
End Function
' (SS,26/5/07)
Sub GetVATBands
If VATBandsEnabled Then
OpenQuery("SELECT * FROM vatbands ORDER BY SortOrder, VATBand")
FVATBandCount = 0
Do While Not EndOfQuery
FVATBandCount = FVATBandCount + 1
FVATBands(FVATBandCount, 0) = GetQueryValue("VATBand")
FVATBands(FVATBandCount, 1) = NZ(GetQueryValue("VATRate"))
NextQueryRecord
Loop
CloseQuery
End If
End Sub
' (SS,26/5/07) returns the VAT rate for product, takes VAT Bands into account if enabled
' assumes VATBand is in FVATBand
Function GetVATRateForProduct
Dim i, LVATRate
LVATRate = 0
If FVATRegistered Then
If VATBandsEnabled Then
Dim LFound
LFound = False
For i = 1 To FVATBandCount
If FVATBands(i, 0) = FVATBand Then
LVATRate = FVATBands(i, 1)
LFound = True
Exit For
End If
Next
If Not LFound Then SetAlertMessage("VAT Band " + FVATBand + " not found") ' if not found alert user with message
Else
LVATRate = GetVATRate
End If
End If
GetVATRateForProduct = LVATRate
End Function
' (SS,27/5/07) calls GetVATRateForProduct to take VAT Band into account
Function GetVATForProduct(AValue)
Dim LVATRate
LVATRate = GetVATRateForProduct
If PricesEnteredIncludeVAT Then
GetVATForProduct = AValue * (1 - (1 / (1 + LVATRate)))
Else
GetVATForProduct = LVATRate * AValue
End If
End Function
' (SS,23/6/14) adds VAT to given price that excludes VAT
' Used by Galleon, 4 price bands to show inc VAT prices because Google Shopping requires it
Function GetPriceIncVAT(AValue)
GetPriceIncVAT = AValue + GetVATForProduct(AValue)
End Function
' (SS,28/5/07)
Function GetVATLabel
GetVATLabel = FVATLabel
End Function
' (SS,28/5/07)
Sub SetVATLabel(AIsVATDeducted)
If AIsVATDeducted Then
FVATLabel = "VAT Deducted"
ElseIf PricesEnteredIncludeVAT Then
FVATLabel = "VAT Included"
Else
FVATLabel = "VAT"
End If
FVATLabel = tr(FVATLabel) ' (SS,19/3/10) for translation
End Sub
' (SS,19/2/04)
Sub EmptyBasket(ASessionID)
' empty the basket for the session
' (SS,28/12/14) modified to now also delete options missed from before (noticed shopping_basket_options records weren't being deleted, should have been added in 6/7/11)
If ProductOptionsEnabled Then
ExecuteQuery("DELETE t1, t2 FROM shoppingbaskets t1, shopping_basket_options t2 WHERE t1.ItemID = t2.ItemID AND t1.SessionID = '" & CleanSQLStr(ASessionID) & "'")
End If
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
ExecuteQuery("DELETE FROM shoppingbaskets WHERE SessionID = '" & CleanSQLStr(ASessionID) & "'")
End Sub
' (SS,15/6/04) returns true if item is in stock '
' depends on ANumInStock value and AllowProductOrderIfNoStock '
' if AllowProductOrderIfNoStock is True then this function '
' always returns true '
' (SS,24/2/05) if AllowProductOrderIfNoStock is false
' the ANumInStock value when blank is treated is if AllowProductOrderIfNoStock
' is false, i.e. there is always stock.
' (SS,24/3/20) added call to OrderingSuspended
Function InStock(ANumInStock)
If OrderingSuspended Then ' (SS,24/3/20)
InStock = False
Else
' (SS,19/7/10) replaced AllowProductOrderIfNoStock with GetAllowOutOfStockOrdering
If GetAllowOutOfStockOrdering Then
InStock = True
Else
If IsNull(ANumInStock) Then
InStock = True ' (SS,24/2/05) changed to True from False, if blank then instock is assumed '
Else
InStock = (CLng(ANumInStock) > 0)
End If
End If
End If
End Function
' (SS,20/4/10) similar to InStock but doesn't return True if item out of stock and can be ordered
Function ReallyInStock(ANumInStock)
If IsNull(ANumInStock) Then
ReallyInStock = True
Else
ReallyInStock = (CLng(ANumInStock) > 0)
End If
End Function
' (SS,19/7/10)
Function SetAllowOutOfStockOrdering(AAllowOutOfStockOrdering)
FAllowOutOfStockOrdering = AAllowOutOfStockOrdering
End Function
' (SS,19/7/10)
Function GetAllowOutOfStockOrdering
GetAllowOutOfStockOrdering = FAllowOutOfStockOrdering
End Function
' (SS,16/5/11) called from SetStockInfo, saves NumInStock into global for use by template routines to which NumInStock parameter isn't passed
Sub SetNumInStock(ANumInStock)
FNumInStock = ANumInStock
End Sub
' (SS,16/5/11) gets NumInStock from new global for use by template routines to which NumInStock parameter isn't passed
Function GetNumInStock
GetNumInStock = FNumInStock
End Function
' (SS,16/11/07) used to pass stock info to inc-template-product-detail.asp (rather than adding new parameter)
' (SS,24/3/20) added called to OrderingSuspended and setting appropriate message
Sub SetStockInfo(ANumInStock, ADateStockDue)
SetNumInStock ANumInStock ' (SS,16/5/11) sets NumInStock to be used by routines to which the NumInStock parameter isn't passed
If OrderingSuspended Then
' (SS,24/3/20)
FStockInfo = tr("Ordering temporarily suspended")
ElseIf IsNull(ANumInStock) Then
' always in stock so just say in stock
FStockInfo = tr("In stock")
Else
If Not ShowStockInfo Then
If ANumInStock = 0 Then ' (SS,21/1/09) corrected was LNumInStock
FStockInfo = tr(GetOutOfStockMessage) ' the original out of stock message
Else
FStockInfo = ""
End If
Else
Dim LNumInStock
LNumInStock = CLng(ANumInStock)
If LNumInStock > 0 Then
' (SS,25/6/10) added If ShowStockAmount and Else FStockInfo = tr("In stock")
If ShowStockAmount Then
' (SS,5/12/11) added StockAmountInfoSpec
If StockAmountInfoSpec <> "" Then
FStockInfo = GetStockAmountInfo(LNumInStock, StockAmountInfoSpec)
If FStockInfo = "" Then
FStockInfo = tr("In stock")
Else
FStockInfo = FStockInfo + " " + tr("in stock")
End If
Else
FStockInfo = Iif(LNumInStock > 9, "10+", LNumInStock) & " " & tr("in stock")
End If
Else
FStockInfo = tr("In stock")
End If
Else ' i.e. 0 zero stock
' (SS,23/6/10) set stock availability, i.e. Available in 2 to 3 days,
Dim LStockAvailabilityInfo
LStockAvailabilityInfo = GetStockAvailability
' (SS,20/7/10) if custom stock info enabled then call the custom routine
If CustomStockInfoEnabled Then
FStockInfo = CustomGetStockInfo(ADateStockDue, GetAllowOutOfStockOrdering, LStockAvailabilityInfo)
Else
' (SS,10/5/12) added following for Under Offer attribute added to covdem
If ProductAttributeExists(GetProductIDForProductCode(GetProductCode), "Under Offer") Then
FStockInfo = "Currently under offer"
Else
' (SS,26/7/11) shortened "Currently out of stock" to "Out of stock" due to limited space in related product section
FStockInfo = tr("Out of stock") + ", "
' if date is null or before today then show "soon", else show the date
' (SS,26/5/10) added tr and slight changes to FStockInfo to help with translation
If IsNull(ADateStockDue) Then
' FStockInfo = FStockInfo + tr("more due soon")
' (SS,1/2/16) replaced above "more due soon" with following as requested by Express Music
FStockInfo = FStockInfo + GetPleaseContactForAvailabilityInfo
If LStockAvailabilityInfo <> "" Then FStockInfo = LStockAvailabilityInfo ' (SS,24/6/10) override with stock availability info
Else
If ADateStockDue < Date Then
FStockInfo = FStockInfo + GetPleaseContactForAvailabilityInfo ' (SS,2/1/16) replaced [tr("more due soon")] with GetPleaseContactForAvailabilityInfo
If LStockAvailabilityInfo <> "" Then FStockInfo = LStockAvailabilityInfo ' (SS,24/6/10) override with stock availability info
ElseIf ADateStockDue = Date Then
FStockInfo = FStockInfo + tr("more due today")
Else
' (SS,26/7/11) shortened "more due on" to "more due" due to limited space in related product section
FStockInfo = FStockInfo + tr("more due") & " " & ADateStockDue
End If
End If
' (SS,12/1/11) if Preorder then replaced "Out of stock, more " with "Preorder, ", a bit messy and not multi-language friendly, but quickest and easiest way
If LStockAvailabilityInfo = "Preorder" then FStockInfo = ReplaceStr(FStockInfo, "Out of stock, more ", "Preorder, ")
End If
End If
' (SS,10/6/09) added a red font and bold to make it stand out
' (SS,26/5/10) removed the font stuff
' FStockInfo = "<font color=red><b>" & FStockInfo & "</b></font>"
End If
End If
End If
End Sub
' (SS,2/1/16) added as separate routine because it may also be called from CustomUtils (i.e. CustomGetStockInfo)
Function GetPleaseContactForAvailabilityInfo
GetPleaseContactForAvailabilityInfo = "<a href=""products.asp?page=contact&enquiry=" & URLEncode("Availability of " & GetProductNameNoHTMLEncode(GetProductCode) & " (" & GetProductCode & ")") & """ rel=""nofollow"">contact us</a> for availability"
End Function
' (SS,5/12/11) returns customisable stock range, blank string is returned if stock is 0 or less
' ANoInStock is the amount in stock
' AStockAmountInfoSpec is the specification in format x-y:sss, x-y:sss: x+:sss etc.
' e.g. 1-3:1-3, 3-9:3-9, 10+:10+
' this can be written: 1-3, 3-9, 10+
' GetStockAmountInfo(3, "1-3, 3-9, 10+") will return "3-9"
Function GetStockAmountInfo(ANoInStock, AStockAmountInfoSpec)
GetStockAmountInfo = ""
If AStockAmountInfoSpec = "" Or IsNull(ANoInStock) Or ANoInStock = "" Then Exit Function
If NZ(ANoInStock) <= 0 Then Exit Function ' would fail if ANoInStock was "" and added as Or to above
Dim LPairs, i, LSections, LLeft, LRight, LRange, LStart, LEnd
' separate into pairs using comma
LPairs = Split(AStockAmountInfoSpec, ",")
For i = LBound(LPairs) To UBound(LPairs)
' separate each pair from colon into two sections, hold start-end on the left, and the actual string to show on right
LSections = Split(LPairs(i), ":")
LLeft = Trim(LSections(0))
If UBound(LSections) > 0 Then
LRight = Trim(LSections(1))
Else
LRight = LLeft
End If
' split left to get start and end separated by dash
LRange = Split(LLeft, "-")
LStart = ParseInt(Trim(LRange(0))) ' also removes the + if it was there
If UBound(LRange) > 0 Then
LEnd = ParseInt(Trim(LRange(1)))
Else
LEnd = 0
End If
' if in range, or end is 0 then match found
If LEnd = 0 Or (ANoInStock >= LStart And ANoInStock <= LEnd) Then
GetStockAmountInfo = CStr(LRight)
Exit For
End If
Next
End Function
' (SS,16/11/07) used to pass stock info to inc-template-product-detail.asp (rather than adding new parameter)
Function GetStockInfo
GetStockInfo = FStockInfo
End Function
' (SS,23/6/10) returns true if StockAvailability feature enabled and StockAvailability is not blank, i.e. stock is available to order even if not in stock
' (SS,19/7/10) added GetAllowOutOfStockOrdering which is set per product
Function IsStockAvailable
' (SS,3/8/10) fixed bug which wasn't allowing ordering because they don't have stock availability feature enabled (replaced And with Or)
IsStockAvailable = GetAllowOutOfStockOrdering Or (StockAvailabilityEnabled And GetStockAvailability <> "")
End Function
' (SS,23/6/10)
Function StockAvailabilityEnabled
StockAvailabilityEnabled = FStockAvailabilityEnabled
End Function
' (SS,24/3/20) same as ProductOrderingSuspended but without AProductID, i.e. calls ProductOrderingSuspended with GetProductID
Function OrderingSuspended
OrderingSuspended = ProductOrderingSuspended(GetProductID)
End Function
' (SS,6/4/20) new version of OrderingSuspended with AProductID specified, OrderingSuspended above now calls this
Function ProductOrderingSuspended(AProductID)
Dim LResult
If FOrderingSuspended Then
' if flag indicates that ordering is suspended then also do an extra check which allows ordering of particular products to be suspended
If FunctionExists("CustomOrderingSuspended") Then
LResult = CustomOrderingSuspended(AProductID) ' (SS,6/4/20) added AProductID parameter
Else
LResult = True
End If
Else
LResult = False
End If
ProductOrderingSuspended = LResult
End Function
' (SS,25/6/10) returns list of stock availability fields to use from products and supplier tables
' called from several places, minimises repetitive code
' (SS,20/7/10) added products.AllowOutOfStockOrdering
Function GetStockAvailabilityFieldList
GetStockAvailabilityFieldList = "products.AvailabilityDaysFrom, products.AvailabilityDaysTo, suppliers.AvailabilityDaysFrom AS SupplierAvailabilityDaysFrom, suppliers.AvailabilityDaysTo AS SupplierAvailabilityDaysTo, products.AllowOutOfStockOrdering"
End Function
' (SS,25/6/10) calls SetStockAvailability using the 4 availability fields, minimises repetitive code
Sub SetStockAvailabilityFromFields
SetAllowOutOfStockOrdering IntToBool(GetFieldValue("AllowOutOfStockOrdering")) ' (SS,20/7/10)
SetStockAvailability GetFieldValue("AvailabilityDaysFrom"), GetFieldValue("AvailabilityDaysTo"), GetFieldValue("SupplierAvailabilityDaysFrom"), GetFieldValue("SupplierAvailabilityDaysTo")
End Sub
' (SS,23/6/10) if StockAvailability featured enabled then this sets the availability string
' Product takes priority over supplier over global
Sub SetStockAvailability(AProductAvailabilityDaysFrom, AProductAvailabilityDaysTo, ASupplierAvailabilityDaysFrom, ASupplierAvailabilityDaysTo)
FStockAvailability = "" ' i.e. not available
If Not StockAvailabilityEnabled Then Exit Sub
Dim LDaysFrom, LDaysTo
' get from global
LDaysFrom = FStockAvailableDaysFrom
LDaysTo = FStockAvailableDaysTo
' get from supplier if defined
If NB(ASupplierAvailabilityDaysFrom) <> "" Then
LDaysFrom = CStr(ASupplierAvailabilityDaysFrom)
If NB(ASupplierAvailabilityDaysTo) = "" Then
LDaysTo = ""
Else
LDaysTo = CStr(ASupplierAvailabilityDaysTo)
End If
End If
' get from product if defined
If NB(AProductAvailabilityDaysFrom) <> "" Then
LDaysFrom = CStr(AProductAvailabilityDaysFrom)
If NB(AProductAvailabilityDaysTo) = "" Then
LDaysTo = ""
Else
LDaysTo = CStr(AProductAvailabilityDaysTo)
End If
End If
If LDaysFrom <> "" And LDaysFrom <> "0" Then
' (SS,28/6/10) replaced tr("Available in") with tr("Delivery") as requested by clifton
FStockAvailability = tr("Delivery") & " " & LDaysFrom
If LDaysTo <> "" Then
FStockAvailability = FStockAvailability & " " & tr("to") & " " & LDaysTo
End If
If CInt(LDaysFrom) = 1 And LDaysTo = "" Then
FStockAvailability = FStockAvailability & " " & tr("day")
ElseIf CInt(LDaysFrom) <= -1 Then ' (SS,12/1/11) added preorder if days from is negative
FStockAvailability = "Preorder"
Else
FStockAvailability = FStockAvailability & " " & tr("days")
End If
End If
' DebugLog "###StockAvailability###: From: " & LDaysFrom & " to: " & LDaysTo & " str: " & FStockAvailability
End Sub
' (SS,23/6/10)
Function GetStockAvailability
GetStockAvailability = FStockAvailability
End Function
' (SS,17/2/16)
Function GetStockAvailabilityMicrodata(AProductCode, AProductName, ANumInStock)
' (SS,26/9/17) moved code from here to GetStockAvailabilityForMicrodata below, result LResult with call to new GetProductAvailabilityForMicrodata
GetStockAvailabilityMicrodata = "<link itemprop=""availability"" href=""http://schema.org/" + GetProductAvailabilityForMicrodata(AProductCode, AProductName, ANumInStock) + """ />"
End Function
' (SS,26/9/17) similar to GetStockAvailabilityMicrodata but returns just the value, GetStockAvailabilityMicrodata now calls this to get the value
Function GetProductAvailabilityForMicrodata(AProductCode, AProductName, ANumInStock)
Dim LResult
If ProductDiscontinued(AProductCode, AProductName) Then
LResult = "Discontinued"
Else
If ReallyInStock(ANumInStock) Then
LResult = "InStock"
Else
If IsStockAvailable Then
LResult = "PreOrder"
Else
LResult = "OutOfStock"
End If
End If
End If
GetProductAvailabilityForMicrodata = LResult
End Function
' (SS,15/11/07) returns true if product is discontinued, i.e. if ProductName contains '*discontinued*'
' buy button, out of stock and other stock won't be shown
' ProductCode also passed here for future use, in case another method is used to detect discontinued product
' (SS,16/11/07) added DISCONTINUED_TEXT2 to also do the same for *special order*
Function ProductDiscontinued(AProductCode, AProductName)
ProductDiscontinued = InStr(1, AProductName, DISCONTINUED_TEXT, 1) > 0 Or InStr(1, AProductName, DISCONTINUED_TEXT2, 1) > 0' text compare
End Function
' (SS,18/4/12) stored in session because disabled checked doesn't return a value
Function SetCollectionOnlyOrder(AValue)
Session("CollectionOnlyOrder") = AValue
End Function
' (SS,18/4/12) Session("CollectionOnlyOrder") will be "" when this session variable hasn't been assigned that's why we check for True
Function CollectionOnlyOrder
CollectionOnlyOrder = (Session("CollectionOnlyOrder") = True)
End Function
' this routine gets called by worldpay-callback.asp which is normally requested '
' by worldpay callback when payment has been made or cancelled '
' (SS,16/7/04) added setting of PaymentMethod and PaymentReference fields in Orders table
' (SS,12/8/08) replaced all Request.Form with Request, allowing the query string to be pasted into URL to manually perform a failed callback
' (SS,20/9/12) change to prevent payments when test callback received while in live mode
' (SS,3/3/14) modified to call ProcessOrderThirdPartyPayment
Sub ProcessWorldPayCallback
Dim LSQL, LOrderNo, LSessionID, LStatus, LName, LEmail, LDesc
Dim LtransID, LfuturePayID, LtransStatus, LtransTime, LauthAmount, LauthCurrency
Dim LauthAmountString, LrawAuthMessage, LrawAuthCode, LcallbackPW, LcardType, LcountryMatch, LAVS
' get values from query forms etc (passed on values) '
LOrderNo = CleanSQLStr(CleanRequest("M_OrderNo"))
LSessionID = CleanSQLStr(CleanRequest("M_SessionID"))
LName = CleanSQLStr(CleanRequest("M_Name"))
LEmail = CleanSQLStr(CleanRequest("M_Email"))
LDesc = CleanSQLStr(CleanRequest("M_Desc"))
If LOrderNo = "" Or LSessionID = "" Then
Response.Write("<br><br>Both Order No and Session ID must be specified.<br>")
Exit Sub
End If
' worldpay values '
LtransID = CleanSQLStr(CleanRequest("transID"))
LfuturePayID = CleanSQLStr(CleanRequest("futurePayID"))
LtransStatus = CleanSQLStr(CleanRequest("transStatus"))
LtransTime = CleanSQLStr(CleanRequest("transTime"))
LauthAmount = CleanSQLStr(CleanRequest("authAmount"))
LauthCurrency = CleanSQLStr(CleanRequest("authCurrency"))
LauthAmountString = CleanSQLStr(CleanRequest("authAmountString"))
LrawAuthMessage = CleanSQLStr(CleanRequest("rawAuthMessage"))
LrawAuthCode = CleanSQLStr(CleanRequest("rawAuthCode"))
LcallbackPW = CleanSQLStr(CleanRequest("callbackPW"))
LcardType = CleanSQLStr(CleanRequest("cardType"))
LcountryMatch = CleanSQLStr(CleanRequest("countryMatch"))
LAVS = CleanSQLStr(CleanRequest("AVS"))
' insert the record '
LSQL = "INSERT INTO worldpaycallbacks SET OrderNo=" & LOrderNo &_
", SessionID='" & LSessionID & "'" &_
", Name='" & LName & "'" &_
", Email='" & LEmail & "'" &_
", Description='" & LDesc & "'" &_
", transID='" & LtransID & "'" &_
", futurePayID='" & LfuturePayID & "'" &_
", transStatus='" & LtransStatus & "'" &_
", transTime='" & LtransTime & "'" &_
", authAmount='" & LauthAmount & "'" &_
", authCurrency='" & LauthCurrency & "'" &_
", authAmountString='" & LauthAmountString & "'" &_
", rawAuthMessage='" & LrawAuthMessage & "'" &_
", rawAuthCode='" & LrawAuthCode & "'" &_
", callbackPW='" & LcallbackPW & "'" &_
", cardType='" & LcardType & "'" &_
", countryMatch='" & LcountryMatch & "'" &_
", AVS='" & LAVS & "'"
ExecuteQuery(LSQL)
' if paid then mark the order as paid, if cancelled then mark as cancel '
Dim LPaymentMethod, LPaymentReference
LPaymentMethod = ""
LPaymentReference = ""
If LtransStatus = "Y" Then
' (SS,20/9/12) if in live mode then don't allow test transactions, i.e. only allow rawAuthMessage of cardbe.msg.authorised or paypal.msg.authorised
' this is to prevent potential hacking of test mode in order to receive goods, because Galleon aren't checking payment receipts from WorldPay
Dim LTestTransactionInLiveMode
If GetWorldPayTestMode = "0" Then
If LCase(Trim(LrawAuthMessage)) = "cardbe.msg.authorised" Or LCase(Trim(LrawAuthMessage)) = "paypal.msg.authorised" Then
LTestTransactionInLiveMode = False
Else
LTestTransactionInLiveMode = True
End If
Else ' i.e. site is in worldpay test mode, so treat as normal, send email and mark as paid
LTestTransactionInLiveMode = False
End If
' (SS,20/9/12) added "if" so that payment is ignored if test payment made in live mode
If LTestTransactionInLiveMode Then
LStatus = ""
Else
LStatus = PAYMENT_RECEIVED_STATUS
LPaymentMethod = "WORLDPAY"
LPaymentReference = CleanSQLStr(LtransID)
End If
ElseIf LtransStatus = "C" Then
LStatus = ORDER_CANCELLED_STATUS
Else
LStatus = ""
End If
' set the status and date on the order record (only if it's ORDER PLACED or cancelled or awaiting payment, this prevents a second callback from overwriting the same record, in case user goes back and makes another payment)
If LStatus <> "" Then ' (SS,3/3/14) removed [And LOrderNo <> ""] not necessary
'UpdateOrderStatusForOrderPlaced LOrderNo, LStatus, LPaymentMethod, LPaymentReference
' (SS,3/3/14) replacing above with following (to make consistent with other payment methods, aftering noticing that CustomerID was being set for WorldPay payments in Clifton
' (SS,21/1/15) added "" parameter for PaymentInfo
' (SS,25/6/18) added new PaymentProviderFee parameter i.e. 0 to the end
' (SS,26/6/18) added two more parameters, PaymentAmountReceived and PaymentProviderSurcharge. PaymentAmountReceived is also sent a value, i.e. ParseFloat(LAmount). The other two are 0.
' (SS,26/6/18) also added PaymentReceivedCurrencyCode, set this to LauthCurrency
' (SS,28/6/18) corrected LAmount to LauthAmount
ProcessOrderThirdPartyPayment True, LOrderNo, LSessionID, ptWorldPay, LPaymentReference, LStatus, "", ParseFloat(LauthAmount), LauthCurrency, 0, 0
End If
' email the order if paid '
' (SS,21/9/12) added LStatus <> ""
' (SS,3/3/14) following 3 lines replaced by ProcessOrderThirdPartyPayment above
'If LStatus <> "" And LtransStatus = "Y" Then
' EmailOrder(LOrderNo)
'End If
' empty the basket for the session '
' (SS,3/3/14) following replaced by ProcessOrderThirdPartyPayment above
' EmptyBasket(LSessionID)
End Sub
' this routine gets called by paypal-callback.asp which is normally call '
' by PayPal callback when payment has been made '
' (SS,20/1/15) slight improvement to mark payment as awaiting if status is pending, previously it only did this for echecks
Sub ProcessPayPalCallback
Dim LSQL, LOrderNo, LSessionID, Lbusiness, Lfirst_name, Llast_name
Dim Lpayment_date, Lpayment_gross, Lpayment_type, Lpayment_status, Lpayment_fee, Litem_name
Dim Litem_number, Ltest_ipn, Lquantity, Lmc_currency, Lmc_gross, Lmc_fee, Lshipping
Dim Ltax, Lpayer_status, Lpayer_email, Lpayer_id, Lreceiver_id, Lreceiver_email
Dim Ltxn_type, Ltxn_id, Lverify_sign, Lcharset, Lnotify_version
' get values from query forms etc (passed on values) '
LOrderNo = CleanSQLStr(CleanRequestForm("invoice"))
LSessionID = CleanSQLStr(CleanRequestForm("custom"))
If LOrderNo = "" Or LSessionID = "" Then
Response.Write("<br><br>Both Order No and Session ID must be specified.<br>")
Exit Sub
End If
' PayPal values '
Lbusiness = CleanSQLStr(CleanRequestForm("business"))
Lfirst_name = CleanSQLStr(CleanRequestForm("first_name"))
Llast_name = CleanSQLStr(CleanRequestForm("last_name"))
Lpayment_date = CleanSQLStr(CleanRequestForm("payment_date"))
Lpayment_gross = CleanSQLStr(CleanRequestForm("payment_gross"))
Lpayment_type = CleanSQLStr(CleanRequestForm("payment_type"))
Lpayment_status = CleanSQLStr(CleanRequestForm("payment_status"))
Lpayment_fee = CleanSQLStr(CleanRequestForm("payment_fee"))
Litem_name = CleanSQLStr(CleanRequestForm("item_name"))
Litem_number = CleanSQLStr(CleanRequestForm("item_number"))
Ltest_ipn = CleanSQLStr(CleanRequestForm("test_ipn"))
Lquantity = CleanSQLStr(CleanRequestForm("quantity"))
Lmc_currency = CleanSQLStr(CleanRequestForm("mc_currency"))
Lmc_gross = CleanSQLStr(CleanRequestForm("mc_gross"))
Lmc_fee = CleanSQLStr(CleanRequestForm("mc_fee"))
Lshipping = CleanSQLStr(CleanRequestForm("shipping"))
Ltax = CleanSQLStr(CleanRequestForm("tax"))
Lpayer_status = CleanSQLStr(CleanRequestForm("payer_status"))
Lpayer_email = CleanSQLStr(CleanRequestForm("payer_email"))
Lpayer_id = CleanSQLStr(CleanRequestForm("payer_id"))
Lreceiver_id = CleanSQLStr(CleanRequestForm("receiver_id"))
Lreceiver_email = CleanSQLStr(CleanRequestForm("receiver_email"))
Ltxn_type = CleanSQLStr(CleanRequestForm("txn_type"))
Ltxn_id = CleanSQLStr(CleanRequestForm("txn_id"))
Lverify_sign = CleanSQLStr(CleanRequestForm("verify_sign"))
Lcharset = CleanSQLStr(CleanRequestForm("charset"))
Lnotify_version = CleanSQLStr(CleanRequestForm("notify_version"))
' insert the record '
LSQL = "INSERT INTO paypalcallbacks SET OrderNo=" & LOrderNo &_
", SessionID='" & LSessionID & "'" &_
", business='" & Lbusiness & "'" &_
", first_name='" & Lfirst_name & "'" &_
", last_name='" & Llast_name & "'" &_
", payment_date='" & Lpayment_date & "'" &_
", payment_gross='" & Lpayment_gross & "'" &_
", payment_type='" & Lpayment_type & "'" &_
", payment_status='" & Lpayment_status & "'" &_
", payment_fee='" & Lpayment_fee & "'" &_
", item_name='" & Litem_name & "'" &_
", item_number='" & Litem_number & "'" &_
", test_ipn='" & Ltest_ipn & "'" &_
", quantity='" & Lquantity & "'" &_
", mc_currency='" & Lmc_currency & "'" &_
", mc_gross='" & Lmc_gross & "'" &_
", mc_fee='" & Lmc_fee & "'" &_
", shipping='" & Lshipping & "'" &_
", tax='" & Ltax & "'" &_
", payer_status='" & Lpayer_status & "'" &_
", payer_email='" & Lpayer_email & "'" &_
", payer_id='" & Lpayer_id & "'" &_
", receiver_id='" & Lreceiver_id & "'" &_
", receiver_email='" & Lreceiver_email & "'" &_
", txn_type='" & Ltxn_type & "'" &_
", txn_id='" & Ltxn_id & "'" &_
", verify_sign='" & Lverify_sign & "'" &_
", charset='" & Lcharset & "'" &_
", notify_version='" & Lnotify_version & "'"
ExecuteQuery(LSQL)
' if paid then mark the order as paid and send email
' (SS,19/2/08) added "Or ((Lpayment_type = "echeck") And (Lpayment_status = "Pending"))" for echeck payments
'If ((Lpayment_status = "Completed") Or ((Lpayment_type = "echeck") And (Lpayment_status = "Pending"))) And LOrderNo <> "" Then
' (SS,20/1/15) replaced above with following to prevent pending PayPal order from being cancelled, and mark order as AWAITING_PAYMENT
If (Lpayment_status = "Completed" Or Lpayment_status = "Pending") And LOrderNo <> "" Then
' (SS,19/2/08) added following to make payment as echeck in payment reference field
Dim LStatus
' (SS,16/5/12) added following to set status to awaiting payment if echeck and pending
' (SS,20/1/15) now set to payment received when completed, otherwise awaiting payment
If Lpayment_status = "Completed" Then
LStatus = PAYMENT_RECEIVED_STATUS
Else ' i.e. "Pending"
LStatus = AWAITING_PAYMENT_STATUS
End If
' (SS,21/1/15) removed following for echeck, because it's now saved in new PaymentInfo field below
'If Lpayment_type = "echeck" Then
' Ltxn_id = "echeck " + Ltxn_id
'End If
' following will update the status, send email and empty basket
' (SS,5/10/07) added ptPayPal
' (SS,16/5/12) added LStatus
' (SS,21/1/15) added Lpayment_type + Lpayment_status for new PaymentInfo parameter
' (SS,25/6/18) added new PaymentProviderFee parameter i.e. Lmc_fee to the end (ParseFloat(Lmc_fee))
' (SS,26/6/18) added three more parameters: PaymentReceivedAmount, PaymentReceivedCurrencyCode and PaymentProviderSurcharge.
' PaymentAmountReceived is also set a value, i.e. ParseFloat(Lmc_gross). PaymentReceivedCurrencyCode is set to Lmc_currency and surcharge to 0.
ProcessOrderThirdPartyPayment True, LOrderNo, LSessionID, ptPayPal, Ltxn_id, LStatus, Lpayment_type + " " + Lpayment_status, ParseFloat(Lmc_gross), Lmc_currency, 0, ParseFloat(Lmc_fee)
End If
End Sub
' (SS,20/9/17) process TransferWise bank transfer payments from email. Called from EventHandlers.vbs (Sub ProcessTransferWisePayment) from rule in hMailServer matched to emails from TransferWise
Sub ProcessTransferWiseCallback
Dim LInsertSQL, LOrderNo
Dim LAmount, LCurrency, LPayerName, LTransferNumber
' get values from query forms etc (passed on values) '
LAmount = CleanSQLStr(Trim(CleanRequest("Amount")))
LCurrency = CleanSQLStr(CleanRequest("Currency"))
LPayerName = CleanSQLStr(CleanRequest("PayerName"))
LTransferNumber = CleanSQLStr(CleanRequest("TransferNumber"))
If LAmount = "" Then
Response.Write("Amount must be specified")
Exit Sub
End If
Dim LPaymentMethod
If LCurrency = "EUR" Then
LPaymentMethod = ptBankTransferEUR
ElseIf LCurrency = "USD" Then
LPaymentMethod = ptBankTransferUSD
ElseIf LCurrency = "GBP" Then
LPaymentMethod = ptBankTransferGBP ' (SS,29/9/17) renamed ptBankTransfer to ptBankTransferGBP
Else
Response.Write("Currency must be EUR, USD or GBP")
Exit Sub
End If
' insert the record '
LInsertSQL = "INSERT INTO transferwise_payment_log SET LogDateTime=NOW()" &_
", Amount='" & LAmount & "'" &_
", CurrencyCode='" & LCurrency & "'" &_
", PayerName='" & LPayerName & "'" &_
", TransferNumber='" & LTransferNumber & "'"
' (SS,29/9/17) removed following, don't know why it was here
'If LOrderNo <> "" Then LSQL = LSQL & ", OrderNo='" & LOrderNo & "'"
' find order number in awaiting payment status, no more than one matching order, and exact same value in same currency
Dim LSQLWhere, LMatchingOrders
' (SS,29/9/17) removed " AND PaymentMethod = '" & LPaymentMethod & "'", no longer checks for matching payment method, no need, e.g. for GBP it could "BANK TRANSFER" for HSBC "BANK TRANSFER - GBP" for TransferWise
LSQLWhere = "WHERE Status = '" & AWAITING_PAYMENT_STATUS & "'" &_
" AND DefaultPaymentCurrencyCode = '" & LCurrency & "' AND ROUND(GrandTotal * DefaultPaymentCurrencyRate, 2) = '" & LAmount & "'"
LMatchingOrders = GetSQLValue("SELECT COUNT(*) FROM orders " + LSQLWhere)
If LMatchingOrders = 1 Then
LOrderNo = GetSQLValueAsString("SELECT OrderNo FROM orders " + LSQLWhere)
Else ' abort if 0 or more than 1 matching orders found
' insert to log without order no
ExecuteQuery(LInsertSQL)
Response.Write("Matching orders found: " & LMatchingOrders)
Exit Sub
End If
' insert to log with order no
LInsertSQL = LInsertSQL & ", OrderNo='" & LOrderNo & "'"
ExecuteQuery(LInsertSQL)
' mark the order as paid and send email
If LOrderNo <> "" Then
' we need to get SessionID because it's not passed on from ePDQ
Dim LSessionID
LSessionID = GetSessionIDForOrder(LOrderNo)
' following will update the status, send email and empty basket
' TransferNumber is used as PaymentReference
Dim LPaymentInfo
LPaymentInfo = "Auto-entry via email" & NL
LPaymentInfo = LPaymentInfo & "Date/Time: " & Now & NL
LPaymentInfo = LPaymentInfo & "Amount: " & LAmount & NL
LPaymentInfo = LPaymentInfo & "Currency: " & LCurrency & NL
LPaymentInfo = LPaymentInfo & "From: " & LPayerName & NL
LPaymentInfo = LPaymentInfo & "Transfer No: " & LTransferNumber
' (SS,25/6/18) added new PaymentProviderFee parameter i.e. 0 to the end
' (SS,26/6/18) added three more parameters: PaymentReceivedAmount, PaymentReceivedCurrencyCode and PaymentProviderSurcharge.
' PaymentAmountReceived is to ParseFloat(LAmount). PaymentReceivedCurrencyCode is set to LCurrency and surcharge to 0.
ProcessOrderThirdPartyPayment True, LOrderNo, LSessionID, LPaymentMethod, LTransferNumber, PAYMENT_RECEIVED_STATUS, LPaymentInfo, ParseFloat(LAmount), LCurrency, 0, 0
Response.Write("Payment succesfully applied to order " & LOrderNo & NL)
' send payment received email
Dim LURL, LIPAddress
LIPAddress = GetOrderFieldValue(LOrderNo, "IPAddress")
LURL = GetStoreURL & "/payment-received-email.asp?orderno=" & LOrderNo & "&sid=" & LSessionID & "&ip=" & LIPAddress & "&cmd=email"
GetWebPage(LURL)
Response.Write("Payment Received email sent")
End If
End Sub
' (SS,24/11/11) send payment mismatch email,
' (SS,17/4/18) was used by ProcessGoogleCheckoutCallback, not used anymore, perhaps add to other payment methods in future
Sub SendPaymentMismatchEmail(APaymentProvider, AOrderNo, AOrderTotal, APaymentReceived)
Dim LSubject, LBody
LSubject = "PAYMENT MISMATCH ALERT!!! " + APaymentProvider + " for " + GetStoreName
LBody =_
"Order No : " & AOrderNo & NL &_
"Order Total : " & AOrderTotal & NL &_
"Payment Received: " & APaymentReceived
SendEmail FEmailOrderConfirmationBcc1, FEmailOrderConfirmationBcc2, "", FEmailOrderConfirmationFrom, LSubject, LBody, False
End Sub
' (SS,8/9/06)
Function GetSessionIDForOrder(AOrderNo)
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQuery("SELECT SessionID FROM orders WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'")
If Not EndOfQuery Then
GetSessionIDForOrder = GetQueryValue("SessionID")
Else
GetSessionIDForOrder = ""
End If
CloseQuery
End Function
' (SS,5/8/04)
Sub ProcessOrderOnAccount(AOrderNo, ASessionID)
' (SS,21/1/15) added "" for APaymentInfo parameter
' (SS,8/6/18) added 0 to the end, new APaymentReceivedAmount parameter
' (SS,25/6/18) added 0, 0 to the end for new APaymentProviderSurcharge, APaymentProviderFee parameters
' (SS,26/6/18) added "GBP" for new APaymentReceovedCurrencyCode parameter
UpdateOrderStatusForOrderPlaced AOrderNo, PAYMENT_ON_ACCOUNT_STATUS, "ON ACCOUNT", "", 0, "GBP", 0, 0
ProcessOrderFinalise AOrderNo, ASessionID
End Sub
' (SS,28/5/09)
' (SS,6/10/16) renamed from ProcessOrderChequeOrBankTransferPayment to ProcessOrderChequeOrBankTransferOrLocalModePayment
' also changed to set correct status and payment reference
Sub ProcessOrderChequeOrBankTransferOrLocalModePayment(AOrderNo, ASessionID)
' (SS,4/8/17) added following to allow payment method to be overridden with special one e.g "BANK TRANSFER - EUR" or "BANK TRANSFER - USD"
Dim LChosenPaymentType
LChosenPaymentType = GetChosenPaymentType
' (SS,21/6/17) check if bank transfer option chosen i.e. which Bank, UK, EU or USA, if not blank then use this to update the default currency code and rate and get the correct bank details
If IsChosenPaymentBankTransfer Then
Dim LBankTransferOption
LBankTransferOption = CleanRequestForm("banktransferoption")
' Response.Write("##LBankTransferOption" & ":" & LBankTransferOption & "##" & BR)
If LBankTransferOption <> "" Then
Dim LDefaultPaymentCurrencyCode
If LBankTransferOption = "USA" Then
LDefaultPaymentCurrencyCode = "USD"
LChosenPaymentType = ptBankTransferUSD ' (SS,4/8/17)
ElseIf LBankTransferOption = "EU" Then
LDefaultPaymentCurrencyCode = "EUR"
LChosenPaymentType = ptBankTransferEUR ' (SS,4/8/17)
Else ' i.e. UK
LDefaultPaymentCurrencyCode = "GBP"
End If
UpdateOrderDefaultPaymentCurrency AOrderNo, LDefaultPaymentCurrencyCode
UpdateBankTransferDetails(AOrderNo)
End If
End If
' (SS,12/2/15) log the bank transfer details if bank transfer to PaymentLog
' (SS,23/8/17) removed True and added "Bank Transfer Details" to call to AddToPaymentLog
' (SS,18/4/18) added payment log for cheque
If IsChosenPaymentBankTransfer Then
If Session("BankTransferDetails") <> "" Then
AddToPaymentLog "Bank Transfer Details", Session("BankTransferPaymentLine") & NL & Session("BankTransferDetails")
End If
' (SS,18/4/18)
ElseIf IsChosenPaymentCheque Then
AddToPaymentLog "Cheque", ""
End If
' (SS,6/10/16) added following to set awaiting payment for bank transfer or cheque, received for others, also get payment reference from session
Dim LStatus, LPaymentReference
If IsChosenPaymentBankTransfer Or IsChosenPaymentCheque Then
LStatus = AWAITING_PAYMENT_STATUS
Else
LStatus = PAYMENT_RECEIVED_STATUS
End If
If IsLocalMode Then
LPaymentReference = GetLocalModePaymentReference
Else
LPaymentReference = ""
End If
' (SS,21/1/15) added "" for APaymentInfo parameter
' (SS,6/10/16) replaced AWAITING_PAYMENT_STATUS with LStatus and "" with LPaymentReference
' (SS,4/8/17) replaced GetChosenPaymentType with LChosenPaymentType
' (SS,8/6/18) added 0 to the end, new APaymentReceivedAmount parameter, *** may need to change 0 to actual amount
' (SS,25/6/18) added 0, 0 to the end for new APaymentProviderSurcharge, APaymentProviderFee parameters
' (SS,26/6/18) added "GBP" to new APaymentReceivedCurrencyCode parameter
UpdateOrderStatusForOrderPlaced AOrderNo, LStatus, LChosenPaymentType, LPaymentReference, "", 0, "GBP", 0, 0
ProcessOrderFinalise AOrderNo, ASessionID
End Sub
' (SS,10/12/14)
Sub ProcessOrderPayPalExpressPayment(AOrderNo, ASessionID)
' Response.Write "### ProcessOrderPayPalExpressPayment 1 ###<br>"
' (SS,20/1/14) improved to pass back AOrderStatus, APaymentReference, APaymentInfo
' (SS,26/6/18) changed A prefix to L, added LPaymentReceivedAmount, LPaymentReceivedCurrencyCode, LPaymentProviderSurcharge, LPaymentProviderFee
Dim LOrderStatus, LPaymentReference, LPaymentInfo, LPaymentReceivedAmount, LPaymentReceivedCurrencyCode, LPaymentProviderSurcharge, LPaymentProviderFee
' (SS,8/6/18) added new APaymentAmountReceived parameter to PayPalExpressConfirmPayment, and Dim above
' (SS,25/6/18) added new APaymentProviderSurcharge and APaymentProviderFee, and to Dim above
' (SS,26/6/18) renamed APaymentAmountReceived to APaymentReceivedAmount, added APaymentReceivedCurrencyCode, then corrected to use L prefix instead of A
If PayPalExpressConfirmPayment(LOrderStatus, LPaymentReference, LPaymentInfo, LPaymentReceivedAmount, LPaymentReceivedCurrencyCode, LPaymentProviderSurcharge, LPaymentProviderFee) Then
' (SS,10/12/14) NB. marked as PayPal rather than PayPal Express, APaymentReference holds text to indicate express
' (SS,8/6/18) added APaymentAmountReceived to the end, which is the actual amount received
' (SS,25/6/18) added APaymentProviderSurcharge, APaymentProviderFee
' (SS,26/6/18) renamed to use L prefixed instead of A and added LPaymentReceivedCurrencyCode
UpdateOrderStatusForOrderPlaced AOrderNo, LOrderStatus, ptPayPal, LPaymentReference, LPaymentInfo, LPaymentReceivedAmount, LPaymentReceivedCurrencyCode, LPaymentProviderSurcharge, LPaymentProviderFee
ProcessOrderFinalise AOrderNo, ASessionID
' (SS,16/12/14) added following to redirect to final page
If ReturnFromPayPalExpressLaterStage Then SetPageRedirection GetReturnFromPaymentURL
End If
End Sub
' (SS,28/5/09) moved repeated code from ProcessOrderOnAccount, ProcessOrderDirectPayment & ProcessOrderChequeOrBankTransferPayment here
Sub ProcessOrderFinalise(AOrderNo, ASessionID)
UpdateCustomerRecord(AOrderNo) ' (SS,19/12/12) (SS,22/2/13) added to update or add a customer record
EmailOrder(AOrderNo)
EmptyBasket(ASessionID)
Session("AccountID") = "" ' clear the account ID ready for next order '
ShowOrderThankYou AOrderNo, GetStoreName
' (SS,18/4/16) noticed that same was order no was being used if customer places another order and order being overwritten, added following to clear the order and prevent this from happening
' (SS,20/4/16) renamed from ClearOrderNo to StopOrderNoReuse, which clears a flag to prevent order no reuse, allows same order no reuse until new order needs to be created
StopOrderNoReuse
' (SS,6/10/16) clear local mode settings
If IsLocalMode Then ClearLocalModeSettings
' (SS,6/10/16) clear voucher code
Session("VoucherCode") = ""
End Sub
' (SS,14/8/05) called from PayPal auto return browser link and PayPal IPN callback
' (SS,8/9/06) also called from ePDQ
' (SS,5/10/07) added APaymentType
' (SS,16/5/12) added AStatus, which can be AWAITING PAYMENT or PAYMENT RECEIVED, this allows pending paypal echeck to set as AWAITING PAYMENT, completed as PAYMENT RECEIVED
' (SS,21/3/13) modified to cope with SagePay where the callback is only done via browser return
' (SS,21/1/15) added APaymentInfo parameter
' (SS,26/6/18) added new APaymentReceivedAmount, APaymentReceivedCurrencyCode, APaymentProviderSurcharge, APaymentProviderFee parameters
Sub ProcessOrderThirdPartyPayment(AFromCallback, AOrderNo, ASessionID, APaymentType, APaymentRef, AStatus, APaymentInfo, APaymentReceivedAmount, APaymentReceivedCurrencyCode, APaymentProviderSurcharge, APaymentProviderFee)
If AOrderNo <> "" Then
' if payment hasn't already been processed
If Not CheckPaymentReceived(AOrderNo) Then
' (SS,17/4/18) replaced IsPayPalPayment with APaymentType = ptPayPal because IsPayPalPayment not accurate and was removed
If AFromCallback Or APaymentType = ptPayPal Then
' mark as payment received and send email
Dim LPaymentRef
LPaymentRef = APaymentRef
' if no payment ref defined then create one
If LPaymentRef = "" Then LPaymentRef = "BRWRTN-" & ASessionID ' BRWRTN stands for browser return
' (SS,16/5/12) replaced PAYMENT_RECEIVED_STATUS with AStatus
' (SS,21/1/15) added APaymentInfo
' (SS,8/6/18) added 0 to the end, new APaymentAmountReceived parameter
' (SS,25/6/18) added 0 to the end for new APaymentProviderSurcharge and added new APaymentProviderFee parameter now passed to this routine
' (SS,26/6/18) replaced the 0's with parameter now passed to this routine i.e. APaymentReceivedAmount, APaymentProviderSurcharge
UpdateOrderStatusForOrderPlaced AOrderNo, AStatus, APaymentType, LPaymentRef, APaymentInfo, APaymentReceivedAmount, APaymentReceivedCurrencyCode, APaymentProviderSurcharge, APaymentProviderFee
' (SS,3/3/14) added If to following to only update/add customer record if payment received or awaiting payment status, now called from WorldPay which can have cancelled status
If AStatus = PAYMENT_RECEIVED_STATUS Or AStatus = AWAITING_PAYMENT_STATUS Then
UpdateCustomerRecord(AOrderNo) ' (SS,19/12/12) (SS,22/2/12) added to update or add a customer record
EmailOrder(AOrderNo)
End If
End If
EmptyBasket(ASessionID)
Session("AccountID") = "" ' clear the account ID ready for next order '
End If
' show thank you if from customer's browser (not callback from ThirdParty payment server)
' (SS,21/3/13) Added APaymentType = ptSagePay because SagePay is a special case where callback is done with the browser return
' (SS,5/4/18) added Payzone (ptPayzone) which works like SagePay, i.e. callback done via browser
' (SS,27/4/18) added ptEPDQ which now works the same way (latest account type, not CPI migrated)
If Not AFromCallback Or APaymentType = ptSagePay Or APaymentType = ptPayzone Or APaymentType = ptEPDQ Then ShowOrderThankYou AOrderNo, GetStoreName
End If
End Sub
' (SS,14/8/05) returns true if payment already made for given order, or order completed
Function CheckPaymentReceived(AOrderNo)
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQuery("SELECT COUNT(*) AS RecCount FROM orders WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "' AND (Status = '" & PAYMENT_RECEIVED_STATUS & "' OR Status = '" & ORDER_COMPLETED_STATUS & "')")
CheckPaymentReceived = GetQueryValue("RecCount") > 0
CloseQuery
End Function
' (SS,27/6/16) returns true if given order is in given status
Function CheckOrderInStatus(AOrderNo, AStatus)
'Response.Write "###CheckOrderInStatus called with " & AOrderNo & ", " & AStatus & BR
CheckOrderInStatus = GetSQLRecordExists("SELECT OrderNo FROM orders WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "' AND Status = '" & AStatus & "'")
End Function
' (SS,5/8/04)
' (SS,11/4/12) modified update query to set new PaymentReceived field
' (SS,21/1/15) added APaymentInfo field
' (SS,8/6/18) added new APaymentAmountReceived parameter, new field PaymentAmountReceived was also added to orders
' (SS,25/6/18) added new APaymentProviderSurcharge, APaymentProviderFee
' (SS,26/6/18) renamed APaymentAmountReceived to APaymentReceivedAmount, added APaymentReceivedCurrencyCode
Sub UpdateOrderStatusForOrderPlaced(AOrderNo, AStatus, APaymentMethod, APaymentReference, APaymentInfo, APaymentReceivedAmount, APaymentReceivedCurrencyCode, APaymentProviderSurcharge, APaymentProviderFee)
' set the status and date on the order record (only if it's ORDER PLACED, this prevents a second callback from overwriting the same record, in case user goes back and makes another payment)
' (SS,28/5/09) added ORDER_CANCELLED_STATUS in case order become cancelled because customer took too long to pay, I think this may have occurred with Clifton
' (SS,29/5/09) added code to not set DatTimePaid when status is payment on account or awaiting payment
Dim LDateTimePaid
If AStatus = PAYMENT_ON_ACCOUNT_STATUS Or AStatus = AWAITING_PAYMENT_STATUS Then
LDateTimePaid = ""
Else
LDateTimePaid = "DateTimePaid=NOW(), "
End If
' (SS,23/11/09) setting of NewCustomer flag is there is a customer record for this order and customer hasn't ordered before
Dim LNewCustomer
LNewCustomer = False
' if payment received, awaiting payment or payment on account then check if first customer order and flag it as such
If AStatus = PAYMENT_RECEIVED_STATUS Or AStatus = AWAITING_PAYMENT_STATUS Or AStatus = PAYMENT_ON_ACCOUNT_STATUS Then
Dim LCustomerID
LCustomerID = NB(GetValueFromQuery("CustomerID", "SELECT CustomerID FROM orders WHERE OrderNo = " & AOrderNo))
' if there is a customer id then count how many orders placed so far by customer which are not cancelled, order placed not including this one
' if there are zero then set LNewCustomer to true
If LCustomerID <> "" Then
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
If NZD(GetValueFromQuery("OrderCount", "SELECT COUNT(*) AS OrderCount FROM orders WHERE CustomerID = '" & CleanSQLStr(LCustomerID) & "' AND Status <> '" & ORDER_CANCELLED_STATUS & "' AND Status <> '" & ORDER_PLACED_STATUS & "' AND OrderNo <> '" & CleanSQLStr(AOrderNo) & "'")) = 0 Then
LNewCustomer = True
End If
End If
End If
' (SS,23/11/09) added
' (SS,11/4/12) added setting PaymentReceived to TRUE or FALSE depending on AStatus
' (SS,8/6/18) added LPaymentAmountReceived
' (SS,26/6/18) renamed LPaymentAmountReceived to LPaymentReceivedAmount
Dim LPaymentReceived, LPaymentReceivedAmount
If AStatus = PAYMENT_RECEIVED_STATUS Then
LPaymentReceived = True
LPaymentReceivedAmount = APaymentReceivedAmount
Else
LPaymentReceived = False
LPaymentReceivedAmount = 0
End If
' (SS,27/11/12) check for cancelled status because items would have been taken out of stock, send alert email
If GetSQLValueAsString("SELECT OrderNo FROM orders WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "' AND Status = '" & ORDER_CANCELLED_STATUS & "'") <> "" Then
Dim LBody
LBody =_
"Order No: " & AOrderNo & NL &_
"Status: " & AStatus & NL &_
"Payment Method: " & APaymentMethod & NL &_
"Payment Reference: " & APaymentReference
' SendAlertEmail "Cancelled order paid for. Stock may need to be decreased.", LBody
' (SS,18/4/16) added following to automatically decrease stock, and modified alert message
TakeProductsFromStock AOrderNo, True
' (SS,24/8/17) removed following alert email. Perhaps add the above body to PaymentLog in future, replaced with AddToPaymentLog
' SendAlertEmail "Cancelled order paid for. Stock automatically decreased.", LBody, False
AddToPaymentLog "Cancelled order paid for", "Stock automatically decreased" & NL & LBody ' (SS,24/8/17) replaces alert email above
End If
' (SS,25/5/12) added "OR Status = '" & AWAITING_PAYMENT_STATUS & " so that PayPal echeck's that have cleared and were awaiting payment can be set to payment received
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
' (SS,21/1/14) added new PaymentInfo field
' (SS,8/6/18) added ", PaymentAmountReceived = '" & CleanSQLStr(LPaymentAmountReceived) &
' (SS,25/6/18) added "', PaymentProviderSurcharge = '" & CleanSQLStr(APaymentProviderSurcharge) & "', PaymentProviderFee = '" & CleanSQLStr(APaymentProviderFee) & "'"
' (SS,26/6/18) renamed PaymentAmountReceived to PaymentReceivedAmount, added PaymentReceivedCurrencyCode, split across multiple lines
ExecuteQuery("UPDATE orders SET Status = '" & CleanSQLStr(AStatus) & "', " & LDateTimePaid & "PaymentMethod = '" + CleanSQLStr(APaymentMethod) + "', PaymentReference = '" + CleanSQLStr(APaymentReference) + "', PaymentInfo = '" + CleanSQLStr(APaymentInfo) & "'" &_
", PaymentReceived = " & BoolToInt(LPaymentReceived) &_
", PaymentReceivedAmount = '" & CleanSQLStr(LPaymentReceivedAmount) & "'" &_
", PaymentReceivedCurrencyCode = '" & CleanSQLStr(Left(Trim(APaymentReceivedCurrencyCode), 3)) & "'" &_
", PaymentProviderSurcharge = '" & CleanSQLStr(APaymentProviderSurcharge) & "'" &_
", PaymentProviderFee = '" & CleanSQLStr(APaymentProviderFee) & "'" &_
", NewCustomer = " & BoolToInt(LNewCustomer) &_
" WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "' AND (Status = '" & ORDER_PLACED_STATUS & "' OR Status = '" & ORDER_CANCELLED_STATUS & "' OR Status = '" & AWAITING_PAYMENT_STATUS & "')")
End Sub
' (SS,11/8/05)
Sub UpdateOrderCardDetails(AOrderNo)
Dim LSQL
' (SS,12/6/12) added CleanSQLStr before AOrderNo to prevent SQL injection
LSQL = "UPDATE orders SET CardType='" & CleanSQLStr(Session("CardType")) & "'" &_
", CardNumber=AES_ENCRYPT('" & CleanSQLStr(SimpleEncryptString(Session("CardNumber"))) & "', 'itp123')" &_
", CardHoldersName='" & CleanSQLStr(Session("CardHoldersName")) & "'" &_
", CardSecurityCode='" & CleanSQLStr(Session("CardSecurityCode")) & "'" &_
", CardIssueNumber='" & CleanSQLStr(Session("CardIssueNumber")) & "'" &_
", CardValidFrom='" & CleanSQLStr(Session("CardValidFromMonth") + "/" + Session("CardValidFromYear")) & "'" &_
", CardExpiryDate='" & CleanSQLStr(Session("CardExpiryMonth") + "/" + Session("CardExpiryYear")) & "'" &_
" WHERE OrderNo='" & CleanSQLStr(AOrderNo) & "'"
ExecuteQuery(LSQL)
End Sub
' (SS,17/11/04) sets javascript alert message call "onload" of body '
' can be called more than once, each new message is separated by newline
' (SS,4/6/09) added check for "", only adds if AMessage <> ""
' (SS,22/7/10) noticed that the alert wasn't appearing when message contained double quote
' escaped using \ to fix this for single and double quotes
Sub SetAlertMessage(AMessage)
If AMessage <> "" Then
If gsITP_ErrorMessage <> "" Then
gsITP_ErrorMessage = gsITP_ErrorMessage + "\n" ' i.e. newline in javascript '
End If
Dim LMessage
LMessage = Replace(AMessage, BR, "\n") ' (SS,1/6/07) replace <br> with "\n" ' (SS,7/6/11) replaced <br> with BR constant
LMessage = Replace(LMessage, "'", "\'") ' (SS,22/7/10) escape single quotes
LMessage = Replace(LMessage, """", "\""") ' (SS,22/7/10) escape double quotes
LMessage = Replace(LMessage, " ", " ") ' (SS,19/4/16) replaced non-breaking space (HTML) to normal space
gsITP_ErrorMessage = gsITP_ErrorMessage + Replace(LMessage, "<br>", "\n")
End If
End Sub
' (SS,15/4/16) returns True of alert message already contains given text
Function AlertMessageContains(AContains)
AlertMessageContains = InStr(1, gsITP_ErrorMessage, AContains, vbTextCompare) > 0
End Function
' (SS,19/4/16) add a line to separate message if already a message
Function AddAlertMessageSection
If gsITP_ErrorMessage <> "" Then
gsITP_ErrorMessage = gsITP_ErrorMessage + "\n" ' i.e. newline in javascript '
End If
End Function
' (SS,28/5/07) moved here from SetAlertMessage, so it can be run from finalise at the end of the page
Sub ShowAlertMessage
If gsITP_ErrorMessage <> "" Then
%>
<script language="JavaScript" type="text/JavaScript">
<!--
ITP_ErrorMessage = "<%=gsITP_ErrorMessage%>";
//-->
</script>
<%
End If
End Sub
' (SS,7/6/11)
Sub SetConfirmDialog(AMessage, AFunction)
%>
<script language="JavaScript" type="text/JavaScript">
ITP_ConfirmMessage = "<%=AMessage%>"
ITP_ConfirmFunction = "<%=AFunction%>"
</script>
<%
End Sub
' (SS,20/2/14) redirect to basket, show a pop-up alert or neither depending on setting
Sub SetAddToBasketAlert(AQty, AProductCode)
If FAddToBasketAlertType = "Redirect" Then
SetPageRedirection GetBasketPageURL
ElseIf FAddToBasketAlertType = "PopUp" Then
SetPopUpMessage AQty & " x " & GetProductName(AProductCode) & " added to basket"
End If
End Sub
' (SS,20/2/14)
' (SS,15/4/16) changed to append to existing message rather than overwrite
Sub SetPopUpMessage(AMessage)
FPopUpMessage = FPopUpMessage & IIf(FPopUpMessage = "", "", BR) & AMessage
End Sub
' (SS,20/2/14)
Sub ShowPopUpMessage
If FPopUpMessage <> "" Then
%>
<div id="popupmessage" style="display: none; position: absolute; top: 0px; right: 0px; background-color: #FFF; text-align: center; width: 394px; padding: 25px; border: 2px solid red;">
<p><%=FPopUpMessage%></p>
</div>
<script language="JavaScript" type="text/JavaScript">
$("#popupmessage").show();
/* (SS,11/6/14) added following to position below the shopping status */
l_top = $("#shopping-status").offset().top + $("#shopping-status").height() + 5;
l_left = $("#shopping-status").offset().left + $("#shopping-status").width() - $("#popupmessage").outerWidth() + 3;
$("#popupmessage").css({top: l_top, left: l_left});
$("#popupmessage").fadeOut(4000);
</script>
<%
End If
End Sub
' ----------------------------------- '
' Start of Currency Exchange Routines '
' ----------------------------------- '
' (SS,15/9/06) Currency routines
' (SS,9/6/14) removed routines GetLatestExchangeRatesIfNeeded, GetWorldPayExchangeRates and UpdateExchangeRate, not called here
' sets the currency code using users selection, cookie
' code must be called before sending HTML tag because of cookie
' (SS,28/10/09) added ACurrencyCode parameter, also called from GetEmailBody, added to fix incorrect currency symbol appearing in HTML emails
' ACurrencyCode is "" else called from GetEmailBody
' (SS,10/6/14) minor improvements
Sub SetCurrencyCode(ACurrencyCode)
Dim LCurrencyCode
If CleanRequestQueryString("xcmd") = "currency" Then
LCurrencyCode = CleanRequestQueryString("xcode")
If IsAllowedCurrency(LCurrencyCode) Then
Session("CurrencyCode") = LCurrencyCode
' set cookie to hold selected currency
' (SS,20/2/12) cookie code moved to separate Sub SetCookie
SetCookie "CurrencyCode", LCurrencyCode
Else
Session("CurrencyCode") = FCurrencyDefaultCode ' (SS,10/6/14) change from "" to FCurrencyDefaultCode
End If
' (SS,10/6/14) combined into one if/else and removed If Session("CurrencyCode") = "" Then
Else
' (SS,28/10/09) added following if
If ACurrencyCode <> "" Then ' (SS,28/10/09)
LCurrencyCode = ACurrencyCode ' (SS,28/10/09)
Else ' (SS,28/10/09)
' try getting from cookie
LCurrencyCode = GetCookie("CurrencyCode") ' (SS,20/2/12) replaced Request.Cookies with GetCookie
End If ' (SS,28/10/09)
If IsAllowedCurrency(LCurrencyCode) Then
Session("CurrencyCode") = LCurrencyCode
Else
Session("CurrencyCode") = ""
End If
' set default if it's still blank
If Session("CurrencyCode") = "" Then Session("CurrencyCode") = FCurrencyDefaultCode ' (SS,14/11/07) was DEFAULT_CURRENCY_CODE
End If
End Sub
' (SS,19/3/15)
Function GetDefaultCurrencyCode
GetDefaultCurrencyCode = FCurrencyDefaultCode
End Function
' (SS,6/6/14)
Function GetCurrencyCodeForCountry(ACountry)
Dim LPostalArea, LCurrencyCode
If GetSQL2Values("SELECT PostalArea, COALESCE(Currency, '') FROM countries WHERE Country = '" & CleanSQLStr(ACountry) & "'", LPostalArea, LCurrencyCode) Then
' determine currency code from postal area, UK = GBP, EU = EUR, RW = USD
If LCurrencyCode = "" Then
Select Case LPostalArea
Case "EU": LCurrencyCode = "EUR"
Case "RW": LCurrencyCode = "USD"
Case Else: LCurrencyCode = "GBP"
End Select
End If
Else ' use default if not found
LCurrencyCode = "GBP"
End If
GetCurrencyCodeForCountry = LCurrencyCode
End Function
' (SS,18/9/04) this is called once in the initialise routine
' gets various currency values from the exchange rates table
' (SS,5/6/14) changes to add new local table currencies used to hold exchange rates
Sub GetCurrencyExchangeRate
FCurrencyCode = Session("CurrencyCode")
If FCurrencyCode = FCurrencyDefaultCode Or Not IsMulticurrency Then ' (SS,14/11/07) replaced DEFAULT_CURRENCY_CODE with FCurrencyDefaultCode
SetCurrencyDefaults ' (SS,6/6/14) moved code from here to new routine
Else
' (SS,5/6/14) added "If" to separate original from new local table method
If IsMulticurrencyMethodOriginal Then
OpenDatabaseCommon
' (SS,26/5/07) modified to use OpenQueryCommon instead of OpenQuery
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQueryCommon("SELECT * FROM exchangerates WHERE Source = 'WP' AND CurrencyCode = '" + CleanSQLStr(FCurrencyCode) + "'")
' (SS,28/10/09) in following lines replaced EndOfQuery with EndOfQueryCommon and GetQueryValue with GetQueryValueCommon
If Not EndOfQueryCommon Then
FCurrencyRate = CDbl(GetQueryValueCommon("ExchangeRate"))
FCurrencySymbol = GetQueryValueCommon("SymbolHTML") ' (SS,28/10/09) replaced Symbol with SymbolHTML
' (SS,20/2/12) removed following because we're now looking up this from the countries table below
' FCurrencyShowVAT = IntToBool(GetQueryValueCommon("ShowVAT"))
FCurrencyCountry = GetQueryValueCommon("DefaultCountry") ' used to work out approx delivery when showing basket
' (SS,20/2/12) currency country is fine as a default, but if user has already set country in basket or checkout then we need to use that instead
SetCountryFromBasketForm ' if user has changed country in basket form then we need to use the newly selected country
If Session("Country") <> "" Then
FCurrencyCountry = Session("Country")
ElseIf Session("DeliveryCountry") <> "" Then
FCurrencyCountry = Session("DeliveryCountry")
End If
' we need to use the VAT setting in the countries table to decide whether to show vat, not "ShowVAT" from the exchangerates rate
' because it can vary from site to site, hyperflight currently uses flat rate and charges VAT to RW unlike clifton
FCurrencyShowVAT = GetCountryChargeVATFlag(FCurrencyCountry)
Else ' (SS,6/6/14) set defaults if not found
SetCurrencyDefaults
End If
' (SS,26/5/07) modified to use CloseQueryCommon instead of CloseQuery and CloseDatabaseCommon instead of CloseDatabase
CloseQueryCommon
CloseDatabaseCommon
Else ' (SS,5/6/14) new code to use local currencies table i.e. hyperflight
' (SS,18/12/14) some code moved to new GetCurrencyExchangeRateAdjusted
If GetCurrencyExchangeRateAdjusted(FCurrencyCode, FCurrencyRate, FCurrencySymbol) Then
FCurrencyCountry = GetDeliveryCountry
FCurrencyShowVAT = GetCountryChargeVATFlag(FCurrencyCountry)
Else ' (SS,6/6/14) set defaults if not found
SetCurrencyDefaults
End If
CloseQuery
End If
End If
' (SS,8/6/07) replaced "�" with HTML code, mainly because � shows as # in HTML emails
' (SS,28/10/09) removed following SymbolHTML field above now returns the correct HMTL symbol
'If FCurrencySymbol = "�" Then FCurrencySymbol = "£"
'If FCurrencySymbol = "�" Then FCurrencySymbol = "€" ' (SS,29/11/07)
End Sub
' (SS,6/6/14) called when currency code not found or applicable
Sub SetCurrencyDefaults
FCurrencyRate = FCurrencyDefaultRate ' (SS,14/11/07) was DEFAULT_CURRENCY_RATE
FCurrencySymbol = FCurrencyDefaultSymbol ' (SS,14/11/07) was DEFAULT_CURRENCY_SYMBOL
FCurrencyShowVAT = FCurrencyDefaultShowVAT ' (SS,14/11/07) was DEFAULT_CURRENCY_SHOW_VAT
FCurrencyCountry = FCurrencyDefaultCountry ' (SS,14/11/07) was DEFAULT_CURRENCY_COUNTRY
End Sub
' (SS,28/10/09) called from GetEmailBody
Sub SetCurrencySymbol(ACurrencyCode)
Dim LCurrencyCode
LCurrencyCode = ACurrencyCode
If LCurrencyCode = "" Then LCurrencyCode = FCurrencyCode
' (SS,5/6/14) added If because exchange rate can be in two different tables
If IsMulticurrencyMethodOriginal Then
OpenDatabaseCommon
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQueryCommon("SELECT * FROM exchangerates WHERE Source = 'WP' AND CurrencyCode = '" + CleanSQLStr(LCurrencyCode) + "'")
If Not EndOfQueryCommon Then
FCurrencySymbol = GetQueryValueCommon("SymbolHTML")
End If
CloseQueryCommon
CloseDatabaseCommon
Else ' (SS,5/6/14) use local currencies table i.e. hyperflight
FCurrencySymbol = GetSQLValueAsString("SELECT SymbolHTML FROM currencies WHERE Currency = '" + CleanSQLStr(LCurrencyCode) + "'")
End If
End Sub
' (SS,19/9/06) set the currency code, rate, symbols for calls to CorrectCurrencyNV etc to work correctly
' (SS,1/8/17) moved code to this separate SetCurrencyForOrder routine, originally in GetEmailBodyOrderDetails
' now also called from ShowOrderDetailsMaster, ShowPreviousOrders
Sub SetCurrencyForOrder(ACurrencyCode, AExchangeRate)
SetCurrencyRate(CDbl(AExchangeRate))
SetCurrencyCode(ACurrencyCode) ' (SS,28/10/09) added this to correct pound symbol incorrectly appearing in HTML emails when non UK currency
SetCurrencySymbol(ACurrencyCode) ' (SS,28/10/09) sets the currency symbol
End Sub
Function IsMulticurrency
IsMulticurrency = FMulticurrencyEnabled ' (SS,13/9/07) replaced Application("Multicurrency") = True with FMulticurrencyEnabled
End Function
' (SS,5/6/14)
Function IsMulticurrencyMethodOriginal
IsMulticurrencyMethodOriginal = Not IsMulticurrencyMethodLocalTable
End Function
' (SS,5/6/14)
Function IsMulticurrencyMethodLocalTable
IsMulticurrencyMethodLocalTable = FMulticurrencyMethod = "LocalTable"
End Function
' (SS,13/9/07) replaced Application("AllowedCurrencies") with FAllowedCurrencies
' (SS,5/6/14) added code for new local currencies table
Function IsAllowedCurrency(ACurrencyCode)
If FAllowedCurrencies = "" Then
IsAllowedCurrency = False
Exit Function
End If
' (SS,5/6/14) added If
If IsMulticurrencyMethodOriginal Then
IsAllowedCurrency = InStr("," & FAllowedCurrencies & ",", "," & ACurrencyCode & ",") > 0
Else ' (SS,5/6/14) check if currency exists in the currencies table
IsAllowedCurrency = GetSQLRecordExists("SELECT * FROM currencies WHERE Currency = '" + CleanSQLStr(ACurrencyCode) + "'")
End If
End Function
' (SS,18/12/14)
Function IsMainCurrency(ACurrencyCode)
IsMainCurrency = InStr("," & MAIN_CURRENCIES & ",", "," & ACurrencyCode & ",") > 0
End Function
' (SS,5/6/14)
' (SS,18/12/14) complete rewrite, added ACurrencyCode because there are now two ratios, one for main currencies e.g. EUR & USD, and another for other
' also changed to work with two different ratios, existing ratio renamed to FExchangeRateAdjustmentRatioMain, new FExchangeRateAdjustmentRatioOther added
Function GetExchangeRateAdjustmentRatio(ACurrencyCode)
If IsMainCurrency(ACurrencyCode) And ACurrencyCode <> DEFAULT_CURRENCY_CODE And FExchangeRateAdjustmentRatioMain <> "" Then
GetExchangeRateAdjustmentRatio = CDbl(FExchangeRateAdjustmentRatioMain)
ElseIf Not IsMainCurrency(ACurrencyCode) And FExchangeRateAdjustmentRatioOther <> "" Then
GetExchangeRateAdjustmentRatio = CDbl(FExchangeRateAdjustmentRatioOther)
Else
GetExchangeRateAdjustmentRatio = 1
End If
End Function
' (SS,18/12/14) new routine which returns currency rate and symbol for given currency adjusted by the exchange rate adjustment ratio, returns false if currency not found
Function GetCurrencyExchangeRateAdjusted(ACurrencyCode, ByRef ACurrencyRate, ByRef ACurrencySymbol)
If GetSQL2Values("SELECT ExchangeRate, SymbolHTML FROM currencies WHERE Currency = '" + CleanSQLStr(ACurrencyCode) + "'", ACurrencyRate, ACurrencySymbol) Then
ACurrencyRate = CDbl(ACurrencyRate) * GetExchangeRateAdjustmentRatio(ACurrencyCode)
GetCurrencyExchangeRateAdjusted = True
Else
GetCurrencyExchangeRateAdjusted = False
End If
End Function
' (SS,18/12/14) get the default payment currency from currencies table where applicable, mainly to determine amount to show for bank transfers
' called from TryProcessOrder
Sub GetDefaultPaymentCurrency(ACurrencyCode, ByRef ADefaultPaymentCurrencyCode, ByRef ADefaultPaymentCurrencyRate)
ADefaultPaymentCurrencyCode = DEFAULT_CURRENCY_CODE
ADefaultPaymentCurrencyRate = DEFAULT_CURRENCY_RATE
If IsMulticurrency And Not IsMulticurrencyMethodOriginal Then
Dim LPaymentCurrencyCode
LPaymentCurrencyCode = GetSQLValueAsString("SELECT DefaultPaymentCurrency FROM currencies WHERE Currency = '" & CleanSQLStr(GetCurrencyCode) & "'")
If LPaymentCurrencyCode <> "" Then
Dim LCurrencySymbol ' this is ignored, required by GetCurrencyExchangeRateAdjusted
If GetCurrencyExchangeRateAdjusted(LPaymentCurrencyCode, ADefaultPaymentCurrencyRate, LCurrencySymbol) Then
ADefaultPaymentCurrencyCode = LPaymentCurrencyCode
End If
End If
End If
End Sub
' (SS,18/9/06) returns true if currency is vatable (i.e. the default for currency)
' when checking out the country will be used to get the VAT Falg
' (SS,16/1/13) this routine may now be redundant, IsCountryVATable is now used instead
Function IsCurrencyVATable
IsCurrencyVATable = FCurrencyShowVAT
End Function
Function GetCurrencyCode
GetCurrencyCode = Session("CurrencyCode")
End Function
' (SS,17/2/12)
Function IsCurrencyGBP
IsCurrencyGBP = GetCurrencyCode = "GBP"
End Function
' (SS,7/11/12)
Function IsCurrencyUSD
IsCurrencyUSD = GetCurrencyCode = "USD"
End Function
' (SS,7/11/12)
Function IsCurrencyEUR
IsCurrencyEUR = GetCurrencyCode = "EUR"
End Function
Function GetCurrencyRate
GetCurrencyRate = FCurrencyRate
End Function
Function SetCurrencyRate(ACurrentRate)
FCurrencyRate = ACurrentRate
End Function
Function GetCurrencySymbol
GetCurrencySymbol = FCurrencySymbol
End Function
Function GetCurrencyPostalArea
GetCurrencyPostalArea = GetPostalArea(GetDeliveryCountry) ' (SS,28/5/07) replaced GetCurrencyCountry with GetInvoiceCountry
End Function
' (SS,27/01/10) gets the correct region/area to show in basket footer to indicate which region the delivery has been calculated for
' in hyperflight it would show RW if customer had selected United States, changed to show the delivery region code if not blank, this would show USA
Function GetRegionCodeOrPostalArea
Dim LDeliveryCountry, LRegionCode
LDeliveryCountry = GetDeliveryCountry
LRegionCode = GetRegionCode(LDeliveryCountry)
If LRegionCode = "" Then LRegionCode = GetPostalArea(LDeliveryCountry)
GetRegionCodeOrPostalArea = LRegionCode
End Function
' returns value with currency symbols, commas, 2 dp
Function CorrectCurrency(APrice)
CorrectCurrency = CorrectCurrencyActual(APrice, True)
End Function
' returns correct currency with no format
Function CorrectCurrencyNF(APrice)
CorrectCurrencyNF = CorrectCurrencyActual(APrice, False)
End Function
' returns currency with VAT removed if currency has ShowVAT flag set to false
' (SS,20/10/08) changed to take PricesEnteredIncludeVAT into account, if false then VAT is not deducted because prices don't already include VAT
' had to do this because price in CovDem product pages and shopping status was deducting VAT when it shouldn't because entered prices don't include VAT
Function CorrectCurrencyNV(APrice)
If IsNull(APrice) Or APrice = "" Then
CorrectCurrencyNV = APrice
Else
Dim LPrice
LPrice = CDbl(APrice)
' (SS,20/10/08) added PricesEnteredIncludeVAT
' (SS,2/11/12) replaced IsCurrencyVATable with IsCountryVATable so that VAT can also be removed from GBP
' (SS,12/12/12) added "Or IsIntraCommunitySupply" so that VAT is deducted for EU sales where VAT Number is entered for sites that have Prices Entered Inc VAT e.g. HyperFlight
If PricesEnteredIncludeVAT And (Not IsCountryVATable Or IsIntraCommunitySupply) Then LPrice = LPrice - GetVATAmount(LPrice)
CorrectCurrencyNV = CorrectCurrencyActual(LPrice, True)
End If
End Function
' (SS,17/10/06) returns currency with VAT added to given price if currency has ShowVAT flag set to true
Function CorrectCurrencyWV(APrice)
If IsNull(APrice) Or APrice = "" Then
CorrectCurrencyWV = APrice
Else
Dim LPrice
LPrice = CDbl(APrice)
' (SS,22/6/10) modified GetVATAmountToAdd to GetVATAmountToAddNC because GetVATAmountToAdd was changed to use IsCountryVATable which we don't want to do
' (SS,2/11/12) replaced IsCurrencyVATable with IsCountryVATable so that VAT can also be removed from GBP
If IsCountryVATable Then LPrice = LPrice + GetVATAmountToAddNC(LPrice)
Dim LResult ' (SS,26/2/15)
LResult = CorrectCurrencyActual(LPrice, True)
' (SS,26/2/15) added logging to help find issue where inc VAT prices showing same as exc VAT sometimes for CIRC
' comment out after fixing bug
' TempDebugLog "SessionID = " & GetSessionID & ", CorrectCurrencyWV(" & APrice & "), Result = " & LResult & ", IsCountryVATable = " & IsCountryVATable & ", APrice = " & APrice & ", LPrice = " & LPrice & ", UseVATFlagFromCountries = " & UseVATFlagFromCountries & ", GetInvoiceCountry = """ & GetInvoiceCountry & """, GetInvoiceCountry = """ & GetDeliveryCountry & """, GetCookie(""DeliveryCountry"") = """ & GetCookie("DeliveryCountry") & """"
CorrectCurrencyWV = LResult
End If
End Function
Function CorrectCurrencyActual(APrice, AIsFormatted)
If IsNull(APrice) Or APrice = "" Then
CorrectCurrencyActual = APrice
Else
Dim LPrice
LPrice = CDbl(APrice) ' needs to be as double so we can do calculations on it
LPrice = LPrice * FCurrencyRate
If AIsFormatted Then
' (SS,12/5/10) replaced "GetCurrencySymbol &" with AddCurrencySymbol and added SwapCommasAndDP
CorrectCurrencyActual = AddCurrencySymbol(CorrectCommasAndDP(FormatNumber(LPrice, 2)))
Else
CorrectCurrencyActual = LPrice
End If
End If
End Function
' (SS,12/5/10) for French and German swap commas and decimal point
' (SS,21/6/10) renamed SwapCommasAndDP to CorrectCommasAndDP
Function CorrectCommasAndDP(AFormattedValue)
If GetTranslationLanguage = "French" Or GetTranslationLanguage = "German" Then
Dim LResult
LResult = ReplaceStr(AFormattedValue, ",", "@")
LResult = ReplaceStr(LResult, ".", ",")
If GetTranslationLanguage = "French" Then
CorrectCommasAndDP = ReplaceStr(LResult, "@", " ") ' France using space as thousands separator
Else
CorrectCommasAndDP = ReplaceStr(LResult, "@", ".")
End If
Else ' no change if English
CorrectCommasAndDP = AFormattedValue
End If
End Function
' (SS,12/5/10)
Function AddCurrencySymbol(AFormattedAmount)
If GetTranslationLanguage = "French" Or GetTranslationLanguage = "German" Then
AddCurrencySymbol = AFormattedAmount & " " & GetCurrencySymbol' e.g. X,XX �
Else ' e.g. �X.XX
AddCurrencySymbol = GetCurrencySymbol & AFormattedAmount
End If
End Function
' (SS,16/2/16) removes the currency symbol added using AddCurrencySymbol
Function RemoveCurrencySymbol(AFormattedAmount)
RemoveCurrencySymbol = ReplaceStr(AFormattedAmount, GetCurrencySymbol, "")
End Function
' (SS,13/11/07) converts given price (in non GBP currency) back to internal GBP currency
' used in custom delivery to ensure delivery is in fixed in EURO or other currency
Function ConvertCurrencyInternal(APrice)
If IsNull(APrice) Or APrice = "" Then
ConvertCurrencyInternal = APrice
Else
ConvertCurrencyInternal = CDbl(APrice) / FCurrencyRate
End If
End Function
' (SS,12/5/10) returns MySQL date format for DATE_FORMAT with time if AWithTime is True, to return correct date format for language
Function GetLanguageDateFormat(AWithTime)
Dim LResult
If GetTranslationLanguage = "French" Then
LResult = "%d/%m/%Y"
ElseIf GetTranslationLanguage = "German" Then
LResult = "%d.%m.%Y"
Else ' i.e. English
LResult = "%d/%m/%Y"
End If
If AWithTime Then LResult = LResult + " %H:%i"
GetLanguageDateFormat = LResult
End Function
' (SS,19/12/14) new routine used to show a default payment currency value in bank transfers
Function GetCurrencyFormattedValue(ACurrencyCode, ACurrencyRate, AValue)
' look up the currency symbol from currencies table
Dim LCurrencySymbol, LConvertedValue
LCurrencySymbol = GetSQLValueAsString("SELECT SymbolHTML FROM currencies WHERE Currency = '" & CleanSQLStr(ACurrencyCode) & "'")
If LCurrencySymbol = "" And ACurrencyCode = "GBP" Then LCurrencySymbol = "£" ' (SS,4/4/17) use pound symbol if nothing found and code is GBP i.e. currencies table not populated
GetCurrencyFormattedValue = LCurrencySymbol & CorrectCommasAndDP(FormatNumber(CDbl(ACurrencyRate) * CDbl(AValue), 2))
End Function
' (SS,19/12/14) used in both email and web page, orders table is open with access to fields using GetFieldValue()
' (SS,4/4/17) minor text changes as requested for CIRC, now can get customised message from GetMessage function
Function GetBankTransferPaymentLine
Dim LBankTransferText
' LBankTransferText = "Please make a payment to the following account for "
' (SS,4/4/17) replaced above with following
' (SS,4/8/17) moved following to below
' LBankTransferText = "Please make payment of "
' (SS,4/8/17) added following to simplify the code and work correctly for HF to only show one option for new BANK TRANSFER - EUR or BANK TRANSFER - USD
Dim LPaymentAmountText
LPaymentAmountText = ""
' if applicable and if default payment currency code is different to the one chosen in the website then also determine the amount to show in the default payment currency
' (SS,4/8/17) replaced LBankTransferText with LPaymentAmountText, and moved " or " below
If IsMulticurrency And IsMulticurrencyMethodLocalTable And GetFieldValue("CurrencyCode") <> GetFieldValue("DefaultPaymentCurrencyCode") Then
LPaymentAmountText = "<b>" & GetCurrencyFormattedValue(GetFieldValue("DefaultPaymentCurrencyCode"), GetFieldValue("DefaultPaymentCurrencyRate"), GetFieldValue("GrandTotal")) & "</b>"
End If
' (SS,4/8/17) include following if LPaymentAmountText not set above i.e. is "", also changed LBankTransferText to LPaymentAmountText
' (SS,12/9/17) removed ' Or GetFieldValue("DefaultPaymentCurrencyCode") = "GBP" to only show one option for GBP (not "or" selected currency) due to high costs with HSBC
If LPaymentAmountText = "" Then
' (SS,12/9/17) removed following, no longer applicable due to removable of "Or GetFieldValue("DefaultPaymentCurrencyCode") = "GBP"" from If condition above
' If LPaymentAmountText <> "" Then LPaymentAmountText = LPaymentAmountText & " or "
LPaymentAmountText = LPaymentAmountText & "<b>" & GetCurrencyFormattedValue(GetFieldValue("CurrencyCode"), GetFieldValue("ExchangeRate"), GetFieldValue("GrandTotal")) & "</b>"
End If
' (SS,4/4/17) added following, above line replaced
' (SS,4/8/17) adjusted to use new LPaymentAmountText
LBankTransferText = "Please make payment of " & LPaymentAmountText & " to the following account," ' (SS,4/4/17)
LBankTransferText = LBankTransferText & " entering <b>" & GetFieldValue("OrderNo") & "</b> in the bank's reference field." ' (SS,4/8/17) removed the & BR after LBankTransferText added space before "entering"
' (SS,5/4/17) following allows default text which is now "Failure to pay immediately will result in your order being delayed." to be overridden
' (SS,6/4/17) removed the default, now just adds extra text if defined
Dim LAdditionalText
LAdditionalText = GetMessage("BankTransferExtra")
If LAdditionalText <> "" Then
LBankTransferText = LBankTransferText & BR & LAdditionalText
End If
GetBankTransferPaymentLine = LBankTransferText
End Function
' --------------------------------- '
' End of Currency Exchange Routines '
' --------------------------------- '
' (SS,17/9/08) added following to allow the page to be default to a specific category
' without having to use a query string, routine called from the main page
' for galleon web promotion to allow doorway domains/pages to show content from the database
' new global variable FDefaultCategory used
Sub SetDefaultCategory(ACategory)
FDefaultCategory = ACategory
End Sub
// (SS,17/9/08) returns the default category if set
Function GetDefaultCategory
GetDefaultCategory = FDefaultCategory
End Function
' (SS,17/9/08) returns true if default category page has been set, used by inc-pages.asp
Function HasDefaultCategory
HasDefaultCategory = GetDefaultCategory <> ""
End Function
' (SS,17/5/11)
Function GetGroup
GetGroup = CleanRequestQueryString("grp")
End Function
' (SS,25/7/11)
Function GetSearch
GetSearch = CleanRequestQueryString("search")
End Function
' (SS,25/7/11)
Function IsSpecialOfferSearch
IsSpecialOfferSearch = LCase(GetSearch) = SPECIAL_OFFERS_SEARCH_TEXT
End Function
' (SS,27/7/11) returns true if a group / category / subcategory has been specified, product code also means it's specified because product belongs to a category
Function CategoryOrProductSpecified
CategoryOrProductSpecified = Not ((GetProductCodeQS = "") and (CleanRequestQueryString("grp") = "") and (CleanRequestQueryString("cat") = "") and (CleanRequestQueryString("subcat") = ""))
End Function
' (SS,28/7/11) returns true if only group is specified
Function GroupOnlySpecified
GroupOnlySpecified = GetGroup <> "" And GetCategory = "" And GetSubcategory = "" And GetProductCode = ""
End Function
' (SS,17/9/08) returns category from query string or the default setting
' *** all CleanRequestQueryString("cat") to be replaced by GetCategory in future
Function GetCategory
If HasDefaultCategory Then
GetCategory = GetDefaultCategory
Else
GetCategory = CleanRequestQueryString("cat")
End If
End Function
' (SS,12/4/11) returns subcategory from query string
Function GetSubcategory
GetSubcategory = CleanRequestQueryString("subcat")
End Function
' (SS,15/2/12) called from ShowSingleProduct, to overcome issue when we go directly to a product due to single product found in search
' and query string doesn't contain the code
Sub SetProductCode(AProductCode)
FProductCode = AProductCode
End Sub
' (SS,12/4/11)
' (SS,15/2/12) modified to use FProductCode if CleanRequestQueryString("code") is blank
Function GetProductCode
If GetProductCodeQS = "" Then ' (SS,7/11/18) replaced CleanRequestQueryString("code") with GetProductCodeQS
GetProductCode = FProductCode
Else
GetProductCode = GetProductCodeQS ' (SS,12/9/12) replaced CleanRequestQueryString("code") with GetProductCodeQS
End If
End Function
' (SS,12/9/12) get product code from query string
Function GetProductCodeQS
' (SS,7/11/18) added UCase to force uppercase, because items were ending up in basket with lowercase product codes
GetProductCodeQS = UCase(CleanRequestQueryString("code"))
End Function
' (SS,25/8/17) similar to GetProductCode except it always returns the value set using SetProductCode, instead of looking at the query string "code"
' returns the product code used by product list, related product, or product code, whichever is the most applicable
' fixes the bug in related products where link was incorrectly using the main product code from the product page for "notify me when back in stock" link
' via call in Function CustomGetStockInfo for HyperFlight, using this new GetActiveProductCode instead of GetProductCode (was which getting code from the query string when not blank) fixes this problem
Function GetActiveProductCode
GetActiveProductCode = FProductCode
End Function
' (SS,22/8/14)
Function GetProductID
GetProductID = FProductID
End Function
' (SS,18/7/11)
Function GetPageName
GetPageName = LCase(CleanRequestQueryString("page"))
End Function
' (SS,18/7/11) returns true is current page is in given comma separated list
Function IsPageName(AList)
Dim LPageName, LResult
LPageName = GetPageName
If LPageName <> "" And InStr(1, "," + AList + ",", "," + LPageName + ",", 1) > 0 Then
LResult = True
Else
LResult = False
End If
IsPageName = LResult
End Function
' (SS,9/9/14)
Function GetCmd
GetCmd = LCase(CleanRequest("cmd"))
End Function
' (SS,9/9/14)
Function IsCmd(AList)
Dim LCmd, LResult
LCmd = GetCmd
If LCmd <> "" And InStr(1, "," + AList + ",", "," + LCmd + ",", 1) > 0 Then
LResult = True
Else
LResult = False
End If
IsCmd = LResult
End Function
' (SS,9/9/14)
Function IsPageNameOrCmd(AList)
IsPageNameOrCmd = IsPageName(AList) Or IsCmd(AList)
End Function
' (SS,17/9/08) used to prefix given link with full store URL when default category used
Function AddStoreURL(ALink)
If HasDefaultCategory Then
AddStoreURL = GetStoreURL + "/" + ALink
Else
AddStoreURL = ALink ' no prefix
End If
End Function
' (SS,10/11/08) AFormName could be frmWorldPay, frmPayPal
' (SS,20/11/08) replaced AFormName with APaymentType
Function GetPaymentSubmitJavascriptLink(APaymentType)
' Dim Result
' Result = ""
' If FGoogleAnalyticsWebPropertyID <> "" And FEcommerceTrackingEnabled Then
' Result = "pageTracker._trackTrans();" + Result
' End If
' If FAdwordsConversionTrackingID <> "" And FAdWordsPurchaseConversionActivated Then
' Result = "itp_gawct();" + Result
' End If
' Result = "javascript:" + Result + "document." + AFormName + ".submit()"
' GetPaymentSubmitJavascriptLink = Result
' (SS,20/11/08) replaced above with following, also added javascript function itp_payment to javascript.js
GetPaymentSubmitJavascriptLink = "javascript:itp_payment('" + APaymentType + "')"
End Function
' (SS,21/7/17) new payment with bank transfer link where customer can choose which bank to pay into
Function GetPaymentBankTranferOptionSubmitLink(ABankTransferOption)
GetPaymentBankTranferOptionSubmitLink = "javascript:itp_payment_banktransfer('" + ABankTransferOption + "')"
End Function
' -------------------------------------------
' Start of web analytics routines
' -------------------------------------------
' (SS,6/3/18) routine called from HTMLHeadEnd to take care of the analytics i.e. Google Analytics and Facebook Pixel
Sub DoWebAnalytics
' only do Google Analytics and Facebook Pixel it it's a product page i.e. products.asp
If ScriptIsProducts Then
IncrementPageViewCount
DoGoogleAnalytics
DoFacebookPixel
' if purchasing conversion was necessary then mark as done, so that it's not repeated for same order
If TrackPurchaseConversion Then
' SendWebLogEmail "###TrackPurchaseConversion###"
TrackPurchaseConversionAddToLog ' add to log
TrackPurchaseConversionDone ' mark as done, to ensure it's not repeated for same order no
End If
End If
End Sub
' (SS,6/3/18) do what needs going at the of the page for web analytics e.g. update the the conversion_tracking_log with payment method
Sub FinaliseWebAnalytics
TrackPurchaseConversionUpdateLog
End Sub
' (SS,10/11/08)
' (SS,27/11/13) asynchronous version of Google Analytics script, placed in header of inc-template-main.asp
' (SS,27/11/13) added AAsync parameter which can be true or false, and modified to do asynchronous as well
' (SS,12/2/15) modified to allow different DoubleClick.Net URL depending on setting
' (SS,3/3/16) modified to use the latest analytics.js for ASync method also added cross domain tracking
' (SS,1/3/18) modified extensively to use new Global Site Tag (gtag.js), removed AAsync parameter, now redundant
' also no longer calls DoGoogleAdWordsConversionTracking which it did when AAsync i.e. when called from Finalise
' now called from HTMLHeadEnd instead of head section in inc-template-main.asp
' Now does both Analytics and AdWords conversion tracking
Sub DoGoogleAnalytics
' immediately exit if no analytics web property ID
If FGoogleAnalyticsWebPropertyID = "" Then Exit Sub
' if purchase conversion applicable then get the conversion amount
Dim LTrackPurchaseConversion, LConversionValue
LTrackPurchaseConversion = TrackPurchaseConversion And FAdwordsConversionTrackingID <> "" And FAdWordsConversionLabelPurchase <> ""
If LTrackPurchaseConversion Then
LConversionValue = GetConversionValue(Session("OrderNoPlaced"))
End If
' track new account conversion, only if label name specified, new account conversion activated and not already done
Dim LTrackNewAccountConversion
LTrackNewAccountConversion = FAdwordsConversionTrackingID <> "" And FAdWordsConversionLabelNewAccount <> "" And FAdWordsNewAccountConversionActivated And Session("AdWordsNewAccountConversionTrackingDone") <> True
' (SS,7/3/18) for AdWords conversion, replaced gtag('event', 'purchase' with gtag('event', 'conversion'
%><!-- Global Site Tag (gtag.js) - Google Analytics and AdWords -->
<script async src="https://www.googletagmanager.com/gtag/js?id=<%=FGoogleAnalyticsWebPropertyID%>"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '<%=FGoogleAnalyticsWebPropertyID%>');
<%If FAdwordsConversionTrackingID <> "" Then%> gtag('config', 'AW-<%=FAdwordsConversionTrackingID%>');<%End If%>
<%If LTrackPurchaseConversion Then%>
gtag('event', 'conversion', {
'send_to': 'AW-<%=FAdwordsConversionTrackingID%>/<%=FAdWordsConversionLabelPurchase%>',
'value': <%=LConversionValue%>,
'currency': 'GBP',
'transaction_id': '<%=Session("OrderNoPlaced")%>'
});
<%End If
' (SS,5/3/18) removed True (AAsync) parameter
' (SS,10/4/10) renamed DoGoogleAnalyticsEcommerce to DoTrackPurchaseEvent and added ttGoogle (this now works for Facebook too)
DoTrackPurchaseEvent ttGoogle
' (SS,5/3/18) following does a new account conversion if label for this is defined
If LTrackNewAccountConversion Then%>
gtag('event', 'sign_up', {
'account_type': 'basic',
'send_to': 'AW-<%=FAdwordsConversionTrackingID%>/<%=FAdWordsConversionLabelNewAccount%>'
});
<%Session("AdWordsNewAccountConversionTrackingDone") = True ' make sure new account conversion is only done once per session
End If%></script>
<!-- End Global Site Tag -->
<%
End Sub
' (SS,9/10/17) Facebook pixel tracking, called from Finalise
' (SS,10/10/17) !!! perhaps in future we need to do the conversion value when returning from payment provider (IsReturnFromPaymentProviderPage)
' this will ensure page is loaded for longer (i.e. not redirecting) and conversion only counted when payment actually made
' (SS,3/11/17) removed the noscript tag, causing missing alt tag error in W3C validator
' (SS,9/4/19) added Facebook event code for tracking ViewContent (i.e. product page view) and AddToCart (i.e. add to basket)
' (SS,10/4/19) added to content_type and content_ids to ViewContent and AddToCart. Also added content_type and contents to Purchase
' For purchase tracking added call to DoTrackPurchaseEvent which handles Facebook as well as Google
' (SS,11/4/19) removed the additional (not necessary I think) <noscript><img> tracking because it was interfering with the Facebook Pixel Helper add-on
' (SS,12/4/19) added events Search, InitiateCheckout, InitiateCheckout, AddPaymentInfo
Sub DoFacebookPixel
' exit if not the product.asp page or Pixel ID not set
' (SS,2/3/18) removed Not ScriptIsProducts Or because this is now done in calling routine i.e. HTMLHeadEnd
If FFacebookPixelID = "" Then Exit Sub
' if purchase conversion activated then get the conversion amount
Dim LTrackConversion, LConversionValue
LTrackConversion = TrackPurchaseConversion ' (SS,2/3/18) TrackPurchaseConversion replaces previous if conditions
If LTrackConversion Then
' (SS,2/3/18) removed following, no longer necessary
' Session("FacebookPurchaseConversionTrackingDone") = True ' so that we only it do it once per session
LConversionValue = GetConversionValue(Session("OrderNoPlaced"))
End If
%>
<!-- Facebook Pixel Code -->
<script>
!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window, document,'script','//connect.facebook.net/en_US/fbevents.js');
fbq('init', '<%=FFacebookPixelID%>');
fbq('track', 'PageView');
<%If IsProductDetailsPage Then%>
fbq('track', 'ViewContent', {
content_ids: ['<%=GetProductCode%>'],
content_type: 'product'
});
<%End If%>
<%If IsAddToBasketPage Then%>
fbq('track', 'AddToCart', {
content_ids: ['<%=GetBasketProductCode%>'],
content_type: 'product'
});
<%End If%>
<%If IsNewSearch Then%>
fbq('track', 'Search', {
search_string: '<%=JavaScriptStrEscape(GetSearch)%>'
});
<%End If%>
<%If IsStartOfCheckout Then%>
fbq('track', 'InitiateCheckout', {
num_items: <%=GetTotalItemsInBasket%>
});
<%End If%>
<%If IsCheckoutFilled Then%>
fbq('track', 'AddPaymentInfo');
<%End If%>
<%If LTrackConversion Then%>
<%DoTrackPurchaseEvent ttFacebook%>
<%End If%>
</script>
<!-- End Facebook Pixel Code -->
<%
' (SS,3/11/17) removed the noscript version because it was causing W3C validator to give error message, replaced < with [, and > with ] to avoid script compile error
'<noscript><img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=[%=FFacebookPixelID%]&ev=PageView&noscript=1" /></noscript>
End Sub
' (SS,27/11/13) added AAsync parameter which can be true or false, and modified to do asynchronous
' (SS,11/04/16) had stopped working when changed over from ga.js to analytics.js on 3/3/16
' was due to not adding ga('require', 'ecommerce'); ga('ecommerce:addTransaction',
' mentioned here https://developers.google.com/analytics/devguides/collection/upgrade/reference/gajs-analyticsjs#snippet
' (SS,31/7/17) added order no existence check to prevent error when no session
' (SS,5/3/18) removed AAsync parameter, converted to use gtag, added Discount line, also corrected value by removing discount from it
' (SS,10/4/19) renamed DoGoogleAnalyticsEcommerce to DoTrackPurchaseEvent to also work for Facebook
' added ATrackType parameter which can be ttGoogle for Google or ttFacebook for Facebook
' (SS,11/4/19) added JavaScriptStrEscape to GetStoreName
Sub DoTrackPurchaseEvent(ATrackType)
If Not FEcommerceTrackingEnabled Or Not TrackPurchaseConversion Then Exit Sub ' (SS,5/3/18) replaced FEcommerceTrackingActivated with TrackPurchaseConversion
Dim LOrderNo
LOrderNo = Session("OrderNoPlaced")
' main transaction details
OpenQuery("SELECT o.*, v.VoucherCode FROM orders o LEFT JOIN vouchers v ON v.VoucherID = o.VoucherID WHERE OrderNo = '" & CleanSQLStr(LOrderNo) & "'")
Dim LSubtotal, LDelivery, LVATIncluded, LVATDeducted, LTax, LDiscount, LVoucherCode
LSubtotal = GetFieldValue("Subtotal")
LDelivery = GetFieldValue("Delivery")
LDiscount = GetFieldValue("Discount") ' (SS,5/3/18) discount held as a negative
LVATIncluded = GetFieldValue("VATIncluded")
LVATDeducted = GetFieldValue("VATDeducted")
LTax = LVATIncluded
LVoucherCode = NB(GetFieldValue("VoucherCode"))
' if prices include VAT then deduct VAT from LSubTotal and LDelivery
If PricesEnteredIncludeVAT Then
LSubtotal = LSubtotal - GetVATAmount(LSubtotal)
LDelivery = LDelivery - GetVATAmount(LDelivery)
LDiscount = LDiscount - GetVATAmount(LDiscount) ' (SS,5/3/18)
End If
LSubtotal = LSubtotal + LDiscount ' (SS,5/3/18) i.e. minus because discount negative
' Format2dpnc makes sure that there are always 2 dp e.g. 10.1 will be 10.10, 5 will be 5.00, also there are no commas for thousands
If ATrackType = ttGoogle Then
%>
gtag('event', 'purchase', {
"transaction_id": "<%=LOrderNo%>",
"affiliation": "<%=JavaScriptStrEscape(GetStoreName)%>",
"value": <%=Format2dpnc(LSubTotal)%>,
"currency": "GBP",
"tax": <%=Format2dpnc(LVATIncluded)%>,
"shipping": <%=Format2dpnc(LDelivery)%>,
"items": [<%
Else ' ttFacebook
%>
fbq('track', 'Purchase', {
content_type: 'product',
value: <%=Format2dpnc(LSubTotal)%>,
currency: 'GBP',
contents: [<%
End If
CloseQuery
' individual item details
' (SS,31/10/18) added GetOrderDetailsFilter to filter out subproducts
OpenQuery("SELECT * FROM orderdetails WHERE OrderNo = '" & CleanSQLStr(LOrderNo) & "'" & GetOrderDetailsFilter & " ORDER BY OrderDetailID")
Dim LCount, LProductCode, LCategory, LGroup, LSubcategory, LPriceEach
' (SS,5/3/18) removed following, no longer used or applicable here
' ClearRemarketingTagProductIDs ' (SS,28/11/13)
LCount = 0
Do While Not EndOfQuery
LCount = LCount + 1
LProductCode = GetFieldValue("ProductCode")
' (SS,5/3/18) removed following, no longer used or applicable here
'AddRemarketingTagProductID(LProductCode) ' (SS,28/11/13)
LPriceEach = GetFieldValue("PriceEach")
' if prices already include VAT then deduct the VAT
If PricesEnteredIncludeVAT Then
LPriceEach = LPriceEach - GetVATAmount(LPriceEach)
End If
' lookup the first category for this product
GetFirstCatSubcatForProduct LProductCode, LGroup, LCategory, LSubcategory
LCategory = Trim(LSubcategory & " - " & LCategory) ' NB. subcategory first because we don't want it to be chopped, don't mind category being chopped
If LCategory = "-" Then LCategory = ""
' (SS,11/4/16) replaced above with following, new Universal Analytics version
' (SS,5/3/18) replaced with new gtag version (Global Site Tag), implemented in GoogleAnalyticsEcommerceAddItem
' (SS,10/4/19) replaced GoogleAnalyticsEcommerceAddItem with TrackPurchaseEventAddItem which also works for Facebook, added ATypeType parameter
TrackPurchaseEventAddItem ATrackType, LCount, LProductCode, GetFieldValue("ProductName"), LCategory, GetFieldValue("Qty"), LPriceEach
NextQueryRecord
Loop
CloseQuery
' (SS,5/3/18) added discount line
If LDiscount <> 0 Then
' (SS,10/4/19) renamed GoogleAnalyticsEcommerceAddItem to TrackPurchaseEventAddItem, added ATrackType so that it also works for Facebook
TrackPurchaseEventAddItem ATrackType, LCount + 1, "DISCOUNT", "Discount Voucher Code: " & LVoucherCode, "Discount", 1, LDiscount
End If
' (SS,5/3/18) close the gtag (or fbq (10/4/19))
%>
]
});
<%
End Sub
' (SS,5/3/18) add ecommerce item to Google Analytics gtag
' (SS,10/4/19) renamed GoogleAnalyticsEcommerceAddItem to TrackPurchaseEventAddItem, added ATrackType to also work for Facebook
' (SS,11/4/19) replaced 2 x SingleQuotedJavascriptString with JavaScriptStrEscape because product name may contain a double quote
Sub TrackPurchaseEventAddItem(ATrackType, ACount, AProductCode, AProductName, ACategory, AQuantity, APriceEach)
' add a comma as a separator if not first item
If ACount > 1 Then
Response.Write(",")
End If
If ATrackType = ttGoogle Then%>
{
"id": "<%=AProductCode%>",
"name": "<%=JavaScriptStrEscape(AProductName)%>",
"category": "<%=JavaScriptStrEscape(ACategory)%>",
"quantity": <%=AQuantity%>,
"price": "<%=Format2dpnc(APriceEach)%>"
}<%
Else%>
{
id: '<%=AProductCode%>',
quantity: <%=AQuantity%>,
item_price: <%=Format2dpnc(APriceEach)%>
}<%
End If
End Sub
' (SS,12/11/08)
Sub ActivateGoogleAdWordsNewAccountConversion
FAdWordsNewAccountConversionActivated = True
End Sub
' (SS,10/10/17) returns conversion value used for Google AdWords and Facebook Pixel
' value returned is the subtotal with discount applied and VAT removed (delivery is ignored)
Function GetConversionValue(AOrderNo)
Dim LResult, LSQL, LSubtotal, LDiscount, LVATIncluded
LResult = "0.00"
LSQL = "SELECT Subtotal, Discount, VATIncluded FROM orders WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'"
If GetSQL3Values(LSQL, LSubtotal, LDiscount, LVATIncluded) Then
LResult = LSubtotal + LDiscount ' discount is negative so we add
' if prices include VAT and VAT was charged (will not be charged for RW orders)
If PricesEnteredIncludeVAT And LVATIncluded > 0 Then LResult = LResult - GetVATAmount(LResult)
LResult = Format2dpnc(LResult) ' convert to sign to 2dp and no commas
End If
GetConversionValue = LResult
End Function
' (SS,5/3/18) keeps a count of the number of main page views
Sub IncrementPageViewCount
If Session("PageViewCount") = "" Then Session("PageViewCount") = 0
Session("PageViewCount") = Session("PageViewCount") + 1
End Sub
' (SS,5/3/18) returns page view count from session variable (also see Sub IncrementPageViewCount)
Function GetPageViewCount
GetPageViewCount = IIf(Session("PageViewCount") = "", 0, Session("PageViewCount"))
End Function
' (SS,2/3/18) returns true if conversion tracking needs to be done
' i.e. when return from payment provider, and order no exists and same order number not done before in this session
Function TrackPurchaseConversion
' (SS,30/4/18) replaced IsReturnFromPaymentProviderPage with IsSuccessfulReturnFromPaymentProviderPage which checks to make sure certain payment providers return successful status i.e. Payzone and EPDQ
TrackPurchaseConversion = IsReturnFromPaymentProviderPage And Session("OrderNoPlaced") <> "" And Session("PurchaseConversionTrackedOrderNo") <> Session("OrderNoPlaced")
End Function
' (SS,2/3/18) marks in session the order no for which conversion tracking has been done so that it's not done again for same order no
Sub TrackPurchaseConversionDone
Session("PurchaseConversionTrackedOrderNo") = Session("OrderNoPlaced")
End Sub
' (SS,5/3/18) logs the purchase conversion tracking to conversion_tracking_log table
Sub TrackPurchaseConversionAddToLog
Const MAX_SIZE = 255
Dim LOrderNo, LURL, LQueryString, LReferrer, LUserAgent, LCookie, LSQL
LOrderNo = Session("OrderNoPlaced")
LURL = Left(Request.ServerVariables("URL"), MAX_SIZE)
LQueryString = Left(Request.ServerVariables("QUERY_STRING"), MAX_SIZE)
LReferrer = Left(Request.ServerVariables("HTTP_REFERER"), MAX_SIZE)
LUserAgent = Left(Request.ServerVariables("HTTP_USER_AGENT"), MAX_SIZE)
LCookie = Request.ServerVariables("HTTP_COOKIE")
' NB. PaymentMethod can't be set here due to it not being set yet in the order record, this is updated in Sub TrackPurchaseConversionUpdateLog later
LSQL = "INSERT INTO conversion_tracking_log SET DateTimeLogged = NOW()" &_
", OrderNo = '" & CleanSQLStr(LOrderNo) & "'" &_
", ConversionValue = '" & GetConversionValue(LOrderNo) & "'" &_
", GrandTotal = '" & CleanSQLStr(GetOrderFieldValue(LOrderNo, "GrandTotal")) & "'" &_
", PageViews = '" & GetPageViewCount & "'" &_
", SessionID = '" & Session.SessionID & "'" &_
", IPAddress = '" & CleanSQLStr(Request.ServerVariables("REMOTE_ADDR")) & "'" &_
", URL = '" & CleanSQLStr(LURL) & "'" &_
", QueryString = '" & CleanSQLStr(LQueryString) & "'" &_
", Referrer = '" & CleanSQLStr(LReferrer) & "'" &_
", UserAgent = '" & CleanSQLStr(LUserAgent) & "'" &_
", Cookie = '" & CleanSQLStr(LCookie) & "'"
ExecuteQuery LSQL
' get the newly inserted ID to use for updating later, i.e. PaymentMethod is added in TrackPurchaseConversionUpdateLog
Session("ConversionTrackingLogID") = GetSQLLastInsertID
End Sub
' (SS,6/3/18) called from Sub Finalise, sets the PaymentMethod which can't be set earlier in TrackPurchaseConversionAddToLog
Sub TrackPurchaseConversionUpdateLog
' if record inserted earlier then update the PaymentMethod
If Session("ConversionTrackingLogID") <> "" Then
ExecuteQuery "UPDATE conversion_tracking_log SET PaymentMethod = '" & CleanSQLStr(GetOrderFieldValue(Session("OrderNoPlaced"), "PaymentMethod")) & "' WHERE ID = " & Session("ConversionTrackingLogID")
Session("ConversionTrackingLogID") = "" ' clear it so that it's not done again unless requested
End If
End Sub
' -------------------------------------------
' End of web analytics routines
' -------------------------------------------
' ----------------------------------- '
' Start of Voucher Code Routines '
' ----------------------------------- '
' (SS,15/5/09) check voucher, return true if valid and discount <> 0, else false with error message in AErrorMessage
Function CheckVoucherCode(AVoucherCode, ByRef AErrorMessage)
CheckVoucherCode = False
AErrorMessage = ""
If AVoucherCode = "" Then Exit Function
If Not IsVoucherCodeValid(AVoucherCode, AErrorMessage) Then Exit Function
' determine the LSubtotal for current basket items, other variables are required by GetOrderTotalsForBasket
Dim LSubtotal, LDiscount, LTotalWeight, LNonWeightPrice, LVATTotal, LDelivery, LGrandTotal, LVATIncluded, LVATDeducted
GetOrderTotalsForBasket FSessionID, LSubtotal, LDiscount, LDelivery, LGrandTotal, LVATIncluded, LVATDeducted, LTotalWeight
' GetDiscountUsingVoucherCode is called by GetOrderTotalsForBasket to return LDiscount
If LDiscount = 0 Then
AErrorMessage = tr("Voucher code") & " '" & AVoucherCode & "' " & tr("results in no discount") ' (SS,12/5/10) removed " for this order"
Else
CheckVoucherCode = True
End If
End Function
' (SS,22/4/09) checks if given voucher code is valid
' also makes sure if single use voucher and usage count is 0 or 1 and used by this order only
' (SS,13/5/09) single usage now checks single usage per customer
' (SS,14/5/09) single usage replaced with voucher type, which can be
' "S" for Single Use, "SC" Single Use for Customer or "M" Multiple Use
' (SS,15/5/09) added ByRef AErrorMessage to return error message when voucher code is invalid
' improved to return useful message
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
' (SS,16/2/15) voucher type can now be "A" for Auto which is also multiple
' (SS,17/2/15) voucher type can now also be "MC" for multiple use per customer
Function IsVoucherCodeValid(AVoucherCode, ByRef AErrorMessage)
IsVoucherCodeValid = False
AErrorMessage = ""
If AVoucherCode = "" Then Exit Function
If IsNull(GetValueFromQuery("VoucherCode", "SELECT * FROM vouchers WHERE VoucherCode = '" & CleanSQLStr(AVoucherCode) & "'")) Then
AErrorMessage = tr("Voucher code") & " '" & AVoucherCode & "' " & tr("is not valid")
Exit Function
End If
Dim LResult, LVoucherType
LResult = False
LVoucherType = GetValueFromQuery("VoucherType", "SELECT * FROM vouchers WHERE VoucherCode = '" & CleanSQLStr(AVoucherCode) & "' AND CURDATE() >= ValidFromDate AND CURDATE() <= ExpiryDate")
If IsNull(LVoucherType) Then
AErrorMessage = tr("Voucher code") & " '" & AVoucherCode & "' " & tr("has expired")
Exit Function
End If
' (SS,17/2/15) moved voucher ID lookup code here from below and added If LVoucherType <> "M"
Dim LVoucherID
If LVoucherType <> "M" Then LVoucherID = GetVoucherIDForVoucherCode(AVoucherCode)
' if voucher found then make sure it's only been used once
If LVoucherType = "S" Or LVoucherType = "SC" Then
' (SS,13/5/09) check single usage for this customer (by CustomerID or email address)
Dim LSQL, LOrderCount, LLastOrderNo
LSQL = "SELECT COUNT(*) AS OrderCount, MAX(OrderNo) AS LastOrderNo FROM orders WHERE VoucherID = " & LVoucherID & " AND Discount <> 0 AND Status <> '" & ORDER_CANCELLED_STATUS & "'"
If LVoucherType = "SC" Then
' (SS,17/2/15) added CleanSQLStr to following two lines to prevent SQL injection, missed from before
LSQL = LSQL + " AND (EmailAddress = '" & CleanSQLStr(Session("Email")) & "'"
If Session("AccountID") <> "" Then LSQL = LSQL + " OR CustomerID = '" & CleanSQLStr(Session("AccountID")) & "'"
LSQL = LSQL & ")" ' close the bracket
End If
OpenQuery2(LSQL)
LOrderCount = GetQueryValue2("OrderCount")
LLastOrderNo = GetQueryValue2("LastOrderNo")
CloseQuery2
If LOrderCount = 1 Then ' used only by current order
LResult = Session("OrderNoPlaced") = LLastOrderNo
ElseIf LOrderCount > 1 Then ' used more than once already
LResult = False
Else ' i.e. not used so far
LResult = True
End If
' (SS,17/2/15) multiple use per customer
ElseIf LVoucherType = "MC" Then
' allow it if email not currently entered, i.e. at basket page
If Session("Email") = "" Then
LResult = True
Else
' set to True if not used by another customer
LResult = GetSQLValue("SELECT COUNT(*) FROM orders WHERE VoucherID = " & LVoucherID & " AND Discount <> 0 AND Status <> '" & ORDER_CANCELLED_STATUS & "' AND EmailAddress <> '" & CleanSQLStr(Session("Email")) & "'") = 0
End If
Else ' i.e. "M" multiple
LResult = True
End If
If LResult = False Then
AErrorMessage = "Voucher code '" & AVoucherCode & "' has already been used"
If LVoucherType = "SC" Then
AErrorMessage = AErrorMessage & " by you"
' (SS,17/2/15)
ElseIf LVoucherType = "MC" Then
AErrorMessage = AErrorMessage & " by another customer"
End If
End If
IsVoucherCodeValid = LResult
End Function
' (SS,16/2/15) returns True if given voucher code is an Auto one
' used to decide whether to apply auto voucher, is existing one isn't auto and is valid then auto voucher code isn't applied
Function IsVoucherCodeAuto(AVoucherCode)
Dim LResult
If AVoucherCode = "" Then
LResult = False
Else
LResult = Not IsNull(GetValueFromQuery("VoucherCode", "SELECT * FROM vouchers WHERE VoucherCode = '" & CleanSQLStr(AVoucherCode) & "' AND VoucherType = 'A'"))
End If
IsVoucherCodeAuto = LResult
End Function
' (SS,16/2/15)
' (SS,19/3/20) added ADelivery and ByRef AFreeDelivery to allow free delivery value to be passed back
Function GetDiscountUsingAutoVoucherCode(ABasketSessionID, ASubtotal, ADelivery, ByRef AFreeDelivery)
Dim LVoucherCodes, LVoucherCodeCount
ReDim LVoucherCodes(-1)
LVoucherCodeCount = 0
' find all auto voucher codes still valid
OpenQuery("SELECT * FROM vouchers WHERE VoucherType = 'A' AND CURDATE() >= ValidFromDate AND CURDATE() <= ExpiryDate")
Do While Not EndOfQuery
ReDim Preserve LVoucherCodes(UBound(LVoucherCodes) + 1)
LVoucherCodes(LVoucherCodeCount) = GetQueryValue("VoucherCode")
LVoucherCodeCount = LVoucherCodeCount + 1
NextQueryRecord
Loop
CloseQuery
' determine the voucher code with maximum discount
Dim i, LDiscount, LMaxDiscount, LMaxDiscountVoucherCode
LMaxDiscount = 0
LMaxDiscountVoucherCode = ""
For i = 0 To UBound(LVoucherCodes)
LDiscount = GetDiscountUsingVoucherCode(LVoucherCodes(i), ABasketSessionID, ASubtotal, ADelivery, AFreeDelivery) ' (SS,19/3/20) added ADelivery, AFreeDelivery
If LDiscount < LMaxDiscount Then ' discount is negative
LMaxDiscount = LDiscount
LMaxDiscountVoucherCode = LVoucherCodes(i)
End If
'Response.Write "Auto voucher code: " & LVoucherCodes(i) & ", Discount: " & LDiscount & BR
Next
'Response.Write "Max auto voucher code: " & LMaxDiscountVoucherCode & ", Max discount: " & LMaxDiscount & BR
SetVoucherCode LMaxDiscountVoucherCode ' save the voucher code or clear it
GetDiscountUsingAutoVoucherCode = LMaxDiscount
End Function
' (SS,23/4/09) must be called with valid VoucherCode
' (SS,19/12/18) modified to handle new MinimumQty field
' (SS,19/3/20) modified to add Free Delivery feature, and also additional range, added ADelivery and ByRef AFreeDelivery parameter
Function GetDiscountUsingVoucherCode(AVoucherCode, ABasketSessionID, ASubtotal, ADelivery, ByRef AFreeDelivery)
' default to zero discount
GetDiscountUsingVoucherCode = 0
AFreeDelivery = False ' (SS,19/3/20) default to no free delivery
' get the voucher values
Dim LMinimumSpend, LMinimumQty, LDiscountAmount, LDiscountPercentage, LCategory, LSubcategory
LDiscountAmount = 0
LDiscountPercentage = 0
' (SS,19/3/20)
Dim LFreeDelivery, LMinimumSpend2, LMinimumQty2, LDiscountAmount2, LDiscountPercentage2, LFreeDelivery2
LFreeDelivery = False
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQuery("SELECT * FROM vouchers WHERE VoucherCode = '" + CleanSQLStr(AVoucherCode) + "'")
If Not EndOfQuery Then
' NZD used because fields are DECIMAL (not FLOAT) and convert to string causing Type Mismatch error
LMinimumSpend = NZD(GetQueryValue("MinimumSpend"))
LMinimumQty = NZL(GetQueryValue("MinimumQty")) ' (SS,19/12/18) MinimumQty is an integer so NZL used instead of NZD
LDiscountAmount = NZD(GetQueryValue("DiscountAmount"))
LDiscountPercentage = NZD(GetQueryValue("DiscountPercentage"))
' (SS,19/3/20)
LFreeDelivery = IntToBool(GetQueryValue("FreeDelivery"))
LMinimumSpend2 = NZD(GetQueryValue("MinimumSpend2"))
LMinimumQty2 = NZL(GetQueryValue("MinimumQty2"))
LDiscountAmount2 = NZD(GetQueryValue("DiscountAmount2"))
LDiscountPercentage2 = NZD(GetQueryValue("DiscountPercentage2"))
LFreeDelivery2 = IntToBool(GetQueryValue("FreeDelivery2"))
LCategory = NB(GetQueryValue("Category"))
LSubcategory = NB(GetQueryValue("Subcategory"))
End If
CloseQuery
' exit with 0 if discount amount and percentage are both zero
' (SS,19/3/20) added LFreeDelivery = False, and also second set of values
If LDiscountAmount = 0 And LDiscountPercentage = 0 And LFreeDelivery = False And LDiscountAmount2 = 0 And LDiscountPercentage2 = 0 And LFreeDelivery2 = False Then Exit Function
' (13/5/09) if list of product codes this voucher is valid for
Dim LProductCodeList
LProductCodeList = VoucherProductCodeList(AVoucherCode)
If LProductCodeList <> "" Then LProductCodeList = "," + LProductCodeList + ","
' determine total value of order, if category specified then only total value for products belonging to this category/subcategory
' (SS,19/12/18) added LDiscountableQty
Dim LProductCode, LInclude, LPriceEach, LQty, LDiscountableTotal, LDiscountableQty
LDiscountableTotal = 0
LDiscountableQty = 0 ' (SS,19/12/18)
' (SS,26/10/18) added False parameter to GetOrderItemsQuery to exclude subproducts
OpenQuery(GetOrderItemsQuery(ABasketSessionID, False))
Do While Not EndOfQuery
LProductCode = GetQueryValue("ProductCode")
If InStr(LProductCodeList, "," + LProductCode + ",") > 0 Then ' (SS,13/5/09)
LInclude = True
ElseIf LSubcategory <> "" Then
LInclude = IsProductInSubcategory(LProductCode, LCategory, LSubcategory)
ElseIf LCategory <> "" Then
LInclude = IsProductInCategory(LProductCode, LCategory)
ElseIf LProductCodeList = "" Then ' (SS,13/5/09)
LInclude = True
Else
LInclude = False
End If
If LInclude Then
LQty = GetQueryValue("Qty")
LDiscountableQty = LDiscountableQty + LQty ' (SS,19/12/18)
' (SS,6/7/11) removed LQty parameter, added True, to look up options price
GetPricingFromProductRecord(True)
LPriceEach = GetProductPrice
LDiscountableTotal = LDiscountableTotal + (LPriceEach * LQty)
End If
NextQueryRecord
Loop
CloseQuery
' (SS,19/3/20) code above moved to separate GetDiscountForRange
Dim LDiscountApplied, LDoDiscountAmount, LDoFreeDelivery
LDiscountApplied = GetDiscountForRange(ASubtotal, LDiscountableTotal, LDiscountableQty, LMinimumSpend, LMinimumQty, LDiscountAmount, LDiscountPercentage, LFreeDelivery, LDoDiscountAmount, LDoFreeDelivery)
Dim LDiscountApplied2, LDoDiscountAmount2, LDoFreeDelivery2
LDiscountApplied2 = GetDiscountForRange(ASubtotal, LDiscountableTotal, LDiscountableQty, LMinimumSpend2, LMinimumQty2, LDiscountAmount2, LDiscountPercentage2, LFreeDelivery2, LDoDiscountAmount2, LDoFreeDelivery2)
' apply whichever is the greater discount, default to nothing first
Dim LDiscount
LDiscount = 0
AFreeDelivery = False
If LDiscountApplied And Not LDiscountApplied2 Then
LDiscount = LDoDiscountAmount
AFreeDelivery = LDoFreeDelivery
ElseIf Not LDiscountApplied And LDiscountApplied2 Then
LDiscount = LDoDiscountAmount2
AFreeDelivery = LDoFreeDelivery2
ElseIf LDiscountApplied And LDiscountApplied2 Then
' work out which is of the two is the highest taking into account the delivery saved if free delivery and use that
Dim LDoDiscountAmountIncDelivery, LDoDiscountAmountIncDelivery2
If LDoFreeDelivery Then
LDoDiscountAmountIncDelivery = LDoDiscountAmount + ADelivery
Else
LDoDiscountAmountIncDelivery = LDoDiscountAmount
End If
If LDoFreeDelivery2 Then
LDoDiscountAmountIncDelivery2 = LDoDiscountAmount2 + ADelivery
Else
LDoDiscountAmountIncDelivery2 = LDoDiscountAmount2
End If
If LDoDiscountAmountIncDelivery >= LDoDiscountAmountIncDelivery2 Then
LDiscount = LDoDiscountAmount
AFreeDelivery = LDoFreeDelivery
Else
LDiscount = LDoDiscountAmount2
AFreeDelivery = LDoFreeDelivery2
End if
End If
GetDiscountUsingVoucherCode = -LDiscount ' return discount as a negative number
End Function
' (SS,19/3/20) moved code here from GetDiscountUsingVoucherCode, can now be called twice to 2 different ranges
Function GetDiscountForRange(ASubtotal, ADiscountableTotal, ADiscountableQty, AMinimumSpend, AMinimumQty, ADiscountAmount, ADiscountPercentage, AFreeDelivery, ByRef ADoDiscountAmount, ByRef ADoFreeDelivery)
GetDiscountForRange = False ' default to false, i.e. not applied
ADoDiscountAmount = 0 ' default to zero amount
ADoFreeDelivery = False ' default to not free delivery
' if nothing set then exit without setting anything
If ADiscountAmount = 0 And ADiscountPercentage = 0 And AFreeDelivery = False Then Exit Function
' (SS,27/01/2010) corrected ">" to ">="
' (SS,19/12/18) added LDiscountableQty >= LMinimumQty
If ADiscountableTotal >= AMinimumSpend And ADiscountableQty >= AMinimumQty Then
Dim LDiscount
LDiscount = Round2dp(ADiscountAmount + (ADiscountableTotal * ADiscountPercentage / 100))
If LDiscount < 0 Then
LDiscount = 0 ' make sure it never goes below zero
ElseIf LDiscount > ASubtotal Then
LDiscount = ASubtotal ' make sure discount is never above the subtotal of order
End If
ADoDiscountAmount = LDiscount ' return the discount
' (SS,19/3/20) if free delivery then set free delivery
If AFreeDelivery Then
ADoFreeDelivery = True
End If
GetDiscountForRange = True ' set to false to indicate it's been applied due to matching range
End If
' *** debug info
'Response.Write "###" & "MinimumSpend: " & AMinimumSpend & ", DiscountPercentage: " & ADiscountPercentage & ", FreeDelivery: " & AFreeDelivery & ", DoDiscountAmount: " & ADoDiscountAmount & ", DoFreeDelivery: " & ADoFreeDelivery & "###" & BR
End Function
' (SS,13/5/09) comma separated list of product codes for given voucher ID(AVoucherCode)
Function VoucherProductCodeList(AVoucherCode)
Dim LResult, LVoucherID
LResult = ""
LVoucherID = GetVoucherIDForVoucherCode(AVoucherCode)
OpenQuery2("SELECT * FROM voucherproducts WHERE VoucherID = " & LVoucherID)
Do While Not EndOfQuery2
LResult = LResult + iif(LResult = "", "", ",") + GetQueryValue2("ProductCode")
NextQueryRecord2
Loop
CloseQuery2
VoucherProductCodeList = LResult
End Function
' (SS,23/4/09) return VoucherID for VoucherCode, used to store VoucherID in orders table
' (SS,21/2/15) GetValueFromQuery was returning Null when voucher code not found, modified to convert this to NULL as a string
' this was occurring when invalid voucher code entered e.g. 'RAD-5' containing actual quotes, when PayPal Express Checkout Used
' which allows an invalid voucher code get past the order creation stage
Function GetVoucherIDForVoucherCode(AVoucherCode)
Dim LResult
If AVoucherCode = "" Then
LResult = Null
Else
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
LResult = GetValueFromQuery("VoucherID", "SELECT VoucherID FROM vouchers WHERE VoucherCode = '" + CleanSQLStr(AVoucherCode) + "'")
End If
' (SS,21/2/15) NB #LResult = Null' doesn't work, have to use IsNull(LResult) function to detect Null
If IsNull(LResult) Then LResult = "NULL" ' convert to null as a string for MySQL use
GetVoucherIDForVoucherCode = LResult
End Function
' (SS,19/6/09) returns VoucherCode for VoucherID used for direct payments to restore Voucher in the new secure session
Function GetVoucherCodeForVoucherID(AVoucherID)
If NB(AVoucherID) = "" Then
GetVoucherCodeForVoucherID = ""
Else
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
GetVoucherCodeForVoucherID = GetValueFromQuery("VoucherCode", "SELECT VoucherCode FROM vouchers WHERE VoucherID = '" & CleanSQLStr(AVoucherID) & "'")
End If
End Function
' (SS,22/4/09) returns true if given product code is in given category, used in voucher code discount calculation
Function IsProductInCategory(AProductCode, ACategory)
IsProductInCategory = GetValueFromQuery("RecordCount", "SELECT COUNT(*) AS RecordCount FROM productcategories WHERE ProductCode = '" + CleanSQLStr(AProductCode) + "' AND Category = '" + CleanSQLStr(ACategory) + "'") > 0
End Function
' (SS,22/4/09) returns true if given product code is in given category & subcategory, used in voucher code discount calculation
Function IsProductInSubcategory(AProductCode, ACategory, ASubcategory)
IsProductInSubcategory = GetValueFromQuery("RecordCount", "SELECT COUNT(*) AS RecordCount FROM productcategories WHERE ProductCode = '" + CleanSQLStr(AProductCode) + "' AND Category = '" + CleanSQLStr(ACategory) + "' AND Subcategory = '" + CleanSQLStr(ASubcategory) + "'") > 0
End Function
' --------------------------------- '
' End of Voucher Code Routines '
' --------------------------------- '
' (SS,18/5/09) call to ShowProductDetail now goes through here
' this allows other sections to be shown at the end or instead
' (SS,30/6/11) added AProductID
Sub ShowTheProductDetail(AProductID, AProductCode, AProductName, AProductDesc, ADimensions, ASalePrice, AStdPrice, ANumInStock, ANew, ACategory, ASubcategory, AShowLarge, ALargePicWidth, ALargePicHeight)
Dim LShowDescription
LShowDescription = InProductDescription
' (SS,17/6/11) added And Not ProductPageTabs, show that reviews only show in ShowProductDetail when product page tabs are enabled
If InShowReviews And Not ProductPageTabs Then
ShowReviews(AProductCode)
ElseIf InWriteReview And Not ProductPageTabs Then
WriteReview(AProductCode)
ElseIf InStockNotify Then
StockNotify(AProductCode)
Else
' (SS,6/3/18) removed following due to migration to gtag.js (Global Site Tag)
'SetRemarketingTagTotalValue GetProductPrice ' (SS,28/11/13)
' (SS,30/6/11) added AProductID
ShowProductDetail AProductID, AProductCode, AProductName, AProductDesc, ADimensions, ASalePrice, AStdPrice, ANumInStock, ANew, ACategory, ASubcategory, AShowLarge, ALargePicWidth, ALargePicHeight, LShowDescription
ShowRelatedProducts(AProductCode) ' (SS,13/5/11) NB will return without doing anything if already called from without the product detail template
End If
' (SS,8/5/09) only show if in product description (i.e. not review)
' (SS,13/5/11) NB will return without doing anything if already called from product detail template
If InProductDescription Then ShowViewOtherProducts(AProductCode)
End Sub
' (SS,13/5/11) code moved here from ShowTheProductDetail, may also be called from inc-template-product-detail.asp
' (SS,14/3/12) added code to show related category if set in product attribute, allows miscellaneous link to be added to categories without listing related products
Sub ShowViewOtherProducts(AProductCode)
' (SS,13/5/11) prevent it from being called twice
If FViewOtherProductsCalled Then Exit Sub
FViewOtherProductsCalled = True
' list all the categories this product belongs to '
ShowProductDetailViewOtherProductsHeader
OpenQuery(GetCategoryListSQL(AProductCode))
Do While Not EndOfQuery
ShowProductDetailViewOtherProductsItem GetFieldValue("GroupName"), GetFieldValue("Category"), GetFieldValue("Subcategory")
NextQueryRecord
Loop
CloseQuery
' (SS,14/3/12) added following for related category held in product attributes
Dim LAttributeValue
LAttributeValue = GetProductAttributeByName(GetProductIDForProductCode(AProductCode), "Related Category/Subcategory")
If LAttributeValue <> "" Then
Dim LPos, LGroup, LCategory, LSubcategory
' (SS,15/3/12) changed separated from forwardslash to backslash and added guillemet (forward arrow)
LPos = InStr(LAttributeValue, "\")
If LPos = 0 Then LPos = InStr(LAttributeValue, "�")
If LPos > 0 Then
LCategory = Trim(Left(LAttributeValue, LPos - 1))
LSubcategory = Trim(Mid(LAttributeValue, LPos + 1, Len(LAttributeValue)))
Else
LCategory = Trim(LAttributeValue)
LSubcategory = ""
End If
LGroup = GetGroupForCategory(LCategory)
ShowProductDetailViewOtherProductsItem LGroup, LCategory, LSubcategory
End If
ShowProductDetailViewOtherProductsFooter
End Sub
' (SS,17/2/16) returns Brand and MPN for given product, False is returns if not found with values set to blank
Function GetBrandAndMPN(AProductID, AProductCode, ByRef ABrand, ByRef AMPN)
Dim LResult
LResult = GetSQL2Values("SELECT BrandName, MPN FROM products LEFT JOIN brands ON brands.BrandID = products.BrandID WHERE ProductID = '" & CleanSQLStr(AProductID) & "'", ABrand, AMPN)
If LResult Then
' use product code for MPN if blank
If AMPN = "" Then
AMPN = AProductCode
End If
Else
ABrand = ""
AMPN = ""
End If
GetBrandAndMPN = LResult
End Function
' --------------------------------- '
' Start of Product Review Routines '
' --------------------------------- '
' (SS,1/5/09) returns 0 if no reviews, else number containing 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5 or 5 depending on rating
' (SS,22/1/19) now rounds to 1 dp
Function GetReviewRatingAndCount(AProductCode, ByRef AReviewCount)
Dim LSQL, LRating ' (SS,22/1/19) removed LFraction
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
' (SS,22/1/19) added ROUND( ,1) to round to 1 dp
LSQL = "SELECT ROUND(AVG(ProductRating), 1) AS AverageRating, COUNT(*) AS ReviewCount FROM productreviews WHERE ProductCode = '" & CleanSQLStr(AProductCode) & "' AND ShowOnSite = True"
'if product just has comment it shows
'"1 review" and no image on product listing, on product detail it has image with "NOT YET RATED"
'e.g. http://www.ebuyer.com/store/Components/cat/Graphics-Cards-Nvidia
'http://www.ebuyer.com/product/160628
OpenQuery2(LSQL)
LRating = NZD(GetQueryValue2("AverageRating"))
AReviewCount = GetQueryValue2("ReviewCount")
CloseQuery2
' round to nearest 0.5
' e.g. >= 0.75 becomes 1, >= 0.25 becomes 0.5, else 0
' (SS,22/1/19) removed following, now rounded in SQL to 1 dp
'LFraction = LRating - Int(LRating)
'LRating = Int(LRating)
'If LFraction >= 0.75 Then
' LRating = LRating + 1
'ElseIf LFraction >= 0.25 Then
' LRating = LRating + 0.5
'End If
GetReviewRatingAndCount = LRating
End Function
' (SS,17/1/13) replaced & with & to reduce HTML errors in http://validator.w3.org
Function GetShowReviewsLink(AProductCode)
' (SS,19/7/17) added #reviews to the end to go to the correct position (i.e. anchor)
GetShowReviewsLink = AddStoreURL(GetScriptName + "?code=" + AProductCode + "&cmd=showreviews#reviews")
End Function
' (SS,17/1/13) replaced & with & to reduce HTML errors in http://validator.w3.org
Function GetWriteReviewLink(AProductCode)
GetWriteReviewLink = AddStoreURL(GetScriptName + "?code=" + AProductCode + "&cmd=writereview")
End Function
' (SS,28/9/12) used in review request emails
' (SS,17/1/13) replaced & with & to reduce HTML errors in http://validator.w3.org
Function GetWriteReviewFullLink(AProductCode)
GetWriteReviewFullLink = GetStoreURL + "/" + "products.asp?code=" + AProductCode + "&cmd=writereview"
End Function
Function InProductDescription
InProductDescription = Not (InShowReviews Or InWriteReview Or InStockNotify)
End Function
Function InShowReviews
InShowReviews = CleanRequestQueryString("cmd") = "showreviews"
End Function
Function InWriteReview
InWriteReview = CleanRequestQueryString("cmd") = "writereview"
End Function
' (SS,17/6/11) returns given class suffixed by "-active" when second parameter is True, else trues class with "-active"
Function GetActiveClass(AClass, AActive)
Dim LResult
If AActive Then
LResult = AClass + "-active"
Else
LResult = AClass
End If
GetActiveClass = LResult
End Function
' (SS,17/6/11) returns given text with href added if ADoAdd True
Function AddHRefIfTrue(AText, AHRef, ADoAdd)
Dim LResult
If ADoAdd Then
LResult = "<a href=""" & AHRef & """>" & AText & "</a>"
Else
LResult = AText
End If
AddHRefIfTrue = LResult
End Function
' (SS,13/9/14) replaced ReplaceNewLinesWithBR with ParagraphiseText
Sub ShowReviews(AProductCode)
Dim LRecordCount, LCount, LHeaderShown, LReviewText, LReviewerName, LProductName, LProductURL
LProductName = GetProductName(AProductCode)
LProductURL = GetProductLink(AProductCode)
' (SS,12/5/10) replaced '%D %b %Y, %l:%i %p' with GetLanguageDateFormat(True)
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQuery("SELECT *, DATE_FORMAT(ReviewDateTime, '" & GetLanguageDateFormat(True) & "') AS FormattedDateTime FROM productreviews WHERE ProductCode = '" + CleanSQLStr(AProductCode) + "' AND ShowOnSite = 1 ORDER BY ReviewDateTime DESC")
LRecordCount = GetQueryRecordCount
LHeaderShown = False
LCount = 0
Do While Not EndOfQuery
LCount = LCount + 1
SetAtFirstOrLastProduct LCount, LRecordCount
If Not LHeaderShown Then
ShowReviewHeader AProductCode, LProductName, LProductURL
LHeaderShown = True
End If
LReviewerName = GetQueryValue("ReviewerName")
If LReviewerName = "" Then LReviewerName = "Anonymous" ' (SS,15/5/09)
'LReviewText = ReplaceNewLinesWithBR(GetQueryValue("ReviewText"))
' (SS,13/9/14) replaced above with following, does a much better jobs using paragraphs, with more compact spacing (i.e. <p>'s instead of <br>'s and removing of starting and trailing newlines
LReviewText = ParagraphiseText(GetQueryValue("ReviewText"))
ShowReviewItem GetQueryValue("ReviewType"), GetQueryValue("FormattedDateTime"), GetQueryValue("ReviewTitle"), GetQueryValue("ProductRating"), LReviewText, LReviewerName
NextQueryRecord
Loop
CloseQuery
' (SS,19/7/17) if not shown i.e. no reviews we still need to show to show the write review button
If Not LHeaderShown Then
ShowReviewHeader AProductCode, LProductName, LProductURL
LHeaderShown = True
End If
If LHeaderShown Then ShowReviewFooter AProductCode, LProductName, LProductURL
End Sub
' (SS,15/5/09) added review email
Sub WriteReview(AProductCode)
Dim LSubmitting, LShowForm, LProductName, LProductURL
LSubmitting = CleanRequestForm("Submit") <> ""
LShowForm = True
LProductName = GetProductName(AProductCode)
LProductURL = GetProductLink(AProductCode)
' if submitting
If LSubmitting Then
Session("ReviewType") = CleanRequestForm("ReviewType")
If Session("ReviewType") = tr("-SELECT-") Then Session("ReviewType") = ""
Session("ReviewTitle") = Trim(CleanRequestForm("ReviewTitle"))
Session("ProductRating") = CleanRequestForm("ProductRating")
If Session("ProductRating") = tr("-SELECT-") Then Session("ProductRating") = ""
Session("ReviewerName") = Trim(CleanRequestForm("ReviewerName"))
Session("ReviewerEmail") = Trim(CleanRequestForm("ReviewerEmail"))
Session("ReviewText") = Trim(CleanRequestForm("ReviewText"))
Else ' default values
Session("ReviewType") = tr("Review")
Session("ReviewTitle") = ""
Session("ProductRating") = "5 " + tr("stars")
Session("ReviewerName") = ""
Session("ReviewerEmail") = ""
Session("ReviewText") = ""
End If
If LSubmitting Then
If CheckReviewDetails Then ' here because we don't want it called when LSubmitting is False
SaveReview(AProductCode)
SendReviewReceivedEmail(AProductCode) ' (SS,15/5/09)
ShowReviewThankYou AProductCode, LProductName, LProductURL
LShowForm = False
End If
End If
If LShowForm Then ShowReviewEntryForm gsErrorMessage, GetWriteReviewLink(AProductCode), AProductCode, LProductName, LProductURL
End Sub
Function CheckReviewDetails
Dim LError
LError = ""
If Session("ReviewType") = "" Then LError = LError + IIf(LError = "", "", ", ") + "Review Type" ' (SS,13/5/10) this will never occur
If Session("ReviewType") = tr("Review") And Session("ProductRating") = "" Then LError = LError + IIf(LError = "", "", ", ") + tr("Product Rating")
If Session("ReviewText") = "" Then LError = LError + IIf(LError = "", "", ", ") + tr("Review")
' (SS,23/3/12) added following, email now has to be entered
If Session("ReviewerEmail") = "" Then LError = LError + IIf(LError = "", "", ", ") + tr("Email Address")
If LError <> "" Then LError = tr("Following fields must be filled in") + ": " + LError + "."
' If Session("ReviewerEmail") <> "" And Not IsValidEmailAddress(Session("ReviewerEmail")) Then LError = LError + IIf(LError = "", "", "<br>") + tr("Valid Email address must be entered") + "."
' (SS,23/3/12) removed above, replaced with following which does a check to see if email address has placed an order
If Session("ReviewerEmail") <> "" Then
If Not EmailAddressHasOrdered(Session("ReviewerEmail")) Then LError = LError + IIf(LError = "", "", "<br>") + tr("No completed order found for the email address entered") + "."
End If
' (SS,23/3/12) following removed because it's no longer prevented spam, even after further distorting the image
' hoping that above method checking for completed order with email address does a better job
' If Not SecurityOK(False) Then LError = LError + IIf(LError = "", "", "<br>") + tr("Security Code is incorrect, please retype") + "."
gsErrorMessage = LError
CheckReviewDetails = (LError = "")
End Function
Sub SaveReview(AProductCode)
Dim LReviewType, LProductRating
If Session("ReviewType") = tr("Review") Then
LReviewType = "R"
LProductRating = Left(Session("ProductRating"), 1) ' i.e. ignore the "star(s)" word
Else
LReviewType = "C"
LProductRating = "Null"
End If
' (SS,12/6/12) added CleanSQLStr before AProductCode to prevent SQL injection
' (SS,10/6/14) replaced ShowOnSite=0 with "ShowOnSite=" & BoolToInt(ProductsShowReviewsImmediately)
ExecuteQuery("INSERT INTO productreviews SET ReviewDateTime=NOW(), ProductCode='" & CleanSQLStr(AProductCode) & "', ReviewType='" & LReviewType & "', ReviewTitle='" & CleanSQLStr(Session("ReviewTitle")) & "', ReviewerName='" & CleanSQLStr(Session("ReviewerName")) & "', ReviewerEmail='" & CleanSQLStr(Session("ReviewerEmail")) & "', ReviewText='" & CleanSQLStr(Session("ReviewText")) & "', ProductRating=" & LProductRating & ", ShowOnSite=" & BoolToInt(ProductsShowReviewsImmediately))
End Sub
' (SS,15/5/09)
' (SS,19/3/10) no need to translate this because it's only sent to the store
' (SS,17/3/20) changed from email from FEmailContactFrom to reviewer's email address
Sub SendReviewReceivedEmail(AProductCode)
Dim LSubject, LBody
LSubject = "Review received by " + GetStoreName
LBody =_
"Review Type : " & Session("ReviewType")& NL &_
"Product Code : " & AProductCode & NL &_
"Review Title : " & Session("ReviewTitle") & NL &_
"Product Rating : " & Session("ProductRating") & NL &_
"Reviewer Name : " & Session("ReviewerName") & NL &_
"Reviewer Email : " & Session("ReviewerEmail") & NL &_
"Review Text :" & NL & Session("ReviewText")
'SendEmail FEmailContactTo, FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom, LSubject, LBody, False
' (SS,17/3/20) replaced FEmailContactFrom with Session("ReviewerEmail") to make it easier to reply to a reviewer's email (requested by Neil)
SendEmail FEmailContactTo, FEmailContactBcc1, FEmailContactBcc2, Session("ReviewerEmail"), LSubject, LBody, False
End Sub
' (SS,13/5/10)
Function GetReviewCombo
GetReviewCombo = GetListCombo("ReviewType", tr("-SELECT-") & NL & tr("Review") & NL & tr("Comment"), Session("ReviewType"), False)
End Function
' (SS,15/9/17) added following for short reviews in product description
Sub ShowProductReviewsInDesc(AProductCode, AMaxReviews)
Dim LRecordCount, LReviewText, LReviewerName
OpenQuery("SELECT *, DATE_FORMAT(ReviewDateTime, '%e %M %Y') AS FormattedDateTime FROM productreviews WHERE ProductCode = '" + CleanSQLStr(AProductCode) + "' AND ShowOnSite = 1 AND ReviewType = 'R' ORDER BY ReviewDateTime DESC LIMIT " & AMaxReviews)
LRecordCount = GetQueryRecordCount
If LRecordCount > 0 Then ShowProductReviewsInDescHeader
Do While Not EndOfQuery
LReviewerName = GetQueryValue("ReviewerName")
If LReviewerName = "" Then LReviewerName = "Anonymous"
LReviewText = ParagraphiseText(GetQueryValue("ReviewText"))
ShowProductReviewsInDescItem GetQueryValue("FormattedDateTime"), GetQueryValue("ReviewTitle"), GetQueryValue("ProductRating"), LReviewText, LReviewerName
NextQueryRecord
Loop
CloseQuery
If LRecordCount > 0 Then ShowProductReviewsInDescFooter
End Sub
' (SS,24/10/19) returns overall review count and average rating from all reviews including disabled products, only from those chosen to show on site
Function GetReviewAggregateRating(ByRef AReviewCount, ByRef AAverageRating)
Dim LResult, LSQL
LSQL = "SELECT COUNT(*) AS ReviewCount, ROUND(AVG(ProductRating), 2) AS AverageRating" &_
" FROM productreviews pr" &_
" INNER JOIN products p ON p.ProductCode = pr.ProductCode" &_
" WHERE ShowOnSite AND ReviewType = 'R'"
GetReviewAggregateRating = GetSQL2Values(LSQL, AReviewCount, AAverageRating)
End Function
' --------------------------------- '
' End of Product Review Routines '
' --------------------------------- '
' ------------------------------------ '
' Start of Stock Notification Routines '
' ------------------------------------ '
' (SS,15/5/09)
Function GetStockNotificationLink(AProductCode)
GetStockNotificationLink = AddStoreURL(GetScriptName + "?code=" + AProductCode + "&cmd=stocknotify")
' for example see http://www.sweetwater.com/store/detail/Mboxto003RFact/notify
End Function
' (SS,14/2/12) special version of GetStockNotificationLink with option added
Function GetStockNotificationWithOptionLink(AProductCode, AOption)
GetStockNotificationWithOptionLink = AddStoreURL(GetScriptName + "?code=" + AProductCode + "&option=" + URLEncode(AOption) + "&cmd=stocknotify")
End Function
Function InStockNotify
InStockNotify = CleanRequestQueryString("cmd") = "stocknotify"
End Function
' (SS,14/2/12) added Option, to be notified when particular option comes into stock
Sub StockNotify(AProductCode)
Dim LSubmitting, LShowForm, LProductName, LProductURL, LOption
LSubmitting = CleanRequestForm("Submit") <> ""
LShowForm = True
' if submitting
If LSubmitting Then
Session("StockNotifyName") = Trim(CleanRequestForm("StockNotifyName"))
Session("StockNotifyEmail") = Trim(CleanRequestForm("StockNotifyEmail"))
Else ' default values
Session("StockNotifyName") = ""
Session("StockNotifyEmail") = ""
End If
LProductName = GetProductName(AProductCode)
LProductURL = GetProductLink(AProductCode)
' (SS,14/2/12)
LOption = Trim(CleanRequest("option"))
If LOption <> "" Then LProductName = LProductName + " (" + LOption + ")"
If LSubmitting Then
If CheckStockNotifyDetails Then ' here because we don't want it called when LSubmitting is False
SaveStockNotification AProductCode, LOption ' (SS,14/2/12) added LOption
SendStockNotificationReceivedEmail AProductCode, LOption ' (SS,14/2/12) added LOption
ShowStockNotificationThankYou AProductCode, LProductName, LProductURL
LShowForm = False
End If
End If
If LShowForm Then
' (SS,14/2/12) modified to handle LOption
Dim LStockNotificationLink
If LOption = "" Then
LStockNotificationLink = GetStockNotificationLink(AProductCode)
Else
LStockNotificationLink = GetStockNotificationWithOptionLink(AProductCode, LOption)
End If
ShowStockNotificationForm gsErrorMessage, LStockNotificationLink, AProductCode, LProductName, LProductURL
End If
End Sub
Function CheckStockNotifyDetails
Dim LError
LError = ""
If Session("StockNotifyName") = "" Then LError = LError + IIf(LError = "", "", ", ") + "Name"
If Session("StockNotifyEmail") = "" Then LError = LError + IIf(LError = "", "", ", ") + "Email"
If LError <> "" Then LError = tr("Following fields must be filled in") + ": " + LError + "."
If Session("StockNotifyEmail") <> "" And Not IsValidEmailAddress(Session("StockNotifyEmail")) Then LError = LError + IIf(LError = "", "", "<br>") + "Valid Email address must be entered."
If Not SecurityOK(False) Then LError = LError + IIf(LError = "", "", "<br>") + tr("Security Code is incorrect, please retype") + "."
gsErrorMessage = LError
CheckStockNotifyDetails = (LError = "")
End Function
' (SS,14/2/12) added AOption parameter and modified query to save Option
' (SS,18/3/19) added size limit for 50 for name and 100 for email using Left function prevent "Data too long for column 'Name'" MySQL error
Sub SaveStockNotification(AProductCode, AOption)
' (SS,12/6/12) added CleanSQLStr before AProductCode to prevent SQL injection
ExecuteQuery("INSERT INTO stocknotifications SET ProductCode='" & CleanSQLStr(AProductCode) & "', ProductOption='" & CleanSQLStr(AOption) & "', Name='" & Left(CleanSQLStr(Session("StockNotifyName")), 50) & "', Email='" & Left(CleanSQLStr(Session("StockNotifyEmail")), 100) & "', DateTimeAdded=NOW()")
End Sub
' (SS,19/3/10) no need to translate this because it's only sent to the store
' (SS,14/2/12) added AOption parameter, Option shown if not blank
' (SS,24/8/17) added Num in Stock to help debug an issue, also changed to HTML to make it look tidier
Sub SendStockNotificationReceivedEmail(AProductCode, AOption)
Dim LSubject, LBody
LSubject = "Stock Notification Request received by " + GetStoreName & " - " & GetSQLLastInsertID ' (SS,14/2/12) added GetSQLLastInsertID
' (SS,13/1/11) replaced GetProductName with GetProductNameNoHTMLEncode, to prevent "&" encoding as "&"
LBody =_
"Product Code: " & AProductCode & NL &_
"Product Name: " & GetProductNameNoHTMLEncode(AProductCode) & NL
If AOption <> "" Then LBody = LBody & "Option : " & AOption & NL ' (SS,14/2/12)
LBody = LBody & "Num in Stock: " & GetFieldForProduct(AProductCode, "NumInStock") & NL ' (SS,24/8/17) added for debugging purpose after noticing some buyers were sending notification requests when in stock for HF
LBody = LBody &_
"Name : " & Session("StockNotifyName") & NL &_
"Email : " & Session("StockNotifyEmail")
' (SS,24/8/17)
LBody = LBody & NL & "Referrer: " & CleanRequestForm("Referrer")
LBody = LBody & NL & "URL: " & Request.ServerVariables("HTTP_REFERER")
' (SS,24/8/17) converted above to a HTML table
LBody = GetTextAsHTMLTable(LBody, ": ", True, True, "right")
SendEmail FEmailContactTo, FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom, LSubject, LBody, True ' (SS,24/8/17) changed last para False to True to make it HTML
End Sub
' (SS,19/3/10) added translations using tr and trx
' (SS,14/2/12) added Option
' (SS,14/3/12) modified to work in staggered mode, so that the maximum notifications sent match the amount in stock
' the rest are delayed until the next day
Sub SendStockNotificationEmails
Dim LStaggered, LStaggerDelayHours, LSQL, LWhereStaggered, LHTML
' (SS,27/4/12) delete duplicates, keeping the oldest
StockNotificationsRemoveDuplicates
' (SS,14/3/12) staggered mode enabled if greater than 0, normally it'll be 23 hours
LStaggerDelayHours = FBackInStockNotificationsStaggeredDelay
LStaggered = LStaggerDelayHours > 0
If LStaggered Then
LWhereStaggered = "AND (DelayUntil IS NULL OR DelayUntil <= NOW()) "
Else
LWhereStaggered = ""
End If
If Not ProductOptionsEnabled Then
' (SS,14/3/12) added NumInStock and LWhereStaggered
LSQL = "SELECT stocknotifications.*, NumInStock FROM stocknotifications " +_
"INNER JOIN products ON products.ProductCode = stocknotifications.ProductCode " +_
"WHERE DateTimeNotified IS NULL AND NumInStock > 0 " + LWhereStaggered +_
"ORDER BY ProductCode, DateTimeAdded"
Else ' (SS,14/2/12) following does the selection for sites where ProductOption is used, above is how it used to be
' (SS,14/3/12) added ProductOption to order by, also added LWhereStaggered, NumInStock and pov.NumInStock
' (SS,27/4/12) added LWhereStaggered which I had missed from the first query before the UNION ALL
LSQL = "SELECT sn.*, NumInStock FROM stocknotifications sn " +_
"INNER JOIN products ON products.ProductCode = sn.ProductCode " +_
"WHERE DateTimeNotified IS NULL AND NumInStock > 0 AND ProductOption = '' " + LWhereStaggered +_
"UNION ALL " + _
"SELECT sn.*, pov.NumInStock FROM stocknotifications sn " +_
"INNER JOIN products p ON p.ProductCode = sn.ProductCode " +_
"INNER JOIN product_option_values pov ON pov.ProductID = p.ProductID AND pov.OptionValue = sn.ProductOption " +_
"WHERE DateTimeNotified IS NULL AND pov.NumInStock > 0 AND ProductOption <> '' " + LWhereStaggered +_
"ORDER BY ProductCode, ProductOption, DateTimeAdded"
End If
OpenQuery(LSQL)
LHTML = StockNotificationHeader ' (SS,14/3/12) code moved to new routine
Dim LCount, LID, LProductCode, LOption, LNotificationCount ' (SS,14/3/12) LCount renamed to LNotificationCount
Dim LPrevProductCode, LPrevOption, LProductOptionCount, LWhere, LMaximumReached ' (SS,14/3/12)
LCount = 0
LNotificationCount = 0
LPrevProductCode = ""
LPrevOption = ""
Do While Not EndOfQuery
LCount = LCount + 1 ' (SS,14/3/12)
LID = GetQueryValue("ID")
LProductCode = GetQueryValue("ProductCode")
' (SS,14/2/12)
If ProductOptionsEnabled Then
LOption = GetQueryValue("ProductOption")
Else
LOption = ""
End If
' (SS,14/3/12)
If (LPrevProductCode <> LProductCode) Or (LPrevOption <> LOption) Then
LProductOptionCount = 0
LMaximumReached = False
End If
LProductOptionCount = LProductOptionCount + 1
If LStaggered Then
' if no notifications sent for this product/option exceeds number in stock then don't send any more notifications for this product/option
' (SS,27/4/12) added "* FBackInStockNotificationsStockMultiplier" to allow multiple emails to be sent per stock item, default is 1
If LProductOptionCount > (GetQueryValue("NumInStock") * FBackInStockNotificationsStockMultiplier) Then
LMaximumReached = True
' delay all the notifications that haven't yet been sent for product/option
If ProductOptionsEnabled Then
LWhere = " AND ProductOption = '" + CleanSQLStr(LOption) + "'"'
Else
LWhere = ""
End If
LSQL = "UPDATE stocknotifications SET DelayUntil = NOW() + INTERVAL " & LStaggerDelayHours & " HOUR WHERE DateTimeNotified IS NULL AND ProductCode = '" & CleanSQLStr(LProductCode) + "'" + LWhere
ExecuteQuery(LSQL)
End If
End If
LHTML = LHTML & StockNotificationRow(LCount, LID, LProductCode, LOption, LProductOptionCount, IIf(LMaximumReached, "Delayed " & LStaggerDelayHours & " hours", "Sent"))
' (SS,14/3/12) only send if maximum hasn't been reached
If Not LMaximumReached Then
' (SS,14/3/12) code below moved to new routine, following sends the email and marks it as send
SendStockNotificationEmail LID, LProductCode, LOption
LNotificationCount = LNotificationCount + 1
End If
LPrevProductCode = LProductCode
LPrevOption = LOption
NextQueryRecord
Loop
CloseQuery
LHTML = LHTML & StockNotificationFooter(LNotificationCount) ' (SS,14/3/12) code moved to new routine
' (SS,14/3/12) email the results of this process, i.e. the table show on screen
EmailSendStockNotificationEmailsResult LHTML
End Sub
' (SS,27/4/12) remove duplicate notifications, keeping the oldest
Sub StockNotificationsRemoveDuplicates
Dim LSQL, LSQL2
LSQL = "SELECT Email, ProductCode, ProductOption, COUNT(*) AS NotificationCount, MIN(ID) AS LowestID FROM stocknotifications " +_
"WHERE DateTimeNotified IS NULL " +_
"GROUP BY Email, ProductCode, ProductOption " +_
"HAVING NotificationCount > 1"
OpenQuery LSQL
Do While Not EndOfQuery
LSQL2 = "DELETE FROM stocknotifications WHERE Email = '" & CleanSQLStr(GetQueryValue("Email")) & "' AND ProductCode = '" & CleanSQLStr(GetQueryValue("ProductCode")) & "' AND ProductOption = '" & CleanSQLStr(GetQueryValue("ProductOption")) & "' AND ID > " & GetQueryValue("LowestID")
ExecuteQuery LSQL2
NextQueryRecord
Loop
CloseQuery
End Sub
' (SS,14/3/12) email the results of the send notifications emails process
Sub EmailSendStockNotificationEmailsResult(ABody)
Dim LSubject
LSubject = "Back in Stock Notification Emails Result for " + GetStoreName + " on " & Now
SendEmail FEmailContactTo, FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom, LSubject, ABody, True
End Sub
' (SS,14/3/12) from SendStockNotificationEmails above, returns the HTML as well as outputting using Response.Write
' added extra columns for No, No in Stock, Product Count, Result
' (SS,15/3/12) added NL to end of each HTML line because emailer is added a space after 1000 characters
Function StockNotificationHeader
Dim LResult
LResult = "<table border=1 cellspacing=0>" & NL
LResult = LResult & "<tr><td colspan=""" & IIf(ProductOptionsEnabled, 9, 8) & """>" & "Back in Stock Notifications" & "</td></tr>" & NL ' (SS,14/3/12) replaced 4 with IIf(ProductOptionsEnabled(9, 8)
LResult = LResult & "<tr>" & NL
LResult = LResult & "<td>No</td>" & NL
LResult = LResult & "<td>ID</td>" & NL
LResult = LResult & "<td>Product Code</td>" & NL
If ProductOptionsEnabled Then LResult = LResult & "<td>Option</td>" & NL ' (SS,14/2/12)
LResult = LResult & "<td>Email</td>" & NL
LResult = LResult & "<td>Product Name</td>" & NL
LResult = LResult & "<td>No in Stock</td>" & NL
LResult = LResult & "<td>Count</td>" & NL
LResult = LResult & "<td>Result</td>" & NL
LResult = LResult & "</tr>" & NL
Response.Write LResult
StockNotificationHeader = LResult
End Function
' (SS,14/3/12) from SendStockNotificationEmails above, returns the HTML as well as outputting using Response.Write
Function StockNotificationFooter(ACount)
Dim LResult
' (SS,14/2/12) colspan increased from 4 to 5 if ProductOptionsEnabled
' (SS,14/3/12) colspan increased to 9 and 8
LResult = "<tr><td colspan=""" & IIf(ProductOptionsEnabled, "9", "8") & """>Total of " & PluralString(ACount, "email", "emails") & " sent" & "</td></tr>" & NL
LResult = LResult & "</table>" & NL
Response.Write LResult
StockNotificationFooter = LResult
End Function
Function StockNotificationRow(ANo, AID, AProductCode, AOption, AProductCount, AResult)
Dim LResult
LResult = "<tr>" & NL
LResult = LResult & "<td align=center>" & ANo & "</td>" & NL
LResult = LResult & "<td>" & AID & "</td>" & NL
LResult = LResult & "<td>" & AProductCode & "</td>" & NL
If ProductOptionsEnabled Then LResult = LResult & "<td>" & AOption & " </td>" & NL ' (SS,14/2/12)
LResult = LResult & "<td>" & GetQueryValue("Email") & "</td>" & NL
LResult = LResult & "<td>" & GetProductName(AProductCode) & "</td>" & NL
LResult = LResult & "<td align=center>" & GetQueryValue("NumInStock") & "</td>" & NL
LResult = LResult & "<td align=center>" & AProductCount & "</td>" & NL
LResult = LResult & "<td>" & AResult & "</td>" & NL
LResult = LResult & "</tr>" & NL
Response.Write LResult
StockNotificationRow = LResult
End Function
' (SS,14/3/12) created from code in SendStockNotificationEmails above, to make it easier to read, also returns the output HTML
Sub SendStockNotificationEmail(AID, AProductCode, AOption)
Dim LSubject, LName, LEmail, LBody, LProductNameWithOptionForEmail
LSubject = trx("Back in Stock Notification from <x>", GetStoreName)
LName = GetQueryValue("Name")
LEmail = GetQueryValue("Email")
' (SS,14/2/12)
LProductNameWithOptionForEmail = GetProductNameNoHTMLEncode(AProductCode)
If AOption <> "" Then LProductNameWithOptionForEmail = LProductNameWithOptionForEmail + " (" + AOption + ")"
' (SS,13/1/12) replaced LProductName with GetProductNameNoHTMLEncode(AProductCode) to sort "&" appearing as "&" in the plain text emails
' (SS,14/2/12) later replaced with LProductNameWithOptionForEmail
LBody =_
tr("Dear") & " " & LName & "," & NL & NL &_
trx("The <x> is now back in stock", LProductNameWithOptionForEmail) & "." & NL & NL &_
tr("Please click the link below to visit the product page") & "." & NL &_
GetFullProductLink(AProductCode) & NL & NL
' (SS,6/4/20) add additional text if defined
Dim LAdditionalText
LAdditionalText = TrimAll(GetSiteDetail("Other", "StockNotificationEmailAdditional"))
If LAdditionalText <> "" Then
LBody = LBody & LAdditionalText & NL & NL
End If
LBody = LBody &_
tr("Thank you for your interest") & "." & NL & NL &_
tr("Kind regards") & "," & NL & NL & GetStoreName
' send the email
SendEmail LEmail, FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom, LSubject, LBody, False
' mark as email sent
'&&& removed for debug purposes
ExecuteQuery("UPDATE stocknotifications SET DateTimeNotified=NOW() WHERE ID = " & AID)
End Sub
' ---------------------------------- '
' End of Stock Notification Routines '
' ---------------------------------- '
' Change to AddToRecentlyViewed and ShowRecentlyViewed to filter out currently shown product.
' ------------------------------- '
' Start of Recently View Routines '
' ------------------------------- '
' (SS,4/6/09)
' (SS,16/5/11) modified to use GetMaxRecentlyUsed instead of MAX_RECENTLY_VIEWED
Sub AddToRecentlyViewed(AProductCode)
Dim i, LFound, LMaxRecentlyViewed
LMaxRecentlyViewed = GetMaxRecentlyViewed + 1 ' (SS,5/9/12) added 1 to filter out current product, in ShowRecentlyViewed currently product isn't shown
' find if product code already added
LFound = 0
For i = 1 TO LMaxRecentlyViewed
If Session("RecentlyViewedProductCode" & i) = AProductCode Then
LFound = i
Exit For
End If
Next
' if found then shift all product codes below it up one effectively deleting AProductCode
If LFound > 0 Then
For i = LFound + 1 TO LMaxRecentlyViewed
Session("RecentlyViewedProductCode" & (i - 1)) = Session("RecentlyViewedProductCode" & i)
Session("RecentlyViewedProductName" & (i - 1)) = Session("RecentlyViewedProductName" & i)
If Session("RecentlyViewedProductCode" & i) = "" Then Exit For
Next
If i <= LMaxRecentlyViewed Then Session("RecentlyViewedProductCode" & i) = ""
End If
' shift everything down one making space for new
For i = LMaxRecentlyViewed TO 2 STEP -1
Session("RecentlyViewedProductCode" & i) = Session("RecentlyViewedProductCode" & (i - 1))
Session("RecentlyViewedProductName" & i) = Session("RecentlyViewedProductName" & (i - 1))
Next
' place new one at the top
Session("RecentlyViewedProductCode1") = AProductCode
Session("RecentlyViewedProductName1") = GetProductName(AProductCode)
End Sub
' (SS,4/6/09)
' (SS,16/5/11) MAX_RECENTLY_VIEWED replaced with GetMaxRecentlyViewed
' (SS,5/9/12) improved to not show current product if in product details page
' (SS,12/9/12) further improvements
Sub ShowRecentlyViewed
Dim LCurrentProductCode
LCurrentProductCode = GetProductCodeQS ' (SS,5/9/12), (SS,12/9/12) replaced GetProductCode with GetProductCodeQS
If Session("RecentlyViewedProductCode1") = "" Or GetRecentlyViewedCount(LCurrentProductCode) = 0 Then ' if nothing to show (SS,12/9/12) added GetRecentlyViewedCount
ShowRecentlyViewedEmpty
Else
ShowRecentlyViewedHeader
Dim i, LMaxRecentlyViewed, LLeftToShow
LLeftToShow = GetMaxRecentlyViewed ' (SS,5/9/12)
LMaxRecentlyViewed = GetMaxRecentlyViewed + 1 ' (SS,5/9/12) added + 1 because current product code is filtered out
For i = 1 TO LMaxRecentlyViewed
If Session("RecentlyViewedProductCode" & i) <> "" Then
If LCurrentProductCode <> Session("RecentlyViewedProductCode" & i) Then
ShowRecentlyViewedItem i, Session("RecentlyViewedProductCode" & i), Session("RecentlyViewedProductName" & i)
LLeftToShow = LLeftToShow - 1 ' (SS,5/9/12)
If LLeftToShow = 0 Then Exit For ' (SS,5/9/12)
End If
End If
Next
ShowRecentlyViewedFooter
End If
End Sub
' (SS,12/9/12) returns count, excluding given product code
Function GetRecentlyViewedCount(AProductCode)
Dim LResult, i, LMaxRecentlyViewed
LResult = 0
LMaxRecentlyViewed = GetMaxRecentlyViewed + 1 ' (SS,5/9/12) added + 1 because current product code is filtered out
For i = 1 TO LMaxRecentlyViewed
If Session("RecentlyViewedProductCode" & i) <> "" Then
If AProductCode <> Session("RecentlyViewedProductCode" & i) Then
LResult = LResult + 1
End If
End If
Next
GetRecentlyViewedCount = LResult
End Function
' ------------------------------- '
' End of Recently Viewed Routines '
' ------------------------------- '
' (SS,5/6/09)
Sub AddGoogleSitemapURL(AURL, APriority)
Const IDENT1 = " "
Const IDENT2 = " "
Response.Write(IDENT1 & "<url>" & NL)
' HTMLEncode will convert & to & as required by Google
Response.Write(IDENT2 & "<loc>" & HTMLEncode(AURL) & "</loc>" & NL)
Response.Write(IDENT2 & "<changefreq>" & "weekly" & "</changefreq>" & NL)
Response.Write(IDENT2 & "<priority>" & APriority & "</priority>" & NL)
Response.Write(IDENT1 & "</url>" & NL)
End Sub
' (SS,5/6/09) NB. URLEncode only used on separate fields to convert + and - to correct values (not on complete URL)
' (SS,20/2/13) replaced "&" with "&" for proper entity escape
' (SS,2/3/15) added SEO friendly product name
' (SS,28/10/19) change to 2nd querystring parameter to use "&" instead of "&" investigate issue in Google Search Console
Sub AddGoogleSitemapLevel(APage, ASQL, APriority, AFieldName1, AQueryStringFieldName1, AFieldName2, AQueryStringFieldName2)
Dim LURL, LPage
If APage = "" Then
LPage = "products.asp"
Else
LPage = APage
End If
OpenQuery(ASQL)
Do While Not EndOfQuery
LURL = AQueryStringFieldName1 & "=" & URLEncode(GetFieldValue(AFieldName1))
If AFieldName2 <> "" Then
' (SS,20/2/13) replaced "&" with "&"
' (SS,28/10/19) replaced "&" with "&"
LURL = LURL & "&" & AQueryStringFieldName2 & "="
' (SS,2/3/15) added ProductName SEO friendlym previously it was just code, now both code and name used
If AFieldName2 = "ProductName" Then
LURL = LURL & URLEncode(SEOFriendlyStr(GetFieldValue(AFieldName2)))
Else
LURL = LURL & URLEncode(GetFieldValue(AFieldName2))
End If
End If
AddGoogleSitemapURL GetStoreURL & "/" & LPage & "?" & LURL, APriority
NextQueryRecord
Loop
CloseQuery
End Sub
' (SS,5/6/09) called from google-sitemap.asp
' (SS,2/3/15) added ProductName
Sub DoGoogleSitemap
Response.ContentType = "text/xml" ' tells browses that it's an XML document
' add the main links held in site details
Dim LList, LLine, LStart, LEndReached, LPos
LList = GetSiteDetail("Other", "GoogleSitemapList")
LStart = 1
LEndReached = False
Do
LPos = InStr(LStart, LList, NL)
If LPos = 0 Then
LPos = Len(LList) + 1 ' go to end + 1
LEndReached = True
End If
LLine = Mid(LList, LStart, LPos - LStart)
LStart = LPos + 2 ' move beyond the new line
AddGoogleSitemapURL GetStoreURL & "/" & LLine, "1.0"
Loop Until LEndReached
Dim LSQL
' (SS,23/6/09) add the site pages if there are any (e.g. CovDem has them using index.asp)
LSQL = "SELECT Name FROM sitedetails WHERE Type = 'Pages' AND CheckBoxField = True ORDER BY SortOrder, Name"
OpenQuery(LSQL)
Do While Not EndOfQuery
AddGoogleSitemapURL GetStoreURL & "/index.asp" & "?page=" & CleanPageName(GetFieldValue("Name")), "1.0"
NextQueryRecord
Loop
CloseQuery
If GroupsEnabled Then
' add the group level links if groups enabled, only groups that contain enabled products
' (SS,31/10/18) added GetProductsFilter to filter out subproducts
LSQL = "SELECT GroupName, COUNT(*) AS Products FROM productcategories" &_
" INNER JOIN categories ON categories.Category = productcategories.Category" &_
" INNER JOIN products ON products.ProductCode = productcategories.ProductCode" &_
" WHERE ProductDisabled = False" & GetProductsFilter &_
" GROUP BY GroupName"
AddGoogleSitemapLevel "", LSQL, "0.3", "GroupName", "grp", "", ""
End If
' add the category level links for the ones that contain products
' (SS,31/10/18) added GetProductsFilter to filter out subproducts
LSQL = "SELECT productcategories.Category, COUNT(*) AS Products FROM productcategories" &_
" INNER JOIN products ON products.ProductCode = productcategories.ProductCode" &_
" WHERE ProductDisabled = False" & GetProductsFilter &_
" GROUP BY Category"
AddGoogleSitemapLevel "", LSQL, "0.4", "Category", "cat", "", ""
' add the subcategory level links for the ones that contain products
' (SS,31/10/18) added GetProductsFilter to filter out subproducts
LSQL = "SELECT productcategories.Category, productcategories.Subcategory, COUNT(*) AS Products FROM productcategories" &_
" INNER JOIN products ON products.ProductCode = productcategories.ProductCode" &_
" WHERE ProductDisabled = False" & GetProductsFilter &_
" GROUP BY Category, Subcategory"
AddGoogleSitemapLevel "", LSQL, "0.5", "Category", "cat", "Subcategory", "subcat"
' add the product level links for products that are not disabled
' (SS,31/10/18) added GetProductsFilter to filter out subproducts
LSQL = "SELECT ProductCode, ProductName FROM products" &_
" WHERE ProductDisabled = False" + GetProductsFilter + " ORDER BY ProductCode"
' (SS,2/1/5) added ProductName and name
AddGoogleSitemapLevel "", LSQL, "1.0", "ProductCode", "code", "ProductName", "name"
' add the product level links for products2.asp, uses same query as above
' (SS,16/11/12) removed links for products2.asphy
'AddGoogleSitemapLevel "products2.asp", LSQL, "1.0", "ProductName", "name", "ProductCode", "code"
' (SS,19/2/13) call GetCustomXMLSitemap routine if it exists in customutils.asp to add extra sitemap items
If FunctionExists("GetCustomXMLSitemap") Then GetCustomXMLSitemap
' CloseDatabase used instead of Finalise in google-sitemap.asp, because Finalise adds google analytics stuff
CloseDatabase
End Sub
' (SS,12/6/09) these are defined in
' show the banners on the ride hand side, shuffle randomly or shifted up or down
' AShiftMethod can be "R" for random, "U" to shift up "D" for down, blank for not change
Sub ShowCustomBanners(AShiftMethod)
Dim i, LSwap, LFirstTime, LBannerCount
' make sure the session variables have the default values and determine the count
If Session("CustomBannerCount") = "" Then
LFirstTime = True ' used to show in default order for first time when down is shift up/down
Session("CustomBannerCount") = 0
i = 1
Do While FunctionExists("CustomBanner" & i)
Session("CustomBanner" & i) = i
Session("CustomBannerCount") = i
i = i + 1
Loop
Else
LFirstTime = False
End If
LBannerCount = Session("CustomBannerCount")
' shift up/down or shuffle randomly
If AShiftMethod = "R" Then
' randomly swap
Dim LIndex1, LIndex2
For i = 1 To 20
LIndex1 = RandomInteger(1, LBannerCount)
LIndex2 = RandomInteger(1, LBannerCount)
LSwap = Session("CustomBanner" & LIndex1)
Session("CustomBanner" & LIndex1) = Session("CustomBanner" & LIndex2)
Session("CustomBanner" & LIndex2) = LSwap
Next
ElseIf AShiftMethod = "U" And Not LFirstTime Then
' shift up
LSwap = Session("CustomBanner1")
For i = 1 To LBannerCount - 1
Session("CustomBanner" & i) = Session("CustomBanner" & i + 1)
Next
Session("CustomBanner" & LBannerCount) = LSwap
ElseIf AShiftMethod = "D" And Not LFirstTime Then
' shift down
LSwap = Session("CustomBanner" & LBannerCount)
For i = LBannerCount TO 2 STEP -1
Session("CustomBanner" & i) = Session("CustomBanner" & i - 1)
Next
Session("CustomBanner1") = LSwap
End If ' if
' show the banners in new order
For i = 1 To LBannerCount
Eval("CustomBanner" & Session("CustomBanner" & i))
Next
End Sub
' -------------------------------------- '
' Start of Language Translation Routines '
' -------------------------------------- '
' (SS,17/3/10) this is called once in the initialise routine
' if translation is enabled it reads the contents of the common translation table into arrays
' the two arrays are used by the tr function
Sub SetUpTranslation
If Not FTranslationEnabled Or FTranslationLanguage = "" Then Exit Sub ' exit if translation not enabled
Dim i, LTotalLines
OpenDatabaseCommon
' count total lines for setting size of array
OpenQueryCommon("SELECT COUNT(*) AS TotalLines FROM translation")
LTotalLines = CInt(GetQueryValueCommon("TotalLines"))
CloseQueryCommon
' set size of array and read the lines into the array
ReDim FTransArrayEnglish(LTotalLines - 1), FTransArrayLanguage(LTotalLines - 1)
OpenQueryCommon("SELECT * FROM translation ORDER BY English")
i = 0
Do While Not EndOfQueryCommon
FTransArrayEnglish(i) = GetQueryValueCommon("English")
FTransArrayLanguage(i) = GetQueryValueCommon(FTranslationLanguage)
NextQueryRecordCommon
i = i + 1
Loop
CloseQueryCommon
CloseDatabaseCommon
End Sub
' (SS,17/3/10) looks up and returns the text for given English
Function tr(AText)
' exit with same string if translation not enabled
If Not FTranslationEnabled Then
tr = AText
Exit Function
End If
' lookup the equivalent text from array
Dim i, Result
Result = ""
For i = 0 To UBound(FTransArrayEnglish)
' do a case insensitive compare
If StrComp(FTransArrayEnglish(i), AText, 1) = 0 Then
Result = NB(FTransArrayLanguage(i))
Exit For
End If
Next
If Result = "" Then Result = AText
tr = Result
End Function
' (SS,18/3/10) calls tr and converts to lowercase
Function trl(AText)
trl = LCase(tr(AText))
End Function
' (SS,19/3/10) calls tr to get translation, replaces the token <x> with the text in AReplaceWith
Function trx(AText, AReplaceWith)
Dim Result
Result = tr(AText)
trx = ReplaceStr(Result, "<x>", AReplaceWith)
End Function
' (SS,19/3/10) calls tr to get translation, replaces <x> with AReplaceWith1, and </x> with AReplaceWith2 useful for adding a href in part of text
Function trx2(AText, AReplaceWith1, AReplaceWith2)
Dim Result
Result = tr(AText)
Result = ReplaceStr(Result, "<x>", AReplaceWith1)
trx2 = ReplaceStr(Result, "</x>", AReplaceWith2)
End Function
' ------------------------------------ '
' End of Language Translation Routines '
' ------------------------------------ '
' ---------------------------- '
' Start of New Button Routines '
' ---------------------------- '
' (SS,6/5/10) caption changed from "add to basket" to "Buy"
Function GetButtonBuy(AProductCode)
GetButtonBuy = GetButton(GetBuyBLink(AProductCode), "images/add.gif", "Buy", "")
End Function
' list version, gets link using GetBuyLLink instead of GetBuyBLink, caption changed from "add to basket" to "Buy"
Function GetButtonBuyL(AProductCode)
GetButtonBuyL = GetButton(GetBuyLLink(AProductCode), "images/add.gif", "Buy", "")
End Function
Function GetButtonSearch
GetButtonSearch = GetButton("javascript:itp_search()", "images/bt-go.gif", "Go", "")
End Function
Function GetButtonPreviousPage(AHRef)
GetButtonPreviousPage = GetButton(AHRef, "images/previous.gif", "Previous page", "")
End Function
Function GetButtonNextPage(AHRef)
GetButtonNextPage = GetButton(AHRef, "images/next.gif", "Next page", "")
End Function
Function GetButtonLogin
GetButtonLogin= GetButton("javascript:document.frmAccountLoginMain.submit()", "images/login.gif", "Log in", "") ' (SS,15/6/11) changed "Login" to "Log in" because it's a verb in this case not a noun
End Function
' login in first checkout page (*** not yet tested because not used by Galleon)
Function GetButtonLoginInCheckout
' original had align="absbottom" in img src, may need to add later
GetButtonLoginInCheckout = GetButton(GetCheckoutSubmitLink, "images/login.gif", "Log in", "") ' (SS,15/6/11) changed "Login" to "Log in" because it's a verb in this case not a noun
End Function
' was also login, but changed to My Account because it has same function as "My Account"
' (SS,13/9/12) renamed from GetButtonMyAccount to GetButtonYourAccount, caption "My Account" changed to "Your Account"
Function GetButtonYourAccount
GetButtonYourAccount = GetButton("products.asp?page=account", "images/login.gif", "Your Account", "")
End Function
' (SS,1/3/15) replaced & with &
Function GetButtonLogOut
GetButtonLogOut = GetButton("products.asp?page=account&cmd=logout", "images/logout.gif", "Log out", "")
End Function
' (SS,1/3/15) replaced & with &
Function GetButtonCreateAccount
GetButtonCreateAccount = GetButton("products.asp?page=account&cmd=create", "images/create-account.gif", "Create account", "")
End Function
' (SS,1/3/15) replaced & with &
Function GetButtonEditAccount
GetButtonEditAccount = GetButton("products.asp?page=account&cmd=update", "images/edit-details.gif", "Edit account details", "")
End Function
Function GetButtonSubmitForgotPassword
GetButtonSubmitForgotPassword = GetButton("javascript:document.frmForgotPassword.submit()", "images/submit.gif", "Submit", "")
End Function
' (SS,18/6/12)
Function GetButtonSubmitResetPassword
GetButtonSubmitResetPassword = GetButton("javascript:document.frmResetPassword.submit()", "images/submit.gif", "Submit", "")
End Function
' used to create account and edit account
' (SS,6/10/11) changed for VAT number validation, added id="accountsubmit"
' (SS,9/9/14) changed to show different button caption for create account or edit account
Function GetButtonSubmitCreateAccount
' (SS,9/9/14) added following
Dim LCaption
If AccountLoggedIn Then
LCaption = "Save changes"
Else
LCaption = "Create account"
End If
' GetButtonSubmitCreateAccount = GetButton("javascript:document.frmCreateAccount.submit()", "images/submit.gif", "Submit", "")
' (SS,6/10/11) replaced above with following because it now does VAT number validation if applicable
' (SS,9/9/14) replaced "Submit" with LCaption
GetButtonSubmitCreateAccount = GetButton("javascript:itp_accounts_submit()", "images/submit.gif", LCaption, "id=accountsubmit")
End Function
' used after login
Function GetButtonContinueShopping
GetButtonContinueShopping = GetButton("products.asp", "images/continue-shopping.gif", "Continue shopping", "")
End Function
' on basket page
Function GetButtonBasketContinue
GetButtonBasketContinue = GetButton(GetContinueLink, "images/continue-shop.gif", "Continue shopping", "")
End Function
Function GetButtonBasketRecalculate
GetButtonBasketRecalculate = GetButton("javascript:document.frmBasket.submit()", "images/recalculate.gif", "Recalculate", "")
End Function
' (SS,15/1/15) same as recalculate but with "Apply Voucher Code" text
Function GetButtonBasketApplyVoucherCode
GetButtonBasketApplyVoucherCode = GetButton("javascript:document.frmBasket.submit()", "", "Apply Voucher Code", "")
End Function
Function GetButtonProceedToCheckout
' (SS,13/10/14) moved to GetButtonProceedToCheckoutWithID and added "id=btnProceedToCheckout"
' id allows easy jQuery to override click to show a modal
GetButtonProceedToCheckout = GetButtonProceedToCheckoutWithID("id=btnProceedToCheckout")
End Function
' (SS,13/10/14) another version of GetButtonProceedToCheckout but with no ID
' allows the ID one (above) to have a button override using jquery, without affecting this one, to allow a modal to be added in basket page
Function GetButtonProceedToCheckoutNoID
GetButtonProceedToCheckoutNoID = GetButtonProceedToCheckoutWithID("")
End Function
' (SS,13/10/14) code from GetButtonProceedToCheckout moved here
Function GetButtonProceedToCheckoutWithID(AID)
GetButtonProceedToCheckoutWithID = GetButton(GetCheckoutLink, "images/pro-checkout.gif", "Proceed to checkout", AID)
End Function
' (SS,6/10/11) added id="nextstage"
' (SS,8/9/14) added chevron-right to options for RWD, bootstrap
Function GetButtonProceedToNextStage(AHRef)
GetButtonProceedToNextStage = GetButton(AHRef, "images/next-stage.gif", "Proceed to next stage", "id=nextstage,chevron-right")
End Function
' used by cheque or bank transfer (*** not yet tested)
Function GetButtonConfirmOrder(AHRef)
GetButtonConfirmOrder = GetButton(AHRef, "images/confirm-order.gif", "Confirm order", "")
End Function
Function GetButtonMakePayment(AHRef)
GetButtonMakePayment = GetButton(AHRef, "images/make-payment.gif", "Make payment", "")
End Function
' also make sure new button not used for certain image files e.g. worldpay, paypal, googlecheckout
' (SS,19/1/15) added new ACaption parameter, which simplifies this routine and allows both button and images to be shown
' (SS,4/2/15) improved layout for not responsive design
Function GetButtonPayment(AHRef, ACaption, AImageFile)
Dim LResult
LResult = GetButton(AHRef, AImageFile, ACaption, "")
If AImageFile <> "" Then
Dim LImageHTML, LHeight
If IsRWD Then
LHeight = "48"
Else
LHeight = "28"
End If
LImageHTML = "<a href=""" + AHRef + """><img style=""max-height:" & LHeight & "px;vertical-align: bottom"" src=""" + AImageFile + """ alt=""" + ACaption + """ border=""0""></a>"
If IsRWD Then
LResult = LResult & " " & LImageHTML
Else
'LResult = "<div style=""margin-top:10px;margin-bottom:2px"">" & LImageHTML & "</div>" & LResult
'LResult = LImageHTML & " " & LResult
LResult = LResult & " " & LImageHTML
End If
End If
GetButtonPayment = LResult
End Function
' (SS,26/5/10)
Function GetButtonOutOfStock
GetButtonOutOfStock = GetButton("", "images/out-of-stock.gif", "Out of stock", "")
End Function
' (SS,20/1/11) returns true if AOptionToFind exists in comma separated list, the comparison is case insensitve add to dbfunctions in future?
Function IsOption(AOptionList, AOptionToFind)
IsOption = InStr(1, "," + AOptionList + ",", "," + AOptionToFind + ",", 1) > 0
End Function
' (SS,6/10/11) returns the value after x=, e.g. GetOptionEquals("abc,id=button1,def", "id") returns button1, if not found then "" is returned
Function GetOptionEquals(AOptionList, AOptionToFind)
Dim LStrToSearch, LResult, LPos, LEnd
LStrToSearch = "," + AOptionList + ","
LPos = InStr(1, LStrToSearch, "," + AOptionToFind + "=", 1)
If LPos > 0 Then
LEnd = InStr(LPos + 1, LStrToSearch, ",", 1)
LResult = Mid(LStrToSearch, LPos + Len(AOptionToFind) + 2, LEnd - LPos - Len(AOptionToFind) - 2)
Else
LResult = ""
End If
GetOptionEquals = LResult
End Function
' (SS,20/1/11) improved AOptions handling, AOptions can contain
' "original" to force original
' "nav", "other" to force this class
' "targetblank" to add target="_blank" to link
' options can be comma separated
' (SS,6/10/11)
' added id, which can be passed here by adding "id=xyz" to AOptions, comma separated
' new function GetOptionEquals added above, this returns the value after id=
' this was added to allow an element to be found easily and hidden, mainly for account form validation please wait
' (SS,28/8/14) changes for RWD Bootstrap
' (SS,26/2/15) added title option for adding tooltip
Function GetButton(ALink, AImage, ACaption, AOptions)
Dim LButtonType, LClassType
'DebugMsg (ALink & ", " & AImage & ", " & ACaption & ", " & AOptions)
' (SS,20/1/11) replaced Or AOptions = "O" with IsOption(AOptions, "original")
' (SS,17/6/11) added Or ACaption = "", to use original button if no caption, new buttons need a caption
If FButtonType = BT_ORIGINAL Or IsOption(AOptions, "original") Or ACaption = "" Then
LButtonType = BT_ORIGINAL
Else
LButtonType = FButtonType
End If
' (SS,20/1/11) added [Or LCase(AOptions) = "nav"] to force nav if specified in options
If InStr(1, ",Go,Next page,Previous page,", "," + ACaption + ",", 1) > 0 Or IsOption(AOptions, "nav") Then
LClassType = "nav" ' e.g. in blue
' (SS,20/1/11) added [Or LCase(AOptions) = "other"] to force other if specified in options
ElseIf InStr(1, ",Login,Log out,Edit account details,Continue shopping,Recalculate,", "," + ACaption + ",", 1) > 0 Or IsOption(AOptions, "other") Then
LClassType = "other" ' e.g. green
Else
LClassType = "main" ' e.g. red
End If
' translate and also prefix with "<" or ">" if previous or next page
Dim LTransCaption
LTransCaption = tr(ACaption)
If ACaption = "Previous page" Then
LTransCaption = "« " + LTransCaption ' (SS,25/7/11) replaced � with «
ElseIf ACaption = "Next page" Then
LTransCaption = LTransCaption + " »" ' (SS,25/7/11) replaced � with »
End If
' (SS,26/5/10) if there is no link then don't show the hand cursor, i.e. change to default cursor and disable the click
Dim LAddToHRef
If ALink = "" Then
LAddToHRef = " onMouseOver=""this.style.cursor='default'"" onClick=""return false;"""
Else
LAddToHRef = ""
End If
' (SS,20/1/11) added following to add target="_blank"
If IsOption(AOptions, "targetblank") Then
LAddToHRef = LAddToHRef + " target=""_blank"""
End If
' (SS,6/10/11) if id specified then add it to the "a href"
Dim LID
LID = GetOptionEquals(AOptions, "id")
If LID <> "" Then LID = "id=""" & LID & """ "
' (SS,26/2/15) if title attribute specified then add it to the "a href"
Dim LTitle
LTitle = GetOptionEquals(AOptions, "title")
If LTitle <> "" Then LTitle = " title=""" & LTitle & """"
' (SS,20/1/11) added And AImage <> "" to only do this if image specified
' (SS,6/10/11) added LID
' (SS,26/8/14) added If IsRWD for bootstrap buttons, similar to BT_TYPE1 but with different class names
' (SS,10/9/14) swapped If and ElseIf - IsRWD moved to second If i.e. ElseIf
If LButtonType = BT_ORIGINAL And AImage <> "" Then
GetButton = "<a " & LID & "href=""" + ALink + """" + LAddToHRef + LTitle + "><img src=""" + AImage + """ alt=""" + LTransCaption + """ border=""0""></a>"
ElseIf IsRWD Then ' (SS,26/8/14) for bootstrap
If LClassType = "nav" then
LClassType = "primary"
Else
LClassType = "success"
End If
' (SS,8/9/14) added chevron if "chevron-right" in options
Dim LButtonHTML
LButtonHTML = "<a " & LID & "href=""" + ALink + """ class=""btn btn-default btn-" + LClassType + """" + LAddToHRef + LTitle + ">" + LTransCaption
If IsOption(AOptions, "chevron-right") Then
LButtonHTML = LButtonHTML + " <span class=""glyphicon glyphicon-chevron-right""></span>"
End If
LButtonHTML = LButtonHTML + "</a>"
GetButton = LButtonHTML
Else ' (SS,20/1/11) removed [If LButtonType = BT_TYPE1 Then]
GetButton = "<a " & LID & "href=""" + ALink + """ class=""button button-" + LClassType + """" + LAddToHRef + LTitle + ">" + LTransCaption + "</a>"
End If
End Function
' login, logout, create account, submit, edit account details, continue shopping, proceed to checkout
' next, previous, proceed to next stage, make payment
' ------------------------------------ '
' End of Language Translation Routines '
' ------------------------------------ '
' (SS,21/3/11) Banner stuff, returns True if banner image found
' calls ShowBannerHeader, ShowBannerItem and ShowBannerFooter in inc-template-other.asp
Function ShowBanner(AShowCaption)
ShowBanner = False
Dim LFileName, LCaption, LURL, LPos
OpenQuery("SELECT Type, Code2, FileName, Extra FROM files WHERE Type = 'P' AND Code = 'BANNER' ORDER BY Code2")
If Not EndOfQuery Then
ShowBannerHeader
' for each image
Do While Not EndOfQuery
LFileName = GetQueryField("FileName")
' take caption from filename just before the extension
If AShowCaption Then
LPos = InStrRev(LFileName, ".")
If LPos > 0 Then
LCaption = Trim(Mid(LFileName, 1, LPos - 1))
Else
LCaption = ""
End If
End If
' (SS,12/9/12) replaced GetProductFileLink with GetProductFileLinkCached to ensure image is cached
' (SS,25/5/17) added new Button Caption parameter, currently blank
ShowBannerItem GetProductFileLinkCached("BANNER", GetQueryField("Code2")), LCaption, GetQueryField("Extra"), ""
NextQueryRecord
Loop
ShowBannerFooter
ShowBanner = True
End If
CloseQuery
End Function
' (SS,30/6/11)
' -------------------------------- '
' Start of Product Option Routines '
' -------------------------------- '
Sub ShowProductOptions(AProductID)
' show the hidden values, used to make add to basket work
' "mainbuy" indicates that this has come from the main buy button on the product details page
' this can be used to prevent redirection to main product page, when add clicked (elsewhere) on a product with options
' (SS,17/1/13) added missing / from mainbuy input tag
%>
<input type="hidden" name="cmd" value="buy" />
<input type="hidden" name="mainbuy" value="yes" />
<input type="hidden" name="productid" value="<%=AProductID%>" />
<%
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQuery("SELECT * FROM product_options WHERE ProductID = '" & CleanSQLStr(AProductID) & "' ORDER BY SortOrder, OptionName")
If Not EndOfQuery Then
ShowProductOptionsHeader AProductID
Dim LOptionName, LInputType, LSplitIndex, LSplits, LSplit, LSplitCount
' for each option
Do While Not EndOfQuery
LOptionName = GetQueryField("OptionName")
LInputType = GetQueryField("InputType")
If IsSplitCombo(LOptionName, LInputType) Then
LSplits = Split(LOptionName, PO_SPLIT_COMBO_SEPARATOR)
LSplitCount = 0
For Each LSplit In LSplits
LSplitCount = LSplitCount + 1
' (SS,14/2/12) added GetQueryField("ExtraSettings")
ShowProductOption GetQueryField("ProductOptionID"), LSplit, POIT_COMBO_BOX, GetQueryField("Required"), GetQueryField("ExtraSettings"), LSplitCount
Next
Else
' (SS,14/2/12) added GetQueryField("ExtraSettings")
ShowProductOption GetQueryField("ProductOptionID"), LOptionName, LInputType, GetQueryField("Required"), GetQueryField("ExtraSettings"), 0
End If
NextQueryRecord
Loop
ShowProductOptionsFooter AProductID
End If
CloseQuery
End Sub
' (SS,15/7/11)
Function IsSplitCombo(AOptionName, AInputType)
IsSplitCombo = AInputType = POIT_SPLIT_COMBO And InStr(AOptionName, PO_SPLIT_COMBO_SEPARATOR) > 0
End Function
' (SS,14/7/11) separated from ShowProductOptions above to implement the split combo
' (SS,14/7/11) added ASplitIndex, this is 0 for normal combo, > 1 for split combo
' (SS,14/2/12) added AExtraSettings
' (SS,26/8/14) added ability to override the default option HTML via CustomGetProductOptionHTML when setting contains "CUSTOM"
Sub ShowProductOption(AProductOptionID, AOptionName, AInputType, ARequired, AExtraSettings, ASplitIndex)
Dim LIsCustomSetting, LInputLabelHTML, LInputHTML
LIsCustomSetting = InStr(1, "," + AExtraSettings + ",", ",CUSTOM,", vbTextCompare) ' (SS,26/8/14) set custom flag
' (SS,9/6/17) added GetRWDFullWidthColClass
Response.Write "<div class=""product-option"">" & NL
' (SS,26/8/14) replaced Response.Write with "LInputLabel =" to allow Custom routine to override the label as well
LInputLabelHTML = ""
' label, if required then an * is shown
If ARequired Then LInputLabelHTML = LInputLabelHTML + "<span class=""product-option-required"">*</span>" & NL
LInputLabelHTML = LInputLabelHTML + "<b>" + AOptionName + ":</b>" + BR
' (SS,26/8/14) if custom setting set then call the custom routine to allow the default option label HTML to be overridden
If LIsCustomSetting Then
If FunctionExists("CustomGetProductOptionLabelHTML") Then
LInputLabelHTML = CustomGetProductOptionLabelHTML(LInputLabelHTML, AOptionName, AInputType, ARequired, AExtraSettings)
End If
End If
Response.Write LInputLabelHTML
Dim LValuesOutOfStock ' (SS,14/2/12) array holding list of values are are out of stock, used to show list of option values that are out of stock
If AInputType = POIT_COMBO_BOX Or AInputType = POIT_RADIO Or AInputType = POIT_CHECK_BOX Then
If ASplitIndex = 0 Then
LInputHTML = LInputHTML + GetProductOptionComboBox(AProductOptionID, AInputType, LValuesOutOfStock, AOptionName, LIsCustomSetting) ' (SS,14/2/12) added LValuesOutOfStock ' (SS,27/8/14) added AOptionName and LIsCustomSetting
Else
LInputHTML = LInputHTML + GetProductOptionSplitComboBox(AProductOptionID, ASplitIndex)
End If
ElseIf AInputType = POIT_TEXT Then
LInputHTML = LInputHTML + "<input type=""text"" name=""option-" & AProductOptionID & """ value="""" />" & NL
Else
LInputHTML = ""
End If
' (SS,26/8/14) if custom setting set then call the custom routine to allow the default option HTML to be overridden
If LIsCustomSetting Then
If FunctionExists("CustomGetProductOptionHTML") Then
LInputHTML = CustomGetProductOptionHTML(LInputHTML, AProductOptionID, AOptionName, "option-" & AProductOptionID, AInputType, ARequired, AExtraSettings, ASplitIndex)
End If
End If
Response.Write LInputHTML
' (SS,14/2/12) if LO option set then show the options that are out of stock
' (SS,26/8/14) corrected by swapping ",LO," with AExtraSettings
If InStr("," + AExtraSettings + ",", ",LO,") > 0 And VarType(LValuesOutOfStock) >= vbArray Then
Dim i, LMaxCount
LMaxCount = UBound(LValuesOutOfStock) + 1
If LMaxCount > 0 Then
Response.Write BR & "Following " + Iif(LMaxCount = 1, "option", "options") + " " + Iif(LMaxCount = 1, "is", "are") + " out of stock:" & BR
For i = 0 To UBound(LValuesOutOfStock)
Response.Write "<a href=""" & GetStockNotificationWithOptionLink(GetProductCode, LValuesOutOfStock(i)) & """><b>" & LValuesOutOfStock(i) & "</b></a>" & BR & NL
Next
Response.Write "Please click option above to be notified when back in stock." & BR
End If
End If
' title is used by itp_buyo routine to display the option name is required error, so it knows what is required
Response.Write "<div id=""product-option-error-" & AProductOptionID & IIf(ASplitIndex = 0, "", "-split-" & ASplitIndex) & """ class=""product-option-error"" title=""" & AOptionName &""">" & "</div>" & NL
Response.Write "</div>" & NL
End Sub
' returns true if given product has options, single quote used in query in case routine is called with a blank product id
' if ARequired is True then True is returned if product has required options, else it returns true if it has any option
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
Function ProductHasOptions(AProductID, ARequired)
Dim LSQL
LSQL = "SELECT ProductOptionID FROM product_options WHERE ProductID = '" & CleanSQLStr(AProductID) & "'"
If ARequired Then LSQL = LSQL + " AND Required = TRUE"
ProductHasOptions = GetSQLValueAsString(LSQL) <> ""
End Function
' ID is returned as a string, when not found a blank string is return
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
Function GetProductIDForProductCode(AProductCode)
GetProductIDForProductCode = GetSQLValueAsString("SELECT ProductID FROM products WHERE ProductCode = '" & CleanSQLStr(AProductCode) & "'")
End Function
' (SS,20/7/11)
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
Function GetProductCodeForProductID(AProductID)
GetProductCodeForProductID = GetSQLValueAsString("SELECT ProductCode FROM products WHERE ProductID = '" & CleanSQLStr(AProductID) & "'")
End Function
' (SS,18/10/18) returns given field for given product ID
Function GetFieldForProductID(AProductID, AFieldName)
GetFieldForProductID = GetSQLValue("SELECT " & CleanSQLStr(AFieldName) & " FROM products WHERE ProductID = '" & CleanSQLStr(AProductID) & "'")
End Function
' return true if the buy or add to basket request is the main one on product details page
' this can be used to prevent redirection to main product page, when add clicked (elsewhere) on a product with options
Function IsMainBuy
IsMainBuy = CleanRequest("mainbuy") = "yes"
End Function
' (SS,30/6/11) can be called for input types: combo box, radio, or check box
' (SS,2/2/12) modified to show all options if none in stock to allow preorder, empty combo is no good
' (SS,14/2/12) added ByRef AValuesOutOfStock to pass back list of values out of stock
' (SS,5/2/13) modified to default to first item in radio group, hoping it'll cause less confusion on an iPad, also changed order so that most in stock is shown first
' (SS,13/12/13) modified to show radio label text with clickable picture if picture exists for option value
' (SS,19/12/13) improved radio alignment in IE using new div with class option-radio-div
' (SS,27/8/14) added AOptionName, AIsCustomSetting parameters to allow custom routine to be called if custom
' (SS,12/6/17) various changes around this date for Bootstrap (and HF)
' (SS,28/7/17) change to now call Function GetProductDetailOptionImageHTML in inc-template-product-detail.asp for option image render
' (SS,31/3/20) added form-control to class for combo box
Function GetProductOptionComboBox(AProductOptionID, AInputType, ByRef AValuesOutOfStock, AOptionName, AIsCustomSetting)
Dim LResult, LPriceAdjust, LOptionValue, LOptionValueWithPrice, LInStock, LOptionValueID, LName, LLabelID, LOptionValueCount, LSortOrder ' (SS,5/2/13) added LSortOrder
Dim LNumInStock, LNumInStockForLabel ' (SS,12/6/17) to allow showing of amount in stock if more than 1
Dim LPicture ' (SS,13/12/13)
LResult = ""
LOptionValueCount = 0 ' (SS,26/4/12)
ReDim AValuesOutOfStock(-1) ' (SS,14/2/12) reset array with no elements
' (SS,2/2/12) check if no options in stock, in which case all available options are shown rather than an empty combo box
Dim LNoOptionsInStock
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
LNoOptionsInStock = GetSQLValue("SELECT COUNT(*) FROM product_option_values WHERE ProductOptionID = '" & CleanSQLStr(AProductOptionID) & "' AND NumInStock IS NOT NULL AND NumInStock > 0") = 0
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
' (SS,5/2/13) added NumInStock DESC to list most in stock first
OpenQuery2("SELECT * FROM product_option_values WHERE ProductOptionID = '" & CleanSQLStr(AProductOptionID) & "' ORDER BY SortOrder, NumInStock DESC, OptionValue")
If Not EndOfQuery2 Then
LName = "option-" & AProductOptionID
If AInputType = POIT_COMBO_BOX Then
' (SS,27/8/14) added class="" to allow easy class addition via custom routine
' LResult = "<select class="""" name=""" + LName & """ id=""" & LName & """>"
' (SS,31/3/20) there was no class="", added "form-control" for Bootstrap (CIRC), otherwise it looks too plain and square
LResult = "<select class=""form-control"" name=""" + LName & """ id=""" & LName & """>"
LResult = LResult & NL & "<option value="""">" & COMBO_PLEASE_SELECT & "</option>" & NL
End If
' (SS,5/2/13) used to select the first value in option, it should only select first option when radio and only one value exists for the option
' (SS,27/8/14) added LOptionValueHTML
Dim LFirstOptionValueLabelID, LOptionValueHTML
LFirstOptionValueLabelID = ""
Do While Not EndOfQuery2
LOptionValueHTML = "" ' (SS,27/8/14)
LInStock = IsNull(GetQueryValue2("NumInStock")) Or NZL(GetQueryValue2("NumInStock")) > 0
LNumInStock = NZL(GetQueryValue2("NumInStock")) ' (SS,12/6/17)
LOptionValueID = GetQueryValue2("ProductOptionValueID")
LOptionValue = GetQueryValue2("OptionValue")
LPriceAdjust = GetQueryValue2("PriceAdjust")
LSortOrder = GetQueryValue2("SortOrder") ' (SS,5/2/13)
LPicture = IntToBool(GetQueryValue2("Picture")) ' (SS,13/12/13)
' (SS,14/2/12) if not in stock then add to option value out of stock list, which is shown below the combo
' (SS,5/2/13) added And LSortOrder = -1, so that those with -1 are not shown, i.e. they're kept in the table for future reference etc.
If Not LInStock And LSortOrder <> -1 Then
ReDim Preserve AValuesOutOfStock(UBound(AValuesOutOfStock) + 1)
AValuesOutOfStock(UBound(AValuesOutOfStock)) = LOptionValue
End If
' only show option value if in stock
' (SS,2/2/12) added Or LNoOptionsInStock so that all options are shown when none options are in stock
' (SS,5/2/13) added And LSortOrder <> -1 to not show those with SortOrder equal to -1
If LInStock Or (LNoOptionsInStock And LSortOrder <> -1) Then
' (SS,26/4/12) added following to count the values, to allow check option to be selected if radio
LOptionValueCount = LOptionValueCount + 1
' (SS,26/4/12) if not combo, i.e. Radio, then replace semicolons with breaks, and replaced LOptionValue with LOptionValueWithPrice, done before currency because currency will contain ";" for the special char HTML i.e. £
If AInputType <> POIT_COMBO_BOX Then
LOptionValue = ReplaceStr(LOptionValue, ";", "<br />")
End If
If NZD(LPriceAdjust) <> "0" Then
' LOptionValueWithPrice = LOptionValue & " (" & SignedCurrencyPrice(LPriceAdjust) & ")"
' (SS,19/12/13) replaced above with following to prevent the price from wrapping at bracket, + or � sign etc. uses white-space style set to nowrap in a span
LOptionValueWithPrice = LOptionValue & " <span style=""white-space:nowrap;"">(" & SignedCurrencyPrice(LPriceAdjust) & ")</span>"
Else
LOptionValueWithPrice = LOptionValue
End If
' (SS,12/6/17)
' (SS,18/7/17) added And (IsNull(GetNumInStock) Or GetNumInStock > 0) to check if main product is in stock, only then show the stock per option
If LNumInStock >= 1 And (IsNull(GetNumInStock) Or GetNumInStock > 0) Then
LNumInStockForLabel = "<span class=""text-success small"">" & " (" & LNumInStock & " in stock)" & "</span>"
Else
LNumInStockForLabel = ""
End If
If AInputType = POIT_COMBO_BOX Then
' LResult = LResult & "<option value=""" & LOptionValueID & """>" & LOptionValue & "</option>" & NL
'LResult = LResult & "<option value=""" & LOptionValueID & """ title=""icons/" & CleanStrForFileName(LOptionValue) & ".png"">" & LOptionValueWithPrice & "</option>" & NL
LOptionValueHTML = LOptionValueHTML & "<option value=""" & LOptionValueID & """>" & LOptionValueWithPrice & "</option>" & NL
Else
LLabelID = "option-value-" & LOptionValueID
LOptionValueHTML = LOptionValueHTML & "<div class=""row option-row"">" ' (SS,9/6/17) added for Bootstrap & HF
' (SS,19/12/13) added extra div to sort indenting going wrong in IE issue
LOptionValueHTML = LOptionValueHTML & "<div class=""option-radio-div col-lg-12 col-md-12 col-sm-12 col-xs-12"">" & NL
LOptionValueHTML = LOptionValueHTML & "<div class=""option-radio-control col-lg-1 col-md-1 col-sm-1 col-xs-1"">" ' (SS,9/6/17)
LOptionValueHTML = LOptionValueHTML & "<input type=""" & IIf(AInputType = POIT_RADIO, "radio", "checkbox") & """ name=""" & LName & """ value=""" & LOptionValueID & """ id=""" & LLabelID & """ class=""option-radio"" />" & NL
LOptionValueHTML = LOptionValueHTML & "</div>" ' (SS,9/6/17)
' (SS,13/12/13) if picture than add a href for image popup, to be changed to use Magic Zoom
' (SS,17/12/13) added underline using style="text-decoration:underline"
' (SS,9/6/17) temporarily removed following by commented out
'If LPicture Then
'LOptionValueWithPrice = "<a href=""" & GetProductOptionValueImageLink(LOptionValueID) & """ style=""text-decoration:underline"" target=""_blank"">" & LOptionValueWithPrice & "</a>"
'LOptionValueWithPrice = "<a href=""" & GetProductOptionValueImageLink(LOptionValueID) & """ style=""text-decoration:underline"" class=""MagicThumb"" title=""" & LOptionValue & """>" & LOptionValueWithPrice & "</a>"
' (SS,18/12/13) replaced target=""_blank"" with class=""MagicThumb"" for Magic Zoom popup, also added title for caption
' (SS,19/12/13) replaced above with following, most "rel" properties from hyperflight/inc-template-product-detail.asp
'Dim LRel
'LRel = "background-opacity:70; background-color:#000; caption-source:span; hint:false; selectors-effect:pounce; expand-effect:linear; restore-effect:linear; pan-zoom:false"
' (SS,9/6/17) added "o" to GetProductOptionValueImageLink, previously no size parameter
'LOptionValueWithPrice = "<a href=""" & GetProductOptionValueImageLink(LOptionValueID, "o") & """ style=""text-decoration:underline"" class=""MagicThumb"" title=""Click for picture"" rel=""" & LRel & """><span>" & LOptionValueWithPrice & "</span></a>"
' End If
' (SS,26/4/12) replaced LOptionValue with LOptionValueWithPrice, semicolons replaced with break above, added class="option-radio-text" to improve the alignment, removed <br /> after
' LResult = LResult & "<label for=""" & LLabelID & """>" & LOptionValue & "</label><br />" & NL
LOptionValueHTML = LOptionValueHTML & "<div class=""option-radio-label col-lg-9 col-md-9 col-sm-5 col-xs-7"">" ' (SS,9/6/17)
' (SS,12/6/17) added LNumInStockForLabel to following
LOptionValueHTML = LOptionValueHTML & "<label for=""" & LLabelID & """ class=""option-radio-text"">" & LOptionValueWithPrice & LNumInStockForLabel & "</label>" & NL
LOptionValueHTML = LOptionValueHTML & "</div>" ' (SS,9/6/17)
' (SS,9/6/17) show thumbnail after label
If LPicture Then
' (SS,28/7/27) code from below moved to new Function GetProductDetailOptionImageHTML in inc-template-product-detail.asp, where it should have been before
' *** I think I added here due to html above, perhaps need to move some of above HTML to separate template
LOptionValueHTML = LOptionValueHTML & GetProductDetailOptionImageHTML(LOptionValue, LOptionValueID)
End If
' (SS,19/12/13) end of extra div to sort indenting going wrong in IE issue
LOptionValueHTML = LOptionValueHTML & "</div>"
LOptionValueHTML = LOptionValueHTML & "</div>" ' (SS,9/6/17) added for Bootstrap & HF, end of row div
If LFirstOptionValueLabelID = "" Then LFirstOptionValueLabelID = LLabelID ' (SS,5/2/13)
End If
' (SS,27/8/14) if custom setting set then call the custom routine to allow the default option value HTML to be overridden
If AIsCustomSetting Then
If FunctionExists("CustomGetProductOptionValueHTML") Then
LOptionValueHTML = CustomGetProductOptionValueHTML(LOptionValueHTML, LOptionValueID, LOptionValueCount, LOptionValue, LOptionValueWithPrice, AOptionName, AInputType, LPicture)
End If
End If
LResult = LResult & LOptionValueHTML ' (SS,27/6/14)
End If
NextQueryRecord2
Loop
If AInputType = POIT_COMBO_BOX Then
LResult = LResult & "</select>"
' if radio and only one option value
' (SS,5/2/13) removed "And LOptionValueCount = 1" to make sure first option is always selected
ElseIf AInputType = POIT_RADIO Then
' select the option because there's only one
' (SS,5/2/13) replaced name with id and LName with LFirstOptionValueLabelID
%>
<script type="text/javascript">
$(document).ready(function() {
$('input[id="<%=LFirstOptionValueLabelID%>"]').prop('checked', true);
});
</script>
<%
End If
End If
CloseQuery2
GetProductOptionComboBox = LResult
End Function
' (SS,9/6/17)
Function GetRWDFullWidthColClass
Dim Result
If IsRWD Then
' for bootstrap, space prefix important due to being added to other class
Result = " col-lg-12 col-md-12 col-sm-12 col-xs-12"
Else
Result = ""
End If
GetRWDFullWidthColClass = Result
End Function
' (SS,14/6/11) called for split combo box
Function GetProductOptionSplitComboBox(AProductOptionID, ASplitIndex)
Dim LResult, LPriceAdjust, LOptionValue, LInStock, LName, LUsedList
LResult = ""
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQuery2("SELECT * FROM product_option_values WHERE ProductOptionID = '" & CleanSQLStr(AProductOptionID) & "' ORDER BY SortOrder, OptionValue")
If Not EndOfQuery2 Then
LName = "option-" & AProductOptionID & "-split-" & ASplitIndex
LResult = "<select name=""" + LName & """>"
LResult = LResult & NL & "<option value="""">" & COMBO_PLEASE_SELECT & "</option>" & NL
LUsedList = "" ' used to prevent duplicates
Do While Not EndOfQuery2
LInStock = IsNull(GetQueryValue2("NumInStock")) Or NZL(GetQueryValue2("NumInStock")) > 0
' only show option value if in stock
If LInStock Then
LOptionValue = NthSeparatedValue(GetQueryValue2("OptionValue"), PO_SPLIT_COMBO_SEPARATOR, ASplitIndex)
If LOptionValue <> "" Then
If InStr(1, PO_SPLIT_COMBO_SEPARATOR + LUsedList + PO_SPLIT_COMBO_SEPARATOR, PO_SPLIT_COMBO_SEPARATOR + LOptionValue + PO_SPLIT_COMBO_SEPARATOR, 1) = 0 Then
' Server.HTMLEncode allows quotes etc
LResult = LResult & "<option value=""" & Server.HTMLEncode(LOptionValue) & """>" & LOptionValue & "</option>" & NL
LUsedList = LUsedList + IIf(LOptionValue = "", "", PO_SPLIT_COMBO_SEPARATOR) + LOptionValue
End If
End If
End If
NextQueryRecord2
Loop
LResult = LResult & "</select>"
End If
CloseQuery2
GetProductOptionSplitComboBox = LResult
End Function
' (SS,30/6/11) returns given price in currency prefixed with + or -, or neither if 0
' used by product options to display additional cost of options
' (SS,5/2/13) replaced "-" with "‑" to prevent wordwrap after "-"
Function SignedCurrencyPrice(APrice)
Dim LPrice, LResult
If IsNull(APrice) Or APrice = "" Then
LResult = APrice
Else
LPrice = CDbl(APrice) ' CDbl used in case it's a string
If LPrice > 0 Then
LResult = "+"
ElseIf LPrice < 0 Then
LResult = "‑" ' (SS,5/2/13) replaced "-" with "‑" because "-" can wordwrap "‑" is a non-breaking hyphen
Else
LResult = ""
End If
LResult = LResult & CorrectCurrencyNV(Abs(LPrice)) ' (SS,2/11/12) Replaced CorrectCurrency with CorrectCurrencyNV so that VAT is removed for non-vatable countries
End If
SignedCurrencyPrice = LResult
End Function
' place chosen options into array with ProductOptionID and Value pair
' returns number of options, 0 if no options
' (SS,8/8/17) fixed bug where comma entered into a text input type was result in extra option values to be incorrectly added (call to new GetInputTypeForProductOptionID)
Sub ProductOptionsToArray(ByRef LOptions, ByRef LValues)
Dim LOptionCount, LName, LOptionValue, LOptionValues, i, LOptionID, LIgnore, LSplitIndex, LMaxSplits
' set up the arrays with no elements
ReDim LOptions(-1), LValues(-1)
If Not ProductOptionsEnabled Then Exit Sub ' (SS,15/4/16) quick exist if options not enabled, because all sites now call this routine
LOptionCount = 0
For Each LName in Request.Form
If Left(LName, 7) = "option-" Then
LOptionValue = CleanRequestForm(LName)
If LOptionValue <> "" Then
LIgnore = False
LOptionID = ParseInt(Mid(LName, 8, Len(LName)))
' (SS,15/7/11) added split combo handling
If InStr(LName, "-split-") > 0 Then
LSplitIndex = ParseInt(Mid(LName, InStr(LName, "-split-") + 7, Len(LName)))
If LSplitIndex = 1 Then
' use the option name to determine how many splits there are
LMaxSplits = StrCount(GetSQLValueAsString("SELECT OptionName FROM product_options WHERE ProductOptionID = " & LOptionID), PO_SPLIT_COMBO_SEPARATOR) + 1
LIgnore = True
' handle up to LMaxSplits splits, get list of values from the split combos
LOptionValues = ""
For i = 1 To 5
LOptionValue = CleanRequestForm("option-" & LOptionID & "-split-" & i)
If LOptionValue <> "" Then
LOptionValues = LOptionValues + IIf(LOptionValues = "", "", PO_SPLIT_COMBO_SEPARATOR) + LOptionValue
End If
Next
' using the LOptionValues, lookup the OptionValueID
LOptionValue = GetProductOptionValueIDForOptionValue(LOptionID, LOptionValues)
LIgnore = LOptionValue = "" ' ignore if match not found
Else ' to ignore splits 2 or more because they're handled above
LIgnore = True
End If
End If
If Not LIgnore Then
' (SS,8/8/17) to fix bug when commas entered into a free form text option value was causing extra options to be added due to splitting into array below separated by comma
' now checking for input type text prevents this (! not even sure about why things are being split below, kept code because I didn't want to break anything)
If GetInputTypeForProductOptionID(LOptionID) = POIT_TEXT Then
LOptionValues = Split(LOptionValue, "#@�?%~") ' using an odd separate to prevent separation so code below works without changes
Else ' (SS,8/8/17) as before
LOptionValues = Split(LOptionValue, ",") ' for checkboxes it could contain more than one
End If
For i = 0 To UBound(LOptionValues)
' add another element to the arrays
ReDim Preserve LOptions(UBound(LOptions) + 1), LValues(UBound(LValues) + 1)
LOptions(LOptionCount) = NB(LOptionID)
LValues(LOptionCount) = NB(Trim(LOptionValues(i)))
LOptionCount = LOptionCount + 1
Next
End If
End If
End If
Next
End Sub
' (SS,5/7/11) returns list of required options that are missing
' comma separated list of option IDs, blank if nothing missing
Function ProductOptionsMissingRequiredList(AProductID)
Dim LResult, LProductOptionID, LOptionName, LSplits, LSplit, LSplitCount, LInputNameSuffix, LOptionValue, LOptionValues, LAlert, LProductOptionValueID
LResult = ""
LAlert = ""
' (SS,15/7/11) added OptionName, InputType for new split combo function
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQuery("SELECT ProductOptionID, OptionName, InputType, Required FROM product_options WHERE ProductID = '" & CleanSQLStr(AProductID) & "' ORDER BY ProductOptionID")
Do While Not EndOfQuery
LProductOptionID = GetQueryValue("ProductOptionID")
LOptionName = GetQueryValue("OptionName")
If IntToBool(GetQueryValue("Required")) Then
' (SS,15/7/11) modified to handle split combo
If IsSplitCombo(LOptionName, GetQueryValue("InputType")) Then
LSplits = Split(LOptionName, PO_SPLIT_COMBO_SEPARATOR)
LSplitCount = 0
LOptionValues = ""
For Each LSplit In LSplits
LSplitCount = LSplitCount + 1
LInputNameSuffix = LProductOptionID & "-split-" & LSplitCount
LOptionValue = CleanRequestForm("option-" & LInputNameSuffix)
If Trim(LOptionValue) = "" Then
LResult = LResult + IIf(LResult = "", "", ",") & LInputNameSuffix
Else
LOptionValues = LOptionValues + IIf(LOptionValues = "", "", PO_SPLIT_COMBO_SEPARATOR) + LOptionValue
End If
Next
' if result okay so far then do check to see if such a valid combination exists
If LResult = "" Then
LProductOptionValueID = GetProductOptionValueIDForOptionValue(LProductOptionID, LOptionValues)
If LProductOptionValueID = "" Then
LAlert = LAlert + IIf(LAlert = "", "", "##") + LOptionValues + " is not available" '\n wasn't working, ## used and replaced by javascript
Else
' do a stock check
If Not IsProductOptionValueIDInStock(LProductOptionValueID) Then LAlert = LAlert + IIf(LAlert = "", "", "##") + LOptionValues + " is out of stock"
End If
End If
Else
If Trim(CleanRequestForm("option-" & LProductOptionID)) = "" Then
LResult = LResult + IIf(LResult = "", "", ",") & LProductOptionID
End If
End If
End If
NextQueryRecord
Loop
CloseQuery
If LResult = "" And LAlert <> "" Then LResult = "ALERT:" + LAlert ' if result okay, but an alert then return the alert
ProductOptionsMissingRequiredList = LResult
End Function
' (SS,15/7/11) returns ProductOptionValueID for given product option and value, blank is returned if not found
Function GetProductOptionValueIDForOptionValue(AProductOptionID, AOptionValue)
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
GetProductOptionValueIDForOptionValue = GetSQLValueAsString("SELECT ProductOptionValueID FROM product_option_values WHERE ProductOptionID = '" & CleanSQLStr(AProductOptionID) & "' AND OptionValue = '" & CleanSQLStr(AOptionValue) & "'")
End Function
' (SS,8/8/17)
Function GetInputTypeForProductOptionID(AProductOptionID)
GetInputTypeForProductOptionID = GetSQLValueAsString("SELECT InputType FROM product_options WHERE ProductOptionID = '" & CleanSQLStr(AProductOptionID) & "'")
End Function
' (SS,15/7/11) returns true if given product option value id is in stock, also returns true if out of stock ordering is allowed
Function IsProductOptionValueIDInStock(AProductOptionValueID)
' = LNumInStockOption
' to be continued
Dim LSQL, LResult, LNumInStockOption, LNumInStockProduct, LAllowOutOfStockOrdering, LProductInStock, LOptionInStock
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
LSQL = "SELECT product_option_values.NumInStock, products.NumInStock, products.AllowOutOfStockOrdering FROM product_option_values INNER JOIN products ON products.ProductID = product_option_values.ProductID WHERE ProductOptionValueID = '" & CleanSQLStr(AProductOptionValueID) & "'"
LResult = GetSQL3Values(LSQL, LNumInStockOption, LNumInStockProduct, LAllowOutOfStockOrdering)
If LResult Then
If Not IntToBool(LAllowOutOfStockOrdering) Then
If (Not IsNull(LNumInStockProduct) And NZ(LNumInStockProduct) = 0) Or (Not IsNull(LNumInStockOption) And NZ(LNumInStockOption) = 0) Then
LResult = False
End If
End if
End If
IsProductOptionValueIDInStock = LResult
End Function
' returns the matching ItemID in basket, which is the same product and has the same options
' ItemID is returned if match found, 0 is returned if no match found
Function GetItemIDForMatchingProduct(AProductID, AOptions, AValues)
Dim LNewOptionCount, i
LNewOptionCount = UBound(AOptions) + 1 ' size of array is number of options
Dim LMatches, LExistingOptionCount, LMatchingItemID, LMatchingOptionCount
Dim LItemID, LProductOptionID, LOptionValue
LMatchingItemID = 0 ' default to no match found
' repeat for each item with same product id in basket
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQuery("SELECT ItemID FROM shoppingbaskets WHERE SessionID = '" & CleanSQLStr(FSessionID) & "' AND ProductID = '" & CleanSQLStr(AProductID) & "' ORDER BY ItemID")
Do While Not EndOfQuery
LItemID = GetQueryValue("ItemID")
LExistingOptionCount = 0
LMatchingOptionCount = 0
' repeat for each option for this item, and count how many options match
OpenQuery2("SELECT * FROM shopping_basket_options WHERE ItemID = " & LItemID & " ORDER BY ShoppingBasketOptionID")
Do While Not EndOfQuery2
LExistingOptionCount = LExistingOptionCount + 1
LProductOptionID = NB(GetQueryValue2("ProductOptionID"))
' if OptionValueID is zero then it means the value is in OptionValue, i.e. a Text box, else ID is the value
If GetQueryValue2("ProductOptionValueID") = 0 Then
LOptionValue = NB(GetQueryValue2("OptionValue"))
Else
LOptionValue = NB(GetQueryValue2("ProductOptionValueID"))
End If
For i = 0 To LNewOptionCount - 1
' (SS,20/7/11) add CStr because value may be either string or number
If CStr(AOptions(i)) = CStr(LProductOptionID) And CStr(AValues(i)) = CStr(LOptionValue) Then
LMatchingOptionCount = LMatchingOptionCount + 1
End If
Next
NextQueryRecord2
Loop
CloseQuery2
'(SS,4/7/11) to be deleted
'Response.Write "LItemID: " & LItemID & "<br />"
'Response.Write "LNewOptionCount: " & LNewOptionCount & "<br />"
'Response.Write "LExistingOptionCount: " & LExistingOptionCount & "<br />"
'Response.Write "LMatchingOptionCount: " & LMatchingOptionCount & "<br />"
' if all options match then we have found a match, set the matching id and exist
If LNewOptionCount = LExistingOptionCount And LNewOptionCount = LMatchingOptionCount Then
LMatchingItemID = LItemID
Exit Do
End If
NextQueryRecord
Loop
CloseQuery
GetItemIDForMatchingProduct = LMatchingItemID
End Function
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
Sub InsertOptionsIntoBasket(AProductID, AItemID, AOptions, AValues)
Dim i, LProductOptionID, LProductOptionValueID, LOptionName, LOptionValue, LInputType, LDoInsert
' repeat for each option, inserting the basket option record
For i = LBound(AOptions) to UBound(AOptions)
LProductOptionID = AOptions(i)
LProductOptionValueID = AValues(i) ' only if Input Type isn't text
LOptionValue = AValues(i) ' if Input Type is text
' Response.Write AOptions(i) & " = " & AValues(i) & "<br />" & NL
' insert the option, ProductOptionValueID, ProductOptionName and ProductOptionValue, need to be looked up later, although ProductOptionValue will set correctly fine text input type
' ProductID in WHERE is strictly not necessary but added as an extra safety measure
' (SS,20/7/11) added If Not IsNumeric.. to allow special options to be added e.g. products added in a special way e.g. covdem radiator builder, both ids 0
If Not IsNumeric(LProductOptionID) Then
LOptionName = LProductOptionID
LProductOptionID = 0
LProductOptionValueID = 0
LDoInsert = True
ElseIf GetSQL2Values("SELECT OptionName, InputType FROM product_options WHERE ProductID = '" & CleanSQLStr(AProductID) & "' AND ProductOptionID = " & LProductOptionID, LOptionName, LInputType) Then
If LInputType = POIT_TEXT Then
LProductOptionValueID = 0 ' because a text input type has no value record
LDoInsert = True
Else
LOptionValue = GetSQLValueAsString("SELECT OptionValue FROM product_option_values WHERE ProductOptionID = '" & CleanSQLStr(LProductOptionID) & "' AND ProductOptionValueID = '" & CleanSQLStr(LProductOptionValueID) & "'")
LDoInsert = LOptionValue <> ""
End If
Else
LDoInsert = False
End If
If LDoInsert Then
ExecuteQuery "INSERT INTO shopping_basket_options (ItemID, ProductOptionID, ProductOptionValueID, OptionName, OptionValue) VALUES ('" & CleanSQLStr(AItemID) & "', '" & CleanSQLStr(LProductOptionID) & "', '" & CleanSQLStr(LProductOptionValueID) & "', '" & CleanSQLStr(LOptionName) & "', '" & CleanSQLStr(LOptionValue) & "');"
End If
Next
End Sub
' returns string with options list as a string
' AOptions for future expansion
' OpenQuery2 used in case another query loop is used by calling routine
' (SS,9/10/14) added AobjOptionsDict
Function GetOptionsListForBasketItem(AItemID, AOptions, AobjOptionsDict)
Dim LResult, LSpace
LResult = ""
AobjOptionsDict.RemoveAll ' (SS,9/10/14) make sure previous values in dictionary are cleared
' repeat for each option for this item
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQuery2("SELECT * FROM shopping_basket_options WHERE ItemID = '" & CleanSQLStr(AItemID) & "' ORDER BY ShoppingBasketOptionID")
Do While Not EndOfQuery2
AobjOptionsDict.Add GetQueryValue2("OptionName"), GetQueryValue2("OptionValue") ' (SS,9/10/14)
LResult = LResult & Iif(LResult = "", "", ", ") & GetQueryValue2("OptionName") & ": " & GetQueryValue2("OptionValue")
NextQueryRecord2
Loop
CloseQuery2
GetOptionsListForBasketItem = LResult
End Function
' returns False to allow calling AddToBasket procedure to continue, True to exit
' (SS,20/7/11) modified to allow calling from within server to add to basket, e.g CovDem radiator builder
' added AAllowRedirect to prevent the redirect, added AOptions, AValues so that options are passed directly
' rather than being taken from form elements
' (SS,13/4/16) modified to handle stock for out of stock items, i.e. it doesn't allow adding to stock if there is no stock
' previously there was no stock restriction, it allow anything to be added, even if out of stock
' renamed from ProcessAddToBasketForOptions to AddProductToBasketIncludingOptions
' change to function which returns True if adding to basket was successful
' added new parameter AItemID, AAllowAlert
' (SS,18/10/18) now calls CustomAddSubproductsToBasket if set up and subproducts feature enabled
' (SS,25/10/18) Renamed LQty to LQtyToAdd, added LQtyInBasket
Function AddProductToBasketIncludingOptions(AProductCode, AQty, AItemID, AAllowRedirect, AAllowAlert, AOptions, AValues)
ClearBasketOutOfStockMessage
' basket operation issued, make sure items previously reserved are cleared to ensure accuracy of following
' (SS,15/4/16) False to make sure detail records aren't deleted
ClearExistingOrderPlaced False
AddProductToBasketIncludingOptions = True ' (SS,14/4/16) default to returning True
Dim LProductID, LQtyToAdd, LInStock
LQtyToAdd = NZL(Trim(AQty))
' exit if qty is zero or blank
If LQtyToAdd = 0 Then Exit Function
LProductID = GetProductIDForProductCode(AProductCode)
' if not main buy button on detail page, and product has required options then redirect to the detail page, to allow user to select the options
If AAllowRedirect Then
If Not IsMainBuy And ProductHasOptions(LProductID, True) Then
DoRedirect GetProductLink(AProductCode), False
' following doesn't actually get run because DoRedirect will exit the page
Exit Function
End If
End If
' make sure the product is valid and not disabled
Dim LValidProduct
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
LValidProduct = GetSQLValueAsString("SELECT ProductID FROM products WHERE ProductDisabled = FALSE AND ProductID = '" & CleanSQLStr(LProductID) & "'") <> ""
If Not LValidProduct Then Exit Function
' place options into arrays for easier handling
' (SS,14/4/16) added If AItemID = 0 to only do this if ItemID not specified (i.e. not called from RecalculateBasket)
If AItemID = 0 Then
Dim LOptions, LValues, LItemID
' (SS,20/7/11) if array was provided then use it for options, else get array of options from form
If VarType(AOptions) >= vbArray Then
LOptions = AOptions
LValues = AValues
Else
ProductOptionsToArray LOptions, LValues
End If
End If
' (SS,25/10/18)
Dim LQtyInBasket
' # Add product and options into stock
' if same product already exists in the basket, with same options then we need to increase the qty for the item rather than add a new item
' following returns ItemID of matching item in basket, 0 if not found
If AItemID = 0 Then
LItemID = GetItemIDForMatchingProduct(LProductID, LOptions, LValues)
Else ' (SS,14/4/16) use the provided ItemID because it was called from recalculate basket
LItemID = AItemID
End If
' if not found then insert new basket record and new options, else update the quantity
If LItemID = 0 Then
LQtyInBasket = 0 ' (SS,25/10/18)
If LQtyToAdd > 0 Then ' (SS,13/4/16) added If to only insert if qty is positive
' insert the basket record
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
ExecuteQuery "INSERT INTO shoppingbaskets (SessionID, ProductID, ProductCode, Qty) VALUES ('" & CleanSQLStr(FSessionID) & "', '" & CleanSQLStr(LProductID) & "', '" & CleanSQLStr(AProductCode) & "', " & LQtyToAdd & ");"
LItemID = GetSQLLastInsertID
' insert the options
InsertOptionsIntoBasket LProductID, LItemID, LOptions, LValues
End If
Else
' (SS,25/10/18) LQtyInBasket is passed on to CustomAddSubproductsToBasket later
LQtyInBasket = CLng(GetSQLValue("SELECT Qty FROM shoppingbaskets WHERE ItemID = '" & CleanSQLStr(LItemID) & "'"))
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
ExecuteQuery "UPDATE shoppingbaskets SET Qty = Qty + " & LQtyToAdd & " WHERE ItemID = '" & CleanSQLStr(LItemID) & "'"
End If
' Response.Write("Options List: " & GetOptionsListForBasketItem(LItemID, "") & "<br />")
' (SS,18/10/18) added following to handle new subproducts feature, custom routine is called if subproducts enabled and this product has subproducts
If ProductSubproductsEnabled Then
If IntToBool(GetFieldForProductID(LProductID, "HasSubproducts")) Then
If FunctionExists("CustomAddSubproductsToBasket") Then
CustomAddSubproductsToBasket LProductID, LItemID, LQtyInBasket, LQtyToAdd
End If
End If
End If
' # Check enough in stock if Qty > 0
' (SS,13/4/16) if Qty is > 0 then check item in stock
Dim LQtyAdded, LProductHasOptions
LQtyAdded = LQtyToAdd
If LQtyToAdd > 0 Then
Dim LNumOutOfStock
LInStock = IsStockAvailable ' this returns true if out of stock ordering allowed, or stock availability is enabled and available soon
' NB. can't do "If IsStockAvailable Or CheckProductInBasketInStock" due to VB always calling second condition even when first is True
If Not LInStock Then
LInStock = CheckProductInBasketInStock(LProductID, LProductHasOptions, LNumOutOfStock) ' if not fully in stock then LNumOutOfStock is set to number out of stock
' if not enough in stock then adjust LQty, add back in stock what we don't have in stock
If Not LInStock Then
If LProductHasOptions Then
LQtyAdded = 0
LNumOutOfStock = LQtyToAdd
Else
LQtyAdded = LQtyToAdd - LNumOutOfStock
End If
' main item, remove by using negative LQty
' remove item/qty added above because not enough were in stock
' remove number that aren't in stock using -LNumOutOfStock
ExecuteQuery "UPDATE shoppingbaskets SET Qty = Qty + " & -LNumOutOfStock & " WHERE ItemID = '" & CleanSQLStr(LItemID) & "'"
' options will automatically be deleted by clean up basket below which removes orphaned records
End If
End If
Else
LInStock = True
End If
' # Clean up basket
' (SS,14/4/16) clean up basket, removing those with zero or negative qty for this session
ExecuteQuery "DELETE FROM shoppingbaskets WHERE SessionID = '" & CleanSQLStr(FSessionID) & "' AND Qty <= 0"
' remove orphaned options records due to deletion above (it removes across all sessions which shouldn't be a problem)
ExecuteQuery "DELETE FROM shopping_basket_options WHERE ItemID NOT IN (SELECT ItemID FROM shoppingbaskets)"
' # Show alert if applicable
' (SS,19/2/14) if item was added then redirect to basket page at the end of script
' (SS,20,2/14) replaced SetPageRedirection with SetAddToBasketAlert
' (SS,13/4/16) added If LQty and If LInStock, also added SetAlertMessage to show out of stock
' (SS,14/4/16) added AAllowAlert
If AAllowAlert Then
If LQtyAdded > 0 Then
SetAddToBasketAlert LQtyAdded, AProductCode
End If
If GetBasketOutOfStockMessage <> "" Then
If Not AlertMessageContains("Sorry, following out of stock:") Then SetAlertMessage "Sorry, following out of stock:" & BR ' only add this if it doesn't already contain, remember this RecalculateBasket will call for each product line
SetAlertMessage GetBasketOutOfStockMessage
End If
End If
AddProductToBasketIncludingOptions = LInStock ' (SS,14/4/16)
End Function
' (SS,18/4/16) returns True if current basket is fully in stock, other False and alert is display
Function CheckBasketInStock(AShowOutOfStockAlert)
Dim LSQL, LInStock
Dim LProductHasOptions, LNumOutOfStock ' dummy variables to call CheckProductInBasketInStock, not used in this routine
ClearBasketOutOfStockMessage
' create unique product ID list in order they are in the basket, and call CheckProductInBasketInStock to check each
LSQL = "SELECT ProductID, MIN(ItemID) AS MinItemID FROM shoppingbaskets" & NL _
+ "WHERE SessionID = '" & CleanSQLStr(FSessionID) & "'" & NL _
+ "GROUP BY ProductID" & NL _
+ "ORDER BY MinItemID"
OpenQuery(LSQL)
LInStock = True
Do While Not EndOfQuery
LInStock = LInStock And CheckProductInBasketInStock(GetQueryValue("ProductID"), LProductHasOptions, LNumOutOfStock)
NextQueryRecord
Loop
CloseQuery
If AShowOutOfStockAlert And GetBasketOutOfStockMessage <> "" Then
AddAlertMessageSection ' (19/4/16) add line separator because there might be a previous stock message
SetAlertMessage "Sorry, following out of stock, please adjust basket:" & BR
SetAlertMessage GetBasketOutOfStockMessage
End If
CheckBasketInStock = LInStock
End Function
' (SS,13/4/16) AProductHasOptions set to true if product has options, ANumOutOfStock number of product that were out of stock
Function CheckProductInBasketInStock(AProductID, ByRef AProductHasOptions, ByRef ANumOutOfStock)
Dim LSQL, LMainInStock, LOptionsInStock, LNumInStock, LQtyRequired, LProductName
AProductHasOptions = ProductHasOptions(AProductID, False)
' check main product in stock
' (SS,18/4/16) added GREATEST(, 0) to prevent negative value giving more than this customer added to basket
' (SS,18/4/16) can't use s alias for shoppingbaskets and p for products because it's used on ProcessOrder with read lock
' (SS,19/4/16) added NOT AllowOutOfStockOrdering AND, top level ignores NumInStock if this flag is set
LSQL = "SELECT s1.QtyRequired, GREATEST(products.NumInStock, 0), products.ProductName FROM" _
+ " (SELECT shoppingbaskets.ProductID, SUM(shoppingbaskets.Qty) AS QtyRequired" & NL _
+ " FROM shoppingbaskets" & NL _
+ " WHERE SessionID = '" & CleanSQLStr(FSessionID) & "' AND ProductID = " & AProductID & NL _
+ " ) s1" & NL _
+ "INNER JOIN products ON products.ProductID = s1.ProductID" & NL _
+ "WHERE NOT AllowOutOfStockOrdering AND NumInStock IS NOT NULL AND QtyRequired > NumInStock"
LMainInStock = Not GetSQL3Values(LSQL, LQtyRequired, LNumInStock, LProductName)
If Not LMainInStock Then
ANumOutOfStock = CLng(LQtyRequired) - CLng(LNumInStock)
AddBasketOutOfStockMessage ANumOutOfStock & " x " & LProductName
Else
ANumOutOfStock = 0
End If
LOptionsInStock = True
' to add qty in stock for option free product
' to show options out of stock for options product
' (SS,19/4/16) removed "LInStock And" because even if main product is out of stock we still want to know the options that are out of stock because more than one of the same may be in basket
' and customers may need to know which one to remove
If AProductHasOptions Then
' check options in stock
' (SS,18/4/16) added GREATEST(, 0)
' (SS,18/4/16) can't use s alias for shoppingbaskets, so for shopping_basket_options, po for product_options, and pv for product_option_values because it's used on ProcessOrder with read lock
' (SS,19/4/16) added products.ProductName, product_options.OptionName
LSQL = "SELECT s1.*, products.ProductName, product_options.OptionName, GREATEST(product_option_values.NumInStock, 0) AS NumInStock, product_option_values.OptionValue FROM" _
+ " (SELECT shoppingbaskets.ProductID, shopping_basket_options.ProductOptionID, shopping_basket_options.ProductOptionValueID, SUM(shoppingbaskets.Qty) AS QtyRequired" & NL _
+ " FROM shoppingbaskets" & NL _
+ " INNER JOIN shopping_basket_options ON shopping_basket_options.ItemID = shoppingbaskets.ItemID" & NL _
+ " WHERE SessionID = '" & CleanSQLStr(FSessionID) & "' AND ProductID = " & AProductID & NL _
+ " GROUP BY ProductOptionValueID) s1" & NL _
+ "INNER JOIN products ON products.ProductID = s1.ProductID" & NL _
+ "INNER JOIN product_options ON product_options.ProductOptionID = s1.ProductOptionID" & NL _
+ "INNER JOIN product_option_values ON product_option_values.ProductOptionValueID = s1.ProductOptionValueID" & NL _
+ "WHERE product_option_values.NumInStock IS NOT NULL AND QtyRequired > product_option_values.NumInStock" & NL _
+ "ORDER BY product_options.SortOrder, product_option_values.SortOrder"
OpenQuery2(LSQL)
LOptionsInStock = EndOfQuery2
' (SS,19/4/16) added product name before option(s)
If LMainInStock And Not LOptionsInStock Then AddBasketOutOfStockMessage GetQueryValue2("ProductName") & ":"
Do While Not EndOfQuery2
' Response.Write "##QtyRequired:" & GetQueryValue2("QtyRequired") & "##, NumInStock:" & GetQueryValue2("NumInStock") & "##, OptionValue:" & GetQueryValue2("OptionValue") & "##" & LInStock & BR
AddBasketOutOfStockMessage " " & (CLng(GetQueryValue2("QtyRequired")) - CLng(GetQueryValue2("NumInStock"))) & " x " & GetQueryValue2("OptionName") & ": " & GetQueryValue2("OptionValue")
NextQueryRecord2
Loop
CloseQuery2
End If
CheckProductInBasketInStock = LMainInStock And LOptionsInStock
End Function
' (SS,15/4/16)
Sub ClearBasketOutOfStockMessage
FBasketOutOfStockMessage = ""
End Sub
' (SS,15/4/16)
Sub AddBasketOutOfStockMessage(AMessage)
FBasketOutOfStockMessage = FBasketOutOfStockMessage & IIf(FBasketOutOfStockMessage = "", "", BR) & AMessage
End Sub
' (SS,15/4/16)
Function GetBasketOutOfStockMessage
GetBasketOutOfStockMessage = FBasketOutOfStockMessage
End Function
' (SS,7/7/11) this is not strictly part of product options, but placed next to TakeProductOptionsFromStock below
' created when product options feature added
' called from TryProcessOrder, just after the stock has been updated from orderdetails, with ATake set to True
' or just after deleting previous order details with ATake set to False
Sub TakeProductsFromStock(AOrderNo, ATake)
Dim LSQL
' add the items back in stock from orderdetails
'LSQL = "UPDATE products, orderdetails SET NumInStock = NumInStock + orderdetails.Qty" &_
' " WHERE orderdetails.OrderNo = " & LOrderNo & " AND products.ProductCode = orderdetails.ProductCode"
' (SS,7/7/11) replaced above with following, because we can now have same product more than once in a order with different options
' previous query would only update for one of these records, following works correctly by summing the Qty per product, also uses ProductID to link instead of ProductCode
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
LSQL = "UPDATE products, " &_
"(SELECT ProductID, SUM(Qty) AS Qty FROM orderdetails" &_
" WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'" &_
" GROUP BY ProductID" &_
") AS t2" &_
" SET NumInStock = NumInStock " & IIf(ATake, "-", "+") & " Qty" &_
" WHERE products.ProductID = t2.ProductID AND NumInStock IS NOT NULL"
ExecuteQuery(LSQL)
' take/put the options back in/out of stock
' (SS,15/4/16) used it be a separate call in TryProcessOrder and ClearExistingOrderPlaced, simplifies and reduces repeated code by moving here
If ProductOptionsEnabled Then
TakeProductOptionsFromStock AOrderNo, ATake
End If
End Sub
' (SS,6/7/11) adds or subtracts options from stock
' called from TryProcessOrder, just after the stock has been updated from orderdetails, with ATake set to True
' only called when product options are enabled
' just after deleting previous order details with ATake set to False
Sub TakeProductOptionsFromStock(AOrderNo, ATake)
' create a list from order details options for all the product options that need updating, with a qty from order detail record, total up because same one could appear more than once
Dim LSQL
' a multi-table update, using Qty per ProductOptionValueID
' product_option_values link was removed from t2 because it created locking error (product_option_values not locked), wasn't necessary because it was only being used to filter out records with NumInStock IS NOT NULL
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
LSQL = "UPDATE product_option_values, " &_
"(SELECT order_detail_options.ProductOptionValueID, SUM(orderdetails.Qty) AS Qty" &_
" FROM order_detail_options, orderdetails" &_
" WHERE orderdetails.OrderDetailID = order_detail_options.OrderDetailID" &_
" AND order_detail_options.OrderNo = '" & CleanSQLStr(AOrderNo) & "' AND order_detail_options.ProductOptionValueID <> 0" &_
" GROUP BY ProductOptionValueID" &_
") AS t2" &_
" SET NumInStock = NumInStock " & IIf(ATake, "-", "+") & " Qty" &_
" WHERE product_option_values.ProductOptionValueID = t2.ProductOptionValueID AND NumInStock IS NOT NULL"
ExecuteQuery(LSQL)
End Sub
' (SS,7/7/11) returns value of attribute for given product id and attribute name, "" is return if not found
' used by options header and footer in inc-template-other.asp
' (SS,26/4/12) added '' around AProductID in case it's null, which it can be when ProductCode is invalid, this prevent query from failing
' (SS,9/10/14) remove alias 'pa' and 'a' because routine may be called in TryProcessOrder which locks the tables and it can't lock 'p' because it's an alias
Function GetProductAttributeByName(AProductID, AAttributeName)
Dim LResult
If ProductAttributesEnabled Then
Dim LSQL
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
LSQL = "SELECT AttributeValue FROM product_attributes" &_
" INNER JOIN attributes ON attributes.AttributeID = product_attributes.AttributeID" &_
" WHERE product_attributes.ProductID = '" & CleanSQLStr(AProductID) & "' AND attributes.AttributeName = '" & CleanSQLStr(AAttributeName) & "'"
LResult = GetSQLValueAsString(LSQL)
Else
LResult = ""
End If
GetProductAttributeByName = LResult
End Function
' (SS,10/5/12) returns true if given attribute exists for given product
Function ProductAttributeExists(AProductID, AAttributeName)
Dim LResult
If ProductAttributesEnabled Then
Dim LSQL
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
LSQL = "SELECT AttributeValue FROM product_attributes pa" &_
" INNER JOIN attributes a ON a.AttributeID = pa.AttributeID" &_
" WHERE pa.ProductID = '" & CleanSQLStr(AProductID) & "' AND a.AttributeName = '" & CleanSQLStr(AAttributeName) & "'"
LResult = GetSQLRecordExists(LSQL)
Else
LResult = False
End If
ProductAttributeExists = LResult
End Function
' (SS,19/8/14) returns true if given option exists for given product
Function ProductOptionExists(AProductID, AOptionName)
Dim LResult
If ProductOptionsEnabled Then
Dim LSQL
LSQL = "SELECT OptionName FROM product_options" &_
" WHERE ProductID = '" & CleanSQLStr(AProductID) & "' AND OptionName = '" & CleanSQLStr(AOptionName) & "'"
LResult = GetSQLRecordExists(LSQL)
Else
LResult = False
End If
ProductOptionExists = LResult
End Function
' (SS,4/10/11) returns the number of items in basket that have the given attribute name and value
' e.g. GetAttributeInBasketCount("Product Type", "Door")
' (SS,5/10/11) bug in checkout, missed tables, product_attributes, attributes from the LOCK TABLES in process order, removed the aliases too for LOCK TABLES
Function GetAttributeInBasketCount(AAttributeName, AAttributeValue)
Dim LSQL
' (SS,18/3/15) added "If InGoogleShoppingDataFeedthis" to handle when doing this check for Google Shopping delivery costs, i.e. there is no basket
If InGoogleShoppingDataFeed Then
LSQL = "SELECT COUNT(*) AS AttributeCount FROM products" &_
" INNER JOIN product_attributes ON product_attributes.ProductID = products.ProductID" &_
" INNER JOIN attributes ON attributes.AttributeID = product_attributes.AttributeID" &_
" WHERE products.ProductID = " & GetGoogleShoppingDataFeedProductID & " AND attributes.AttributeName = '" & CleanSQLStr(AAttributeName) & "' AND product_attributes.AttributeValue = '" & CleanSQLStr(AAttributeValue) & "'"
Else
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
' (SS,31/10/18) added GetShoppingBasketFilter(False) to filter out subproducts
LSQL = "SELECT SUM(Qty) AS AttributeCount FROM shoppingbaskets " &_
" INNER JOIN product_attributes ON product_attributes.ProductID = shoppingbaskets.ProductID" &_
" INNER JOIN attributes ON attributes.AttributeID = product_attributes.AttributeID" &_
" WHERE SessionID = '" + CleanSQLStr(FSessionID) + "' AND attributes.AttributeName = '" & CleanSQLStr(AAttributeName) & "' AND product_attributes.AttributeValue = '" & CleanSQLStr(AAttributeValue) & "'" + GetShoppingBasketFilter(False)
End If
GetAttributeInBasketCount = NZL(GetSQLValue(LSQL)) ' NZL ensures a long and converts null to 0
End Function
' (SS,18/4/12) returns true if collection only products exist in basket
Function HasCollectionOnlyInBasket
Dim LResult
If ProductAttributesEnabled Then
LResult = GetAttributeInBasketCount("Collection Only", "Yes") > 0
Else
LResult = False
End If
HasCollectionOnlyInBasket = LResult
End Function
' (SS,5/7/12) similar to GetAttributeInBasketCount above but can also link to shopping_basket_options table to lookup the OptionValue
' and compares a decimal value; ACompare can be ">", "<", "=", ">=" or "<="
' if AOptionValue is blank then OptionValue is ignored
' (SS,4/10/12) replaced by GetAttributeInBasketMaxOptionValue below (commented out for now)
'Function GetAttributeInBasketCountNumValCompare(AAttributeName, ACompare, AAttributeValue, AOptionValue)
' Dim LSQL
' LSQL = "SELECT SUM(Qty) AS AttributeCount FROM shoppingbaskets " &_
' " INNER JOIN product_attributes ON product_attributes.ProductID = shoppingbaskets.ProductID" &_
' " INNER JOIN attributes ON attributes.AttributeID = product_attributes.AttributeID"
' If AOptionValue <> "" Then LSQL = LSQL + " INNER JOIN shopping_basket_options ON shopping_basket_options.ItemID = shoppingbaskets.ItemID"
' LSQL = LSQL &_
' " WHERE SessionID = '" + CleanSQLStr(FSessionID) + "' AND attributes.AttributeName = '" & CleanSQLStr(AAttributeName) &_
' "' AND CONVERT(product_attributes.AttributeValue, DECIMAL(12,4)) " & ACompare & "'" & CleanSQLStr(AAttributeValue) & "'"
' If AOptionValue <> "" Then LSQL = LSQL + " AND CONVERT(shopping_basket_options.OptionValue, DECIMAL(12,4)) " & ACompare & "'" & CleanSQLStr(AOptionValue) & "'"
' GetAttributeInBasketCountNumValCompare = NZL(GetSQLValue(LSQL)) ' NZL ensures a long and converts null to 0
'End Function
' (SS,4/10/12) replaces above GetAttributeInBasketCountNumValCompare
' returns max option value (inital integer is parsed), if no option value exists but attribute exists then 0 is return
' if attribute doesn't exist at all then -1 is return
Function GetAttributeInBasketMaxOptionValue(AAttributeName)
Dim LSQL, LAttributeCount, LMaxValue, LResult
LSQL = "SELECT SUM(Qty) AS AttributeCount, MAX(CONVERT(shopping_basket_options.OptionValue, DECIMAL(12,4))) AS MaxLength FROM shoppingbaskets " &_
" INNER JOIN product_attributes ON product_attributes.ProductID = shoppingbaskets.ProductID" &_
" INNER JOIN attributes ON attributes.AttributeID = product_attributes.AttributeID" &_
" LEFT JOIN shopping_basket_options ON shopping_basket_options.ItemID = shoppingbaskets.ItemID"
LSQL = LSQL &_
" WHERE SessionID = '" + CleanSQLStr(FSessionID) + "' AND attributes.AttributeName = '" & CleanSQLStr(AAttributeName) & "'"
If Not GetSQL2Values(LSQL, LAttributeCount, LMaxValue) Then
LResult = -1 ' this will never actually occur because aggregate query always returns one record
ElseIf NZL(LMaxValue) > 0 Then ' NZL ensures a long and converts null to 0
LResult = NZL(LMaxValue)
ElseIf NZL(LAttributeCount) > 0 Then
LResult = 0
Else
LResult = -1
End If
GetAttributeInBasketMaxOptionValue = LResult
End Function
' (SS,17/7/13)
Function GetAttributeIDForName(AAttributeName)
GetAttributeIDForName = GetSQLValueAsString("SELECT AttributeID FROM attributes WHERE AttributeName = '" & CleanSQLStr(AAttributeName) & "'")
End Function
' (SS,19/10/18) added following to lookup an option value for given option name and basket item.
' Used by subproduct feature for CIRC (custom) to look up paint finish and sections in given basket item
' value is returned as a string
Function GetBasketOptionValueForItemID(AItemID, AOptionName)
GetBasketOptionValueForItemID = GetSQLValueAsString("SELECT OptionValue FROM shopping_basket_options WHERE ItemID = '" & CleanSQLStr(AItemID) & "' AND OptionName = '" & CleanSQLStr(AOptionName) & "'")
End Function
' (SS,30/10/18) added following to look up option value for given option name and order detail line
' Used by custom utils (CIRC) to help create historical subproduct records
Function GetOrderOptionValueForOrderDetailID(AOrderDetailID, AOptionName)
GetOrderOptionValueForOrderDetailID = GetSQLValueAsString("SELECT OptionValue FROM order_detail_options WHERE OrderDetailID = '" & CleanSQLStr(AOrderDetailID) & "' AND OptionName = '" & CleanSQLStr(AOptionName) & "'")
End Function
' ------------------------------ '
' End of Product Option Routines '
' ------------------------------ '
' (SS,19/7/11)
' -------------------------------- '
' Start of Special Basket Routines '
' Used by e.g. CovDem radiator builder
' -------------------------------- '
' looks up price and stock level of given product id, returns true if product in stock
Function GetPriceAndStockForProductID(AProductID, AQty, ByRef AProductPrice)
Dim LResult
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
OpenQuery("SELECT * FROM products WHERE ProductID = '" & CleanSQLStr(AProductID) & "'")
GetPricingFromProductRecord(False)
AProductPrice = GetProductPrice
LResult = InStock(GetQueryValue("NumInStock"))
CloseQuery
GetPriceAndStockForProductID = LResult
End Function
' looks up price of given product and option value, returns true if found, AOptionPrice is set to the price
Function GetPriceForProductOptionValue(AProductID, AOptionValue, ByRef AOptionPrice)
Dim LResult
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
AOptionPrice = GetSQLValueAsString("SELECT PriceAdjust FROM product_option_values WHERE ProductID = '" & CleanSQLStr(AProductID) & "' AND OptionValue = '" & CleanSQLStr(AOptionValue) & "'")
If AOptionPrice = "" Then
LResult = False
AOptionPrice = 0
Else
LResult = True
' (SS,24/11/11) added CDbl because GetSQLValueAsString now definitely returns a string, found that it wasn't really necessary because FormatNumber in Round2dp doesn't mind a string
AOptionPrice = Round2dp(CDbl(AOptionPrice))
End If
GetPriceForProductOptionValue = LResult
End Function
' looks up product option id and value id of given product and option value, returns false if not found
Function GetIDsForProductOptionValue(AProductID, AOptionValue, ByRef AProductOptionID, ByRef AProductOptionValueID)
' (SS,12/6/12) added CleanSQLStr to prevent SQL injection
GetIDsForProductOptionValue = GetSQL2Values("SELECT ProductOptionID, ProductOptionValueID FROM product_option_values WHERE ProductID = '" & CleanSQLStr(AProductID) & "' AND OptionValue = '" & CleanSQLStr(AOptionValue) & "'", AProductOptionID, AProductOptionValueID)
End Function
' returns true if it's an ajax call, i.e. ajax.asp requested
Function IsAjaxRequest
IsAjaxRequest = GetScriptName = "ajax.asp"
End Function
' ------------------------------ '
' End of Special Basket Routines '
' ------------------------------ '
' (SS,30/5/12) called from stock-snapshot.asp adds records to the stock_snapshots table the NumInStock value for each product
Sub StockSnapshot
Dim LSQL
LSQL = "INSERT INTO stock_snapshots " +_
"SELECT CURDATE() AS DateOfStock, CURTIME() AS TimeOfStock, ProductID, NumInStock, ProductDisabled FROM products " +_
"WHERE NOT ProductDisabled OR (ProductDisabled AND NumInStock <> 0)"
ExecuteQuery(LSQL)
End Sub
' (SS,18/6/12) returns SQL used to encrypt the password, password must be passed to this function
' (SS,19/6/12) added AIsField to allow it to be called from UpgradeSchemaTo159 where it encrypts the password for all customer records
Function GetSQLForPasswordEncryption(APassword, AIsField)
Const PWD_KEY = "It@Q2SHNx{UTwq4D,EtGi;q6tKR,c+{*" ' password encryption key
Dim LResult, LPassword
LPassword = CleanSQLStr(APassword)
If Not AIsField Then LPassword = "'" + LPassword + "'"
LResult = "" &_
"AES_ENCRYPT(" &_
" SHA1(" &_
" CONCAT(" & LPassword & ", '" & PWD_KEY & "', " &_
" SHA1(CONCAT('itp~h', CustomerID * 3 + 1, 'itp~h', CRC32(CustomerID + 157)))))" &_
"," &_
" CONCAT('" & PWD_KEY & "', SHA1(CONCAT('itp~e', CustomerID * 2 - 1, 'itp~e', CRC32(CustomerID + 943)))" &_
" )" &_
")"
GetSQLForPasswordEncryption = LResult
End Function
' (SS,13/9/12) new favourites functionality
Sub ShowFavourites
' get customer ID from session, exit if not available
Dim LCustomerID
LCustomerID = GetCustomerID ' (SS,20/9/12) replaced GetSetCookieCustomerID with GetCustomerID
If LCustomerID = "" Then Exit Sub
Dim LSQL
' following retrieves all products ordered by customer in past 3 years, ordered by ave order qty, first 10
' perhaps holds these in session to run query one
' (SS,31/10/18) added GetOrderDetailsFilter to filter out subproducts
LSQL = "SELECT od.ProductCode, p.ProductName, COUNT(*) AS OrderCount, ROUND(AVG(Qty)) AS AveQty FROM orderdetails od" +_
" INNER JOIN orders o ON o.OrderNo = od.OrderNo" +_
" INNER JOIN products p ON p.ProductCode = od.ProductCode" +_
" WHERE CustomerID = '" & CleanSQLStr(LCustomerID) & "' AND Status = 'COMPLETED' AND DateTimeOrdered >= CURDATE() - INTERVAL 3 YEAR AND NOT p.ProductDisabled" + GetOrderDetailsFilter +_
" GROUP BY ProductCode" +_
" ORDER BY OrderCount DESC, AveQty DESC, ProductCode" +_
" LIMIT 10"
OpenQuery(LSQL)
If GetQueryRecordCount > 0 Then
ShowFavouritesHeader
Dim LCount, LProductCode
LCount = 0
Do While Not EndOfQuery
LCount = LCount + 1
LProductCode = GetQueryField("ProductCode")
ShowFavouritesItem LCount, LProductCode, HTMLEncode(GetQueryField("ProductName")), GetQueryField("AveQty")
NextQueryRecord
Loop
ShowFavouritesFooter
End If
CloseQuery
End Sub
' --------------------------------
' Start of Review Request Routines
' --------------------------------
' (SS,29/8/12) currently galleon only, will eventually get from settings
Function GetFeefoLogon
' GetFeefoLogon = "www.galleonsupplies.co.uk"
GetFeefoLogon = FFeefoLogon ' (SS,20/10/14) replaced above hardcoded with this
End Function
' (SS,27/9/12)
Function GetFeefoFTPHost
' GetFeefoFTPHost = "ftp.feefo.com"
GetFeefoFTPHost = FFeefoFTPHost ' (SS,20/10/14) replaced above hardcoded with this
End Function
' (SS,25/9/12)
Function GetFeefoFTPUser
' GetFeefoFTPUser = "www-galleonsupplies-co-uk@feefo.com"
GetFeefoFTPUser = FFeefoFTPUser ' (SS,20/10/14) replaced above hardcoded with this
End Function
' (SS,25/9/12)
Function GetFeefoFTPPassword
' GetFeefoFTPPassword = "Js8ko9~2t#8"
GetFeefoFTPPassword = FFeefoFTPPassword ' (SS,20/10/14) replaced above hardcoded with this
End Function
' (SS,25/9/12) returns True for Galleon, will eventually get from settings
Function ReviewTypeIsFeefo
' If GetStoreName = "Galleon Supplies" Then
If FReviewsType = "Feefo" Then ' (SS,20/10/14) replaced above with this
ReviewTypeIsFeefo = True
Else
ReviewTypeIsFeefo = False
End If
End Function
' (SS,17/8/15)
Function ReviewTypeIsNormal
If FReviewsType = "Normal" Or FReviewsType = "" Then
ReviewTypeIsNormal = True
Else
ReviewTypeIsNormal = False
End If
End Function
' (SS,17/8/15)
Function ReviewTypeIsTrustpilot
If FReviewsType = "Trustpilot" Then
ReviewTypeIsTrustpilot = True
Else
ReviewTypeIsTrustpilot = False
End If
End Function
' (SS,18/5/17)
Function GetTrustpilotAFSEmailAddress
GetTrustpilotAFSEmailAddress = FTrustpilotAFSEmailAddress
End Function
' (SS,1/9/15)
Function GetTrustpilotRating(ByRef ARatingWord)
Dim LRating
If Not GetSQL2Values("SELECT ReviewRating, ReviewTitle FROM trustpilot_reviews WHERE ReviewID = 1", LRating, ARatingWord) Then
LRating = 0
ARatingWord = "Not found"
End If
GetTrustpilotRating = LRating
End Function
' (SS,25/9/12) send review requests, send 7 days after order completed, if date completed is >= 14 Dec and <= 31 Dec then delay until feedback date to 7 Jan check year
' (SS,13/1/20) added new test modes via SEND_EMAIL and UPDATE_ORDER so that it doesn't send the emails and modify the orders when these constants are set to false
' (SS,13/1/20) the CIRC customers getting emails for 4 days in a row was caused by script not completing i.e. timing out, fixed by adding Server.ScriptTimeout = 5 * 60 to review-requests.asp script.
' (SS,14/1/20) further mod (for HF) to remove the >= 14 Dec and <= 31 Dec (Christmas delay), added different delay for non-uk orders (for HF) if set
Sub SendReviewRequests
Const PRODUCT_LEVEL_DEBUG = False
Const TEST_MODE = False
Const SEND_EMAIL = True
Const UPDATE_ORDER = True
' (SS,15/1/13)
If Not ReviewRequestsEnabled Then Exit Sub
' (SS,1/3/13) if order number specified then only send to this, for testing purposes
Dim LRequestedOrderNo
LRequestedOrderNo = CleanRequest("OrderNo")
' list of orders items in order number / order detail id order
' only listing those that need a review request email sent (or upload to Feefo)
' (SS,31/10/18) added GetProductsFilter to filter out subproducts
' (SS,14/1/20) added left join to countries to lookup postal area to use different delay for non-uk orders (HF)
Dim LSQL, LWhere
LSQL = "SELECT EmailAddress, TRIM(CONCAT(Title, ' ', FirstName, ' ', Surname)) AS FullName," &_
" DATE(DateTimeOrdered) AS DateOrdered," &_
" DATE(DateTimeCompleted) AS DateCompleted," &_
" od.ProductName, o.OrderNo, od.ProductCode," &_
" pc.Category, o.CustomerID, Qty * PriceEach AS Amount" &_
" FROM orders o" &_
" INNER JOIN orderdetails od ON od.OrderNo = o.OrderNo" &_
" INNER JOIN products p ON p.ProductCode = od.ProductCode" &_
" LEFT JOIN productcategories pc ON pc.ProductCode = od.ProductCode AND pc.Main = TRUE" &_
" LEFT JOIN countries c ON c.Country = IF(COALESCE(o.DeliveryCountry, '') = '', o.Country, o.DeliveryCountry)" &_
" WHERE p.ProductDisabled = FALSE" & GetProductsFilter & " AND "
' (SS,1/3/13) added If/Else
If LRequestedOrderNo = "" Then
' build WHERE which is also used by UPDATE query later to mark orders with review request sent date
LWhere = "Status = 'COMPLETED' AND ReviewRequestSentDate IS NULL"
' less than 60 days ago, CURDATE() not used in case it passes over midnight between the two queries
LWhere = LWhere & " AND DATEDIFF(" & ConvertUKToMySQLDate(Date) & ", DATE(DateTimeCompleted)) < 60"
' if DateOrdered >= 14 Dec and DateOrder <= 31 Dec then set to 7 Jan of next year else add 7 days
' CAST used because it wouldn't treat number from ConvertUKToMySQLDate as date otherwise, probably due to IF()
' (SS,15/1/13) replaced 7 in "INTERVAL 7 DAY" with ReviewRequestsDays
'LWhere = LWhere & " AND" &_
' " IF(MONTH(DATE(DateTimeCompleted)) = 12 AND DAYOFMONTH(DATE(DateTimeCompleted)) >= 14 AND DAYOFMONTH(DATE(DateTimeCompleted)) <= 31," &_
' " STR_TO_DATE(CONCAT_WS('-', YEAR(DATE(DateTimeCompleted)) + 1, 1, 7), '%Y-%m-%d'), " &_
' " DATE_ADD(DATE(DateTimeCompleted), INTERVAL " & ReviewRequestsDays & " DAY)) <= CAST(" & ConvertUKToMySQLDate(Date) & " AS DATE)"
' (SS,14/1/20) replaced above with following, i.e. removed the delay between 14/12 and 7/1, added difference delay for non uk orders (for HyperFlight)
LWhere = LWhere & " AND" &_
" IF(c.PostalArea = 'UK', DATE_ADD(DATE(DateTimeCompleted), INTERVAL " & ReviewRequestsDays & " DAY), DATE_ADD(DATE(DateTimeCompleted), INTERVAL " & ReviewRequestsDaysNonUK & " DAY)) = CAST(" & ConvertUKToMySQLDate(Date) & " AS DATE)"
' if test mode then only test with orders with my email address
If TEST_MODE Then LWhere = LWhere & " AND EmailAddress = 'surinder@itpartnership.com'"
Else ' (SS,1/3/13) also added o to the update query called later, it'll fail without it
LWhere = LWhere & "o.OrderNo = '" & CleanSQLStr(LRequestedOrderNo) & "'"
End If
LSQL = LSQL & LWhere & " ORDER BY o.OrderNo, OrderDetailID"
' iterate each order line
' upload to feefo or build email body
' if successful then set ReviewRequestSentDate so it isn't sent again
' if next order then
' add to list of requests sent
Dim LReviewTypeIsFeefo
LReviewTypeIsFeefo = ReviewTypeIsFeefo
If LReviewTypeIsFeefo Then SetFeefoFileName
OpenQuery(LSQL)
If Not EndOfQuery Then
If PRODUCT_LEVEL_DEBUG Then
Response.Write "<table>" & NL
Response.Write "<tr>"
Response.Write "<th>Email</th>"
Response.Write "<th>Name</th>"
Response.Write "<th>OrderNo</th>"
Response.Write "<th>OrderDate</th>"
Response.Write "<th>CustomerID</th>"
Response.Write "<th>ProductCode</th>"
Response.Write "<th>ProductName</th>"
Response.Write "<th>Category</th>"
Response.Write "<th>Amount</th>"
Response.Write "<th>ProductLink</th>"
End If
Response.Write "</tr>" & NL
Dim LOrderCount, LPrevOrderNo, LOrderCompleted, LEmailBody, LProductList
LOrderCount = 0
LEmailBody = ""
LProductList = ""
Dim LEmailAddress, LName, LDateOrdered, LDateCompleted, LOrderNo, LProductCode, LProductName, LCategory, LProductLink, LCustomerID, LAmount
Dim LFeefoHeaderAdded, LFeefoRecord
LFeefoHeaderAdded = False
Do While Not EndOfQuery
' added header record with field names if it hasn't already
If LReviewTypeIsFeefo And Not LFeefoHeaderAdded Then
LFeefoRecord = ""
LFeefoRecord = AddFeefoField(LFeefoRecord, "Name")
LFeefoRecord = AddFeefoField(LFeefoRecord, "Email")
LFeefoRecord = AddFeefoField(LFeefoRecord, "Date")
LFeefoRecord = AddFeefoField(LFeefoRecord, "Description")
LFeefoRecord = AddFeefoField(LFeefoRecord, "Logon")
LFeefoRecord = AddFeefoField(LFeefoRecord, "Category")
LFeefoRecord = AddFeefoField(LFeefoRecord, "Feedback Date")
LFeefoRecord = AddFeefoField(LFeefoRecord, "Product search code")
LFeefoRecord = AddFeefoField(LFeefoRecord, "Order Ref")
LFeefoRecord = AddFeefoField(LFeefoRecord, "Product link")
LFeefoRecord = AddFeefoField(LFeefoRecord, "Customer Ref")
LFeefoRecord = AddFeefoField(LFeefoRecord, "Amount")
AddFeefoRecord LFeefoRecord
LFeefoHeaderAdded = True
End If
LEmailAddress = GetQueryField("EmailAddress")
LName = GetQueryField("FullName")
LDateOrdered = GetQueryField("DateOrdered")
LDateCompleted = GetQueryField("DateCompleted")
LOrderNo = GetQueryField("OrderNo")
LProductCode = GetQueryField("ProductCode")
LProductName = GetQueryField("ProductName")
LCategory = GetQueryField("Category")
LCustomerID = NB(GetQueryField("CustomerID")) ' (SS,4/2/14) added NB to prevent Invalid use of Null CStr when customer ID is null
LAmount = Format2dpnc(GetQueryField("Amount"))
LProductLink = GetFullProductLink(LProductCode)
If LReviewTypeIsFeefo Then
LFeefoRecord = ""
LFeefoRecord = AddFeefoField(LFeefoRecord, LName) ' Name
LFeefoRecord = AddFeefoField(LFeefoRecord, LEmailAddress) ' Email
LFeefoRecord = AddFeefoField(LFeefoRecord, LDateOrdered) ' Date (date needs DD/MM/YYYY which it will by default)
LFeefoRecord = AddFeefoField(LFeefoRecord, LProductName) ' Description
LFeefoRecord = AddFeefoField(LFeefoRecord, GetFeefoLogon) ' Logon
LFeefoRecord = AddFeefoField(LFeefoRecord, ReplaceStr(LCategory, "/", "-")) ' Category (forward slash replaced with dash as recommended)
LFeefoRecord = AddFeefoField(LFeefoRecord, "") ' Feedback Date (leave blank)
LFeefoRecord = AddFeefoField(LFeefoRecord, LProductCode) ' Product search code
LFeefoRecord = AddFeefoField(LFeefoRecord, LOrderNo) ' Order Ref
LFeefoRecord = AddFeefoField(LFeefoRecord, LProductLink) ' Product link
LFeefoRecord = AddFeefoField(LFeefoRecord, LCustomerID) ' Customer Ref
LFeefoRecord = AddFeefoField(LFeefoRecord, LAmount) ' Amount
AddFeefoRecord LFeefoRecord
' (SS,2/9/15) added following if review type is Trustpilot, i.e. there is just a list of products, no link to write a review directly on site
ElseIf ReviewTypeIsTrustpilot Then
LProductList = LProductList & "<li>" & LProductName & "</li>" & NL
Else
LProductList = LProductList & Iif(LProductList = "", "", BR) & "<a href=""" & GetWriteReviewFullLink(LProductCode) & """>" & LProductName & "</a>" & NL
End If
If PRODUCT_LEVEL_DEBUG Then
Response.Write "<tr>"
Response.Write "<td>" & LEmailAddress & "</td>"
Response.Write "<td>" & LName & "</td>"
Response.Write "<td>" & LOrderNo & "</td>"
Response.Write "<td>" & LDateOrdered & "</td>"
Response.Write "<td>" & LCustomerID & "</td>"
Response.Write "<td>" & LProductCode & "</td>"
Response.Write "<td>" & LProductName & "</td>"
Response.Write "<td>" & LCategory & "</td>"
Response.Write "<td>" & LAmount & "</td>"
Response.Write "<td>" & LProductLink & "</td>"
Response.Write "</tr>" & NL
End If
NextQueryRecord
' if order has been dealt with, i.e. no more order lines for same order
If EndOfQuery Then
LOrderCompleted = True
ElseIf LOrderNo <> GetQueryField("OrderNo") Then
LOrderCompleted = True
Else ' i.e. LOrderNo = GetQueryField("OrderNo")
LOrderCompleted = False
End If
If LOrderCompleted Then
LOrderCount = LOrderCount + 1
' add table header
If LOrderCount = 1 Then
LEmailBody = LEmailBody + ReviewRequestResultHeader
End If
LEmailBody = LEmailBody + ReviewRequestResultRow(LEmailAddress, LName, LOrderNo, LDateOrdered, LDateCompleted, LCustomerID)
' if not feefo then email the review request
' (SS,13/1/20) added SEND_EMAIL
If Not LReviewTypeIsFeefo And SEND_EMAIL Then
SendReviewRequestEmail LEmailAddress, LName, LProductList, LOrderNo ' (SS,2/11/12) added LOrderNo
End If
LProductList = ""
End If
Loop
If PRODUCT_LEVEL_DEBUG Then
Response.Write "</table>" & NL
End If
' if orders exist
If LOrderCount > 0 Then
LEmailBody = LEmailBody + ReviewRequestResultFooter(LOrderCount)
Dim LSuccess
' if Feefo then FTP the review request to Feefo
If LReviewTypeIsFeefo Then
Dim LFTPLog
LSuccess = FTPFile(GetFeefoFTPHost, GetFeefoFTPUser, GetFeefoFTPPassword, True, "", FFeefoFileName, LFTPLog)
LEmailBody = LEmailBody + BR + "FTP " + Iif(LSuccess, "Successful", "Failed") + BR + ReplaceNewLinesWithBR(LFTPLog)
Else
LSuccess = True
End If
' if successful then mark order with today's date in ReviewRequestSentDate to prevent sending again
' (SS,13/1/20) added UPDATE_ORDER for testing
If LSuccess And UPDATE_ORDER Then
ExecuteQuery "UPDATE orders o SET ReviewRequestSentDate = CURDATE() WHERE " & LWhere
End If
If LReviewTypeIsFeefo Then
EmailReviewRequestsResult LEmailBody, FFeefoFileName
DeleteFile FFeefoFileName ' not required anymore so delete it
Else
EmailReviewRequestsResult LEmailBody, ""
End If
End If
End If
CloseQuery
End Sub
' (SS,28/9/12) email the results of the review request process
' *** email addresses to be corrected
Sub EmailReviewRequestsResult(ABody, AAttachedFileName)
Dim LSubject
LSubject = "Review Requests Result for " + GetStoreName + " on " & Now
' SendEmailByCDO used because it can handle attachments
' ATo, ABcc, ABcc2, AFrom, ASubject, ABody, AFiles, AIsHTML, AUrl
' FEmailContactTo, FEmailContactBcc1
' FEmailContactBcc1, ""
SendEmailByCDO FEmailContactTo, FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom, LSubject, ABody, AAttachedFileName, True, "", ""
End Sub
' (SS,28/9/12)
Function ReviewRequestResultHeader
' NL used at end of each HTML line because emailer is adding a space after 1000 characters
Dim LResult
LResult = "<table border=1 cellspacing=0>" & NL
LResult = LResult & "<tr><th colspan=""6"">" & "Review Requests" & "</th></tr>" & NL
LResult = LResult & "<tr>" & NL
LResult = LResult & "<th>Email</th>" & NL
LResult = LResult & "<th>Name</th>" & NL
LResult = LResult & "<th>OrderNo</th>" & NL
LResult = LResult & "<th>Date Ordered</th>" & NL
LResult = LResult & "<th>Date Completed</th>" & NL
LResult = LResult & "<th>CustomerID</th>" & NL
LResult = LResult & "</tr>" & NL
Response.Write LResult
ReviewRequestResultHeader = LResult
End Function
' (SS,28/9/12)
Function ReviewRequestResultFooter(ACount)
Dim LResult
LResult = "<tr><td colspan=""6"">Reviews requested for " & PluralString(ACount, "order", "orders") & "</td></tr>" & NL
LResult = LResult & "</table>" & NL
Response.Write LResult
ReviewRequestResultFooter = LResult
End Function
' (SS,28/9/12)
Function ReviewRequestResultRow(AEmail, AName, AOrderNo, ADateOrdered, ADateCompleted, ACustomerID)
Dim LResult
LResult = "<tr>" & NL
LResult = LResult & "<td>" & AEmail & "</td>" & NL
LResult = LResult & "<td>" & AName & "</td>" & NL
LResult = LResult & "<td>" & AOrderNo & "</td>" & NL
LResult = LResult & "<td>" & ADateOrdered & "</td>" & NL
LResult = LResult & "<td>" & ADateCompleted & "</td>" & NL
LResult = LResult & "<td>" & ACustomerID & "</td>" & NL
LResult = LResult & "</tr>" & NL
Response.Write LResult
ReviewRequestResultRow = LResult
End Function
' (SS,14/3/12) created from code in SendStockNotificationEmails above, to make it easier to read, also returns the output HTML
' (SS,2/11/12) added AOrderNo
Sub SendReviewRequestEmail(AEmail, ACustomerName, AProductList, AOrderNo)
Const LOGO_FILE_NAME = "logo-review-request.png"
Dim LSubject, LBody
LSubject = "Review Request from " & GetStoreName & " - " & AOrderNo ' (SS,2/11/12) added AOrderNo
LBody = GetSiteDetail("Other", "ReviewRequestTemplate")
If LBody <> "" Then ' only send if template not blank
LBody = ReplaceStr(LBody, "{CUSTOMER_NAME}", ACustomerName)
LBody = ReplaceStr(LBody, "{PRODUCT_LIST}", AProductList)
LBody = ReplaceStr(LBody, "{LOGO}", "<img src=""" + LOGO_FILE_NAME + """>")
' send the email with embedded logo
' SendEmail FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom, LSubject, LBody, True
' "surinder@itpartnership.com", "",
' AEmail, FEmailContactBcc1
SendEmailByCDO AEmail, FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom, LSubject, LBody, "", True, "", LOGO_FILE_NAME
' (SS,20/3/18) send SMS, which will only be sent by SendSMS if SMSs are enabled and telephone is a mobile number
SendSMS AOrderNo, ST_REVIEW_REQUEST
End If
End Sub
' (SS,26/9/12)
Sub SetFeefoFileName
Dim LDate
LDate = Date
FFeefoFileName = TEMP_FOLDER & "feefo_sales_" & Year(LDate) & "-" & Right("0" & Month(LDate), 2) & "-" & Right("0" & Day(LDate), 2) & ".txt"
DeleteFile FFeefoFileName ' delete file if it already exists
End Sub
' (SS,26/9/12) adds tab separated field value to given record string
Function AddFeefoField(ARecordStr, AFieldValue)
Dim LFieldStr
LFieldStr = TrimAll(CStr(AFieldValue)) ' converts to string and removes all leading/trailing spaces and control chars
LFieldStr = ReplaceStr(LFieldStr, Chr(9), " ") ' replace all tabs in field with a space, otherwise it'll break the file format
AddFeefoField = ARecordStr + Iif(ARecordStr = "", "", Chr(9)) + LFieldStr ' add field separated by a tab, if record empty then no tab is added
End Function
' (SS,26/9/12) save record to file
Sub AddFeefoRecord(ARecordStr)
AddRecordToFile ARecordStr, FFeefoFileName, True
End Sub
' (SS,18/5/17)
Sub SendTrustpilotAFSEmail(AName, ARefID, AEmailAddress)
Dim LBody, LScript
LScript = "<script type=""application/json+trustpilot"">" & NL
LScript = LScript & "{" & NL
LScript = LScript & " ""recipientName"": """ & HTMLEncode(AName) & """," & NL
LScript = LScript & " ""referenceId"": """ & ARefID & """," & NL
LScript = LScript & " ""recipientEmail"": """ & AEmailAddress & """" & NL
LScript = LScript & "}" & NL
LScript = LScript & "</script>"
LBody = "<html>" & NL
LBody = LBody & "<head>" & NL
LBody = LBody & LScript & NL
LBody = LBody & "</head>" & NL
LBody = LBody & "<body>" & NL
LBody = LBody & "<pre>"
LBody = LBody & HTMLEncode(LScript) & NL
LBody = LBody & "</pre>" & NL
LBody = LBody & "</body>" & NL
LBody = LBody & "</html>" & NL
If AEmailAddress <> "" Then
Dim LBcc1, LBcc2, LFrom
LBcc1 = FEmailOrderConfirmationBcc1
LBcc2 = FEmailOrderConfirmationBcc2
LFrom = FEmailOrderConfirmationFrom
' ignore BCC to store (i.e. same as LFrom address)
If LBcc1 = LFrom Then LBcc1 = ""
If LBcc2 = LFrom Then LBcc2 = ""
SendEmailByCDO GetTrustpilotAFSEmailAddress, LBcc1, LBcc2, LFrom, "Trustpilot AFS Email from " & GetStoreName, LBody, "", True, "", ""
End If
End Sub
' ------------------------------
' End of Review Request Routines
' ------------------------------
' --------------------------------
' Start of Order Reminder Routines
' --------------------------------
' (SS,28/11/12) send order reminders, send 30 days after customer last ordered ordered, send again every 30 days, perhaps don't send during Christmas period
Sub SendOrderReminders
' don't send between 21 Dec and 7 Jan, i.e. Christmas break
If Month(Date) = 12 And Day(Date) >= 21 Or Month(Date) = 1 And Day(Date) <= 7 Then Exit Sub
Const TEST_MODE = False
Const MIN_DAYS_SINCE_LAST_ORDER = 30
' list of customer who haven't ordered between min days and max days ago
' if order reminder sent in past 30 days then don't send again, if OrderReminderOptOut is true then customer has opted out, so don't send any more reminders
' maximum of 500 are sent at any one time, with newest first
Dim LSQL
' (SS,19/12/12) replaced WHERE Status = 'COMPLETED' with WHERE (Status = '" & ORDER_COMPLETED_STATUS & "' OR Status = '" & PAYMENT_RECEIVED_STATUS & "')"
LSQL = "SELECT c.CustomerID, c.EmailAddress, TRIM(CONCAT(c.Title, ' ', c.FirstName, ' ', c.Surname)) AS FullName, MAX(DATE(DateTimeOrdered)) AS DateLastOrdered, MAX(o.OrderNo) AS LastOrderNo, OrderReminderSentDate" &_
" FROM orders o" &_
" INNER JOIN orderdetails od ON od.OrderNo = o.OrderNo" &_
" INNER JOIN customers c ON c.CustomerID = o.CustomerID" &_
" WHERE (Status = '" & ORDER_COMPLETED_STATUS & "' OR Status = '" & PAYMENT_RECEIVED_STATUS & "')" &_
" AND NOT OrderReminderOptOut AND (OrderReminderSentDate IS NULL OR DATEDIFF(CURDATE(), OrderReminderSentDate) > " & MIN_DAYS_SINCE_LAST_ORDER & ")" &_
" GROUP BY CustomerID, EmailAddress, FullName" &_
" HAVING DATEDIFF(CURDATE(), DateLastOrdered) > " & MIN_DAYS_SINCE_LAST_ORDER
' if test mode then only test with orders with my email address
If TEST_MODE Then LSQL = LSQL + " AND (EmailAddress = 'surinder@itpartnership.com' OR EmailAddress = 'srinda@gmail.com')"
'If TEST_MODE Then LSQL = LSQL + " AND (EmailAddress = 'surinder@itpartnership.com' OR EmailAddress = 'srinda@gmail.com' OR EmailAddress = 'tim@galleonsupplies.com')"
LSQL = LSQL + " ORDER BY MAX(DateTimeOrdered) DESC" &_
" LIMIT 500"
' iterate each customer
' build email body and sent email
' if successful then set OrderReminderSentDate so it isn't sent again for another 30 days
' add to list of order reminders sent
OpenQuery(LSQL)
If Not EndOfQuery Then
Dim LEmailBody, LReminderCount
LEmailBody = ""
LReminderCount = 0
Dim LCustomerID, LEmailAddress, LName, LDateLastOrdered
Do While Not EndOfQuery
LCustomerID = GetQueryField("CustomerID")
LEmailAddress = GetQueryField("EmailAddress")
LName = GetQueryField("FullName")
LDateLastOrdered = GetQueryField("DateLastOrdered")
LReminderCount = LReminderCount + 1
' add table header
If LReminderCount = 1 Then
LEmailBody = LEmailBody + OrderReminderResultHeader
End If
' add the row
LEmailBody = LEmailBody + OrderReminderResultRow(LCustomerID, LEmailAddress, LName, LDateLastOrdered, GetQueryField("OrderReminderSentDate"))
' send the email
SendOrderReminderEmail LCustomerID, LEmailAddress, LName, LDateLastOrdered, GetQueryField("LastOrderNo")
' change the OrderReminderSentDate to today so that we don't send again for another 30 days
ExecuteQuery "UPDATE customers SET OrderReminderSentDate = CURDATE() WHERE CustomerID = '" & CleanSQLStr(LCustomerID) & "'"'
NextQueryRecord
Loop
' if reminders sent then email the results
If LReminderCount > 0 Then
LEmailBody = LEmailBody + OrderReminderResultFooter(LReminderCount)
EmailOrderRemindersResult LEmailBody, ""
End If
End If
CloseQuery
End Sub
' (SS,29/11/12) email the results of the order reminders process
Sub EmailOrderRemindersResult(ABody, AAttachedFileName)
Dim LSubject
LSubject = "Order Reminders Result for " + GetStoreName + " on " & Now
' SendEmailByCDO used because it can handle attachments
' ATo, ABcc, ABcc2, AFrom, ASubject, ABody, AFiles, AIsHTML, AUrl
' FEmailContactTo, FEmailContactBcc1
' FEmailContactBcc1, ""
SendEmailByCDO FEmailContactTo, FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom, LSubject, ABody, AAttachedFileName, True, "", ""
End Sub
' (SS,29/11/12)
Function OrderReminderResultHeader
' NL used at end of each HTML line because emailer is adding a space after 1000 characters
Dim LResult
LResult = "<table border=1 cellspacing=0>" & NL
LResult = LResult & "<tr><th colspan=""5"">" & "Order Reminders" & "</th></tr>" & NL
LResult = LResult & "<tr>" & NL
LResult = LResult & "<th>CustomerID</th>" & NL
LResult = LResult & "<th>Email</th>" & NL
LResult = LResult & "<th>Name</th>" & NL
LResult = LResult & "<th>Last Ordered</th>" & NL
LResult = LResult & "<th>Order Reminder Sent</th>" & NL
LResult = LResult & "</tr>" & NL
Response.Write LResult
OrderReminderResultHeader = LResult
End Function
' (SS,29/11/12)
Function OrderReminderResultFooter(ACount)
Dim LResult
LResult = "<tr><td colspan=""5"">Order reminders for " & PluralString(ACount, "customer", "customers") & "</td></tr>" & NL
LResult = LResult & "</table>" & NL
Response.Write LResult
OrderReminderResultFooter = LResult
End Function
' (SS,29/11/12)
Function OrderReminderResultRow(ACustomerID, AEmail, AName, ADateLastOrdered, AOrderReminderSentDate)
Dim LResult
LResult = "<tr>" & NL
LResult = LResult & "<td>" & ACustomerID & "</td>" & NL
LResult = LResult & "<td>" & AEmail & "</td>" & NL
LResult = LResult & "<td>" & AName & "</td>" & NL
LResult = LResult & "<td>" & ADateLastOrdered & "</td>" & NL
LResult = LResult & "<td>" & AOrderReminderSentDate & "</td>" & NL
LResult = LResult & "</tr>" & NL
Response.Write LResult
OrderReminderResultRow = LResult
End Function
' (SS,29/11/12)
' (SS,31/1/13) changes to work correctly in gmail because gmail ignores the stylesheet set in the header, i.e. only works with inline styles
Sub SendOrderReminderEmail(ACustomerID, AEmail, ACustomerName, ADateLastOrdered, LLastOrderNo)
Const LOGO_FILE_NAME = "logo-email.png" ' (SS,31/1/13) changed from "logo-review-request.png" to "logo-email.png"
' (SS,31/1/13) added LAttachLogoFileName
Dim LAttachLogoFileName, LSubject, LProductItemTemplate, LBody, LProductList, LProductLine, LProductCode, LProductName, LQty
LBody = GetSiteDetail("Other", "OrderReminderTemplate")
' (SS,4/12/12) get subject from template (i.e. <!--SUBJECT=Please check your stock of..-->), if it doesn't exist then use the default
' LSubject = "Order Reminder from " & GetStoreName & " - " & ACustomerID
' (SS,31/1/13) moved code to ExtractSettingFromTemplate so more than one setting can be extracted
LSubject = ExtractSettingFromTemplate(LBody, "SUBJECT")
If LSubject = "" Then LSubject = "Did we do something wrong?" & " (" & GetStoreName & " - " & ACustomerID & ")"
' (SS,31/1/13) product list template is now separate line in header of template called <!--PRODUCT_ITEM_TEMPLATE=
LProductItemTemplate = ExtractSettingFromTemplate(LBody, "PRODUCT_ITEM_TEMPLATE")
LProductList = ""
' (SS,31/10/18) added GetOrderDetailsFilter to filter out subproducts
OpenQuery2("SELECT * FROM orderdetails WHERE OrderNo = '" & LLastOrderNo & "'" + GetOrderDetailsFilter)
Do While Not EndOfQuery2
LProductCode = GetQueryField2("ProductCode")
LProductName = GetQueryField2("ProductName")
LQty = GetQueryField2("Qty")
' (SS,31/1/13) modified to work using inline CSS rather than stylesheet in html header because gmail doesn't support styles in header
LProductLine = LProductItemTemplate
LProductLine = ReplaceStr(LProductLine, "{PRODUCT_LINK}", GetFullProductLink(LProductCode))
LProductLine = ReplaceStr(LProductLine, "{PRODUCT_IMAGE_LINK}", GetFullProductImageLink(LProductCode, "s"))
LProductLine = ReplaceStr(LProductLine, "{PRODUCT_NAME_AND_QTY}", LProductName & " (" & LQty & ")")
LProductLine = ReplaceStr(LProductLine, "{PRODUCT_BUY_LINK}", GetBuyLinkHRef(LProductCode, LQty))
LProductList = LProductList + LProductLine & NL
NextQueryRecord2
Loop
CloseQuery2
If LBody <> "" Then ' only send if template not blank
LBody = ReplaceStr(LBody, "{CUSTOMER_NAME}", ACustomerName)
' (SS,31/1/13) added "if" to only attach logo if template contains {LOGO}, also added LOGO_URL which doesn't embed the URL
If InStr(LBody, "{LOGO}") > 0 Then
LBody = ReplaceStr(LBody, "{LOGO}", "<img src=""" + LOGO_FILE_NAME + """>")
LAttachLogoFileName = LOGO_FILE_NAME
Else
LBody = ReplaceStr(LBody, "{LOGO_URL}", GetStoreURL & "/images/" & LOGO_FILE_NAME)
LAttachLogoFileName = ""
End If
LBody = ReplaceStr(LBody, "{DATE_LAST_ORDERED}", FormatDateTime(ADateLastOrdered, 1))
LBody = ReplaceStr(LBody, "{PRODUCT_LIST}", LProductList)
LBody = ReplaceStr(LBody, "{STORE_URL}", GetStoreURL) ' "http://www.galleonsupplies.co.uk") ' GetStoreURL)
LBody = ReplaceStr(LBody, "{STORE_NAME}", GetStoreName)
LBody = ReplaceStr(LBody, "{STORE_EMAIL}", GetStoreEmail) ' "sales@galleonsupplies.co.uk") ' GetStoreEmail
LBody = ReplaceStr(LBody, "{STORE_TELEPHONE}", GetStoreTelephone)
LBody = ReplaceStr(LBody, "{EMAIL_ADDRESS}", AEmail)
LBody = ReplaceStr(LBody, "{OPT_OUT_URL}", GetStoreURL & "/products.asp?page=optout" & "&email=" & URLEncode(AEmail) & "&id=" & ACustomerID)
' send the email with embedded logo
' SendEmail FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom, LSubject, LBody, True
' "surinder@itpartnership.com", "",
' AEmail, FEmailContactBcc1
SendEmailByCDO AEmail, FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom, LSubject, LBody, "", True, "", LAttachLogoFileName '(SS,31/1/13) changed LOGO_FILE_NAME to LAttachLogoFileName
' SendEmailByCDO "surinder@itpartnership.com", "", FEmailContactBcc2, FEmailContactFrom, LSubject, LBody, "", True, "", LAttachLogoFileName
End If
End Sub
' (SS,31/1/13) returns the setting modifying ATemplate
' looks for a setting held in format <!--ABC=xxxx-->
' e.g. ExtractSettingFromTemplate(LBody, "ABC") will return xxxx and set LBody with section containing <!--ABC removed
' see SendOrderReminderEmail for actual usage
Function ExtractSettingFromTemplate(ByRef ATemplate, ASettingName)
Dim LPos1, LPos2, LResult
LResult = ""
LPos1 = InStr(1, ATemplate, "<!--" + ASettingName + "=", 1)
If LPos1 > 0 Then
LPos2 = InStr(LPos1, ATemplate, "-->", 1)
If LPos2 > 0 Then
LResult = Mid(ATemplate, LPos1 + Len(ASettingName) + 5, LPos2 - LPos1 - (Len(ASettingName) + 5))
' remove the found setting from ATemplate, also trim any newlines and spaces
ATemplate = TrimAll(Mid(ATemplate, 1, LPos1 - 1)) + TrimAll(Mid(ATemplate, LPos2 + 3))
End If
End If
ExtractSettingFromTemplate = LResult
End Function
' (SS,29/11/12)
Sub OrderReminderOptOut
Dim LEmail, LCustomerID
LEmail = CleanRequest("Email")
LCustomerID = CleanRequest("ID")
ExecuteQuery "UPDATE customers SET OrderReminderOptOut = 1 WHERE CustomerID = '" & CleanSQLStr(LCustomerID) & "' AND EmailAddress = '" & CleanSQLStr(LEmail) & "'"
ShowPageContentGiven "Opt-Out", "You have been removed from our list and will not receive further reminder emails."
End Sub
' ------------------------------
' End of Order Reminder Routines
' ------------------------------
' ---------------------------------------
' Start of Chase Cancelled Order Routines
' ---------------------------------------
' (SS,14/2/13) send cancelled order emails, don't send during Christmas period
' (SS,15/2/13) added ability to specify order no, for testing purposes
' (SS,15/2/13) added Title, FirstName, Surname
Sub SendChaseCancelledOrderEmails
' don't send between 21 Dec and 7 Jan, i.e. Christmas break
If Month(Date) = 12 And Day(Date) >= 21 Or Month(Date) = 1 And Day(Date) <= 7 Then Exit Sub
Const TEST_MODE = False
Const MIN_DAYS_AGO = 1
Const MAX_DAYS_AGO = 30
' (SS,15/2/13) if order number specified then only send to this, for testing purposes
Dim LRequestedOrderNo
LRequestedOrderNo = CleanRequest("OrderNo")
' list of cancelled orders between 1 and 30 days ago who haven't ordered since the cancelled order and now, excluding those that have already been sent emails
Dim LSQL
LSQL = "SELECT EmailAddress, OrderNo, TRIM(CONCAT(Title, ' ', FirstName, ' ', Surname)) AS FullName, Title, FirstName, Surname, DateTimeOrdered, DATEDIFF(CURDATE(), DateTimeOrdered) AS DaysAgo FROM orders o1"
If LRequestedOrderNo = "" Then
LSQL = LSQL & " WHERE Status = '" & ORDER_CANCELLED_STATUS & "'" &_
" AND DATEDIFF(CURDATE(), DateTimeOrdered) BETWEEN " & MIN_DAYS_AGO & " AND " & MAX_DAYS_AGO &_
" AND CancelledOrderChasedDate IS NULL" &_
" AND (SELECT EmailAddress FROM orders o2 WHERE o2.Status <> '" & ORDER_CANCELLED_STATUS & "'" &_
" AND o2.EmailAddress = o1.EmailAddress AND o2.DateTimeOrdered > o1.DateTimeOrdered LIMIT 1)" &_
" IS NULL"
Else
LSQL = LSQL & " WHERE OrderNo = '" & LRequestedOrderNo & "'"
End If
' if test mode then only test with orders with my email address
If TEST_MODE Then LSQL = LSQL + " AND (EmailAddress = 'surinder@itpartnership.com' OR EmailAddress = 'srinda@gmail.com')"
LSQL = LSQL + " ORDER BY EmailAddress, OrderNo DESC"
' iterate each customer
' build email body and sent email
' if successful then set CancelledOrderChasedDate to now
' add to list of cancelled orders chased
OpenQuery(LSQL)
If Not EndOfQuery Then
Dim LEmailBody, LChasedCount, LPrevEmail
LEmailBody = ""
LChasedCount = 0
LPrevEmail = ""
Dim LOrderNo, LEmailAddress, LName, LDateTimeOrdered
Do While Not EndOfQuery
LOrderNo = GetQueryField("OrderNo")
LEmailAddress = GetQueryField("EmailAddress")
LName = GetQueryField("FullName")
LDateTimeOrdered = GetQueryField("DateTimeOrdered")
If LPrevEmail <> LEmailAddress Then
LChasedCount = LChasedCount + 1
' add table header
If LChasedCount = 1 Then
LEmailBody = LEmailBody + ChaseCancelledOrdersResultHeader
End If
' add the row
LEmailBody = LEmailBody + ChaseCancelledOrdersResultRow(LOrderNo, LEmailAddress, LName, LDateTimeOrdered)
' send the email
SendChaseCancelledOrderEmail LOrderNo, LEmailAddress, LName, LDateTimeOrdered
' change the CancelledOrderChasedDate to today so that we don't send again
ExecuteQuery "UPDATE orders SET CancelledOrderChasedDate = CURDATE() WHERE OrderNo = '" & CleanSQLStr(LOrderNo) & "'"'
Else ' i.e. matches previous email, duplicate
' set CancelledOrderChasedDate to special date 01/01/1902
ExecuteQuery "UPDATE orders SET CancelledOrderChasedDate = '1902-01-01' WHERE OrderNo = '" & CleanSQLStr(LOrderNo) & "'"'
End If
LPrevEmail = LEmailAddress
NextQueryRecord
Loop
' if reminders sent then email the results
If LChasedCount > 0 Then
LEmailBody = LEmailBody + ChaseCancelledOrdersResultFooter(LChasedCount)
EmailChaseCancelledOrdersResult LEmailBody, ""
End If
End If
CloseQuery
End Sub
' (SS,29/11/12) email the results of the order reminders process
Sub EmailChaseCancelledOrdersResult(ABody, AAttachedFileName)
Dim LSubject
LSubject = "Chase Cancelled Orders Result for " + GetStoreName + " on " & Now
' SendEmailByCDO used because it can handle attachments
' ATo, ABcc, ABcc2, AFrom, ASubject, ABody, AFiles, AIsHTML, AUrl
' FEmailContactTo, FEmailContactBcc1
' FEmailContactBcc1, ""
SendEmailByCDO FEmailContactTo, FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom, LSubject, ABody, AAttachedFileName, True, "", ""
End Sub
' (SS,14/2/13)
Function ChaseCancelledOrdersResultHeader
' NL used at end of each HTML line because emailer is adding a space after 1000 characters
Dim LResult
LResult = "<table border=1 cellspacing=0>" & NL
LResult = LResult & "<tr><th colspan=""4"">" & "Order Reminders" & "</th></tr>" & NL
LResult = LResult & "<tr>" & NL
LResult = LResult & "<th>Order No</th>" & NL
LResult = LResult & "<th>Email</th>" & NL
LResult = LResult & "<th>Name</th>" & NL
LResult = LResult & "<th>Date/Time Ordered</th>" & NL
LResult = LResult & "</tr>" & NL
Response.Write LResult
ChaseCancelledOrdersResultHeader = LResult
End Function
' (SS,14/2/13)
Function ChaseCancelledOrdersResultFooter(ACount)
Dim LResult
LResult = "<tr><td colspan=""4"">" & PluralString(ACount, "email", "emails") & " sent for cancelled orders</td></tr>" & NL
LResult = LResult & "</table>" & NL
Response.Write LResult
ChaseCancelledOrdersResultFooter = LResult
End Function
' (SS,14/2/13)
Function ChaseCancelledOrdersResultRow(AOrderNo, AEmail, AName, ADateTimeOrdered)
Dim LResult
LResult = "<tr>" & NL
LResult = LResult & "<td>" & AOrderNo & "</td>" & NL
LResult = LResult & "<td>" & AEmail & "</td>" & NL
LResult = LResult & "<td>" & AName & "</td>" & NL
LResult = LResult & "<td>" & ADateTimeOrdered & "</td>" & NL
LResult = LResult & "</tr>" & NL
Response.Write LResult
ChaseCancelledOrdersResultRow = LResult
End Function
' (SS,14/2/13) emails sent in plain text
Sub SendChaseCancelledOrderEmail(AOrderNo, AEmail, ACustomerName, ADateTimeOrdered)
Dim LSubject, LBody, LProductList, LProductLine, LProductCode, LProductName, LQty
LSubject = GetStoreName & " Purchase Not Completed - " & AOrderNo ' (SS,15/2/13) changed from "Cancelled Order at " & GetStoreName to this
LBody = TrimAll(GetSiteDetail("Other", "ChaseCancelledOrderTemplate") & "")
If LBody <> "" Then ' only send if template not blank
LBody = ReplaceStr(CStr(LBody), "{CUSTOMER_NAME}", CStr(ACustomerName))
LBody = ReplaceStr(LBody, "{ORDER_NO}", AOrderNo)
LBody = ReplaceStr(LBody, "{DATE_TIME_ORDERED}", FormatDateTime(ADateTimeOrdered, 1))
LBody = ReplaceStr(LBody, "{STORE_URL}", GetStoreURL)
LBody = ReplaceStr(LBody, "{STORE_NAME}", GetStoreName)
LBody = ReplaceStr(LBody, "{STORE_EMAIL}", GetStoreEmail)
LBody = ReplaceStr(LBody, "{STORE_TELEPHONE}", GetStoreTelephone)
LBody = ReplaceStr(LBody, "{EMAIL_ADDRESS}", AEmail)
LBody = ReplaceStr(LBody, "{TITLE}", GetQueryField("Title"))
LBody = ReplaceStr(LBody, "{FIRST_NAME}", GetQueryField("FirstName"))
LBody = ReplaceStr(LBody, "{SURNAME}", GetQueryField("Surname"))
SendEmailByCDO AEmail, FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom, LSubject, LBody, "", False, "", ""
'SendEmailByCDO "surinder@itpartnership.com", "", FEmailContactBcc2, FEmailContactFrom, LSubject, LBody, "", False, "", ""
End If
End Sub
' -------------------------------------
' End of Chase Cancelled Order Routines
' -------------------------------------
' ---------------------------------
' Start of Email Marketing Routines
' ---------------------------------
' (SS,19/12/12) (SS,22/2/13) added to update or add a customer record
' change was made to always ensure a customer record exists for any order for which payment has been received or awaiting payment
Sub UpdateCustomerRecord(AOrderNo)
' if we're forcing account creation then return because account will already exist, code below only runs for sites with optional accounts
If Not AccountsOptional then Exit Sub
' if order already has a Customer ID then not necessary to create an account because customer would have logged in
If GetSQLValueAsString("SELECT CustomerID FROM orders WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'") <> "" Then Exit Sub
Dim LEmailAddress, LCustomerID, LSQL
LEmailAddress = GetSQLValueAsString("SELECT EmailAddress FROM orders WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'")
If LEmailAddress = "" Then Exit Sub ' abort if email address not found, unlikely to ever happen
' lookup for email address in customer table
LCustomerID = GetSQLValueAsString("SELECT CustomerID FROM customers WHERE EmailAddress = '" & CleanSQLStr(LEmailAddress) & "'")
If LCustomerID <> "" Then
' if password is blank in customers table then update all fields (from order to customer record) because this isn't a proper account
' (SS,22/2/13) replaced Password = '' with Password IS NULL because it's a VARBINARY not VARCHAR
If GetSQLValueAsString("SELECT CustomerID FROM customers WHERE CustomerID = '" & CleanSQLStr(LCustomerID) & "' AND Password IS NULL") <> "" Then
LSQL = "UPDATE customers c, orders o" &_
" SET c.Title = o.Title, c.FirstName = o.FirstName, c.Surname = o.Surname," &_
" c.CompanyName = o.CompanyName, c.AddressLine1 = o.AddressLine1, c.AddressLine2 = o.AddressLine2," &_
" c.Town = o.Town, c.County = o.County, c.Postcode = o.Postcode, c.Country = o.Country," &_
" c.Telephone = o.Telephone, c.Mobile = o.Mobile, c.VATNumber = o.VATNumber," &_
" c.DeliveryAddressSameAsInvoice = o.DeliveryAddressSameAsInvoice, c.DeliveryName = o.DeliveryName," &_
" c.DeliveryCompanyName = o.DeliveryCompanyName, c.DeliveryAddressLine1 = o.DeliveryAddressLine1, c.DeliveryAddressLine2 = o.DeliveryAddressLine2," &_
" c.DeliveryTown = o.DeliveryTown, c.DeliveryCounty = o.DeliveryCounty, c.DeliveryPostcode = o.DeliveryPostcode, c.DeliveryCountry = o.DeliveryCountry," &_
" c.Subscribe = o.Subscribe, c.HearAboutUs = o.HearAboutUs" &_
" WHERE o.OrderNo = '" & CleanSQLStr(AOrderNo) & "' AND c.CustomerID = '" & CleanSQLStr(LCustomerID) & "'"
ExecuteQuery LSQL
'Else ' if password exists then update the subscribe field only
' update the subscribe field in customers
' (SS,22/2/13) discovered that there's no need to do the following because once properly logged it the subscribe field isn't shown, so commented out
' Dim LSubscribe
' LSubscribe = GetSQLValueAsString("SELECT Subscribe FROM orders WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'")
' ExecuteQuery "UPDATE customers SET Subscribe = '" & LSubscribe & "' WHERE CustomerID = '" & CleanSQLStr(LCustomerID) & "'"
End If
Else ' LCustomerID = ""
' if customer not found then create new customer record from values in order record
LSQL = "INSERT INTO customers" &_
" (Title, FirstName, Surname, CompanyName," &_
" AddressLine1, AddressLine2, Town, County, Postcode, Country," &_
" Telephone, Mobile, EmailAddress, VATNumber," &_
" DeliveryAddressSameAsInvoice, DeliveryName, DeliveryCompanyName, DeliveryAddressLine1, DeliveryAddressLine2," &_
" DeliveryTown, DeliveryCounty, DeliveryPostcode, DeliveryCountry," &_
" Subscribe, HearAboutUs, DateTimeCreated)" &_
" SELECT Title, FirstName, Surname, CompanyName," &_
" AddressLine1, AddressLine2, Town, County, Postcode, Country," &_
" Telephone, Mobile, EmailAddress, VATNumber," &_
" DeliveryAddressSameAsInvoice, DeliveryName, DeliveryCompanyName, DeliveryAddressLine1, DeliveryAddressLine2," &_
" DeliveryTown, DeliveryCounty, DeliveryPostcode, DeliveryCountry," &_
" Subscribe, HearAboutUs, NOW()" &_
" FROM orders" &_
" WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'"
ExecuteQuery LSQL
' get the newly inserted customer ID to save in orders record below
LCustomerID = GetSQLLastInsertID
End If
' update orders table with customer ID
ExecuteQuery "UPDATE orders SET CustomerID = '" & LCustomerID & "' WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'"
End Sub
' (26/2/13)
Sub SendMarketingEmails
' don't send between 21 Dec and 7 Jan, i.e. Christmas break
' (SS,23/12/13) removed following, requested by Neil, wanted to send Christmas messages
' If Month(Date) = 12 And Day(Date) >= 21 Or Month(Date) = 1 And Day(Date) <= 7 Then Exit Sub
Const TEST_MODE = False
' (SS,27/2/13) if customer id specified then only send to this, for testing purposes
Dim LRequestedCustomerID, LMax, LCmd
LRequestedCustomerID = CleanRequest("CustomerID")
LMax = CleanRequest("Max") ' use to set the maximium amount of emails to send in one go, assumes all if left blank
LCmd = LCase(CleanRequest("cmd")) ' can be clear for next marketing campaign, "list" for
' cmd can be
' "clear" to clear the MarketingEmailSentDate ready for next campaign
' "list" the emails to send only
' "email" to send the emails
' clear the MarketingEmailSentDate clear ready for next campaign
If LCmd = "clear" Then
ExecuteQuery "UPDATE customers SET MarketingEmailSentDate = NULL WHERE MarketingEmailSentDate IS NOT NULL"
Exit Sub
ElseIf LCmd <> "list" And LCmd <> "email" Then
Response.Write "cmd must be specified: clear, list or email.<br />"
Response.Write "Use max to specify the maximum emails to send in one go.<br />"
Response.Write "To send a test email use CustomerID = xxxxx."
End If
' list of customers who have ordered and have subscribed to receive emails
' maximum of 500 are sent at any one time, with smallest customer ID first
Dim LSQL
' new field MarketingEmailSentDate must be null, this is cleared for a new campaign
LSQL = "SELECT DISTINCT c.CustomerID, c.EmailAddress, c.Title, c.FirstName, c.Surname, TRIM(CONCAT(c.Title, ' ', c.FirstName, ' ', c.Surname)) AS FullName" &_
" FROM customers c"
If LRequestedCustomerID = "" Then
LSQL = LSQL &_
" INNER JOIN orders o ON o.CustomerID = c.CustomerID AND (o.Status = '" & ORDER_COMPLETED_STATUS & "' OR o.Status = '" & PAYMENT_RECEIVED_STATUS & "')" &_
" WHERE c.Subscribe = TRUE AND c.MarketingEmailSentDate IS NULL"
' if test mode then only test with customers with my email address
If TEST_MODE Then LSQL = LSQL + " AND (c.EmailAddress = 'surinder@itpartnership.com' OR c.EmailAddress = 'srinda@gmail.com' OR c.EmailAddress = 'neil@itpartnership.com')"
LSQL = LSQL + " ORDER BY CustomerID"
If LMax <> "" Then LSQL = LSQL + " LIMIT " + CleanSQLStr(LMax)
Else
LSQL = LSQL & " WHERE CustomerID = '" & LRequestedCustomerID & "'"
End If
' iterate each customer
' build email body and send email
' if successful then set MarketingEmailSentDate so it isn't sent again
' add to list of marketing emails sent
OpenQuery(LSQL)
If Not EndOfQuery Then
Dim LEmailBody, LCount
LEmailBody = ""
LCount = 0
Dim LCustomerID, LEmailAddress, LName, LDateLastOrdered
Do While Not EndOfQuery
LCustomerID = GetQueryField("CustomerID")
LEmailAddress = GetQueryField("EmailAddress")
LName = GetQueryField("FullName")
LCount = LCount + 1
' add table header
If LCount = 1 Then
LEmailBody = LEmailBody + MarketingEmailResultHeader
End If
' add the row
LEmailBody = LEmailBody + MarketingEmailResultRow(LCustomerID, LEmailAddress, LName)
If LCmd = "email" Then
' send the email
SendMarketingEmail LCustomerID, LEmailAddress, LName
' change the MarketingEmailSentDate to today so that we don't send again during this campaign
ExecuteQuery "UPDATE customers SET MarketingEmailSentDate = CURDATE() WHERE CustomerID = '" & CleanSQLStr(LCustomerID) & "'"'
End If
NextQueryRecord
Loop
' if marketing emails sent then email the results
If LCount > 0 Then
LEmailBody = LEmailBody + MarketingEmailResultFooter(LCount)
If LCmd = "email" Then EmailMarketingEmailsResult LEmailBody, ""
End If
End If
CloseQuery
End Sub
' (SS,26/2/13) email the results of the marketing emails process
Sub EmailMarketingEmailsResult(ABody, AAttachedFileName)
Dim LSubject
LSubject = "Marketing Emails Result for " + GetStoreName + " on " & Now
' SendEmailByCDO used because it can handle attachments
' ATo, ABcc, ABcc2, AFrom, ASubject, ABody, AFiles, AIsHTML, AUrl
' FEmailContactTo, FEmailContactBcc1
' FEmailContactBcc1, ""
SendEmailByCDO FEmailContactTo, FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom, LSubject, ABody, AAttachedFileName, True, "", ""
End Sub
' (SS,26/2/13)
Function MarketingEmailResultHeader
' NL used at end of each HTML line because emailer is adding a space after 1000 characters
Dim LResult
LResult = "<table border=1 cellspacing=0>" & NL
LResult = LResult & "<tr><th colspan=""3"">" & "Marketing Emails" & "</th></tr>" & NL
LResult = LResult & "<tr>" & NL
LResult = LResult & "<th>CustomerID</th>" & NL
LResult = LResult & "<th>Email</th>" & NL
LResult = LResult & "<th>Name</th>" & NL
LResult = LResult & "</tr>" & NL
Response.Write LResult
MarketingEmailResultHeader = LResult
End Function
' (SS,26/2/13)
Function MarketingEmailResultFooter(ACount)
Dim LResult
LResult = "<tr><td colspan=""3"">Marketing emails for " & PluralString(ACount, "customer", "customers") & "</td></tr>" & NL
LResult = LResult & "</table>" & NL
Response.Write LResult
MarketingEmailResultFooter = LResult
End Function
' (SS,26/2/13)
Function MarketingEmailResultRow(ACustomerID, AEmail, AName)
Dim LResult
LResult = "<tr>" & NL
LResult = LResult & "<td>" & ACustomerID & "</td>" & NL
LResult = LResult & "<td>" & AEmail & "</td>" & NL
LResult = LResult & "<td>" & AName & "</td>" & NL
LResult = LResult & "</tr>" & NL
Response.Write LResult
MarketingEmailResultRow = LResult
End Function
' (SS,26/2/13)
Sub SendMarketingEmail(ACustomerID, AEmail, ACustomerName)
Const LOGO_FILE_NAME = "logo-email.png"
Dim LAttachLogoFileName, LSubject, LProductItemTemplate, LBody, LProductList, LProductLine, LProductCode, LProductName, LQty
LBody = GetSiteDetail("Other", "MarketingEmailTemplate")
' get subject from template (i.e. <!--SUBJECT=Sale Now On..-->), if it doesn't exist then use the default
LSubject = ExtractSettingFromTemplate(LBody, "SUBJECT")
If LSubject = "" Then LSubject = "Sale Now On" & " (" & GetStoreName & " - " & ACustomerID & ")"
LSubject = ReplaceStr(LSubject, "{STORE_NAME}", GetStoreName)
LSubject = ReplaceStr(LSubject, "{CUSTOMER_ID}", ACustomerID)
If LBody <> "" Then ' only send if template not blank
LBody = ReplaceStr(LBody, "{CUSTOMER_NAME}", ACustomerName)
LBody = ReplaceStr(LBody, "{TITLE}", GetQueryField("Title"))
LBody = ReplaceStr(LBody, "{FIRST_NAME}", GetQueryField("FirstName"))
LBody = ReplaceStr(LBody, "{SURNAME}", GetQueryField("Surname"))
' to only attach logo if template contains {LOGO}, also added LOGO_URL which doesn't embed the URL
If InStr(LBody, "{LOGO}") > 0 Then
LBody = ReplaceStr(LBody, "{LOGO}", "<img src=""" + LOGO_FILE_NAME + """>")
LAttachLogoFileName = LOGO_FILE_NAME
Else
LBody = ReplaceStr(LBody, "{LOGO_URL}", GetStoreURL & "/images/" & LOGO_FILE_NAME)
LAttachLogoFileName = ""
End If
LBody = ReplaceStr(LBody, "{STORE_URL}", GetStoreURL)
LBody = ReplaceStr(LBody, "{STORE_NAME}", GetStoreName)
LBody = ReplaceStr(LBody, "{STORE_EMAIL}", GetStoreEmail)
LBody = ReplaceStr(LBody, "{STORE_TELEPHONE}", GetStoreTelephone)
LBody = ReplaceStr(LBody, "{EMAIL_ADDRESS}", AEmail)
LBody = ReplaceStr(LBody, "{UNSUBSCRIBE_URL}", GetStoreURL & "/products.asp?page=unsubscribe" & "&email=" & URLEncode(AEmail) & "&id=" & ACustomerID)
' send the email with embedded logo
SendEmailByCDO AEmail, FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom, LSubject, LBody, "", True, "", LAttachLogoFileName
' SendEmailByCDO "surinder@itpartnership.com", "", FEmailContactBcc2, FEmailContactFrom, LSubject, LBody, "", True, "", LAttachLogoFileName
End If
End Sub
' (SS,26/2/13)
Sub MarketingEmailUnsubscribe
Dim LEmail, LCustomerID
LEmail = CleanRequest("Email")
LCustomerID = CleanRequest("ID")
ExecuteQuery "UPDATE customers SET Subscribe = 0 WHERE CustomerID = '" & CleanSQLStr(LCustomerID) & "' AND EmailAddress = '" & CleanSQLStr(LEmail) & "'"
ShowPageContentGiven "Unsubscribe", "You have been unsubscribed from our list and will not receive further marketing emails."
End Sub
' -------------------------------
' End of Email Marketing Routines
' -------------------------------
' (SS,16/7/13) returns comma separated list of product ID and product descriptions of all enabled products
' called via ajax.asp to return complete list to ShoppingAdmin for sending to Google Products, because it needs to convert the tokens
' (SS,1/5/19) I think this is now redundant, so removed (commented out)
'Function ProductDescriptionListForGoogleShopping
' Dim LResult, LDescription
' LResult = NL ' prefix with a newline to make searching for product ID easier using string match
' (SS,31/10/18) added GetProductsFilter to filter out subproducts
' OpenQuery("SELECT ProductID, ProductDescription FROM products WHERE ProductDisabled = FALSE" + GetProductsFilter + "ORDER BY ProductID")
' Do While Not EndOfQuery
' (SS,13/3/15) moved code from here to separate GetProductDescriptionForGoogleShopping
' (SS,16/3/15) added True parameter to compose
' LResult = LResult & GetQueryValue("ProductID") & ", " & GetProductDescriptionForGoogleShopping(GetQueryValue("ProductDescription"), True) & NL
' NextQueryRecord
' Loop
' CloseQuery
' ProductDescriptionListForGoogleShopping = LResult
'End Function
' (SS,13/3/15) moved code here from ProductDescriptionListForGoogleShopping, now also called from DoGoogleShoppingDataFeed
' (SS,16/3/15) added ACompose parameter, because when using GetProductDescriptionFirstSection ACompose will be false because GetProductDescriptionFirstSection does the compose
Function GetProductDescriptionForGoogleShopping(ADescription, ACompose)
Const MAX_LENGTH = 9000 ' max for Google Shopping
Dim LDescription
' (SS,16/3/15) added "If ACompose Then" because it will already be composed if called from DoGoogleShoppingDataFeed
If ACompose Then
LDescription = ComposeDescription(ADescription, True, False)
Else
LDescription = ADescription
End If
' remove HTML Tags
LDescription = RemoveHTMLTags(LDescription)
' replace quote with single quote
LDescription = ReplaceStr(LDescription,"""", "'")
' replace new lines with space and trim leading and trailing spaces
LDescription = Trim(ReplaceStr(LDescription, Chr(13) + Chr(10) , " "))
LDescription = Trim(ReplaceStr(LDescription, Chr(9), " ")) ' (SS,29/11/13) replaced tabs with spaces, because tab is a delimited on the products file sent to Google
If Len(LDescription) > MAX_LENGTH Then
LDescription = Mid(LDescription, 1, MAX_LENGTH) + ".."
Else
LDescription = LDescription
End If
GetProductDescriptionForGoogleShopping = LDescription
End Function
' ---------------------------------
' Start of generic counter routines
' ---------------------------------
' (SS,13/9/14) a generic counter which can be used by templates
Dim FCounter
Sub ClearCounter
FCounter = 0
End Sub
' (SS,13/9/14)
Sub IncCounter
FCounter = FCounter + 1
End Sub
Function GetCounter
GetCounter = FCounter
End Function
' -------------------------------
' End of generic counter routines
' -------------------------------
' (SS,9/10/14)
Function SetDeliveryInfo(ADeliveryInfo)
FDeliveryInfo = ADeliveryInfo
' (SS,18/5/17) saving of delivery info in session for use by saved basket (a bit messy I know, didn't have time to investigate why it needs to be cleared)
If ADeliveryInfo <> "" Then
Session("DeliveryInfoForSavedBasket") = ADeliveryInfo
End If
End Function
' (SS,9/10/14)
Function GetDeliveryInfo
GetDeliveryInfo = FDeliveryInfo
End Function
' (SS,18/5/17) due to it being cleared at start of apputils.asp load, we're saving it for email basket routine and returning it here
Function GetDeliveryInfoForSavedBasket
GetDeliveryInfoForSavedBasket = Session("DeliveryInfoForSavedBasket") & "" ' "" ensures empty string
End Function
' ---------------------------------
' Start of basket saving routines
' ---------------------------------
' (SS,19/2/15) uses current session ID and email address to save basket
' (SS,30/10/18) modified to not save and email the hidden subproducts
Sub SaveBasket
' (SS,19/5/17) added LName and LAddress which only appear in local mode
Dim LEmailAddress, LSessionID, LSavedBasketID, LSavedBasketItemID, LItemID, LMessage, LName, LAddress
LEmailAddress = Trim(CleanRequest("email"))
If LEmailAddress = "" Then Exit Sub
LMessage = Trim(CleanRequest("message"))
' (SS,19/5/17)
LName = Trim(CleanRequest("name"))
LAddress = Trim(CleanRequest("address"))
LSessionID = GetSessionID
' create new saved basket record and save the email address and current date and time
' (SS,19/5/17) added Name and Address fields
ExecuteQuery("INSERT INTO saved_baskets SET EmailAddress = '" & CleanSQLStr(LEmailAddress) & "', DateTimeSaved = NOW(), Message = '" & CleanSQLStr(LMessage) & "', Name = '" & CleanSQLStr(LName) & "', Address = '" & CleanSQLStr(LAddress) & "'")
LSavedBasketID = GetSQLLastInsertID
' copy the current basket items into saved basket items table
' due to options linking to correct item id, it needs to be done on an item by item basis, Qty > 0 added just to be safe, unlikely to occur
' (SS,30/10/18) to prevent new subproducts from being added, added " AND SubproductItemID IS NULL" to WHERE clause
OpenQuery("SELECT * FROM shoppingbaskets WHERE SessionID = '" & CleanSQLStr(LSessionID) & "' AND SubproductItemID IS NULL AND Qty > 0 ORDER BY ItemID")
Do While Not EndOfQuery
LItemID = GetQueryValue("ItemID")
' add the item
' (SS,26/2/15) added OriginalBasketItemID = LItemID, new field, because ProductID isn't unique, same product can be added with different options
ExecuteQuery("INSERT INTO saved_basket_items SET OriginalBasketItemID = " & LItemID & ", SavedBasketID = " & LSavedBasketID & ", ProductID = " & GetQueryValue("ProductID") & ", Qty = " & GetQueryValue("Qty"))
' add the matching options
If ProductOptionsEnabled Then
LSavedBasketItemID = GetSQLLastInsertID
ExecuteQuery("INSERT INTO saved_basket_options SELECT NULL, " & LSavedBasketItemID & ", ProductOptionID, ProductOptionValueID, OptionName, OptionValue FROM shopping_basket_options WHERE ItemID = " & LItemID & " ORDER BY ShoppingBasketOptionID")
End If
NextQueryRecord
Loop
CloseQuery
' send an email with items and link to restore the basket
'Dim LBody
'LBody = "Please click the link below to restore the saved basket." & NL & GetStoreURL & "/products.asp?cmd=restorebasket&id=" & LSavedBasketID & "&email=" & URLEncode(LEmailAddress)
'SendEmail LEmailAddress, FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom, GetStoreName & " Saved Basket - " & LSavedBasketID, LBody, False
' (SS,19/5/17) added LName and LAddress
SendSavedBasketEmail LSavedBasketID, LEmailAddress, LMessage, LName, LAddress
Response.Write "Basket has been saved and emailed to " & LEmailAddress & "." & BR
SetPageRedirection GetBasketPageURL
End Sub
' (SS,19/2/15) uses provided saved basket id and email address to restore basket into current session
' (SS,30/10/18) modified to also create the subproducts for products that have subproducts
' (SS,6/4/20) modified to fix loophole of suspended products from being ordered due to this feature
Sub RestoreBasket
Dim LSavedBasketID, LEmailAddress, LSessionID, LItemID, LProductID, LProductOrderingSuspended
' validate the basket id and email
LSavedBasketID = Trim(CleanRequest("id"))
LEmailAddress = Trim(CleanRequest("email"))
If LSavedBasketID = "" Or LEmailAddress = "" Then Exit Sub
If Not GetSQLRecordExists("SELECT * FROM saved_baskets WHERE SavedBasketID = '" & CleanSQLStr(LSavedBasketID) & "' AND EmailAddress = '" & CleanSQLStr(LEmailAddress) & "'") Then Exit Sub
LSessionID = GetSessionID
' empty current basket
EmptyBasket(LSessionID)
LProductOrderingSuspended = False ' (SS,6/4/20)
' restore the basket in session
' due to options linking to correct item id, it needs to be done on an item by item basis, lookup product code from products table
OpenQuery("SELECT sbi.*, p.ProductCode FROM saved_basket_items sbi INNER JOIN products p ON p.ProductID = sbi.ProductID WHERE SavedBasketID = '" & CleanSQLStr(LSavedBasketID) & "' ORDER BY SavedBasketItemID")
Do While Not EndOfQuery
LProductID = GetQueryValue("ProductID") ' (SS,30/10/18)
' (SS,6/4/20) check the ProductOrderingSuspended routine to fix loophole i.e. if ordering is suspended for this product then don't allow it to be added
If ProductOrderingSuspended(LProductID) Then
LProductOrderingSuspended = True
Exit Do
End If
' add the item
ExecuteQuery("INSERT INTO shoppingbaskets SET SessionID = '" & LSessionID & "', ProductID = " & LProductID & ", ProductCode = '" & GetQueryValue("ProductCode") & "', Qty = " & GetQueryValue("Qty"))
LItemID = GetSQLLastInsertID ' (SS,30/10/18) moved here from "If ProductOptionsEnabled", also used by "If ProductSubproductsEnabled "
' add the matching options
If ProductOptionsEnabled Then
ExecuteQuery("INSERT INTO shopping_basket_options SELECT NULL, " & LItemID & ", ProductOptionID, ProductOptionValueID, OptionName, OptionValue FROM saved_basket_options WHERE SavedBasketItemID = " & GetQueryValue("SavedBasketItemID") & " ORDER BY SavedBasketOptionID")
End If
' (SS,30/10/18) added following to add subproducts if applicable, custom routine is called if subproducts enabled and this product has subproducts
If ProductSubproductsEnabled Then
If IntToBool(GetFieldForProductID(LProductID, "HasSubproducts")) Then
If FunctionExists("CustomAddSubproductsToBasket") Then
CustomAddSubproductsToBasket LProductID, LItemID, 0, GetQueryValue("Qty")
End If
End If
End If
NextQueryRecord
Loop
CloseQuery
' (SS,6/4/20) if a product was suspended then show message and empty basket
If LProductOrderingSuspended Then
Response.Write "Unfortunately, basket could not be restored for " & LEmailAddress & "." & BR
Response.Write "This is due to one or more products being suspended from ordering." & BR
Response.Write "Please try again later. Sorry for any inconvenience caused." & BR
EmptyBasket(LSessionID)
Else ' as before
' redirect to basket page
Response.Write "Basket has been restored for " & LEmailAddress & "." & BR
SetPageRedirection GetBasketPageURL
End If
End Sub
' (SS,20/2/15) based on Galleon's SendOrderReminderEmail
' (SS,19/5/17) added AName and AAddress
' (SS,1/8/18) added call to AddMoreOptionsInfo to add more info if applicable
Sub SendSavedBasketEmail(ASavedBasketID, AEmailAddress, AMessage, AName, AAddress)
Const LOGO_FILE_NAME = "logo-email.png"
Dim LAttachLogoFileName, LSubject, LBody, LProductList, LProductLine, LProductID, LProductCode, LProductName, LProductOptions, LOriginalBasketItemID, LQty, LPriceEach, LSQL
Dim LProductItemTemplate, LDiscountTemplate, LVATTemplate, LBCC, LBCC2, LFrom, LName, LMessage
LBody = GetSiteDetail("Other", "EmailBasketTemplate")
LSubject = ExtractSettingFromTemplate(LBody, "SUBJECT")
LBCC = ExtractSettingFromTemplate(LBody, "BCC")
LFrom = ExtractSettingFromTemplate(LBody, "FROM")
' (SS,26/2/15) if AMessage has a single line with name=xxx then {NAME} is replaced with xxx
LMessage = TrimAll(AMessage) ' trim all removes spaces and control characters
LName = AName ' (SS,19/5/17) replaced "" with AName
' if single line (no new line) and contains name= then get the name
' (SS,19/5/17) added LName = "" because it may already be passed here via new AName
If LName = "" And InStr(LMessage, Chr(13)) = 0 Then
Dim LPos
LPos = InStr(1, LMessage, "name=", vbTextCompare)
If LPos > 0 Then
LName = Trim(Mid(LMessage, LPos + 5)) ' leading space allows separation from salutation
LMessage = "" ' empty it so it doesn't appear in message section
End If
End If
If LName <> "" Then LName = " " & LName ' leading space allows separation from salutation (SS/19/5/17) moved from above to here
If LSubject = "" Then LSubject = "Saved Basket from {STORE_NAME}"
' (SS,19/5/17) SUBJECT2 used for proforma when address provided
If AAddress <> "" Then
Dim LSubject2
LSubject2 = ExtractSettingFromTemplate(LBody, "SUBJECT2")
If LSubject2 <> "" Then LSubject = LSubject2
End If
LSubject = ReplaceStr(LSubject, "{STORE_NAME}", GetStoreName) + " - " & ASavedBasketID
' product list template is separate line in header of template called <!--PRODUCT_ITEM_TEMPLATE=
LProductItemTemplate = ExtractSettingFromTemplate(LBody, "PRODUCT_ITEM_TEMPLATE")
' discount and VAT are separate optional lines
LDiscountTemplate = ExtractSettingFromTemplate(LBody, "DISCOUNT_TEMPLATE")
LVATTemplate = ExtractSettingFromTemplate(LBody, "VAT_TEMPLATE")
LProductList = ""
LSQL = "SELECT sbi.*, p.ProductCode, p.ProductName" &_
" FROM saved_basket_items sbi" &_
" INNER JOIN products p ON p.ProductID = sbi.ProductID" &_
" WHERE SavedBasketID = " & ASavedBasketID &_
" ORDER BY SavedBasketItemID"
Dim LobjBasketPricesDict
' (SS,23/2/15)
Set LobjBasketPricesDict = Session("BasketPricesDict")
OpenQuery(LSQL)
Do While Not EndOfQuery
LProductID = GetQueryField("ProductID")
LProductCode = GetQueryField("ProductCode")
LProductName = GetQueryField("ProductName")
LOriginalBasketItemID = GetQueryField("OriginalBasketItemID") ' (SS,26/12/15) now used instead of ProductID because ProductID can appear multiple times
LQty = GetQueryField("Qty")
' (SS,31/1/13) modified to work using inline CSS rather than stylesheet in html header because gmail doesn't support styles in header
LProductLine = LProductItemTemplate
LProductLine = ReplaceStr(LProductLine, "{PRODUCT_LINK}", GetFullProductLink(LProductCode))
LProductLine = ReplaceStr(LProductLine, "{PRODUCT_IMAGE_LINK}", GetFullProductImageLink(LProductCode, "s"))
LProductLine = ReplaceStr(LProductLine, "{PRODUCT_NAME}", LProductName)
LProductLine = ReplaceStr(LProductLine, "{PRODUCT_QTY}", LQty)
' (SS,23/2/15)
' (SS,26/2/15) LProductID with LOriginalBasketItemID because ProductID isn't unique, same product can be added with different options
If LobjBasketPricesDict.Exists(LOriginalBasketItemID) Then
LPriceEach = LobjBasketPricesDict.Item(LOriginalBasketItemID)
LProductLine = ReplaceStr(LProductLine, "{PRODUCT_PRICE_EACH}", CorrectCurrencyNV(LPriceEach))
LProductLine = ReplaceStr(LProductLine, "{PRODUCT_PRICE_TOTAL}", CorrectCurrencyNV(LPriceEach * LQty))
End If
' add the matching options
LProductOptions = ""
If ProductOptionsEnabled Then
OpenQuery2("SELECT OptionName, OptionValue FROM saved_basket_options WHERE SavedBasketItemID = " & GetQueryField("SavedBasketItemID") & " ORDER BY SavedBasketOptionID")
Do While Not EndOfQuery2
LProductOptions = LProductOptions & Iif(LProductOptions = "", "", ", ") & GetQueryField2("OptionName") & ": " & GetQueryField2("OptionValue")
NextQueryRecord2
Loop
End If
' (SS,1/8/18) added following to add additional option info
LProductOptions = AddMoreOptionsInfo(LProductID, LProductOptions)
LProductLine = ReplaceStr(LProductLine, "{PRODUCT_OPTIONS}", LProductOptions)
LProductList = LProductList + LProductLine & NL
NextQueryRecord
Loop
CloseQuery
' (SS,23/2/15)
Set LobjBasketPricesDict = Nothing
If LBody <> "" Then ' only send if template not blank
' "if" to only attach logo if template contains {LOGO}, also added LOGO_URL which doesn't embed the URL
If InStr(LBody, "{LOGO}") > 0 Then
LBody = ReplaceStr(LBody, "{LOGO}", "<img src=""" + LOGO_FILE_NAME + """>")
LAttachLogoFileName = LOGO_FILE_NAME
Else
LBody = ReplaceStr(LBody, "{LOGO_URL}", GetStoreURL & "/images/" & LOGO_FILE_NAME)
LAttachLogoFileName = ""
End If
LBody = ReplaceStr(LBody, "{PRODUCT_LIST}", LProductList)
LBody = ReplaceStr(LBody, "{STORE_URL}", GetStoreURL)
LBody = ReplaceStr(LBody, "{STORE_NAME}", GetStoreName)
LBody = ReplaceStr(LBody, "{STORE_EMAIL}", GetStoreEmail)
LBody = ReplaceStr(LBody, "{STORE_TELEPHONE}", GetStoreTelephone)
LBody = ReplaceStr(LBody, "{EMAIL_ADDRESS}", AEmailAddress)
LBody = ReplaceStr(LBody, "{RESTORE_BASKET_LINK}", GetRestoreBasketURL(ASavedBasketID, AEmailAddress))
LBody = ReplaceStr(LBody, "{MESSAGE}", ReplaceNewLinesWithBR(LMessage))
LBody = ReplaceStr(LBody, "{NAME}", LName) ' (SS,26/2/15)
' (SS,19/5/15) added following, mainly for CIRC proforma feature
Dim LAddress
LAddress = Trim(AAddress)
If LAddress <> "" Then
If LName <> "" Then LAddress = LName & NL & LAddress
LAddress = "<b>Your address details:</b>" & NL & LAddress
End If
LBody = ReplaceStr(LBody, "{ADDRESS}", ReplaceNewLinesWithBR(LAddress))
LBody = ReplaceStr(LBody, "{STORE_NAME}", GetStoreName)
LBody = ReplaceStr(LBody, "{STORE_ADDRESS}", GetStoreAddressAsHTML)
LBody = ReplaceStr(LBody, "{STORE_EMAIL}", GetStoreEmail)
LBody = ReplaceStr(LBody, "{STORE_FAX}", GetStoreFax)
LBody = ReplaceStr(LBody, "{STORE_TELEPHONE}", GetStoreTelephone)
LBody = ReplaceStr(LBody, "{VAT_REG_NO}", GetStoreVATRegNo)
If GetBasketDiscount <> 0 Then
Dim LVoucherCode
LVoucherCode = GetVoucherCode
If GetVoucherCode <> "" Then LVoucherCode = " (" & LVoucherCode & ")"
LDiscountTemplate = ReplaceStr(LDiscountTemplate, "{VOUCHER_CODE}", LVoucherCode)
LDiscountTemplate = ReplaceStr(LDiscountTemplate, "{DISCOUNT}", CorrectCurrencyNV(GetBasketDiscount))
Else
LDiscountTemplate = ""
End If
' VAT only shown if prices in system exclude VAT
Dim LVAT
LVAT = GetBasketVAT
If LVAT <> 0 And Not PricesEnteredIncludeVAT Then
LVATTemplate = ReplaceStr(LVATTemplate, "{VAT}", CorrectCurrency(GetBasketVAT))
Else
LVATTemplate = ""
End If
LBody = ReplaceStr(LBody, "{SUBTOTAL}", CorrectCurrencyNV(GetBasketSubtotal))
LBody = ReplaceStr(LBody, "{DISCOUNT_LINE}", LDiscountTemplate)
LBody = ReplaceStr(LBody, "{DELIVERY_COUNTRY}", " (" & GetDeliveryCountry & ")")
LBody = ReplaceStr(LBody, "{DELIVERY}", CorrectCurrencyNV(GetBasketDelivery))
LBody = ReplaceStr(LBody, "{VAT_LINE}", LVATTemplate)
LBody = ReplaceStr(LBody, "{GRAND_TOTAL}", CorrectCurrency(GetBasketGrandTotal))
LBody = ReplaceStr(LBody, "{DELIVERY_INFO}", GetDeliveryInfoForSavedBasket) ' (SS,18/5/17)
' (SS,25/2/15) sorted email bcc and from addresses, because we might be overriding the standard ones
If LFrom = "" Then LFrom = FEmailContactFrom
If LBCC = "" Then
LBCC = FEmailContactBcc1
LBCC2 = FEmailContactBcc2
Else
If FEmailContactBcc1 <> "" Then
LBCC2 = FEmailContactBcc1
Else
LBCC2 = FEmailContactBcc2
End If
End If
' SendEmailByCDO AEmailAddress, FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom, LSubject, LBody, "", True, "", LAttachLogoFileName '(SS,31/1/13) changed LOGO_FILE_NAME to LAttachLogoFileName
SendEmailByCDO AEmailAddress, LBCC, LBCC2, LFrom, LSubject, LBody, "", True, "", LAttachLogoFileName '(SS,31/1/13) changed LOGO_FILE_NAME to LAttachLogoFileName
' SendEmailByCDO "surinder@itpartnership.com", "", FEmailContactBcc2, FEmailContactFrom, LSubject, LBody, "", True, "", LAttachLogoFileName
End If
End Sub
' ---------------------------------
' End of basket saving routines
' ---------------------------------
' -------------------------------------------
' Start of Google Shopping Data Feed routines
' -------------------------------------------
' (SS,17/3/15)
Sub StartGoogleShoppingDataFeed
Session("InGoogleShoppingDataFeed") = True
End Sub
Sub EndGoogleShoppingDataFeed
Session("InGoogleShoppingDataFeed") = False
End Sub
Function InGoogleShoppingDataFeed
InGoogleShoppingDataFeed = Session("InGoogleShoppingDataFeed") = True
End Function
Sub SetGoogleShoppingDataFeedProductID(AProductID)
Session("GoogleShoppingDataFeedProductID") = AProductID
End Sub
Function GetGoogleShoppingDataFeedProductID
GetGoogleShoppingDataFeedProductID = Session("GoogleShoppingDataFeedProductID")
End Function
' (SS,13/3/15)
Function GoogleShoppingDataFeedHeaderDone
GoogleShoppingDataFeedHeaderDone = Session("GoogleShoppingDataFeedHeaderDone") = True
End Function
' (SS,13/3/15)
Sub AddGoogleShoppingField(AFieldName, AFieldValue)
' Response.Write AFieldName & " = " & AFieldValue & BR
Dim LFieldValue
LFieldValue = TrimAll(NB(AFieldValue))
If Not FGoogleShoppingDataFeedHeaderDone Then
FGoogleShoppingDataFeedHeader = FGoogleShoppingDataFeedHeader + IIf(FGoogleShoppingDataFeedHeader = "", "", Chr(9)) + AFieldName
End If
' (SS,22/9/17) added following to default the condition product attribute to "new" if blank
' (SS,25/9/17) if set as "damaged" then change to "used". Microdata can be "damaged" but Google Shopping Data Feed prefers "used" for both used and damagad.
If AFieldName = "condition" Then
If LFieldValue = "" Then
LFieldValue = "new"
ElseIf LFieldValue = "damaged" Then
LFieldValue = "used"
End If
End If
FGoogleShoppingDataFeedLine = FGoogleShoppingDataFeedLine + IIf(FGoogleShoppingDataFeedLine = "", "", Chr(9)) + LFieldValue
End Sub
' (SS,13/3/15)
' some of the code taken from ShoppingAdmin procedure TdmMain.CreateFroogleTable
' routines GetFlaggedItemsInBasket and GetAttributeInBasketCount modified to cope with this, these routines may be code from CustomGetDelivery and need to copy directly
' with single product for this Google Shopping Data Feed and shopping basket as before
' above functions InGoogleShoppingDataFeed and GetGoogleShoppingDataFeedProductID used by GetFlaggedItemsInBasket and GetAttributeInBasketCount
' (SS,27/1/16) change to call optional custom function CustomGoogleShoppingProductAdjust to make adjustments
' (SS,19/5/16) change to add gtin and identifier_exists, also not to default mpn to product code
' (SS,28/9/16) added extra product attributes for google shopping, also added new field to attributes table
' GoogleShopping TINYINT(1) DEFAULT '0' COMMENT 'Added 28/9/2016 to specify if it\'s for Google Shopping',
' (SS,1/5/19) Bugfix to product name renaming for HF, setting the product code global before call to ComposeDescription fixed it
Sub DoGoogleShoppingDataFeed
StartGoogleShoppingDataFeed
Response.ContentType = "text/plain" ' tells browser that it's an plain txt document
' Global FGoogleShoppingDataFeedHeader, FGoogleShoppingDataFeedLine, FGoogleShoppingDataFeedHeaderDone
FGoogleShoppingDataFeedHeader = ""
FGoogleShoppingDataFeedHeaderDone = False
' (SS,27/1/16) added LProductID and LProductName
Dim LAttributeID, LSQL, LDefaultProductType, LExpirationDate, LProductID, LProductCode, LProductType, LCategory, LSubcategory, LProductName, LProductDesc, LProductPrice
Dim LProductImageURL, LAvailability, LNumInStock, LMPN, LGTIN, LIdentifierExists
' (SS,27/1/16) Moved following here from loop, don't know why this didn't cause error in the loop because you can't redefine a DIM for same variable bame
Dim LDeliveryCost, LWeight, LFlaggedWeight, LNonFlaggedWeight, LNonWeightPrice, LVATTotal
' (SS,28/9/16) added google shopping detailed product attributes
Dim LGoogleShoppingProductAttributeNames, LGoogleShoppingProductAttributeIDs, i
ReDim LGoogleShoppingProductAttributeNames(-1) ' empty array
ReDim LGoogleShoppingProductAttributeIDs(-1) ' empty array
Dim LConditionInGoogleShoppingAttribute ' (SS,22/9/17) if condition is a product attribute we're using that instead of the default "new" for "condition" field
LConditionInGoogleShoppingAttribute = False ' (SS,22/9/17)
If ProductAttributesEnabled Then
LAttributeID = GetAttributeIDForName("Exclude from Google Shopping")
' (SS,28/9/16) get list of google shopping attributes
LSQL = "SELECT AttributeID, AttributeName FROM attributes WHERE GoogleShopping = TRUE ORDER BY SortOrder"
OpenQuery(LSQL)
Do While Not EndOfQuery
' add to array
ReDim Preserve LGoogleShoppingProductAttributeNames(UBound(LGoogleShoppingProductAttributeNames) + 1)
LGoogleShoppingProductAttributeNames(UBound(LGoogleShoppingProductAttributeNames)) = LCase(GetQueryField("AttributeName"))
ReDim Preserve LGoogleShoppingProductAttributeIDs(UBound(LGoogleShoppingProductAttributeIDs) + 1)
LGoogleShoppingProductAttributeIDs(UBound(LGoogleShoppingProductAttributeIDs)) = GetQueryField("AttributeID")
' (SS,22/9/17) if it's named condition then flag it as such
If LCase(GetQueryField("AttributeName")) = "condition" Then LConditionInGoogleShoppingAttribute = True
NextQueryRecord
Loop
CloseQuery
Else
LAttributeID = ""
End If
LDefaultProductType = FFroogleProductType
LExpirationDate = ISODate(DateAdd("d", 30, Date)) ' today's date plus 30 days, in ISO format
' (SS,19/5/16) added GTIN
LSQL = "SELECT products.ProductID, products.ProductCode, products.ProductName, products.ProductDescription," &_
" products.StdPrice, products.SalePrice, products.Price2, products.Price3, products.Price4, products.Discount, products.VATBand," &_
" productcategories.Category, productcategories.Subcategory, vatbands.VATRate," &_
" subcategories.GoogleProductCategory, BrandName, MPN, GTIN," &_
" products.NumInStock, products.AllowOutOfStockOrdering, products.AvailabilityDaysFrom, products.PostalWeight, products.CustomFlag," &_
" google_adwords_groups.AdWordsGroupName, subcategories.CustomLabel0, subcategories.CustomLabel1," &_
" subcategories.CustomLabel2, subcategories.CustomLabel3, subcategories.CustomLabel4"
' (SS,28/9/16) add the extra google shopping product attribute fields, if applicable
If UBound(LGoogleShoppingProductAttributeIDs) <> -1 Then
For i = LBound(LGoogleShoppingProductAttributeIDs) To UBound(LGoogleShoppingProductAttributeIDs)
LSQL = LSQL + ", pa" & i & ".AttributeValue AS AttributeValue_" & i
Next
End If
LSQL = LSQL &_
" FROM products" &_
" LEFT JOIN productcategories ON productcategories.ProductCode = products.productcode" &_
" LEFT JOIN subcategories ON subcategories.Category = productcategories.Category AND subcategories.Subcategory = productcategories.Subcategory" &_
" LEFT JOIN vatbands ON vatbands.VATBand = products.VATBand" &_
" LEFT JOIN brands ON brands.BrandID = products.BrandID" &_
" LEFT JOIN google_adwords_groups ON google_adwords_groups.AdWordsGroupID = subcategories.AdWordsGroupID"
' (SS,28/9/16) add the extra google shopping product attribute fields, if applicable
If UBound(LGoogleShoppingProductAttributeIDs) <> -1 Then
For i = LBound(LGoogleShoppingProductAttributeIDs) To UBound(LGoogleShoppingProductAttributeIDs)
LSQL = LSQL + " LEFT JOIN product_attributes pa" & i & " ON pa" & i & ".ProductID = products.ProductID AND pa" & i & ".AttributeID = " & LGoogleShoppingProductAttributeIDs(i)
Next
End If
If LAttributeID <> "" Then LSQL = LSQL + " LEFT JOIN product_attributes ON product_attributes.ProductID = products.ProductID AND product_attributes.AttributeID = " & LAttributeID
' (SS,31/10/18) added GetProductsFilter to filter out subproducts even though it's not absolutely necessary here due to subproducts not having a productcategories.Main
LSQL = LSQL + " WHERE products.ProductDisabled = 0 AND productcategories.Main" & GetProductsFilter
if LAttributeID <> "" Then LSQL = LSQL + " AND product_attributes.AttributeID IS NULL"
LSQL = LSQL + " ORDER BY ProductCode"
' LHeader = "id, product_type, google_product_category, expiration_date, price, image_link, link, title, description, condition, availability, brand, mpn, adwords_grouping, shipping_weight, custom_label_0, custom_label_1, custom_label_2, custom_label_3, custom_label_4"
' Response.Write "##" & LSQL & NL
OpenQuery(LSQL)
Do While Not EndOfQuery
LProductID = GetQueryField("ProductID") ' (SS,27/1/16)
SetGoogleShoppingDataFeedProductID LProductID ' used by routines including GetFlaggedItemsInBasket and GetAttributeInBasketCount
' product code
LProductCode = GetQueryField("ProductCode")
' (SS,1/5/19) added following because ComposeDescription will need the current page's product code
' i.e. HyperFlight product name rename feature relies on it (for servo specs)
' It does get set by call to GetPricingFromProductRecord later, but need to set it before calling ComposeDescription
' This fixes a bug which was resulting in some servo products having the wrong names (just the labels without values)
SetProductCode LProductCode
' product type
LCategory = NB(GetQueryField("Category"))
LSubcategory = NB(GetQueryField("Subcategory"))
LProductType = ""
' (SS,27/1/16) product name
LProductName = NB(GetQueryField("ProductName"))
If LCategory <> "" Then LProductType = LCategory
If LSubcategory <> "" Then LProductType = LProductType + IIf(LProductType = "", "", " > ") + LSubcategory
If LProductType = "" Then LProductType = LDefaultProductType
' detokenise etc., then get the first section only (e.g. CIRC which has section), then convert to suitable format for Google
LProductDesc = NB(GetQueryField("ProductDescription"))
LProductDesc = ComposeDescription(LProductDesc, True, False)
LProductDesc = GetProductDescriptionFirstSection(LProductDesc)
LProductDesc = GetProductDescriptionForGoogleShopping(LProductDesc, False)
' product price
GetPricingFromProductRecord(False)
LProductPrice = GetProductPrice
' Add VAT if necessary
If Not PricesEnteredIncludeVAT Then LProductPrice = LProductPrice + GetVATAmountToAddNC(LProductPrice)
' multiply by exchange rate if not 1 (i.e. not GBP) i.e. for Galleon IE, DE and FR
If FCurrencyDefaultRate <> 1 then LProductPrice = LProductPrice * FCurrencyDefaultRate
' product image URL, ### need to check if image already exists
' LProductImageURL = GetStoreURL & "/getpic.asp?size=o&code=" & LProductCode
' (SS,9/8/18) replaced above with following to make use of direct jpg images from files without getpic.asp
LProductImageURL = GetFullProductImageLink(LProductCode, "o")
' availability
LNumInStock = GetQueryField("NumInStock")
If IsNull(LNumInStock) Or NZ(LNumInStock) > 0 Then
LAvailability = "in stock"
ElseIf IntToBool(GetQueryField("AllowOutOfStockOrdering")) Then
If NZ(GetQueryField("AvailabilityDaysFrom")) >= 0 Then
LAvailability = "in stock"
Else
LAvailability = "preorder"
End If
Else
LAvailability = "out of stock"
End If
' GTIN
' (SS,19/5/16) new number that is required, if we don't have it or mpn and identifier_exists must be set to false
' this field was added to the products table on 19/5/16
LGTIN = Trim(NB(GetQueryField("GTIN")))
' MPN
LMPN = Trim(NB(GetQueryField("MPN")))
' (SS,19/5/16) commented out the following we're now leaving blank and setting identifier_exists to false
' If LMPN = "" then LMPN = LProductCode ' i.e. if no MPN then use ProductCode
' (SS,19/5/16) set new field LIdentifierExists to true or false depending on whether GTIN MPN
If LGTIN = "" And LMPN = "" Then
LIdentifierExists = "FALSE"
Else
LIdentifierExists = "TRUE"
End If
' delivery cost
' set up values to call GetDeliveryCost (which is normally called from GetOrderTotalsForBasket)
' following created from code in GetOrderTotalsForBasket
LWeight = NZ(GetQueryField("PostalWeight"))
If LWeight = 0 Then LNonWeightPrice = LProductPrice
If IntToBool(GetFieldValue("CustomFlag")) Then
LFlaggedWeight = LWeight
LNonFlaggedWeight = 0 ' (SS,27/1/16)
Else
LNonFlaggedWeight = LWeight
LFlaggedWeight = 0 ' (SS,27/1/16)
End If
' (SS,27/1/16) hook to allow product price and weight to be adjusted
If FunctionExists("CustomGoogleShoppingProductAdjust") Then
' CustomGoogleShoppingProductAdjust may adjust any of the following: LProductName, LProductPrice, LNonWeightPrice, LWeight, LFlaggedWeight, LNonFlaggedWeight
' was originally added to allow a product to have minimum sections for CIRC, for correct price for e.g. the 3 sections rather than per section
CustomGoogleShoppingProductAdjust LProductID, LProductCode, LProductName, LProductPrice, LNonWeightPrice, LWeight, LFlaggedWeight, LNonFlaggedWeight
End If
' calculate VAT and add to VAT total, also takes VAT Band into account, if PricesEnteredIncludeVAT then it's the VAT content of the Price
LVATTotal = LVATTotal + GetVATForProduct(LProductPrice)
LDeliveryCost = GetDeliveryCost(LProductPrice, LNonWeightPrice, LWeight, LFlaggedWeight, LNonFlaggedWeight, LVATTotal) ' will include VAT if PricesEnteredIncludeVAT
' add delivery cost if PricesEnteredIncludeVAT is false don't include VAT, GetDeliveryCost adds VAT if PricesEnteredIncludeVAT true
If Not PricesEnteredIncludeVAT Then LDeliveryCost = LDeliveryCost + GetVATAmountToAddNC(LDeliveryCost)
FGoogleShoppingDataFeedLine = ""
AddGoogleShoppingField "id", LProductCode
AddGoogleShoppingField "product_type", LProductType
AddGoogleShoppingField "google_product_category", Trim(GetQueryField("GoogleProductCategory"))
AddGoogleShoppingField "expiration_date", LExpirationDate ' ### to check format
AddGoogleShoppingField "price", Format2dpnc(LProductPrice) & " " & GetDefaultCurrencyCode
AddGoogleShoppingField "image_link", LProductImageURL
AddGoogleShoppingField "link", GetFullProductLinkWithName(LProductCode, LProductName)
AddGoogleShoppingField "title", RemoveHTMLTags(LProductName)
AddGoogleShoppingField "description", LProductDesc
if Not LConditionInGoogleShoppingAttribute Then AddGoogleShoppingField "condition", "new" ' (SS,22/9/17) added if condition to not include this default if "condition" product attribute is set
AddGoogleShoppingField "availability", LAvailability
AddGoogleShoppingField "brand", GetQueryField("BrandName")
AddGoogleShoppingField "gtin", LGTIN
AddGoogleShoppingField "mpn", LMPN ' (SS,19/5/16)
AddGoogleShoppingField "identifier_exists", LIdentifierExists ' (SS,19/5/16)
' (SS,28/9/16) add the extra google shopping product attribute fields, if applicable
If UBound(LGoogleShoppingProductAttributeIDs) <> -1 Then
For i = LBound(LGoogleShoppingProductAttributeIDs) To UBound(LGoogleShoppingProductAttributeIDs)
AddGoogleShoppingField LGoogleShoppingProductAttributeNames(i), Trim(NB(GetQueryField("AttributeValue_" & i)))
Next
End If
AddGoogleShoppingField "shipping_weight", LWeight
AddGoogleShoppingField "shipping", "GB:::" & Format2dpnc(LDeliveryCost) & " " & GetDefaultCurrencyCode
AddGoogleShoppingField "adwords_grouping", GetQueryField("AdWordsGroupName")
AddGoogleShoppingField "custom_label_0", GetQueryField("CustomLabel0")
AddGoogleShoppingField "custom_label_1", GetQueryField("CustomLabel1")
AddGoogleShoppingField "custom_label_2", GetQueryField("CustomLabel2")
AddGoogleShoppingField "custom_label_3", GetQueryField("CustomLabel3")
AddGoogleShoppingField "custom_label_4", GetQueryField("CustomLabel4")
If Not FGoogleShoppingDataFeedHeaderDone Then
Response.Write FGoogleShoppingDataFeedHeader & NL
FGoogleShoppingDataFeedHeaderDone = True
End If
Response.Write FGoogleShoppingDataFeedLine & NL
NextQueryRecord
Loop
CloseQuery
' assume the feed is collected by Google, set the date time last sent to now
SetSettingDateTimeNow "Froogle", "DateTimeLastSent"
' CloseDatabase used instead of Finalise in google-shipping.asp, because Finalise adds google analytics stuff
CloseDatabase
EndGoogleShoppingDataFeed
End Sub
' -------------------------------------------
' End of Google Shopping Data Feed routines
' -------------------------------------------
' -------------------------------------------
' Start of Order Progress Email routines
' -------------------------------------------
' (SS,4/7/16) based on Galleon's SendOrderReminderEmail
' (SS,6/7/16) change to show WorkingDaysToDespatch, also added {TITLE} and Order No to subject, improvements to results email
' (SS,8/3/17) adjusted gap from 4 to 2 days
' (SS,26/9/17) amended gap from 2 to LTemplateGapsDays, i.e. getting from email templates
' (SS,6/2/18) improved to now make use of the new order_progress_email_days table, which automatically handles different lead time ranges. No need for templates to be updated
' (SS,27/2/19) change to use DateTimePaid instead of DateTimeOrdered due to bank transfer payment delays
' (SS,10/12/19) modified LWorkingDaysToDespatch to prevent less than 1 appearing in from days. Now the minimum will be 1 - 2.
Sub SendOrderProgressEmails
CONST TEST_MODE = 0 ' can be 0 for off, 1 for log and emails to test account, 2 for log only, 3 log soon in browser not emailed
' check if today is a working day, abort if not
' scan each OrderProgressEmailTemplate (1 to 9)
' extract BCC, FROM, SUBJECT, MIN DESPATCH DAYS, WORKINGS DAY SINCE ORDER from template
' run query to find orders with min despatch days and matching working days since order
' for each order
' add to log
' send the email (test mode, 0 = normal, 1 = log and send emails to test account, 2 = log only)
' exit if today isn't a working day
If Not IsTodayWorkingDay Then Exit Sub
Const LOGO_FILE_NAME = "logo-email.png"
Dim LAttachLogoFileName, LTemplateTitle, LTemplateSubject, LSubject, LTemplateBody, LSQL
' (SS,6/2/18) added LMaxDespatchDays, removed LTemplateGapsDays, LTemplateWorkingDaysSinceOrder and LTemplateMinDespatchDays
Dim LBCC, LBCC2, LFrom, LMinDespatchDays, LMaxDespatchDays, LTemplateNo
Dim LTemplateBCC, LTemplateBCC2, LTemplateFrom
Dim LOrderNo, LOrderDate, LTitle, LFirstName, LSurname, LName, LEmailAddress
Dim LLogBody, LCount, LBody, LAnticipatedDespatchDate, LDedicatedDeliveryFromDate
Dim LWorkingDaysToDespatch ' (SS,6/7/16)
Dim LWorkingDaysField, LWorkingDaysSinceOrder ' (SS,6/2/18)
LLogBody = ""
LTemplateNo = 0
LLogBody = LLogBody + OrderProgressEmailResultOverallHeader
' scan each OrderProgressEmailTemplate (1 to 9)
Do While LTemplateNo < 9
LTemplateNo = LTemplateNo + 1
' get body of template (also check it exists)
LTemplateBody = GetSiteDetail("Other", "OrderProgressEmailTemplate" & LTemplateNo)
Do ' null loop for logic short-circuit (bit like a continue)
If LTemplateBody = "" Then Exit Do
' extract BCC, FROM, SUBJECT, MIN DESPATCH DAYS, WORKINGS DAY SINCE ORDER from template
LTemplateTitle = ExtractSettingFromTemplate(LTemplateBody, "TITLE")
LTemplateSubject = ExtractSettingFromTemplate(LTemplateBody, "SUBJECT")
LTemplateSubject = ReplaceStr(LTemplateSubject, "{STORE_NAME}", GetStoreName)
LTemplateBCC = ExtractSettingFromTemplate(LTemplateBody, "BCC")
LTemplateFrom = ExtractSettingFromTemplate(LTemplateBody, "FROM")
' (SS,6/2/18) removed following 2 lines, no longer applicable
'LTemplateMinDespatchDays = ExtractSettingFromTemplate(LTemplateBody, "MIN_DESPATCH_DAYS")
'LTemplateWorkingDaysSinceOrder = ExtractSettingFromTemplate(LTemplateBody, "WORKING_DAYS_SINCE_ORDER")
' (SS,6/2/18) removed following line
' LTemplateGapsDays = ExtractSettingFromTemplate(LTemplateBody, "WORKING_DAYS_TO_DESPATCH_GAP_DAYS") ' (SS,26/9/17)
' (SS,6/2/18) removed following, keeping just the LTemplateSubject
'If LTemplateSubject = "" Or LTemplateMinDespatchDays = "" Or LTemplateWorkingDaysSinceOrder = "" Then Exit Do ' validation
'LTemplateMinDespatchDays = CLng(LTemplateMinDespatchDays)
'LTemplateWorkingDaysSinceOrder = CLng(LTemplateWorkingDaysSinceOrder)
'If LTemplateMinDespatchDays <= 5 Or LTemplateWorkingDaysSinceOrder <= 1 Then Exit Do ' validation
If LTemplateSubject = "" Then Exit Do ' validation
' (SS,26/9/17) default to 2 if not defined
' (SS,6/2/18) removed following, no longer needed
'If LTemplateGapsDays = "" Then
' LTemplateGapsDays = 2
'Else
' LTemplateGapsDays = CLng(LTemplateGapsDays)
'End If
' replace template variables that weren't change for each order
' "if" to only attach logo if template contains {LOGO}, also added LOGO_URL which doesn't embed the URL
If InStr(LTemplateBody, "{LOGO}") > 0 Then
LTemplateBody = ReplaceStr(LTemplateBody, "{LOGO}", "<img src=""" + LOGO_FILE_NAME + """>")
LAttachLogoFileName = LOGO_FILE_NAME
Else
LTemplateBody = ReplaceStr(LTemplateBody, "{LOGO_URL}", GetStoreURL & "/images/" & LOGO_FILE_NAME)
LAttachLogoFileName = ""
End If
LTemplateBody = ReplaceStr(LTemplateBody, "{STORE_URL}", GetStoreURL)
LTemplateBody = ReplaceStr(LTemplateBody, "{STORE_NAME}", GetStoreName)
LTemplateBody = ReplaceStr(LTemplateBody, "{STORE_EMAIL}", GetStoreEmail)
LTemplateBody = ReplaceStr(LTemplateBody, "{STORE_TELEPHONE}", GetStoreTelephone)
' sort email bcc and from addresses, because we might be overriding the standard ones
If LTemplateFrom = "" Then LTemplateFrom = FEmailOrderConfirmationFrom
If LTemplateBCC = "" Then
LTemplateBCC = FEmailOrderConfirmationBcc1
ElseIf LTemplateBCC = "NONE" Then ' if NONE then don't send BCC
LTemplateBCC = ""
End If
LTemplateBCC2 = FEmailOrderConfirmationBcc2
LCount = 0
'LLogBody = LLogBody + OrderProgressEmailResultHeader(LTemplateTitle, "Min. Despatch Days: " & LTemplateMinDespatchDays & ", Working Days Since Order: " & LTemplateWorkingDaysSinceOrder)
' (SS,6/2/18) replaced above with following, info no longer required
LLogBody = LLogBody + OrderProgressEmailResultHeader(LTemplateTitle, "")
' (SS,6/2/18) old SQL, commented out
'LSQL = "SELECT OrderNo, DATE(DateTimeOrdered) AS OrderDate, Title, FirstName, Surname, EmailAddress, DeliveryInfo, common.GetFirstInteger(DeliveryInfo) AS MinDespatchDays, " &_
' "common.GetNthWorkingDay(DATE(DateTimeOrdered), common.GetFirstInteger(DeliveryInfo)) AS AnticipatedDespatchDate, common.GetNthWorkingDay(DATE(DateTimeOrdered), common.GetFirstInteger(DeliveryInfo) + 2) AS DedicatedDeliveryFromDate " &_
' "FROM orders WHERE Status = 'PAYMENT RECEIVED' AND common.GetFirstInteger(DeliveryInfo) >= " & LTemplateMinDespatchDays & " AND common.GetNthWorkingDay(DATE(DateTimeOrdered), " & LTemplateWorkingDaysSinceOrder & ") = CURDATE()"
' (SS,6/2/18) new version of SQL which looks at the new order_progress_email_days table, saves having to keep modifying the templates everytime the lead time changes
' (SS,27/2/19) modified to use DateTimePaid as the OrderDate, i.e. replaced 3 occurrences of DateTimeOrdered with DateTimePaid (due to bank transfer payment delays)
LSQL = "SELECT * FROM " & NL &_
"(" & NL &_
"SELECT OrderNo, DATE(DateTimePaid) AS OrderDate, Title, FirstName, Surname, EmailAddress, DeliveryInfo, " & NL &_
"common.GetFirstInteger(DeliveryInfo) AS MinDespatchDays, " & NL &_
"common.GetSecondInteger(DeliveryInfo) AS MaxDespatchDays, " & NL &_
"common.GetNthWorkingDay(DATE(DateTimePaid), common.GetFirstInteger(DeliveryInfo)) AS AnticipatedDespatchDate, " & NL &_
"common.GetNthWorkingDay(DATE(DateTimePaid), common.GetFirstInteger(DeliveryInfo) + 2) AS DedicatedDeliveryFromDate " & NL &_
"FROM orders " & NL &_
"WHERE Status = 'PAYMENT RECEIVED'" & NL &_
") o" & NL &_
"INNER JOIN order_progress_email_days op ON op.LeadTimeMinDays = o.MinDespatchDays AND op.LeadTimeMaxDays = o.MaxDespatchDays" & NL
' (SS,6/2/18) look at the title and create the appropriate WHERE SQL, i.e. field OrderProgressEmailDays or DespatchUpdateEmailDays
If LTemplateTitle = "Order Progress" Then
LWorkingDaysField = "OrderProgressEmailDays"
Else
LWorkingDaysField = "DespatchUpdateEmailDays"
End If
LSQL = LSQL & "WHERE common.GetNthWorkingDay(OrderDate, op." + LWorkingDaysField + ") = CURDATE()"
' run query to find orders with min despatch days and matching working days since order
OpenQuery(LSQL)
' for each order
Do While Not EndOfQuery
LCount = LCount + 1
LOrderNo = GetQueryField("OrderNo")
LOrderDate = GetQueryField("OrderDate")
LTitle = Trim(GetQueryField("Title"))
LFirstName = Trim(GetQueryField("FirstName"))
LSurname = Trim(GetQueryField("Surname"))
LName = Trim(LTitle & IIf(LTitle = "", "", " ") & LFirstName & IIf(LFirstName = "", "", " ") & LSurname)
LEmailAddress = GetQueryField("EmailAddress")
LAnticipatedDespatchDate = GetQueryField("AnticipatedDespatchDate")
LDedicatedDeliveryFromDate = GetQueryField("DedicatedDeliveryFromDate")
LMinDespatchDays = GetQueryField("MinDespatchDays")
LMaxDespatchDays = GetQueryField("MaxDespatchDays") ' (SS,6/2/18)
' (SS,6/7/16) e.g. 4-8, (SS,8/3/17) changed from + 4 to + 2 e.g. 5-7
' (SS,26/7/19) replaced + 2 with LTemplateGapsDays, now taken from email template
' (SS,27/7/19) added Max to prevent 0 e.g. 0-3 will become 1-3
'LWorkingDaysToDespatch = Max(1, LMinDespatchDays - LTemplateWorkingDaysSinceOrder) & "-" & (LMinDespatchDays - LTemplateWorkingDaysSinceOrder + LTemplateGapsDays)
' (SS,6/2/18) replaced above with following, i.e. we now have LMaxDespatchDays, no need for LTemplateGapsDays
'LWorkingDaysToDespatch = Max(1, LMinDespatchDays - LTemplateWorkingDaysSinceOrder) & "-" & (LMaxDespatchDays - LTemplateWorkingDaysSinceOrder)
' (SS,6/2/18) replaced above with following
' (SS,10/12/19) to prevent -1 and 0 added Max(1, ..), also added Max(2, ..) to second section to prevent 1 or less i.e. min will be 1 - 2
LWorkingDaysToDespatch = Max(1, GetQueryField("LeadTimeMinDays") - GetQueryField(LWorkingDaysField)) & "-" & Max(2, GetQueryField("LeadTimeMaxDays") - GetQueryField(LWorkingDaysField))
' (SS,6/2/18)
LWorkingDaysSinceOrder = LMinDespatchDays - (GetQueryField("LeadTimeMinDays") - GetQueryField(LWorkingDaysField))
' (SS,6/7/16) changed LDedicatedDeliveryFromDate to LWorkingDaysToDespatch
' (SS,6/2/18) added LWorkingDaysSinceOrder, LMaxDespatchDays
LLogBody = LLogBody + OrderProgressEmailResultRow(LOrderNo, LOrderDate, LName, LEmailAddress, LWorkingDaysSinceOrder, LMinDespatchDays, LMaxDespatchDays, LAnticipatedDespatchDate, LWorkingDaysToDespatch)
' send the email (test mode, 0 = normal, 1 = log and send emails to test account, 2 = log only)
LBody = LTemplateBody
If LBody <> "" Then ' only send if template not blank
' replace order specific template values
LSubject = ReplaceStr(LTemplateSubject, "{ORDER_NO}", LOrderNo)
LBody = ReplaceStr(LBody, "{ORDER_NO}", LOrderNo)
LBody = ReplaceStr(LBody, "{ORDER_DATE}", LOrderDate)
LBody = ReplaceStr(LBody, "{NAME}", LName)
LBody = ReplaceStr(LBody, "{ANTICIPATED_DESPATCH_DATE}", LAnticipatedDespatchDate)
LBody = ReplaceStr(LBody, "{DEDICATED_DELIVERY_FROM_DATE}", LDedicatedDeliveryFromDate)
LBody = ReplaceStr(LBody, "{WORKING_DAYS_TO_DESPATCH}", LWorkingDaysToDespatch) ' (SS,6/7/16)
' send the email with embedded logo, if not test mode or test mode 1
If TEST_MODE = 0 Or TEST_MODE = 1 Then
' if test mode 1 then send email to test address, set the BCCs for normal and test mode
LFrom = LTemplateFrom
If TEST_MODE = 0 Then
LBCC = LTemplateBCC
LBCC2 = LTemplateBCC2
Else ' i.e. 1
LEmailAddress = LTemplateBCC2
LBCC = ""
LBCC2 = ""
End If
' debug code to show email addresses
'Response.Write "LEmailAddress: " & LEmailAddress & BR
'Response.Write "LBCC: " & LBCC & BR
'Response.Write "LBCC2: " & LBCC2 & BR
'Response.Write "LFrom: " & LFrom & BR
If LEmailAddress <> "" Then
SendEmailByCDO LEmailAddress, LBCC, LBCC2, LFrom, LSubject, LBody, "", True, "", LAttachLogoFileName
' (SS,20/3/18) send SMS, title of template should match the SMS constant
' SMS will only be sent by SendSMS if SMSs are enabled and telephone is a mobile number
If LTemplateTitle = ST_ORDER_PROGRESS Or LTemplateTitle = ST_ANTICIPATED_DESPATCH_UPDATE Then
SendSMS LOrderNo, LTemplateTitle
End If
End If
End If
End If
NextQueryRecord
Loop
LLogBody = LLogBody + OrderProgressEmailResultFooter(LTemplateTitle, LCount)
Loop While False ' end of null loop
Loop ' scan each OrderProgressEmailTemplate (1 to 9)
CloseQuery
LLogBody = LLogBody + OrderProgressEmailResultOverallFooter
' send log email
If LLogBody <> "" And TEST_MODE < 3 Then
SendEmailByCDO FEmailContactTo, FEmailContactBcc1, FEmailContactBcc2, FEmailContactFrom, "Order Progress Result for " + GetStoreName + " on " & Now, LLogBody, "", True, "", ""
End If
End Sub
' (SS,4/7/16) returns true if today is a working day
Function IsTodayWorkingDay
IsTodayWorkingDay = GetSQLValue("SELECT common.IsWorkingDay(CURDATE())") = 1
End Function
' (SS,6/7/16)
Function OrderProgressEmailResultOverallHeader
Dim LResult
LResult = ""
LResult = LResult & "<html>" & NL
LResult = LResult & "<head>" & NL
LResult = LResult & "<title>Order Progress Emails " & Now & "</title>" & NL
LResult = LResult & "<style>" & NL
LResult = LResult & "table {border-collapse: collapse;}" & NL
LResult = LResult & "table, th, td {border: 1px solid black;}" & NL
LResult = LResult & "th, td {padding: 5px;}" & NL
LResult = LResult & "</style>" & NL
LResult = LResult & "</head>" & NL
LResult = LResult & "<body>" & NL
LResult = LResult & "<table>" & NL
Response.Write LResult
OrderProgressEmailResultOverallHeader = LResult
End Function
' (SS,6/7/16)
Function OrderProgressEmailResultOverallFooter
Dim LResult
LResult = LResult & "</table>" & NL
LResult = LResult & "</body>" & NL
LResult = LResult & "</html>" & NL
Response.Write LResult
OrderProgressEmailResultOverallFooter = LResult
End Function
' (SS,4/7/16)
' (SS,6/7/16) replaced Dedicated Delivery Date with Working Days to Despatch
Function OrderProgressEmailResultHeader(AInfo1, AInfo2)
' NL used at end of each HTML line because emailer is adding a space after 1000 characters
Dim LResult
LResult = LResult & "<tr><th colspan=""8""> </tr>" & NL ' empty line separator
LResult = LResult & "<tr><th colspan=""4"">" & AInfo1 & "</th><th colspan=""4"">" & AInfo2 & "</th></tr>" & NL
LResult = LResult & "<tr>" & NL
LResult = LResult & "<th>Order No</th>" & NL
LResult = LResult & "<th>Order Date</th>" & NL
LResult = LResult & "<th>Name</th>" & NL
LResult = LResult & "<th>Email</th>" & NL
LResult = LResult & "<th>Working Days<br>Since Order</th>" & NL ' (SS,6/2/18)
LResult = LResult & "<th>Anticipated<br>Despatch Days</th>" & NL ' (SS,6/2/18) was Minimum Despatch Days
LResult = LResult & "<th>Earliest Anticipated<br>Despatch Date</th>" & NL
LResult = LResult & "<th>Days Remaining<br>to Despatch</th>" & NL ' (SS,6/2/18) was Working Days to Despatch
LResult = LResult & "</tr>" & NL
Response.Write LResult
OrderProgressEmailResultHeader = LResult
End Function
' (SS,4/7/16)
Function OrderProgressEmailResultFooter(ATitle, ACount)
Dim LResult
LResult = "<tr><td colspan=""8""><b>" & ATitle & " emails sent for " & PluralString(ACount, "order", "orders") & "</b></td></tr>" & NL
Response.Write LResult
OrderProgressEmailResultFooter = LResult
End Function
' (SS,4/7/16)
' (SS,6/7/16) replaced ADedicatedDeliveryFromDate with AWorkingDaysToDespatch
' (SS,6/2/18) added AWorkingDaysSinceOrder
Function OrderProgressEmailResultRow(AOrderNo, AOrderDate, AName, AEmail, AWorkingDaysSinceOrder, AMinDespatchDays, AMaxDespatchDays, AAnticipatedDespatchDate, AWorkingDaysToDespatch)
Dim LResult, LCentre
LCentre = " style=""text-align:center"""
LResult = "<tr>" & NL
LResult = LResult & "<td" & LCentre & ">" & AOrderNo & "</td>" & NL
LResult = LResult & "<td" & LCentre & ">" & AOrderDate & "</td>" & NL
LResult = LResult & "<td>" & AName & "</td>" & NL
LResult = LResult & "<td>" & AEmail & "</td>" & NL
LResult = LResult & "<td" & LCentre & ">" & AWorkingDaysSinceOrder & "</td>" & NL
LResult = LResult & "<td" & LCentre & ">" & AMinDespatchDays & "-" & AMaxDespatchDays & "</td>" & NL ' (SS,6/2/18) added AMaxDespatchDays
LResult = LResult & "<td" & LCentre & ">" & AAnticipatedDespatchDate & "</td>" & NL
LResult = LResult & "<td" & LCentre & ">" & AWorkingDaysToDespatch & "</td>" & NL
LResult = LResult & "</tr>" & NL
Response.Write LResult
OrderProgressEmailResultRow = LResult
End Function
' -------------------------------------------
' End of Order Progress Email routines
' -------------------------------------------
' -------------------------------------------
' Start of VAT Number checking routines
' -------------------------------------------
Function CheckVATNumber(AVATNumber, AErrorMessage)
' if successful check already done in session on same number then return, no need to check again
' perform check and log to database, hold in session (to later save in order and also alert email to website address)
Dim LResult
Dim LCountryCode, LVATNumber, LValid, LName, LAddress, LErrorOccurred, LErrorMessage
AErrorMessage = "" ' set default error message
' same number already successfully checked, return True
If Session("VATNumberLastChecked") = AVATNumber Then
CheckVATNumber = True
Exit Function
End If
LResult = CheckVATNumberViaWebsite(AVATNumber, LCountryCode, LVATNumber, LValid, LName, LAddress, LErrorOccurred, LErrorMessage)
If Not LResult And LErrorOccurred Then
' Response.Write LErrorMessage
Session("VATNumberResultErrorMessage") = LErrorMessage
ElseIf Not LResult Then
LErrorMessage = "VAT Number " & AVATNumber & " is invalid" ' i.e. check was performed okay but number isn't valid
Else
Session("VATNumberResultErrorMessage") = ""
End If
If LResult Then
Session("VATNumberLastChecked") = AVATNumber
Session("VATNumberResultCountryCode") = LCountryCode
Session("VATNumberResultVATNumber") = LVATNumber
Session("VATNumberResultValid") = LValid
Session("VATNumberResultName") = LName
Session("VATNumberResultAddress") = LAddress
End If
AErrorMessage = LErrorMessage
CheckVATNumber = LResult
End Function
' for further info see php verison here: http://stackoverflow.com/questions/9158119/vies-vat-number-validation
' if error occurs or timeout then AErrorOccurred is True and AErrorMessage contains the error message
Function CheckVATNumberViaWebsite(AVATReg, ByRef ACountryCode, ByRef AVATNumber, ByRef AValid, ByRef AName, ByRef AAddress, ByRef AErrorOccurred, ByRef AErrorMessage)
Const WAIT_TIMEOUT = 10 ' (SS,21/11/18 changed from 5 to 10)
Dim LResult, LVATReg, LCountryCode, LVATNumber
LResult = False
LVATReg = Trim(AVATReg)
LCountryCode = Left(LVATReg, 2)
LVATNumber = Trim(Mid(LVATReg, 3))
' clear the values, i.e. default of blank
ACountryCode = ""
AVATNumber = ""
AValid = ""
AName = ""
AAddress = ""
AErrorOccurred = False
AErrorMessage = ""
Dim oXmlHTTP, LSOAPRequest
Set oXmlHTTP = CreateObject("MSXML2.ServerXMLHTTP") ' NB. "Microsoft.XMLHTTP" doesn't have waitForResponse method to check for timeout
oXmlHTTP.Open "POST", "http://ec.europa.eu/taxation_customs/vies/services/checkVatService", True
oXmlHTTP.setRequestHeader "Content-Type", "text/xml; charset=ISO-8859-1" ' charset=utf-8 charset=ISO-8859-1
oXmlHTTP.setRequestHeader "SOAPAction", "checkVatService"
LSOAPRequest = _
"<s11:Envelope xmlns:s11='http://schemas.xmlsoap.org/soap/envelope/'>" & NL &_
" <s11:Body>" & NL &_
" <tns1:checkVat xmlns:tns1='urn:ec.europa.eu:taxud:vies:services:checkVat:types'>" & NL &_
" <tns1:countryCode>" & LCountryCode & "</tns1:countryCode>" & NL &_
" <tns1:vatNumber>" & LVATNumber & "</tns1:vatNumber>" & NL &_
" </tns1:checkVat>" & NL &_
" </s11:Body>" & NL &_
"</s11:Envelope>"
oXmlHTTP.send LSOAPRequest
' Response.Write oXmlHTTP.responseText
' *** need to add some validation if call fails
Dim LHTTPResponse
On Error Resume Next
If oXmlHTTP.waitForResponse(WAIT_TIMEOUT) Then 'response ready
If Err.Number = 0 Then
LHTTPResponse = oXmlHTTP.responseText
Else
AErrorOccurred = True
AErrorMessage = "Error checking VAT Number: " & Err.Description
End If
Else 'wait timeout exceeded
'Handling timeout etc
AErrorOccurred = True
AErrorMessage = "Timed out while checking VAT Number"
End If
On Error GoTo 0 ' cancel the error handling
Set oXmlHTTP = Nothing
If Not AErrorOccurred Then
LResult = GetVATCheckResultFromXML(LHTTPResponse, ACountryCode, AVATNumber, AValid, AName, AAddress)
' debug code
'If LResult Then
' Response.Write "CountryCode: " & ACountryCode & "<br>"
' Response.Write "VATNumber: " & AVATNumber & "<br>"
' Response.Write "Name: " & AName & "<br>"
' Response.Write "Address: " & AAddress & "<br>"
'End If
End If
' Response.Write "<br>END<hr>"
CheckVATNumberViaWebsite = LResult
End Function
' see http://stackoverflow.com/questions/892161/asp-classic-xml-dom
' following might help: http://www.codeguru.com/csharp/.net/net_asp/article.php/c19551/ServerSide-XML-in-ASP.htm
' also?: http://stackoverflow.com/questions/11522316/reading-xml-data-using-classic-asp
Function GetVATCheckResultFromXML(AXMLStr, ByRef ACountryCode, ByRef AVATNumber, ByRef AValid, ByRef AName, ByRef AAddress)
Dim LResult, objXML, objList, LElem, LChildNodes, LNode
LResult = False
AValid = ""
Set objXML = Server.CreateObject("Microsoft.XMLDOM")
objXML.LoadXml(AXMLStr)
Set objList = objXML.getElementsByTagName("checkVatResponse")
If objList.length > 0 Then
Dim LName, LValue
'Response.Write "<hr>" & vbCrLf
For Each LElem In objList
Set LChildNodes = LElem.childNodes
For Each LNode In LChildNodes
' Response.Write node.nodeName & " = " & Server.HTMLEncode(node.text) & "<br />" & vbCrLf
LName = LNode.nodeName
LValue = LNode.text
If LName = "countryCode" Then
ACountryCode = LValue
ElseIf LName = "vatNumber" Then
AVATNumber = LValue
ElseIf LName = "valid" Then
AValid = LValue
ElseIf LName = "name" Then
AName = LValue
ElseIf LName = "address" Then
AAddress = LValue
End If
'Response.Write node.nodeName & " = " & Server.HTMLEncode(node.text) & "<br />" & vbCrLf
Next
'Response.Write "<hr>" & vbCrLf
Next
End If
Set objXML = Nothing
If AValid <> "" Then
LResult = UCase(AValid) = "TRUE"
End If
GetVATCheckResultFromXML = LResult
End Function
' (SS,6/10/11) to check VAT validation and note in notes field, called for orders and customers (accounts)
' (SS,10/8/16) moved here from just before Function GetOutOfStockDetails
' (SS,10/8/16) rewrote to save the new values set in CheckVATNumber above
' (SS,6/10/16) modified to save in payment log field instead of notes
' (SS,27/1/17) fixed bug which caused when saving account details because PaymentLog field doesn't exist in customers table, now Notes used if table is customers
Sub NoteVATValidation(ATable, ACriteria)
If Session("VATNumberLastChecked") = "" And Session("VATNumberResultErrorMessage") = "" Then Exit Sub
Dim LNote
LNote = NL & "VAT Number Check Result:" & NL
If Session("VATNumberResultErrorMessage") <> "" Then
LNote = LNote & Session("VATNumberResultErrorMessage")
Else
' (SS,23/8/17) moved Valid to the top from below VATNumber
LNote = LNote &_
"Valid: " & Session("VATNumberResultValid") & NL &_
"CountryCode: " & Session("VATNumberResultCountryCode") & NL &_
"VATNumber: " & Session("VATNumberResultVATNumber") & NL &_
"Name: " & Session("VATNumberResultName") & NL &_
"Address: " & Session("VATNumberResultAddress") & NL
End If
' (SS,6/10/16) replaced Notes with PaymentLog
' (SS,27/1/17) fixed bug was was causing "Unknown column 'PaymentLog' in 'field list'" error when updating account details, this is due to customers table not having a PaymentLog field
' Fixed by using PaymentLog field for orders, and Notes for customers table, PaymentLog replaced with LLogField
' (SS,23/8/17) Fixed bug where PaymentLog was being overwritten, now calls AddToPaymentLog routine for "orders" table
If ATable = "orders" Then
AddToPaymentLog "VAT No Check", LNote ' (SS,23/8/17)
Else
ExecuteQuery("UPDATE " & ATable & " SET Notes = CONCAT(COALESCE(Notes, """"), NOW(), "" "", '" & CleanSQLStr(LNote) & "', ""\r\n"") WHERE " & ACriteria)
End If
End Sub
' (SS,10/8/16) added following to add given fields from orders table to VAT alert email, called from EmailOrder
Function EmailOrderVATAlertAddFields(AOrderNo, AFieldList)
Dim LResult, LFields, i, LFieldName
LResult = ""
LFields = Split(AFieldList, ",")
For i = 0 To UBound(LFields)
LFieldName = TrimAll(LFields(i)) ' TrimAll used instead of Trim to also remove leading and trailing newline/carriage returns
LResult = LResult & LFieldName & ": " & NB(GetOrderFieldValue(AOrderNo, LFieldName)) & NL
Next
EmailOrderVATAlertAddFields = LResult
End Function
' (SS,11/8/16) following code moved here from end of EmailOrder
Sub SendVATNumberAlertEmail(AOrderNo, AStatus)
' (SS,12/12/12) if VAT deducted for intracommunity supply then send alert email if enabled
If VATIntraCommunityEmailAlertEnabled And (AStatus = PAYMENT_RECEIVED_STATUS Or AStatus = AWAITING_PAYMENT_STATUS) Then
Dim LVATNumber, LBody
LVATNumber = NB(GetOrderFieldValue(AOrderNo, "VATNumber"))
If LVATNumber <> "" Then
Dim LVATDeducted
LVATDeducted = NZ(GetOrderFieldValue(AOrderNo, "VATDeducted"))
If CDbl(LVATDeducted) > 0 Then
' LBody defined earlier, reused here
' (SS,3/12/15) replaced:
' "Check URL 2: " & "http://isvat.appspot.com/" & Left(LVATNumber, 2) & "/" & Mid(LVATNumber, 3)
' with following and some help text prefixing it:
' "Check URL 2: " & "https://vatlayer.com/?vat_number=" & LVATNumber
LBody =_
"Order No: " & AOrderNo & NL &_
"Status: " & AStatus & NL &_
"VAT Number: " & LVATNumber & NL &_
"VAT Deducted: " & LVATDeducted & NL & NL
' (SS,10/8/16) added following
' (SS,24/8/17) added FirstName and Surname
LBody = LBody & EmailOrderVATAlertAddFields(AOrderNo, "FirstName,Surname,CompanyName,AddressLine1,AddressLine2,Town,County,Postcode,Country,DeliveryAddressSameAsInvoice,DeliveryCompanyName,DeliveryAddressLine1,DeliveryAddressLine2,DeliveryTown,DeliveryCounty,DeliveryPostcode,DeliveryCountry")
' (SS,10/8/16) removed following, replaced with check results below
' & NL &_
' "Check URL 1: " & "http://vatid.eu/check/" & Left(LVATNumber, 2) & "/" & Mid(LVATNumber, 3)& NL &_
' "For following you need to copy and paste VAT No into text box:" & NL &_
' "Check URL 2: " & "https://vatlayer.com/?vat_number=" & LVATNumber
' (SS,10/8/16) following replaces above with notes that should contain the results of the VAT check
' LBody = LBody & NL & GetOrderFieldValue(AOrderNo, "Notes")
' (SS,23/8/17) replaced above with following
LBody = LBody & NL & GetPaymentLogSection(GetOrderFieldValue(AOrderNo, "PaymentLog"), "VAT No Check")
' (SS,24/8/17) to send different subject in email if VAT No was valid or not
Dim LVATNoValid
LVATNoValid = InStr(1, LBody, "Valid: true", vbTextCompare) > 0
' (SS,24/8/17) added following to added a HTML table
LBody = GetTextAsHTMLTable(LBody, ": ", True, True, "right") ' space after : prevents the date/time field from being separated
' SendAlertEmail "VAT deducted for Intra-Community supply. VAT Number to be checked.", LBody, True
' (SS,24/8/17) above replaced by below
SendNotificationEmail "VAT deducted for Intra-Community supply. VAT Number " + iif(LVATNoValid, "Okay", "BAD"), LBody, Not LVATNoValid, True
End If
End If
End If
End Sub
' (SS,23/8/17)
Function GetVATNoCheckResults(AOrderNo)
Dim LVATCheckResult, LPaymentLog
LPaymentLog = GetOrderFieldValue(AOrderNo, "PaymentLog")
End Function
' -------------------------------------------
' End of VAT Number checking routines
' -------------------------------------------
' (SS,3/10/16)
' -------------------------------------------
' Start of Local Mode Features
' -------------------------------------------
Sub SetLocalMode(ALocalMode)
Session("LocalMode") = ALocalMode
End Sub
' checks local mode using querystring localmodekey, sets local mode to true if key matches, key will change daily
Sub CheckLocalMode
If Not LocalModeEnabled Then Exit Sub
Dim LLocalModeKey, LSQL
LLocalModeKey = CleanRequestQueryString("localmodekey")
If Not IsLocalMode And LLocalModeKey <> "" Then
' CAST AS CHAR used for whole value ensure string, else it appears as ????...
LSQL = "SELECT CAST(MD5(CONCAT('" + GetStoreName + "Salt123', CAST(CURDATE() AS CHAR))) AS CHAR) AS LocalModeKey"
'Response.Write "### LOCAL MODE KEY: " & GetSQLValueAsString(LSQL) & "###" & NL
If LLocalModeKey = GetSQLValueAsString(LSQL) Then
Response.Write "### LOCAL MODE HAS BEEN ACTIVATED ###" & NL
SetLocalMode True
End If
End If
End Sub
Function IsLocalMode
IsLocalMode = Session("LocalMode") = True ' ensure "" is treated as False
End Function
Sub GetLocalModeDetailsFromForm
Session("DeliveryCostOverride") = Trim(CleanRequestForm("DeliveryCostOverride"))
Session("DeliveryCostOverrideAmount") = Trim(CleanRequestForm("DeliveryCostOverrideAmount")) ' (SS,24/3/17)
Session("DiscountOverride") = Trim(CleanRequestForm("DiscountOverride"))
Session("Priority") = CleanRequestForm("Priority")
Session("Exchange") = CleanRequestForm("Exchange") = "checked"
Session("ExchangeReason") = Trim(CleanRequestForm("ExchangeReason"))
If Session("ExchangeReason") = COMBO_PLEASE_SELECT Then Session("ExchangeReason") = "" ' (SS,5/4/17) set to blank if "Please select..." selected
Session("Notes") = Trim(CleanRequestForm("Notes"))
End Sub
' clears ready for new session / order
' (SS,6/5/17) renamed from ClearLocalModeDetails to ClearLocalModeSettings
Sub ClearLocalModeSettings
'Session("DeliveryCostOverride") = Empty ' (SS,4/11/16) replaced "" with Empty
'Session("DeliveryCostOverrideAmount") = Empty ' (SS,4/4/17)
'Session("DiscountOverride") = Empty ' (SS,4/11/16) replaced "" with Empty
'Session("Priority") = Empty ' (SS,4/11/16) replaced "" with Empty
'Session("Exchange") = False
'Session("ExchangeReason") = ""
'Session("Notes") = ""
' (SS,6/4/17) replaced above with following, also retains local mode setting which is held in a session variable
If IsLocalMode Then
Session.Contents.RemoveAll()
SetLocalMode True
End If
End Sub
' (SS,1/12/16) priority is now always saved, this sets the default of 5 if it has already been assigned
Sub SetPriorityDefault(ADefaultPriority)
If IsEmpty(Session("Priority")) Then SetPriority(ADefaultPriority)
End Sub
' (SS,1/12/16) sets the priority for the order, can be called from customutils.asp
Sub SetPriority(APriority)
Session("Priority") = APriority
End Sub
' (SS,1/12/16) returns the existing priority, can be called from customutils.asp
Function GetPriority
GetPriority = Session("Priority")
End Function
' (SS,24/3/17) modified to use
Function IsDeliveryCostOverride
Dim LResult
If IsLocalMode Then
' (SS,4/11/16) added IsEmpty because IsNumeric returns True if no value yet set
'LResult = Not IsEmpty(Session("DeliveryCostOverride")) And IsNumeric(Session("DeliveryCostOverride"))
If Session("DeliveryCostOverride") = "Please select..." Then
LResult = False
ElseIf Session("DeliveryCostOverride") = "Other" Then
LResult = Not IsEmpty(Session("DeliveryCostOverrideAmount")) And IsNumeric(Session("DeliveryCostOverrideAmount"))
Else
LResult = True
End If
Else
LResult = False
End If
IsDeliveryCostOverride = LResult
End Function
' need to call IsDeliveryCostOverride first and then call this if IsDeliveryCostOverride returns true
' (SS,24/3/17) new version with ADeliveryCost paramater
Function GetDeliveryCostOverride(ADeliveryCost)
Dim LDeliveryCostOverride, LResult
LDeliveryCostOverride = Session("DeliveryCostOverride")
If LDeliveryCostOverride = "FOC" Then
LResult = 0
ElseIf LDeliveryCostOverride = "Standard delivery and collection" Then
LResult = ADeliveryCost * 2
ElseIf LDeliveryCostOverride = "Other" Then
LResult = Round2dp(Session("DeliveryCostOverrideAmount"))
Else ' i.e. "Standard delivery" or "Collection only"
LResult = ADeliveryCost
End If
GetDeliveryCostOverride = LResult
End Function
' (SS,24/3/17) new version which is now a percentage
Function IsDiscountOverride
Dim LResult
If IsLocalMode Then
If Session("DiscountOverride") <> "" Then
' (SS,23/3/17) now a percentage instead of fixed value
Dim LPercentage
LPercentage = ReplaceStr(Session("DiscountOverride"), "%", "")
LResult = IsNumeric(LPercentage)
' set to false if value is 0
If LResult Then
LResult = CInt(LPercentage) > 0
End If
Else
LResult = False
End If
Else
LResult = False
End If
IsDiscountOverride = LResult
End Function
' need to call IsDiscountOverride first and then call this if IsDiscountOverride returns true
' (SS,23/3/17) added const ASubtotal which discount override is now entered as a percentage, this as a percentage of subtotal is now returned
Function GetDiscountOverride(ASubtotal)
'GetDiscountOverride = -Round2dp(Session("DiscountOverride")) ' minus used to subtract
' (SS,23/3/17) replaced above with following
Dim LPercentage
LPercentage = CInt(ReplaceStr(Session("DiscountOverride"), "%", ""))
GetDiscountOverride = -Round2dp(ASubtotal * LPercentage / 100)
End Function
' (SS,28/3/17) called by custutils.asp when determining price
Function IsExchange
IsExchange = Session("Exchange") = True
End Function
' (SS,28/3/17) called by custutils.asp when determining price
Function GetExchangeReason
GetExchangeReason = Session("ExchangeReason")
End Function
' (SS,6/10/16) used in local mode
Function GetLocalModePaymentReference
GetLocalModePaymentReference = CleanRequestForm("paymentref")
End Function
' (SS,21/10/16) new settings for local mode
Function LocalModeEnabled
LocalModeEnabled = FLocalModeEnabled
End Function
' (SS,21/10/16) new settings for local mode
' (SS,3/8/17) added default if blank, also renamed from LocalModeExchangeReasons to GetLocalModeExchangeReasons
Function GetLocalModeExchangeReasons
If FLocalModeExchangeReasons = "" Then
GetLocalModeExchangeReasons = "Warranty (FOC),Warranty (Charged),Damaged in transit,Other"
Else
GetLocalModeExchangeReasons = FLocalModeExchangeReasons
End If
End Function
' (SS,21/10/16) new settings for local mode
' (SS,3/8/17) added default if blank, also renamed from LocalModePaymentMethods to GetLocalModePaymentMethods
Function GetLocalModePaymentMethods
If FLocalModePaymentMethods = "" Then
GetLocalModePaymentMethods = "BANK TRANSFER,CASH,CHEQUE,PAYPAL MANUAL,EPDQ,CARD OVER PHONE,CARD IN SHOP,OTHER"
Else
GetLocalModePaymentMethods = FLocalModePaymentMethods
End If
End Function
' -------------------------------------------
' End of Local Mode Features
' -------------------------------------------
' -------------------------------------------
' Start of Address Lookup Features
' -------------------------------------------
' (SS,21/10/16) new settings for address lookup
Function AddressLookupEnabled
AddressLookupEnabled = FAddressLookupEnabled
End Function
Function AddressLookupType
AddressLookupType = FAddressLookupType
End Function
Function AddressLookupAccountCode
AddressLookupAccountCode = FAddressLookupAccountCode
End Function
Function AddressLookupLocalModeOnly
AddressLookupLocalModeOnly = FAddressLookupLocalModeOnly
End Function
Sub DoAddressLookup
If Not AddressLookupEnabled Or AddressLookupType = "" Or AddressLookupAccountCode = "" Then Exit Sub
If AddressLookupLocalModeOnly And Not IsLocalMode Then Exit Sub
If AddressLookupType = "PCAPredict" Then
' ITPAR11111
%>
<script>(function (a, c, b, e) {
a[b] = a[b] || {}; a[b].initial = { accountCode: "<%=AddressLookupAccountCode%>", host: "<%=AddressLookupAccountCode%>.pcapredict.com" };
a[b].on = a[b].on || function () { (a[b].onq = a[b].onq || []).push(arguments) }; var d = c.createElement("script");
d.async = !0; d.src = e; c = c.getElementsByTagName("script")[0]; c.parentNode.insertBefore(d, c)
})(window, document, "pca", "//<%=AddressLookupAccountCode%>.pcapredict.com/js/sensor.js");
</script>
<%
End If
End Sub
' -------------------------------------------
' End of Address Lookup Features
' -------------------------------------------
' -------------------------------------------
' Start of Refund Email routines
' -------------------------------------------
' (SS,13/4/17) based on SendOrderProgressEmails, using template from site settings
' called from refund-details.asp orderno is in query string
' if cmd=email then email is sent, else output as HTML,
' (SS,5/12/18) changes to include new RefundAmount and RefundAccountLast4Digits fields
Sub SendRefundEmail
' following taken from ShowEmailConfirmationHTML to only allow if correct details
Dim LOrderNo, LSessionID, LIPAddress, LSendEmail
LOrderNo = CleanRequestQueryString("orderno")
LSessionID = CleanRequestQueryString("sid")
LIPAddress = CleanRequestQueryString("ip")
LSendEmail = CleanRequestQueryString("cmd") = "email"
If LOrderNo = "" Or LSessionID = "" Or LIPAddress = "" Then
Response.Write("Sorry, not enough info to show refund details<br>")
Exit Sub
End If
' check if order has refund amount
' calculate goods refund and vat from refund vat
' replace tokens in template and send email (to same address as order confirmation)
CONST TEST_MODE = 0 ' can be 0 for off, 1 for log and emails to test account, 2 for log only, 3 log soon in browser not emailed
Const LOGO_FILE_NAME = "logo-email.png"
Dim LAttachLogoFileName, LTemplateTitle, LTemplateSubject, LSubject, LTemplateBody, LSQL
Dim LBCC, LBCC2, LFrom
Dim LTemplateBCC, LTemplateBCC2, LTemplateFrom
Dim LOrderDate, LTitle, LFirstName, LSurname, LName, LEmailAddress, LBody
Dim LRefundDate, LRefundDetails, LRefundAmount, LGoodsRefunded, LVATRefunded, LVATIncluded, LGrandTotal, LVATRate
' (SS,5/12/18)
Dim LRefundMethod, LRefundAccountLast4Digits
' get body of template (also check it exists)
LTemplateBody = GetSiteDetail("Other", "RefundEmailTemplate")
Do ' null loop for logic short-circuit (bit like a continue)
If LTemplateBody = "" Then Exit Do
' extract BCC, FROM, SUBJECT, MIN DESPATCH DAYS, WORKINGS DAY SINCE ORDER from template
LTemplateTitle = ExtractSettingFromTemplate(LTemplateBody, "TITLE")
LTemplateSubject = ExtractSettingFromTemplate(LTemplateBody, "SUBJECT")
LTemplateSubject = ReplaceStr(LTemplateSubject, "{STORE_NAME}", GetStoreName)
LTemplateBCC = ExtractSettingFromTemplate(LTemplateBody, "BCC")
LTemplateFrom = ExtractSettingFromTemplate(LTemplateBody, "FROM")
' replace template variables that weren't change for each order
' "if" to only attach logo if template contains {LOGO}, also added LOGO_URL which doesn't embed the URL
If InStr(LTemplateBody, "{LOGO}") > 0 Then
LTemplateBody = ReplaceStr(LTemplateBody, "{LOGO}", "<img src=""" + LOGO_FILE_NAME + """>")
LAttachLogoFileName = LOGO_FILE_NAME
Else
LTemplateBody = ReplaceStr(LTemplateBody, "{LOGO_URL}", GetStoreURL & "/images/" & LOGO_FILE_NAME)
LAttachLogoFileName = ""
End If
LTemplateBody = ReplaceStr(LTemplateBody, "{STORE_URL}", GetStoreURL)
LTemplateBody = ReplaceStr(LTemplateBody, "{STORE_NAME}", GetStoreName)
LTemplateBody = ReplaceStr(LTemplateBody, "{STORE_EMAIL}", GetStoreEmail)
LTemplateBody = ReplaceStr(LTemplateBody, "{STORE_TELEPHONE}", GetStoreTelephone)
' sort email bcc and from addresses, because we might be overriding the standard ones
If LTemplateFrom = "" Then LTemplateFrom = FEmailOrderConfirmationFrom
If LTemplateBCC = "" Then
LTemplateBCC = FEmailOrderConfirmationBcc1
ElseIf LTemplateBCC = "NONE" Then ' if NONE then don't send BCC
LTemplateBCC = ""
End If
LTemplateBCC2 = FEmailOrderConfirmationBcc2
' (SS,20/4/17) removed VATDeducted, CurrencyCode, ExchangeRate
' (SS,5/12/18) added RefundMethod and RefundAccountLast4Digits
LSQL = "SELECT OrderNo, DATE(DateTimeOrdered) AS OrderDate, Title, FirstName, Surname, EmailAddress, RefundDate, RefundAmount, RefundDetails, RefundMethod, RefundAccountLast4Digits, VATIncluded, GrandTotal FROM orders WHERE OrderNo = '" _
& CleanSQLStr(LOrderNo) & "' AND SessionID = '" + CleanSQLStr(LSessionID) + "' AND IPAddress = '" + CleanSQLStr(LIPAddress) + "'"
OpenQuery(LSQL)
If EndOfQuery Then
Response.Write("Sorry, not enough credentials to show order details<br>")
Exit Do
Else
' (SS,13/4/17) handle currency
' (SS,19/4/17) removed to force GBP because exchange rate may be different due to refund issued at a later date
'Dim LCurrencyCode
'LCurrencyCode = GetFieldValue("CurrencyCode")
'SetCurrencyRate(CDbl(GetFieldValue("ExchangeRate")))
'SetCurrencyCode(LCurrencyCode) ' (SS,28/10/09) added this to correct pound symbol incorrectly appearing in HTML emails when non UK currency
'SetCurrencySymbol(LCurrencyCode) ' (SS,28/10/09) sets the currency symbol
' LOrderNo = GetQueryField("OrderNo")
LOrderDate = GetQueryField("OrderDate")
LTitle = Trim(GetQueryField("Title"))
LFirstName = Trim(GetQueryField("FirstName"))
LSurname = Trim(GetQueryField("Surname"))
LName = Trim(LTitle & IIf(LTitle = "", "", " ") & LFirstName & IIf(LFirstName = "", "", " ") & LSurname)
LEmailAddress = GetQueryField("EmailAddress")
LVATIncluded = GetQueryField("VATIncluded")
LGrandTotal = GetQueryField("GrandTotal")
LRefundDate = GetQueryField("RefundDate")
LRefundAmount = Round2dp(GetQueryField("RefundAmount"))
LRefundDetails = GetQueryField("RefundDetails")
' (SS,5/12/18)
LRefundMethod = GetQueryField("RefundMethod")
LRefundAccountLast4Digits = GetQueryField("RefundAccountLast4Digits")
' calculate goods refunded and VAT refunded
LVATRate = Round2dp(LVATIncluded / (LGrandTotal - LVATIncluded)) ' VAT rate calculated from VAT amount of order
LGoodsRefunded = Round2dp(LRefundAmount / (1 + LVATRate))
LVATRefunded = Round2dp(LRefundAmount - LGoodsRefunded)
' format for display
LRefundAmount = CorrectCurrencyActual(LRefundAmount, True)
LGoodsRefunded = CorrectCurrencyActual(LGoodsRefunded, True)
LVATRefunded = CorrectCurrencyActual(LVATRefunded, True)
' send the email (test mode, 0 = normal, 1 = log and send emails to test account, 2 = log only)
LBody = LTemplateBody
' replace order specific template values
LSubject = ReplaceStr(LTemplateSubject, "{ORDER_NO}", LOrderNo)
LBody = ReplaceStr(LBody, "{ORDER_NO}", LOrderNo)
LBody = ReplaceStr(LBody, "{ORDER_DATE}", LOrderDate)
LBody = ReplaceStr(LBody, "{NAME}", LName)
LBody = ReplaceStr(LBody, "{REFUND_DATE}", LRefundDate)
LBody = ReplaceStr(LBody, "{REFUND_AMOUNT}", LRefundAmount)
LBody = ReplaceStr(LBody, "{REFUND_DETAILS}", ConvertNewlinesToHTML(LRefundDetails))
LBody = ReplaceStr(LBody, "{GOODS_REFUNDED}", LGoodsRefunded)
LBody = ReplaceStr(LBody, "{VAT_REFUNDED}", LVATRefunded)
LBody = ReplaceStr(LBody, "{VAT_REG_NO}", GetStoreVATRegNo)
' (SS,5/12/18) added following two
LBody = ReplaceStr(LBody, "{REFUND_METHOD}", LRefundMethod)
LBody = ReplaceStr(LBody, "{REFUND_ACCOUNT_LAST_4_DIGITS}", LRefundAccountLast4Digits)
' send the email with embedded logo, if not test mode or test mode 1
If TEST_MODE = 0 Or TEST_MODE = 1 Then
' if test mode 1 then send email to test address, set the BCCs for normal and test mode
LFrom = LTemplateFrom
If TEST_MODE = 0 Then
LBCC = LTemplateBCC
LBCC2 = LTemplateBCC2
Else ' i.e. 1
LEmailAddress = LTemplateBCC2
LBCC = ""
LBCC2 = ""
End If
' debug code to show email addresses
'Response.Write "LEmailAddress: " & LEmailAddress & BR
'Response.Write "LBCC: " & LBCC & BR
'Response.Write "LBCC2: " & LBCC2 & BR
'Response.Write "LFrom: " & LFrom & BR
If LEmailAddress <> "" And LSendEmail Then
SendEmailByCDO LEmailAddress, LBCC, LBCC2, LFrom, LSubject, LBody, "", True, "", LAttachLogoFileName
Response.Write LBody
Else
Response.Write LBody
End If
End If
End If
Loop While False ' end of null loop
CloseQuery
End Sub
' -------------------------------------------
' End of Refund Email routines
' -------------------------------------------
' -------------------------------------------
' Start of Payment Received Email routines
' -------------------------------------------
' (SS,11/8/17) based on SendRefundEmail above, using template from site settings
' called from payment-received-email.asp orderno is in query string
' if cmd=email then email is sent, else output as HTML,
' (!!!**** I'm aware of the repeated code, perhaps in future commonise some of the code with SendRefundEmail)
Sub SendPaymentReceivedEmail
' following taken from ShowEmailConfirmationHTML to only allow if correct details
Dim LOrderNo, LSessionID, LIPAddress, LSendEmail
LOrderNo = CleanRequestQueryString("orderno")
LSessionID = CleanRequestQueryString("sid")
LIPAddress = CleanRequestQueryString("ip")
LSendEmail = CleanRequestQueryString("cmd") = "email"
If LOrderNo = "" Or LSessionID = "" Or LIPAddress = "" Then
Response.Write("Sorry, not enough info to show payment details<br>")
Exit Sub
End If
' check if order has refund amount
' calculate goods refund and vat from refund vat
' replace tokens in template and send email (to same address as order confirmation)
CONST TEST_MODE = 0 ' can be 0 for off, 1 for log and emails to test account, 2 for log only, 3 log soon in browser not emailed
Const LOGO_FILE_NAME = "logo-email.png"
Dim LAttachLogoFileName, LTemplateTitle, LTemplateSubject, LSubject, LTemplateBody, LSQL
Dim LBCC, LBCC2, LFrom
Dim LTemplateBCC, LTemplateBCC2, LTemplateFrom
Dim LOrderDate, LTitle, LFirstName, LSurname, LName, LEmailAddress, LBody
Dim LPaymentAmount
' get body of template (also check it exists)
LTemplateBody = GetSiteDetail("Other", "PaymentReceivedEmailTemplate")
Do ' null loop for logic short-circuit (bit like a continue)
If LTemplateBody = "" Then Exit Do
' extract BCC, FROM, SUBJECT, MIN DESPATCH DAYS, WORKINGS DAY SINCE ORDER from template
LTemplateTitle = ExtractSettingFromTemplate(LTemplateBody, "TITLE")
LTemplateSubject = ExtractSettingFromTemplate(LTemplateBody, "SUBJECT")
LTemplateSubject = ReplaceStr(LTemplateSubject, "{STORE_NAME}", GetStoreName)
LTemplateBCC = ExtractSettingFromTemplate(LTemplateBody, "BCC")
LTemplateFrom = ExtractSettingFromTemplate(LTemplateBody, "FROM")
' replace template variables that weren't change for each order
' "if" to only attach logo if template contains {LOGO}, also added LOGO_URL which doesn't embed the URL
If InStr(LTemplateBody, "{LOGO}") > 0 Then
LTemplateBody = ReplaceStr(LTemplateBody, "{LOGO}", "<img src=""" + LOGO_FILE_NAME + """>")
LAttachLogoFileName = LOGO_FILE_NAME
Else
LTemplateBody = ReplaceStr(LTemplateBody, "{LOGO_URL}", GetStoreURL & "/images/" & LOGO_FILE_NAME)
LAttachLogoFileName = ""
End If
LTemplateBody = ReplaceStr(LTemplateBody, "{STORE_URL}", GetStoreURL)
LTemplateBody = ReplaceStr(LTemplateBody, "{STORE_NAME}", GetStoreName)
LTemplateBody = ReplaceStr(LTemplateBody, "{STORE_EMAIL}", GetStoreEmail)
LTemplateBody = ReplaceStr(LTemplateBody, "{STORE_TELEPHONE}", GetStoreTelephone)
' sort email bcc and from addresses, because we might be overriding the standard ones
If LTemplateFrom = "" Then LTemplateFrom = FEmailOrderConfirmationFrom
If LTemplateBCC = "" Then
LTemplateBCC = FEmailOrderConfirmationBcc1
ElseIf LTemplateBCC = "NONE" Then ' if NONE then don't send BCC
LTemplateBCC = ""
End If
LTemplateBCC2 = FEmailOrderConfirmationBcc2
LSQL = "SELECT OrderNo, DATE(DateTimeOrdered) AS OrderDate, DATE(DateTimePaid) AS PaymentDate, Title, FirstName, Surname, EmailAddress, GrandTotal, PaymentReceived, PaymentMethod, PaymentReference, DefaultPaymentCurrencyCode, DefaultPaymentCurrencyRate FROM orders WHERE OrderNo = '" _
& CleanSQLStr(LOrderNo) & "' AND SessionID = '" + CleanSQLStr(LSessionID) + "' AND IPAddress = '" + CleanSQLStr(LIPAddress) + "' AND PaymentReceived = TRUE"
OpenQuery(LSQL)
If EndOfQuery Then
Response.Write("Sorry, not enough credentials to show payment details<br>")
Exit Do
Else
' (SS,1/8/17) set the currency code, rate, symbols for calls to CorrectCurrencyActual etc to work correctly to show the values using the actual historical currency and exchange rate
SetCurrencyForOrder GetFieldValue("DefaultPaymentCurrencyCode"), GetFieldValue("DefaultPaymentCurrencyRate")
' LOrderNo = GetQueryField("OrderNo")
LOrderDate = GetQueryField("OrderDate")
LTitle = Trim(GetQueryField("Title"))
LFirstName = Trim(GetQueryField("FirstName"))
LSurname = Trim(GetQueryField("Surname"))
LName = Trim(LTitle & IIf(LTitle = "", "", " ") & LFirstName & IIf(LFirstName = "", "", " ") & LSurname)
LEmailAddress = GetQueryField("EmailAddress")
' format for display
LPaymentAmount = CorrectCurrencyActual(GetQueryField("GrandTotal"), True)
' send the email (test mode, 0 = normal, 1 = log and send emails to test account, 2 = log only)
LBody = LTemplateBody
' replace order specific template values
LSubject = ReplaceStr(LTemplateSubject, "{ORDER_NO}", LOrderNo)
LBody = ReplaceStr(LBody, "{ORDER_NO}", LOrderNo)
LBody = ReplaceStr(LBody, "{ORDER_DATE}", LOrderDate)
LBody = ReplaceStr(LBody, "{NAME}", LName)
LBody = ReplaceStr(LBody, "{PAYMENT_DATE}", GetQueryField("PaymentDate"))
LBody = ReplaceStr(LBody, "{PAYMENT_METHOD}", GetQueryField("PaymentMethod"))
LBody = ReplaceStr(LBody, "{PAYMENT_REFERENCE}", Trim(GetQueryField("PaymentReference")))
LBody = ReplaceStr(LBody, "{PAYMENT_AMOUNT}", LPaymentAmount)
LBody = ReplaceStr(LBody, "{VAT_REG_NO}", GetStoreVATRegNo)
' send the email with embedded logo, if not test mode or test mode 1
If TEST_MODE = 0 Or TEST_MODE = 1 Then
' if test mode 1 then send email to test address, set the BCCs for normal and test mode
LFrom = LTemplateFrom
If TEST_MODE = 0 Then
LBCC = LTemplateBCC
LBCC2 = LTemplateBCC2
Else ' i.e. 1
LEmailAddress = LTemplateBCC2
LBCC = ""
LBCC2 = ""
End If
' debug code to show email addresses
'Response.Write "LEmailAddress: " & LEmailAddress & BR
'Response.Write "LBCC: " & LBCC & BR
'Response.Write "LBCC2: " & LBCC2 & BR
'Response.Write "LFrom: " & LFrom & BR
If LEmailAddress <> "" And LSendEmail Then
SendEmailByCDO LEmailAddress, LBCC, LBCC2, LFrom, LSubject, LBody, "", True, "", LAttachLogoFileName
Response.Write LBody
Else
Response.Write LBody
End If
End If
End If
Loop While False ' end of null loop
CloseQuery
End Sub
' -------------------------------------------
' End of Payment Received Email routines
' -------------------------------------------
' (SS,21/7/17) returns GrandTotal for given order no, used in new bank transfer payment options
Function GetGrandTotalForOrder(AOrderNo)
GetGrandTotalForOrder = GetOrderFieldValue(AOrderNo, "GrandTotal")
End Function
' (SS,21/7/17)
Sub UpdateOrderDefaultPaymentCurrency(AOrderNo, ADefaultPaymentCurrencyCode)
' get the latest adjusted currency rate
Dim LDefaultPaymentCurrencyRate, LCurrencySymbol
GetCurrencyExchangeRateAdjusted ADefaultPaymentCurrencyCode, LDefaultPaymentCurrencyRate, LCurrencySymbol
' save it in order record
ExecuteQuery("UPDATE orders " &_
"SET DefaultPaymentCurrencyCode='" & CleanSQLStr(ADefaultPaymentCurrencyCode) & "'" &_
", DefaultPaymentCurrencyRate='" & CleanSQLStr(LDefaultPaymentCurrencyRate) & "'" &_
" WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'")
End Sub
' (SS,21/7/17) update the bank transfer details to display on form
Sub UpdateBankTransferDetails(AOrderNo)
' following also exists in Sub ShowOrderDetails(AInCheckout, AOrderNo)
' need to open the orders record the get the fields referred to by GetBankTransferPaymentLine, GetBankTransferDetailsAsHTML, messy I know
' (SS,29/9/17) added Country field, required by GetBankTransferDetailsAsHTML, then decided to have the whole record,
' replaced "OrderNo, GrandTotal, CurrencyCode, ExchangeRate, DefaultPaymentCurrencyCode, DefaultPaymentCurrencyRate, ExchangeRate, GrandTotal, Country" with *
OpenQuery("SELECT * FROM Orders WHERE orderNo = '" & CleanSQLStr(AOrderNo) & "'")
Session("BankTransferPaymentLine") = GetBankTransferPaymentLine
Session("BankTransferDetails") = GetBankTransferDetailsAsHTML
CloseQuery
End Sub
' -------------------------------------------
' Start of SMS routines
' -------------------------------------------
' (SS,20/3/18)
' get phone number from order, or pass phone no here
Sub SendSMS(AOrderNo, ASMSType)
Dim LTemplateName, LTemplateBody, LTelephone, LTo, LFrom, LSubject
' exit if SMSs are not enabled
If Not SMSEnabled Then Exit Sub
' default to same template name as ASMSType e.g. "Order Confirmation", "Order Progress", "Anticipated Despatch/Update", "Order Despatched", "Review Request"
LTemplateName = ASMSType
' if custom routine exists then call to check if SMS is necessary, if "" is returned then exit
' e.g. routine could check if customer orders a particular item, e.g. a radiator for CIRC
' Routine may also return the name of the template to use, i.e. used to override the default chosen by ASMSType
If FunctionExists("CustomCheckSMS") Then
LTemplateName = CustomCheckSMS(AOrderNo, ASMSType)
If LTemplateName = "" Then
Exit Sub
End If
End If
' get template and exit if empty
LTemplateBody = Trim(GetSiteDetail("SMS Templates", LTemplateName))
If LTemplateBody = "" Then Exit Sub
' get phone no and check if mobile, exit if not, CleanNum function strips everything except numbers
LTelephone = CleanNum(GetOrderFieldValue(AOrderNo, "Telephone"))
If Not TelephoneIsMobile(LTelephone) Then Exit Sub
' replace tokens in template with values e.g. ORDER_NO
LTemplateBody = ReplaceStr(LTemplateBody, "{ORDER_NO}", AOrderNo)
' send the SMS message, via Textlocal
LTo = LTelephone + "@txtlocal.co.uk"
'LTo = "surinder@itpartnership.com"
' From used (for security) is sms + last 4 digits of store telephone and normal email from address e.g. sms8603shoppingtest@itpartnership.com
LFrom = "sms" & Trim(Right(GetStoreTelephone, 4)) & FEmailOrderConfirmationFrom
LSubject = "SMS from " & GetStoreName & " - " & ASMSType & " - " & AOrderNo
SendEmailByCDO LTo, FEmailOrderConfirmationBcc1, FEmailOrderConfirmationBcc2, LFrom, LSubject, LTemplateBody, "", False, "", ""
End Sub
' (SS,20/3/18)
Function SMSEnabled
SMSEnabled = FSMSEnabled
End Function
' (SS,20/3/18)
Function TelephoneIsMobile(ATelephone)
TelephoneIsMobile = Left(ATelephone, 2) = "07" Or Left(ATelephone, 3) = "447" Or Left(ATelephone, 4) = "4407"
End Function
' -------------------------------------------
' End of SMS routines
' -------------------------------------------
' -------------------------------------------
' Start of Subscription Routines
' -------------------------------------------
' (SS,23/5/18)
Sub DoSubscribe
Dim LCustomerID, LEmail, LOptin
LCustomerID = CleanRequestQueryString("id")
LEmail = CleanRequestQueryString("email")
LOptin = CleanRequestQueryString("optin")
ShowSubscribeHeader
If SetCustomerSubscribed(LCustomerID, LEmail, LOptin = "yes") Then
ShowCustomerSubscription LCustomerID
Else
Response.Write "Failed to change subscription setting."
End If
ShowSubscribeFooter
End Sub
Function GetCustomerIDForOrder(AOrderNo)
GetCustomerIDForOrder = GetOrderFieldValue(AOrderNo, "CustomerID")
End Function
Function IsCustomerOptedIn(ACustomerID)
IsCustomerOptedIn = StrToBool(GetSQLValueAsString("SELECT Subscribe FROM customers WHERE CustomerID = '" & CleanSQLStr(ACustomerID) & "'"))
End Function
' set the subscribed field for given customer to given value, only if customer ID and email address matches. True is returned if change was made.
Function SetCustomerSubscribed(ACustomerID, AEmail, ASubscribe)
Dim LResult
LResult = GetSQLRecordExists("SELECT * FROM customers WHERE CustomerID = '" & CleanSQLStr(ACustomerID) & "' AND EmailAddress = '" & CleanSQLStr(AEmail) & "'")
If LResult Then ExecuteQuery("UPDATE customers SET Subscribe = " & IIF(ASubscribe, "TRUE", "FALSE") & " WHERE CustomerID = '" & CleanSQLStr(ACustomerID) & "'")
SetCustomerSubscribed = LResult
End Function
' AValue is True for Subscribe link, False for Unsubscribe link
Function GetSubscribeLink(ACustomerID, ASubscribe)
Dim LURL, LEmail, LOptin
LURL = ""
If ACustomerID <> "" Then
LEmail = GetSQLValueAsString("SELECT EmailAddress FROM customers WHERE CustomerID = '" & CleanSQLStr(ACustomerID) & "'")
If LEmail <> "" Then
If ASubscribe Then
LOptin = "yes"
Else
LOptin = "no"
End If
LURL = GetStoreURL & "/products.asp?page=account&cmd=subscribe&id=" & ACustomerID & "&email=" & URLEncode(LEmail) & "&optin=" & LOptin
End If
End If
GetSubscribeLink = LURL
End Function
' -------------------------------------------
' End of Subscription Routines
' -------------------------------------------
' -------------------------------------------
' Start of Delivery Options / Tracked Delivery Routines
' -------------------------------------------
' (SS,28/6/18)
' !!! need to set delivery option string or clear it depending on this selection
' AllowTrackedDeliverySelection(Session("DeliveryCountry"), APostalArea, ASubtotal)
Function TrackedDeliveryOptionEnabled
TrackedDeliveryOptionEnabled = IsHyperFlight ' only enabled for HyperFlight
End Function
Sub SetTrackedDeliverySelected(ASelected)
Session("TrackedDeliverySelected") = ASelected
End Sub
Function GetTrackedDeliverySelected
GetTrackedDeliverySelected = Session("TrackedDeliverySelected") = True
End Function
Sub SetTrackedDeliverySelectionAllowed(AAllowed)
Session("TrackedDeliverySelectionAllowed") = AAllowed
End Sub
Function GetTrackedDeliverySelectionAllowed
GetTrackedDeliverySelectionAllowed = Session("TrackedDeliverySelectionAllowed") = True
End Function
Function SetDeliveryOption(ADeliveryOption)
Session("DeliveryOption") = ADeliveryOption
End Function
Function GetDeliveryOption
GetDeliveryOption = Session("DeliveryOption")
End Function
Function GetCountryOptions(ACountry)
GetCountryOptions = GetSQLValueAsString("SELECT Options FROM countries WHERE Country = '" + CleanSQLStr(ACountry) + "'")
End Function
' -------------------------------------------
' End of Tracked Delivery Routines
' -------------------------------------------
' (SS,1/8/18) added following for more options info, i.e. for CIRC
' returns the original options list with extra added to the end if applicable
' current shown only in the basket page, quotation emails and order confirmation emails.
Function AddMoreOptionsInfo(AProductID, AOptionsList)
Dim LResult
' only add if add custom function exists
If FunctionExists("CustomAddMoreOptionsInfo") Then
LResult = CustomAddMoreOptionsInfo(AProductID, AOptionsList)
Else
LResult = AOptionsList ' default to returning what we already have
End If
AddMoreOptionsInfo = LResult
End Function
' -------------------------------------------
' Start of Image / Picture Naming Routines
' -------------------------------------------
' Moved all image URL fetching routines to this new section
' Written to optionally fetch using full JPG file name from cache
' Following routines now removed, no longer used since new RWD framework:
' Function GetProductLinkWithImageNo
' Sub ShowHomePagePictures
' Sub GetProductPictureSize
' Function GetProductImageLinkWithImageNo
' Sub ShowRelatedProductsOldVersion_to_be_deleted
'
' Removed references to strImageNo and imgno from
' Sub ProductsMain and Function GetProductLink
' Also Dim strImageNo removed
' (SS,7/8/18) a setting which returns True to use GetPic.asp (default), else False i.e. JPG saved from database int images/products/ are used instead
Function UseGetPicASP
UseGetPicASP = FUseGetPicASP
End Function
' (SS,18/5/07) added CleanSQLStr to SQL error when category/subcategory contains quote (")
' (SS,12/6/12) added CleanSQLStr before AType to prevent SQL injection
Function PictureExists(AType, ACode, ACode2)
Dim LWhereCode2
If ACode2 <> "" Then
LWhereCode2 = " AND Code2 = """ & CleanSQLStr(ACode2) & """"
Else
LWhereCode2 = ""
End If
OpenQuery2("SELECT Type, Code, Code2 FROM pictures WHERE Type = '" + CleanSQLStr(AType) + "' AND Code = """ & CleanSQLStr(ACode) & """" + LWhereCode2)
PictureExists = Not EndOfQuery2
CloseQuery2
End Function
' (SS,6/7/06) new
' (SS,17/1/13) replaced & with & to reduce HTML errors in http://validator.w3.org
' (SS,11/9/14) new version which works with new pictures table schema, i.e. Code2 not longer used for imageno, SortOrder used instead to order the pictures
' we need to keep the imageno working for old sites, i.e. ExpressMusic
' (SS,7/8/18) modified to allow saved/cached images in actual .jpg files to be used
' to help with SEO, speed up images, allowing web server to manage the images better
' also more SEO friendly names used, removed redundant code referring to strImageNo
Function GetProductImageLink(AProductCode, ASize)
Dim LResult
' (SS,7/8/18) added If UseGetPicASP
If UseGetPicASP Then
LResult = "getpic.asp?size=" + ASize + "&code=" & Server.URLEncode(AProductCode)
Else ' (SS,7/8/18) new method to use JPG file names
' (SS,7/4/19) new version
LResult = GetImageName("P", AProductCode, "", "", ASize)
End If
GetProductImageLink = LResult
End Function
' (SS,5/4/19) new, returns either getpic or jpg image from cache (called from inc-template-category-list.asp)
Function GetCategoryImageLink(ACategory, ASize)
Dim LResult
If UseGetPicASP Then
LResult = "getpic.asp?type=c&size=" & ASize & "&code=" & Server.URLEncode(ACategory)
Else
LResult = GetImageName("C", ACategory, "", "", ASize)
End If
GetCategoryImageLink = LResult
End Function
' (SS,5/4/19) new, returns either getpic or jpg image from cache (called from inc-template-subcategory-list.asp)
Function GetSubcategoryImageLink(ACategory, ASubcategory, ASize)
Dim LResult
If UseGetPicASP Then
LResult = "getpic.asp?type=s&size=" & ASize & "&code=" & Server.URLEncode(ACategory) & "&code2=" & Server.URLEncode(ASubcategory)
Else
LResult = GetImageName("S", ACategory, ASubcategory, "", ASize)
End If
GetSubcategoryImageLink = LResult
End Function
' (SS,13/12/13) for product option value pictures when radio type used
' (SS,9/6/17) added ASize parameter which can be o, s or l
' (SS,5/4/19) modified to allow saved/cached images in actual .jpg files to be used
Function GetProductOptionValueImageLink(AProductOptionValueID, ASize)
Dim LResult
' (SS,5/4/19) added If UseGetPicASP
If UseGetPicASP Then
' (SS,9/6/17) replaced o with ASize
LResult = "getpic.asp?type=ov&size=" & ASize &"&code=" & AProductOptionValueID
Else ' (SS,5/4/18) new method to use JPG file names
LResult = GetImageName("OV", AProductOptionValueID, "", "", ASize)
End If
GetProductOptionValueImageLink = LResult
End Function
' (SS,10/8/11) used by "Facebook like" in meta tag
Function GetFullProductImageLink(AProductCode, ASize)
GetFullProductImageLink = GetStoreURL + "/" + GetProductImageLink(AProductCode, ASize)
End Function
' (SS,22/8/14) returns image link using APictureID
' (SS,7/8/18) modified to make use of more friendly URLs i.e. JPG image files
' call to UseGetPicASP decide which to use
Function GetProductImageLinkForPictureID(APictureID, ASize)
Dim LResult
If UseGetPicASP Then
LResult = "getpic.asp?id=" & APictureID & "&size=" & ASize
Else
LResult = GetImageName("P", "", "", APictureID, ASize)
End If
GetProductImageLinkForPictureID = LResult
End Function
' (SS,17/5/07) new *** to be replaced with more flexible version which has class and width
' (SS,8/8/18) modified to allow actual JPG files to be used if UseGetPicASP is False
Function GetProductImgSrcThumbnailSmall(AProductCode, AProductName)
' GetProductImgSrcThumbnailSmall = "<img src=""getpic.asp?code=" & Server.URLEncode(AProductCode) & """ alt=""" & Server.URLEncode(AProductName) & " (" & AProductCode & ")"" class=""thumbnailsmall"">"
' (SS,8/8/18) replaced above with following to also handle full JPG file names
Dim LLink
If UseGetPicASP Then
LLink = "getpic.asp?code=" & Server.URLEncode(AProductCode)
Else
LLink = GetProductImageLink(AProductCode, "s")
End If
GetProductImgSrcThumbnailSmall = "<img src=""" & LLink & """ alt=""" & Server.URLEncode(AProductName) & " (" & AProductCode & ")"" class=""thumbnailsmall"">"
End Function
' (SS,6/7/11) new get image function, which has class and width
' ASize, AAlt, AClass and AWidth are ignored if blank
' (SS,19/7/11) added AOptions, "t" in options means add title attribute from Alt
' (SS,17/1/13) replaced & with & to reduce HTML errors in http://validator.w3.org
' (SS,8/8/18) modified to allow actual JPG files to be used if UseGetPicASP is False
Function GetProductImgSrc(AProductCode, ASize, AAlt, AClass, AWidth, AOptions)
Dim LResult, LLink
If UseGetPicASP Then
LLink = "getpic.asp?"
If ASize <> "" Then LLink = LLink & "size=" & ASize & "&"
LLink = LLink & "code=" & Server.URLEncode(AProductCode)
Else
LLink = GetProductImageLink(AProductCode, ASize)
End If
LResult = "<img src=""" & LLink & """"
If AAlt <> "" Then
LResult = LResult & " alt=""" & Server.HTMLEncode(AAlt) & """" ' Server.HTMLEncode allows quotes to be included in the string
If InStr(1, AOptions, "t", 1) > 0 Then LResult = LResult & " title=""" & Server.HTMLEncode(AAlt) & """" ' Server.HTMLEncode allows quotes to be included in the string
End If
If AClass <> "" Then LResult = LResult & " class=""" & AClass & """"
If AWidth <> "" Then LResult = LResult & " width=""" & AWidth & """"
LResult = LResult & " />"
GetProductImgSrc = LResult
End Function
' (SS,8/11/07)
' (SS,17/1/13) replaced & with & to reduce HTML errors in http://validator.w3.org
' (SS,5/4/19) added UseGetPicASP to return jpg image from cache
Function GetStoreLogoLink
Dim LResult
If UseGetPicASP Then
LResult = "getpic.asp?type=o&size=o&code=StoreLogo"
Else
LResult = GetImageName("O", "StoreLogo", "", "", "o")
End If
GetStoreLogoLink = LResult
End Function
' (SS,7/8/18)
' if caption missing use sort order?
' for first main image, perhaps don't use caption added to it
' for large thumbnail there's no suffix, for small use "_thm", for original use "_org"
' perhaps don't make lower case as (see https://www.shopify.com/blog/7412852-10-must-know-image-optimization-tips)
' returns friendly image name for given size
' (SS,29/8/19) basket page was showing largest "o" image due to size being set to "". Modified so that "" results in the smallest thumbnail. This was the default for getpic.asp
Function GetSEOFriendlyImageName(LImageName, ASize)
' for main image ASize "o" use no suffix
' for small thumbnail, "s" use width and w as suffix
' for large thumbnail, "l" use width and w as suffix
' (SS,29/8/19) modified so that "s" or "" or any other is the small thumbnail
Dim LSuffix
If ASize = "o" then
LSuffix = ""
ElseIf ASize = "l" then
LSuffix = "-" & LargeThumbnailWidth & "w"
Else ' i.e. "s" or ""
LSuffix = "-" & SmallThumbnailWidth & "w"
End If
GetSEOFriendlyImageName = SEOFriendlyStr(LImageName) + LSuffix + ".jpg"
End Function
' (SS,10/4/19)
Function GetImageCacheFolder
GetImageCacheFolder = "img"
End Function
' (SS,7/4/19) returns image file name for given image
' Also checks existence of image in cache, creates it if it doesn't already exist
Function GetImageName(AType, ACode, ACode2, APictureID, ASize)
Dim LImageName, LPictureID, LLastUpdated, LSQL
' determine name of image, also look up the picture ID in case we don't already have it to pass on to a later routine
If AType = "P" Then
LSQL = "SELECT pictures.PictureID, pictures.Caption, pictures.SortOrder, pictures.LastUpdated, products.ProductName" &_
" FROM pictures" &_
" INNER JOIN products ON products.ProductCode = pictures.Code" &_
" WHERE "
If APictureID = "" Then
' if only product code specified then get the first enabled picture by sort order
LSQL = LSQL + "pictures.Type = 'P' AND pictures.Code = '" & CleanSQLStr(ACode) & "' AND Enabled ORDER BY SortOrder, Code2, PictureID LIMIT 1"
Else
LSQL = LSQL + "pictures.PictureID = '" & APictureID & "'"
End If
Dim LCaption, LSortOrder, LProductName
If GetSQL5Values(LSQL, LPictureID, LCaption, LSortOrder, LLastUpdated, LProductName) Then
LImageName = LProductName
' if caption specified then use it, else add the sort order if not 0 and not 10 (assuming it's the first image)
' (SS,11/8/18) added "If LSortOrder <> 10" if SortOrder is 10 then no caption is added to image name, otherwise caption is added, if not caption then sortorder is added
If LSortOrder <> 10 Then
If LCaption <> "" Then
LImageName = LImageName & "-" & LCaption
Else
LImageName = LImageName & "-" & LSortOrder
End If
End If
Else
LImageName = ""
End If
Else ' i.e. C, S, OV or O
LImageName = ACode
LSQL = "SELECT PictureID, LastUpdated FROM pictures WHERE Type = '" & AType & "' AND Code = '" & CleanSQLStr(ACode) & "'"
If ACode2 <> "" Then
LImageName = LImageName + "-" + ACode2
LSQL = LSQL + " AND Code2 = '" & CleanSQLStr(ACode2) & "'"
End If
If IsNull(GetSQL2Values(LSQL, LPictureID, LLastUpdated)) Then
LImageName = ""
End If
End If
If LImageName <> "" Then
LImageName = GetImageCacheFolder + "/" + LCase(AType) + "/" + GetSEOFriendlyImageName(LImageName, ASize)
EnsureImageExists LImageName, LPictureID, LLastUpdated, ASize
Else ' no image found
' if not already no picture then do a recursive call to determine name of no picture
If AType <> "O" And ACode <> "NoPicture" Then
LImageName = GetImageName("O", "NoPicture", "", "", ASize)
End If
End If
GetImageName = LImageName
End Function
' (SS,7/4/19) if file doesn't already exist in cache with correct date, it creates it
' writing binary files help from https://www.motobit.com/tips/detpg_read-write-binary-files/
Sub EnsureImageExists(AImageName, APictureID, ALastUpdated, ASize)
Dim LResult, LFullFileName
LFullFileName = Server.MapPath(AImageName)
' (SS,23/6/18) check whether file already exists with the same date and time
LResult = FileExistsWithSameDateTime(LFullFileName, ALastUpdated)
' create the file if doesn't exist
If Not LResult Then
' get image from pictures table
Dim LPictureField, LByteArray
Select Case UCase(ASize)
Case "O"
LPictureField = "OriginalPicture"
Case "L"
LPictureField = "LargeThumbnail"
Case Else
LPictureField = "SmallThumbnail"
End Select
LByteArray = GetSQLValue("SELECT " + LPictureField + " FROM pictures WHERE PictureID = '" & APictureID & "'")
If Not IsNull(LByteArray) Then
' (SS,6/4/19) ensure folder exists for this file path
EnsureFolderExistsForFileName(LFullFileName)
' create the jpg file using stream object, binary type
' *** perhaps add an error trap in case file couldn't created
Dim objStream
Set objStream = CreateObject("ADODB.Stream")
objStream.Type = adTypeBinary
objStream.Open
objStream.Write LByteArray
objStream.SaveToFile LFullFileName, adSaveCreateOverWrite
objStream.Close
Set objStream = Nothing
' (SS,7/8/18) set the file last modified date
SetFileLastModifiedDate LFullFileName, ALastUpdated
End If
End If
SetImageCreatedInCache(Not LResult) ' flag for use by CreateImageCache log
End Sub
' (SS,7/4/19) used by CreateImageCache for log
Sub SetImageCreatedInCache(AValue)
FImageCreatedInCache = AValue
End Sub
' (SS,7/4/19)
Function GetImageCreatedInCache
GetImageCreatedInCache = FImageCreatedInCache
End Function
' (SS,23/8/18) Returns true if given file name and date matches existing file.
' Returns false if file doesn't exist, or it exists but with a different date & time.
Function FileExistsWithSameDateTime(AFullFileName, ADateTime)
Dim LResult, objFSO, f
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
' if name matches then check the date & time as well
If objFSO.FileExists(AFullFileName) Then
'Response.Write " File name match" & "<br>" & NL
LResult = objFSO.GetFile(AFullFileName).DateLastModified = ADateTime
'If LResult Then Response.Write " Date time match" & "<br>" & NL
Else
LResult = False
End If
Set objFSO = Nothing
FileExistsWithSameDateTime = LResult
End Function
' (SS,6/4/19) ensure the folder exists for given filename
Sub EnsureFolderExistsForFileName(AFullFileName)
Dim objFSO, LFolderName
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
LFolderName = objFSO.GetParentFolderName(AFullFileName)
' create the folder if it doesn't exist (must have IUSR write access)
If Not objFSO.FolderExists(LFolderName) Then
objFSO.CreateFolder(LFolderName)
End If
Set objFSO = Nothing
End Sub
' (SS,7/8/18) set given file to given date/time
' taken from https://stackoverflow.com/questions/15197043/manually-changing-date-and-time-of-a-file
' *** to tidy with variable names
Sub SetFileLastModifiedDate(AFullFileName, ADateTime)
Dim objFSO, file, app, folder, fileModify
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
Set file = objFSO.GetFile(AFullFileName)
Set app = Server.CreateObject("Shell.Application")
Set folder = app.NameSpace(file.ParentFolder & "\")
Set fileModify = folder.ParseName(file.Name)
fileModify.ModifyDate = ADateTime
Set objFSO = Nothing
Set file = Nothing
Set folder = Nothing
Set app = Nothing
Set fileModify = Nothing
End Sub
' -------------------------------------------
' End of Image / Picture Naming Routines
' -------------------------------------------
' -------------------------------------------
' Start of Cancel Order Routines
' -------------------------------------------
' (SS,12/12/18) To allow orders awaiting payment with payment method of cheque or bank transfer to be cancelled
' (SS,12/12/18) returns true if order cancellation is allowed for given order, i.e. if awaiting payment and payment method is bank transfer or cheque
Function CancelOrderAllowed(AOrderNo)
CancelOrderAllowed = GetSQLRecordExists("SELECT OrderNo FROM orders WHERE OrderNo = '" + CleanSQLStr(AOrderNo) + "' AND Status = '" + AWAITING_PAYMENT_STATUS + "' AND (PaymentMethod = '" + ptCheque + "' OR INSTR(PaymentMethod, '" + ptBankTransfer + "') > 0)")
End Function
' (SS,13/12/18) returns a unique MD5 hash for given order, used for secure cancel order link
Function GetOrderHashKey(AOrderNo)
GetOrderHashKey = GetSQLValueAsString("SELECT CAST(MD5(CONCAT_WS(',', '" + GetStoreName + "', 'Salt123', OrderNo, EmailAddress, SessionID, IPAddress)) AS CHAR) AS KeyVal FROM orders WHERE OrderNo = '" & CleanSQLStr(AOrderNo) & "'")
End Function
' (SS,12/12/18)
Function GetCancelOrderURL(AOrderNo)
GetCancelOrderURL = GetStoreURL & "/products.asp?cmd=cancelorder&orderno=" & AOrderNo & "&key=" & GetOrderHashKey(AOrderNo)
End Function
' (SS,13/12/18) returns true if order has already been cancelled
Function OrderAlreadyCancelled(AOrderNo)
OrderAlreadyCancelled = GetSQLRecordExists("SELECT OrderNo FROM orders WHERE OrderNo = '" + CleanSQLStr(AOrderNo) + "' AND Status = '" + ORDER_CANCELLED_STATUS + "'")
End Function
' (SS,12/12/18)
Sub CancelOrder
Dim LOrderNo, LOrderHashKey
LOrderNo = CleanRequestQueryString("orderno")
LOrderHashKey = CleanRequestQueryString("key")
If LOrderNo = "" Or LOrderHashKey = "" Then
Response.Write("Sorry, not enough info to cancel order")
Exit Sub
End If
' compare the hash key for this order and only allow if they match
If LOrderHashKey <> GetOrderHashKey(LOrderNo) Then
Response.Write("Sorry, not enough credentials to cancel order.")
Exit Sub
End If
' if order cancelling allowed
If CancelOrderAllowed(LOrderNo) Then
' cancel the order
ExecuteQuery("UPDATE orders SET Status = '" & ORDER_CANCELLED_STATUS & "' WHERE OrderNo = '" & CleanSQLStr(LOrderNo) & "' AND Status = '" & AWAITING_PAYMENT_STATUS & "'")
' add items back into stock
TakeProductsFromStock LOrderNo, False
' send the order cancelled confirmation
EmailOrder(LOrderNo)
' display cancellation message
Response.Write("Your order " & LOrderNo & " has been cancelled.<br>Confirmation of the cancellation has been emailed to you.")
ElseIf OrderAlreadyCancelled(LOrderNo) Then
Response.Write("Sorry, order " & LOrderNo & " has already been cancelled.")
Else
Response.Write("Sorry, order " & LOrderNo & " cannot be cancelled.")
End If
End Sub
' -------------------------------------------
' End of Cancel Order Routines
' -------------------------------------------
' --------------------------------------------
' Start of Send Cancelled Order Email Routines
' --------------------------------------------
' (SS,18/4/19)
' (SS,3/6/19) modified to not send if another subsequent order exists
Sub SendCancelledOrderEmail
If Not Application("CancelledOrderEmailsEnabled") Then Exit Sub ' exit if feature not enabled
Dim LOrderNo
If Not AuthenticateOrderNo(LOrderNo) Then Exit Sub ' AuthenticateOrderNo returns LOrderNo, exit if it couldn't be authenticated
Dim LBody
' LBody = "Just testing. Order cancelled was: " & LOrderNo
LBody = GetSiteDetail("Other", "CancelledOrderEmailTemplate")
If LBody = "" Then Exit Sub ' exit if no template or it's blank
' LField4, LField5 are not used but need to be passed onto GetOrderFieldValues which requires 5 variables (ByRef)
Dim LFirstName, LSurname, LEmailAddress, LIsHTML, LField4, LField5
If Not GetOrderFieldValues(LOrderNo, "FirstName", LFirstName, "Surname", LSurname, "EmailAddress", LEmailAddress, "", LField4, "", LField5) Then Exit Sub ' exit if fetching the fields failed
' (SS,3/6/19) don't send if customer has since placed another order, because it may confuse them
If GetSQLRecordExists("SELECT OrderNo FROM orders WHERE OrderNo > '" & CleanSQLStr(LOrderNo) & "' AND EmailAddress = '" & CleanSQLStr(LEmailAddress) & "'") Then Exit Sub
Dim LSubject, LBCC, LBCC2, LFrom
LSubject = ExtractSettingFromTemplate(LBody, "SUBJECT")
LBCC = ExtractSettingFromTemplate(LBody, "BCC")
LFrom = ExtractSettingFromTemplate(LBody, "FROM")
If LSubject = "" Then LSubject = "{STORE_NAME} Order {ORDER_NO} Not Completed Alert"
LSubject = ReplaceStr(LSubject, "{ORDER_NO}", LOrderNo)
LSubject = ReplaceStr(LSubject, "{STORE_NAME}", GetStoreName)
' *** following taken from SendChaseCancelledOrderEmails but should be moved to a separate routine, perhaps there is one or need to create a generic order field replacement routine
LBody = ReplaceStr(LBody, "{ORDER_NO}", LOrderNo)
LBody = ReplaceStr(LBody, "{STORE_URL}", GetStoreURL)
LBody = ReplaceStr(LBody, "{STORE_NAME}", GetStoreName)
LBody = ReplaceStr(LBody, "{STORE_EMAIL}", GetStoreEmail)
LBody = ReplaceStr(LBody, "{STORE_TELEPHONE}", GetStoreTelephone)
LBody = ReplaceStr(LBody, "{FIRST_NAME}", LFirstName)
LBody = ReplaceStr(LBody, "{SURNAME}", LSurname)
' sort email bcc and from addresses, because we might be overriding the standard ones
If LFrom = "" Then LFrom = FEmailContactFrom
If LBCC = "" Then
LBCC = FEmailContactBcc1
LBCC2 = FEmailContactBcc2
Else
If FEmailContactBcc1 <> "" Then
LBCC2 = FEmailContactBcc1
Else
LBCC2 = FEmailContactBcc2
End If
End If
' if template contains <html> tag then assume it's HTML
LIsHTML = InStr(1, LBody, "<html>", vbTextCompare) > 0
SendEmailByCDO LEmailAddress, LBCC, LBCC2, LFrom, LSubject, LBody, "", LIsHTML, "", ""
End Sub
' --------------------------------------------
' End of Send Cancelled Order Email Routines
' --------------------------------------------
' -------------------------------------------
' Start of Mailshot routines
' -------------------------------------------
' (SS,25/3/20) based on Sub SendOrderProgressEmails
' called from send-mailshot.asp
' e.g. send-mailshot.asp?tpn=xxx&ssn=yyy"
' to do an actual run and bypass test mode:
' https://www.castironradiatorcentre.co.uk/send-mailshot.asp?ssn=OutstandingRadiatorOrders&tpn=MailshotTemplateRadiatorOrdersCOVID19&testmode=false
Sub SendMailshot
CONST TEST_MODE = True
CONST TEST_EMAIL = "contactforms@itpartnership.com"
Dim LTestMode
If Request.QueryString("testmode") = "false" Then
LTestMode = False
Else
LTestMode = TEST_MODE
End If
' show a form to get the emailshot name and template name, maybe same name for both
' get emailshot template name and sql select name
' call custom routine to open table, go to next record, and end
' for each record, add to list, build template, send email
' at the end, send the list
Dim LSelectSQLName, LTemplateName
LSelectSQLName = Request.QueryString("ssn")
LTemplateName = Request.QueryString("tpn")
' if no SQL select name and template then exit without doing anything
If LSelectSQLName = "" Or LTemplateName = "" Then
Response.Write "Select SQL Name: " & LSelectSQLName & BR
Response.Write "Template Name: " & LTemplateName & BR
Exit Sub
End If
Const LOGO_FILE_NAME = "logo-email.png"
Dim LAttachLogoFileName, LTemplateTitle, LTemplateSubject, LSubject, LTemplateBody, LSQL
Dim LBCC, LBCC2, LFrom
Dim LTemplateBCC, LTemplateBCC2, LTemplateFrom
Dim LOrderNo, LOrderDate, LTitle, LFirstName, LSurname, LName, LEmailAddress
Dim LLogBody, LCount, LBody
LLogBody = ""
' get body of template (also check it exists and exit if it doesn't)
LTemplateBody = GetSiteDetail("Other", LTemplateName)
If LTemplateBody = "" Then
Response.Write "TemplateBody: " & LTemplateBody & BR
Exit Sub
End If
' make sure CustomMailshotGetSQL exists and returns a value for given SQL
If Not FunctionExists("CustomMailshotGetSQL") Then
Response.Write "No CustomMailshotGetSQL"
Exit Sub
End If
LSQL = CustomMailshotGetSQL(LSelectSQLName)
If LSQL = "" Then
Response.Write "SelectSQLName: " & LSelectSQLName & BR
Response.Write "SQL: " & LSQL & BR
Exit Sub
End If
' extract BCC, FROM, SUBJECT from template
LTemplateTitle = ExtractSettingFromTemplate(LTemplateBody, "TITLE")
LTemplateSubject = ExtractSettingFromTemplate(LTemplateBody, "SUBJECT")
LTemplateSubject = ReplaceStr(LTemplateSubject, "{STORE_NAME}", GetStoreName)
LTemplateBCC = ExtractSettingFromTemplate(LTemplateBody, "BCC")
LTemplateFrom = ExtractSettingFromTemplate(LTemplateBody, "FROM")
' exit if no subject
If LTemplateSubject = "" Then
Response.Write "Template Subject: " & LTemplateSubject & BR
Exit Sub
End If
LLogBody = LLogBody + MailshotResultOverallHeader
' replace template variables that weren't change for each order
' "if" to only attach logo if template contains {LOGO}, also added LOGO_URL which doesn't embed the URL
If InStr(LTemplateBody, "{LOGO}") > 0 Then
LTemplateBody = ReplaceStr(LTemplateBody, "{LOGO}", "<img src=""" + LOGO_FILE_NAME + """>")
LAttachLogoFileName = LOGO_FILE_NAME
Else
LTemplateBody = ReplaceStr(LTemplateBody, "{LOGO_URL}", GetStoreURL & "/images/" & LOGO_FILE_NAME)
LAttachLogoFileName = ""
End If
LTemplateBody = ReplaceStr(LTemplateBody, "{STORE_URL}", GetStoreURL)
LTemplateBody = ReplaceStr(LTemplateBody, "{STORE_NAME}", GetStoreName)
LTemplateBody = ReplaceStr(LTemplateBody, "{STORE_EMAIL}", GetStoreEmail)
LTemplateBody = ReplaceStr(LTemplateBody, "{STORE_TELEPHONE}", GetStoreTelephone)
' sort email bcc and from addresses, because we might be overriding the standard ones
If LTemplateFrom = "" Then LTemplateFrom = FEmailOrderConfirmationFrom
If LTemplateBCC = "" Then
LTemplateBCC = FEmailOrderConfirmationBcc1
ElseIf LTemplateBCC = "NONE" Then ' if NONE then don't send BCC
LTemplateBCC = ""
End If
LTemplateBCC2 = FEmailOrderConfirmationBcc2
LLogBody = LLogBody + MailshotResultHeader(LTemplateTitle)
' run query to find orders
OpenQuery(LSQL)
' for each order
Do While Not EndOfQuery
LCount = LCount + 1
LOrderNo = GetQueryField("OrderNo")
LOrderDate = GetQueryField("OrderDate")
LTitle = Trim(GetQueryField("Title"))
LFirstName = Trim(GetQueryField("FirstName"))
LSurname = Trim(GetQueryField("Surname"))
LName = Trim(LTitle & IIf(LTitle = "", "", " ") & LFirstName & IIf(LFirstName = "", "", " ") & LSurname)
LEmailAddress = GetQueryField("EmailAddress")
LLogBody = LLogBody + MailshotResultRow(LOrderNo, LOrderDate, LName, LEmailAddress)
' debug info in browser
'Response.Write LCount & ". Order No: " & LOrderNo & ", Order Date: " & LOrderDate & ", Name: " & LName & ", EmailAddress: " & LEmailAddress & BR
' send the email (test mode, 0 = normal, 1 = log and send emails to test account, 2 = log only)
LBody = LTemplateBody
If LBody <> "" Then ' only send if template not blank
' replace order specific template values
LSubject = ReplaceStr(LTemplateSubject, "{ORDER_NO}", LOrderNo)
LBody = ReplaceStr(LBody, "{ORDER_NO}", LOrderNo)
LBody = ReplaceStr(LBody, "{ORDER_DATE}", LOrderDate)
LBody = ReplaceStr(LBody, "{NAME}", LName)
' send the email with embedded logo
If Not LTestMode Then
LBCC = LTemplateBCC
LBCC2 = LTemplateBCC2
Else ' test mode, just send the one test email
If LCount = 1 Then
LEmailAddress = TEST_EMAIL
Else
LEmailAddress = "" ' no more emails, just the one due to above
End If
LBCC = ""
LBCC2 = ""
End If
' debug code to show email addresses
'Response.Write "LEmailAddress: " & LEmailAddress & BR
'Response.Write "LBCC: " & LBCC & BR
'Response.Write "LBCC2: " & LBCC2 & BR
'Response.Write "LFrom: " & LFrom & BR
If LEmailAddress <> "" Then
SendEmailByCDO LEmailAddress, LBCC, LBCC2, LTemplateFrom, LSubject, LBody, "", True, "", LAttachLogoFileName
End If
End If
NextQueryRecord
Loop
LLogBody = LLogBody + MailshotResultFooter(LTemplateTitle, LCount)
CloseQuery
LLogBody = LLogBody + MailshotResultOverallFooter
' send log email
If LLogBody <> "" Then
If LTestMode Then
LEmailAddress = TEST_EMAIL
LBCC = ""
LBCC2 = ""
Else
LEmailAddress = FEmailContactTo
LBCC = FEmailContactBcc1
LBCC2 = FEmailContactBcc2
End If
SendEmailByCDO LEmailAddress, LBCC, LBCC2, FEmailContactFrom, "Mailshot Result for " + GetStoreName + " on " & Now, LLogBody, "", True, "", ""
End If
End Sub
Function MailshotResultOverallHeader
Dim LResult
LResult = ""
LResult = LResult & "<html>" & NL
LResult = LResult & "<head>" & NL
LResult = LResult & "<title>Mailshot " & Now & "</title>" & NL
LResult = LResult & "<style>" & NL
LResult = LResult & "table {border-collapse: collapse;}" & NL
LResult = LResult & "table, th, td {border: 1px solid black;}" & NL
LResult = LResult & "th, td {padding: 5px;}" & NL
LResult = LResult & "</style>" & NL
LResult = LResult & "</head>" & NL
LResult = LResult & "<body>" & NL
LResult = LResult & "<table>" & NL
Response.Write LResult
MailshotResultOverallHeader = LResult
End Function
Function MailshotResultOverallFooter
Dim LResult
LResult = LResult & "</table>" & NL
LResult = LResult & "</body>" & NL
LResult = LResult & "</html>" & NL
Response.Write LResult
MailshotResultOverallFooter = LResult
End Function
Function MailshotResultHeader(AInfo)
' NL used at end of each HTML line because emailer is adding a space after 1000 characters
Dim LResult
LResult = LResult & "<tr><th colspan=""4""> </tr>" & NL ' empty line separator
LResult = LResult & "<tr><th colspan=""4"">" & AInfo & "</th></tr>" & NL
LResult = LResult & "<tr>" & NL
LResult = LResult & "<th>Order No</th>" & NL
LResult = LResult & "<th>Order Date</th>" & NL
LResult = LResult & "<th>Name</th>" & NL
LResult = LResult & "<th>Email</th>" & NL
LResult = LResult & "</tr>" & NL
Response.Write LResult
MailshotResultHeader = LResult
End Function
Function MailshotResultFooter(ATitle, ACount)
Dim LResult
LResult = "<tr><td colspan=""4""><b>" & ATitle & " emails sent for " & PluralString(ACount, "order", "orders") & "</b></td></tr>" & NL
Response.Write LResult
MailshotResultFooter = LResult
End Function
Function MailshotResultRow(AOrderNo, AOrderDate, AName, AEmail)
Dim LResult, LCentre
LCentre = " style=""text-align:center"""
LResult = "<tr>" & NL
LResult = LResult & "<td" & LCentre & ">" & AOrderNo & "</td>" & NL
LResult = LResult & "<td" & LCentre & ">" & AOrderDate & "</td>" & NL
LResult = LResult & "<td>" & AName & "</td>" & NL
LResult = LResult & "<td>" & AEmail & "</td>" & NL
LResult = LResult & "</tr>" & NL
Response.Write LResult
MailshotResultRow = LResult
End Function
' -------------------------------------------
' End of Mailshot routines
' -------------------------------------------
%>