Loyalty Scheme Design in Salesforce

Posted by jack on June 23, 2015

Recently I have been implementing a trade loyalty scheme in Salesforce. This is a points based scheme that awards to an individual agent so many points per $ of sales made through the sales channel. Once that agent has accumulated enough points, these can be exchanged for any of a range of reward products. Unused points expire one year after they are earned.

In this specific instance the sale may sometimes subsequently be cancelled, and so initially the points are provisionally awarded as Pending, and only after the last cancellation date is passed are they Confirmed  (& so available to be Consumed). So there are these possible status values for the points:-

  • Pending
  • Confirmed
  • Consumed
  • Expired

The model I adopted to implement this was to create a new custom object called “Points Award” with two master-detail relationships to 1. Contact and 2. The (in this case custom) Sale object.  There is also an “Agent Reward” object with two master-detail relationships to 1. Contact and 2. The (in this case custom) Reward Product object (we could have used the standard Salesforce Product object instead).

The Points Award object has these custom integer fields

  • Points Awarded
  • Points Cancelled
  • Points Expired
  • Points Consumed

There is then a calculated field Points Balance = Points Awarded – Cancelled – Expired – Consumed

A trigger/Class based on the Sale sets the Cancelled amount = Points balance should the Sales be cancelled, and a  trigger on the Agent Reward progressively consumes available points from Points Awards starting with the oldest.

Finally, a schedulable Class is used to update Points Award to

  • Move status from Pending to Confirmed as the associated Sales passes the point where it can be cancelled, and
  • Move status from Confirmed to Expired when unused Points Awards pass their Expiry date.

For Expiry, the Points Expired is set to equal the prior Points Balance – so if the Award has been partially Consumed the amount consumed is retained and the remainder of points Expired.

Comments Off on Loyalty Scheme Design in Salesforce
Categories: Salesforce

Salesforce1 – early experience shows promise….

Posted by jack on March 8, 2014

I have been configuring Salesforce1, the new mobile/tablet interface to Salesforce, for a client’s travelling sales workforce.

This is a big advance in usefuleness over the previous Salesforce Touch and Chatter mobile clients, in that we can now configure for the clients specific needs, including:-

  • Configuring the interface to include the items users want in the order they prefer
  • Setting custom actions – for example rapid lead qualification
  • Coding bespoke requirements – for example displaying nearby accounts on a Google map centred on the user’s current location

The first iteration of this will take less than 2 days to complete & test – pretty cheap compared to a custom app.

Salesforce1 works on recent versions of IO7 and Android.

Comments Off on Salesforce1 – early experience shows promise….
Categories: Salesforce

Salesforce.com – so much more than just a CRM

Posted by jack on April 26, 2013

One issue that deters organisations from adopting Salesforce is the higher cost of licences – if you do some simple arithmetic you quickly end up with a formidable number – especially when comparing Salesforce Enterprise with much of the competition. And the saas model means after spending all that money you don’t even own a copy of the software!

There are a number of reasons why this kind of comparison can be a bit misleading – for example the phrase “perpetual licence ” may legally mean effective ownership, but in practical terms unless you have support and maintenance in place (and the cost of this is normally 20 – 30% of the original box price per year)  then the value of what you purchased quickly decays – even with s&m it is easy to get trapped in  old versions of software due to the costs of upgrade – one client I worked for had a highly configured Pivotal installation that ran their business and could not upgrade it. They were running CRM without email integration!

Like much of IT, software packages of any type do not have as long a life as other assets – which is why accountants tend to write them off over many fewer years than they take for fixed plant for example.  With saas like Salesforce (other brands are available) you are always on the latest version for as long as you subscribe.

But the accounting cost of software is not the theme I wanted to address now, but how Salesforce can quickly become a key component well outside the core CRM use when integrated to other systems, and how that in turn can enrich the CRM. My example is a recent development for a client selling hotel rooms, but can apply to many other businesses.

Hotel rooms can be booked using a wide variety of rate codes; some rates can include or exclude items like breakfast or late check-out, can  be exclusive to specific consumers or travel agency groups, or special offers (effectively sales prices). The number and variety of rates is constantly changing as all those concerned in the business seek new ways to grow their business.

As the use of these rates changes, so do the information reporting requirements for monitoring performance. With several thousand rates in use, rather than individually select every rate required in every different report, the client’s data warehouse has evolved a set of reporting flags and descriptions to allow rapid production and one-point maintenance of such reports.

But the data warehouse has no user front end, so each time a new rate is required, or an existing one is moved into or out of a selected group of rates for a specific requirement, the poor data warehouse manager must be asked to adjust the values in this reference data –  and I have an aversion to being included in business processes and repeatedly doing simple dull SQL updates. Other users were asking the report writer to manually include/exclude rates to get the reports they want quickly – neglecting update of the reference data and causing different reports intended to be comparable to end up including different sets of rates.

In essence, a single table of reference data can be represented as a single object in Salesforce enterprise – so I could rapidly design a new object based on the data fields in the SQL table, using entirely drag & drop customisation. The natural candidate for the Salesforce object Name is the rate code itself (as that is the one fixed value in the table) but Salesforce does not allow imposition of the Unique constraint on object  Names, so I did have to apply a trigger to prevent duplicates.Here is the code for my trigger (Rate_Code_Description__c is the name of the custom object:-

trigger rateCodeDuplicatePreventer on Rate_Code_Description__c (before insert, before update) {

Map<String,Rate_Code_Description__c> rateCodeMap = new Map<String, Rate_Code_Description__c>();
    for (Rate_Code_Description__c rateCode : System.Trigger.new) {
    
    // Make sure we do not treat a rate code that is not changed during an update as a duplicate
    if (( rateCode.Name != null) && (System.Trigger.isInsert || (rateCode.Name !=
    System.Trigger.oldMap.get(rateCode.Id).Name))){
    // Make sure another new rateCode isn’t also a duplicate
        if (rateCodeMap.containsKey(rateCode.Name)) {
            rateCode.Name.addError(‘Duplicate rate code in the same batch’);
        } else {
                rateCodeMap.put(rateCode.Name, rateCode);
           
        }
    }
   }
    //using a single query, finad all the ratecodes with the same name
    for (Rate_Code_Description__c rateCode : [SELECT Name from Rate_Code_Description__c WHERE Name IN :rateCodeMap.KeySet()]){
    Rate_Code_Description__c newRateCode = rateCodeMap.get(rateCode.Name);
    newRateCode.Name.addError(‘This rate code already in Salesforce’);
}
}

Standard salesforce drag & drop form design allowed me to group the various checkboxes into related sections to help users manage their own rates – in this client there is good data discipline and cross-functonal cooperation, so I have not chosen to apply varying field level security – but I could have done so with a little more time.

Integration between salesforce and the data warehouse was undertaken using Cast Iron – the integration is 2 way but not symmetrical, since with the exception of the rate code (object Name) itself, we wanted all the other entries to be mastered in Salesforce, even if originating from the data warehouse table.  And we need to ensure that users get confirmation that entries put in Salesforce have correctly updated the data warehouse table.

New rates can be input in the data warehouse – this happens automatically where a new rate code is in use in incoming reservation data but does not exist in the reference data table – in this event a stub record is created using globally applicable rules to set a few flags to sensible defaults and taking a description from the first incoming reservation record. This new rate then is synched by IBM Cast Iron using a Poll Inserted Rows database activity into Salesforce. Once in salesforce a workflow rule sends an email to a responsible member of staff (identity varies according to the value of rate code input) so that person can then correct the description and set flags to the appropriate values.

The Salesforce object is connected to a second Cast Iron orchestration using a Poll Updated Objects activity – this then send the updated values back to the data warehouse table. Finally, the response from the update activity is used to update the Salesforce object in a single date field to confirm to the user that all the integration has worked correctly and their input has updated the data warehouse table.

Total time taken to develop, test and implement this is less than two days, and the job of maintaining the rate related data is put firmly where it should be – with the business users.

Now, here is the gravy – now that we have all this rate code information in Salesforce I can leverage this to improve the CRM – providing users with information on customer, partner and supplier related activities based on both this rate code data and the other objects already in Salesforce. We’ve not done that part yet, so I will write about that another day.

Jack

 

 

 

 

 

 

Comments Off on Salesforce.com – so much more than just a CRM

Mapping Variables in Cast Iron

Posted by jack on February 3, 2013

The Map Variables activity in Cast Iron is used for many different purposes including:-

  • Mapping from an input schema to output schema. The most obvious use of the activity.
  • Counting records – using the count function to count the rows within an XML variable.
  • Filtering records using XPATH.
  • Merging in data from one variable to another using a custom javascript fiunction
  • Accumulating data in a loop.
  • including CDATA element in an XML variable.

Here are some techniques I have used for these duties:

XPATH filtering is a standard XML technology. To insert a filter expression in Cast Iron Studio, input the XML variable to be filtered on both sides of a Map Variables activity, and map the two instances together, input to output. Right-click on the occurrence label on the output (RH) side and select Filter Recurring Nodes from the pop-up menu.

Limitations on XPATH filtering  in Cast Iron (up to v6.1)    1. XPath only works on the Map Variables activity.  Other activities (for example the output to a Salesforce Upsert Objects activity) include the option to include a filter on the mapping, but in practice these do not work. 2. XPath only works on variables wth a flat structure. Again the interface allows you to input an XPATH expression in the mapping for multi-level variables, but it will not work. 3. The data structure on either side should be similar.  You can map between different input and output variables and include XPATH filters, but base them on the same XSD otherwise you may find they don’t work corrrectly.

Merging in data using a javascript function. Cast Iron offers several options for merging data from one variable into another. One option is to use an XSLT (XML stylesheet) – and often this is the best approach, although it does involve tricky code, and Cast Iron Studio has limited diagnostic support to help you identify and correct coding errrors.

I have a simpler approach using a simple javascript function, and this allows me to merge a single fixed value into a recordset.  To merge two recordsets, the recordset to be merged into the target recordset is cycled through in a For Each activity, and then each member of the merge recordset is mapped in turn.

Here is a typical javascript function

Name: mergeFieldXintoRset

Input parameters: RsetKey, RsetFieldX, MergeKey, MergeFieldX

javscript: 

var returnValue = RsetFieldX ; 

if (RsetKey == MergeKey) returnValue= MergeFieldX;

return returnValue;

This function will transmit the existing value unchanged unless the keys match, when it substitures the matched value for the merge data.

Accumulating data in a loop

To accumulate data in a recordset, the recordset variable is first mapped to itself. Then the variable(s) holding the values to be included in an additional record are added to the LHS of the Map Variables activity. Now click the RH variable on the row identifier (where the anti-clockwise arrow is displayed) and select Expand Occurences from the pop-up menu. This displays the Expand Occurences window with one additional occurence as the option – this is normally what you want. Then map the added variable(s) on the LHS to the  added occurence.

Limitations on use: I have not found a way to add an undefined number of additional values in a single activity – so if you need to do this cycle through the records to add in a For Each loop to add them in one at a time.

 

 

 

 

 

 

 

 

 

 

Cast Iron only implements filtering on flat (single level) xml variables. So this example works:-

 

 

 

 

Comments Off on Mapping Variables in Cast Iron
Categories: Cast Iron

Albacore Ltd for Salesforce with data integration using IBM Cast Iron

Posted by jack on February 2, 2013

For 2013 Albacore Ltd is focussing on the use of IBM Cast Iron to integrate cloud applications with internal systems.

Salesforce.com is the leading Cloud application, and Albacore Ltd has over three years experience in architecting solutions that configure Salesforce and integrate to back office systems using IBM Cast Iron Live.

If you are a UK based organisation considering  Salesforce* then get in touch at enquiries@albacore.ltd.uk

*NOTE: For data integration you must have Salesforce Enterprise or Unliimited licences; small business and workgroup editions do not allow data integration.

Comments Off on Albacore Ltd for Salesforce with data integration using IBM Cast Iron

Keep control – use Web Services

Posted by jack on February 2, 2013

More and more business are sharing information by automated means. In many sectors this is now an essential requirement for survival, and there are few businesses that would not benefit significantly.

A common means of transferring data is by WDSL based web services.

As well as being an economic, modern means of establishinf data integration, providing well engineered web services to your partners ensures that you:-

  • ensure continuing security for your data systems
  • provide maximum flexibility to your partners whilst retaining control of the relationship
  • insulate you from the specifics of your partner’s internal systems.

Given a choice between your partner developing web services  and you providing them yourself, the smart choice is often to develop the web services yourself – that way you control data naming and structures for the interface, and can log usage.

IBM Cast Iron Live is a Cloud based platform for the economic production of WSDL web services to your specification.

Albacore Ltd offers a service implementing data integration using IBM Cast Iron Live for any UK business looking to integrate with partners outside their network. If required we can offer support to your internal IT staff in getting up to speed with Cast Iron development and operation.

 

 

 

Comments Off on Keep control – use Web Services
Categories: Cast Iron

The beauty of Cast Iron is….XML

Posted by jack on February 2, 2013

I’ve been around long enough to remember when there were  only two ways to transfer data between systems:-

  • Text file exchange, with a bespoke pre-agreed file format. In recent years this has simplified (degraded?) to exchanges of .CSV files with headings and not much more, but in the past we had headers and footers to ensure the data was verified and the transfer was correctly completed.  Text file transfer gets more tricky when you ned to transfer formatted data (eg dates) and extended character sets like chinese characters.
  • EDIFACT – a standards based file-based transmission of data between trading partners. Specified in the 1980s, EDIFACT implementations require specialist practitioners to implement.  EDIFACT is still in use by established partners with high volumes to transfer, but nowadays most of us implement XML transfer to achieve the same results with musch lower implementation costs.

My recollection from arond the turn of the century was that there was an expectation that each industry sector would agree a standard XML Schema for their data interchage; in practice whilst there are such standard specifications, much more common are ad-hoc specifications based on shared XSD schema definitions for each data transfer. A set of two XSD (one for input, the other for response) is bundled in a .WSDL file (web services definition language)  and this allows partners to communicate using web services.

IBM Cast Iron Live is one of many data integration products that work entirely in XML.  It allows users to quickly and simply implement WSDL based web services. Using HTTPS this allows an authenticated partner to connect to your data systems in a managed way without having to set up complex access paths through firewalls or compromise your network security in any way.

Albacore Ltd offers a service implementing data integration using IBM Cast Iron Live to any UK business looking to integrate with partners outside their network. If required we can offer support to your internal IT staff in getting up to speed with Cast Iron development and operation.

 

 

Comments Off on The beauty of Cast Iron is….XML
Categories: Cast Iron

SQL Function – sensible display formats depending on the size of the number

Posted by jack on June 20, 2012

I am working on dashboards – this requires the interface to display a wide range of numbers in sensible formats specific to each row – ie a mix of numbers representing currency amounts and counts of units between numbers less than 10 and numbers over a million. So I want to display these in different formats depending on the amount – we cannot do this in the end user tool I am using to generate the user interface (well not without a lot of proprietary coding I would have to learn and repeat on every dashboard)  so I wrote this SQL function to convert an input number into a text display value.
CREATE FUNCTION [dbo].[numberToScaledText]
(
@testValue float
)
RETURNS VARCHAR(255)
AS
BEGIN
DECLARE
@strOut Varchar(255)

Select @strOut = Case when @testValue > 1E7 then
cast(Round(cast(@testValue as bigint)/1E6,1) as varchar(50)) + ‘mill’ else
Case when @testValue > 1E6 then
cast(cast(@testValue as bigint)/1E6 as varchar(50)) + ‘mill’ else
case  when @testValue > 1E3 then
cast(cast(@testValue as int)/1E3 as varchar(50)) +
‘k’ else Case when @testValue > 100 then
cast(cast(@testValue as int) as varchar(6))
else cast(round(@testValue,1)  as varchar(20))

end end end end

RETURN @strOut
END

GO

 

Comments Off on SQL Function – sensible display formats depending on the size of the number
Categories: SQL Server

New SQL Server function – sentenceCase

Posted by jack on May 16, 2012

An extension of previous versions to cover conversion of all upper case to normal sentence case.  If you have embedded proper nouns or intials it won’t give exact results, but a lot prettier than a lot of shouting….

USE [DataMart]
GO
/****** Object:  UserDefinedFunction [dbo].[sentenceCase]    Script Date: 05/16/2012 12:39:55 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
–lifted from
–http://classicasp.aspfaq.com/general/how-do-i-convert-a-name-to-proper-case.html
CREATE FUNCTION [dbo].[sentenceCase]
(
@strIn VARCHAR(255)
)
RETURNS VARCHAR(255)
AS
BEGIN
IF @strIn IS NULL
RETURN NULL

DECLARE
@strOut VARCHAR(255),
@i INT,
@Up BIT,
@c VARCHAR(1),
@cminus1 varchar(1)

SELECT
@strOut = ”,
@i = 0,
@Up = 1

WHILE @i <= DATALENGTH(@strIn)
BEGIN
SET @c = SUBSTRING(@strIn,@i,1)
If @i > 1
BEGIN
SET @cminus1 = SUBSTRING(@strIn,@i-1,1)
END
IF @c IN (‘.’,””) or @i = 0  or (@c = ‘ ‘ and @cminus1 = ‘.’)
BEGIN
SET @strOut = @strOut + @c
SET @Up = 1
END
ELSE
BEGIN
IF @up = 1
SET @c = UPPER(@c)
ELSE
SET @c = LOWER(@c)

SET @strOut = @strOut + @c
SET @Up = 0
END
SET @i = @i + 1
END
RETURN @strOut
END

Comments Off on New SQL Server function – sentenceCase

Visualforce is not hard to master…..

Posted by jack on April 30, 2012

Salesforce normally makes a decent fist of presenting data in a usable way using the standard page designs; in the SME space, I consider avoiding code where practical to be a requirement of the job (to avoid future maintenance headaches once I’ve gone), and so to date page layout has not involved Visualforce.

The client now has a requirement to present the results of an inspection. Inspections in his industry comprise a set of basic facts who/what/when/overall rating at the inspection level, and also the results of many questions, each of which belongs to a section of the inspection and has an individual weighting and assesed mark.

Rather than hard code all the questions, these are represented in a secondary record, an inspectionItem, with a Master-Detail link to the inspection. By including the section and question text as fields, one object can handle all questions.

However when the inspection is presented in Salesforce, the client would like to see the questions arranged by section (there are over 400 questions per inspection) – so the standard Salesforce layout is clearly not fit for purpose.

An additional requirement is that the Salesforce user is able to supplement the basic Inspection record (which is loaded from another system and not to be amended) with attachments and some additional field entries.

Visualforce allows me to lay the form out nicely using tabs – with a filtered list for each tab. A final “other items” tab is included to allow any items with section name not inclueded in the other tabs to be presented to the user pending amendment to the visualforce page/correction of the incoming data as relevent . I was very pleasantly surprised to discover how quickly I could work out how to layout the whole page.

Note that there is not a 1:1 ralationship between tabs and sections – each tab contains several sections – this reflects the limitation on screen width and the number and length of the section names. The entries for each section are ordered by the column Seq_no to reproduce the order in the originating system.

Here is the start of the Visualforce display page (edit page is below):-

<start>

<apex:page standardController=”Inspection__c” id=”inspection” label=”inspection” setup=”false” extensions=”inspectionExtension” sidebar=”false”>
<apex:form >
<apex:commandButton action=”{!edit}” value=”Edit”/>
</apex:form>

<apex:tabPanel >

    <apex:tab label=”General”>
        <apex:pageBlock >
            <apex:pageBlockSection title=”{!Inspection__c.Name}+” columns=”2″>
                <apex:outputField value=”{!Inspection__c.Account__c}”   />
                <apex:outputField value=”{!Inspection__c.Contact__c}”/>
                <apex:outputField value=”{!Inspection__c.Number_or_name_of_room__c}”/>
                <apex:outputField value=”{!Inspection__c.Inspection_Date__c}”/>
                <apex:outputField value=”{!Inspection__c.Checkout_date__c}”/>
                <apex:outputField value=”{!Inspection__c.Total_Quality_Score__c}” />
                <apex:outputField value=”{!Inspection__c.Demo_Salesforce_Added__c}”/>
            </apex:pageblockSection></apex:pageBlock>
          <br/>
          Comments:<i> {!Inspection__c.Inspection_Comments__c}</i>
          <br/>
          <apex:relatedList subject=”{!Inspection__c}” list=”NotesAndAttachments” />
        <apex:pageBlock title=”Quality scores” >
            <apex:pageBlockSection title=”Overall Evaluation” columns=”2″>
                <apex:outputField value=”{!Inspection__c.Overall_Service__c}”   />
                <apex:outputField value=”{!Inspection__c.Overall_Staff__c}”   />
                <apex:outputField value=”{!Inspection__c.Overall_Accommodation__c}”   />
                <apex:outputField value=”{!Inspection__c.Overall_Food__c}”   />
                <apex:outputField value=”{!Inspection__c.Overall_Housekeeping__c}”   />       
            </apex:pageblockSection>
<apex:pageBlockSection title=”General Comments” columns=”1″>
                <apex:outputField value=”{!Inspection__c.Is_the_hotel_a_worthy_member_of_XYZ__c}”   />
                <apex:outputField value=”{!Inspection__c.Left_in_doubt_is_a_Small_Luxury_Hotel__c}”   />
                <apex:outputField value=”{!Inspection__c.Would_book_again__c}”   />
                <apex:outputField value=”{!Inspection__c.Would_encourage_selecting_another_XYZ__c}”   />
                <apex:outputField value=”{!Inspection__c.Confidence_in_Hotel__c}”   />    
                <apex:outputField value=”{!Inspection__c.Further_Comments__c  }”   />   

            </apex:pageblockSection>

        </apex:pageblock>
    </apex:tab>
    <apex:tab label=”Pre-Stay”>
        <apex:pageblock >
            <apex:pageBlockSection title=”Pre-Stay Summary” columns=”2″>
                <apex:outputField value=”{!Inspection__c.XYZ_Directory_and_Website__c}”   />
                <apex:outputField value=”{!Inspection__c.XYZ_Directory_and_Website_Comments__c}”   />      
                <apex:outputField value=”{!Inspection__c.XYZ_Membership_Registration_Procedure__c}”   />
                <apex:outputField value=”{!Inspection__c.XYZ_Membership_Registration_Comments__c}”   />
                <apex:outputField value=”{!Inspection__c.XYZ_Central_Reservation__c}”   />
                <apex:outputField value=”{!Inspection__c.XYZ_Central_Reservation_Comments__c}”   />
            </apex:pageblockSection>
            <apex:pageBlockSection title=”Items” columns=”1″ >
<apex:pageBlockTable value=”{!XYZDirectory}” var=”items”>
<apex:facet name=”header”>XYZ Directory</apex:facet>
<apex:column value=”{!items.Q_ID__c}”/>
<apex:column value=”{!items.Name}” width=”40%”/>
<apex:column value=”{!items.Mark__c}”/>
<apex:column value=”{!items.XYZ_Value__c}”/>
<apex:column value=”{!items.XYZ_Value_earned__c }”/>
<apex:column value=”{!items.Item_comments__c }” width=”30%” />
</apex:pageBlockTable>
<apex:pageBlockTable value=”{!XYZWebsite}” var=”items”>
<apex:facet name=”header”>XYZ Website</apex:facet>
<apex:column value=”{!items.Q_ID__c}”/>

<continues…>

And here is the extension to the standard controller:-

<start>

public class inspectionExtension {
private final Inspection__c item;
// The extension constructor initializes the private member
// variable acct by using the getRecord method from the standard
// controller.
public inspectionExtension(ApexPages.StandardController stdController) {
this.item = (Inspection__c )stdController.getRecord();
}

public String getId() {
return  item.id ;
}

// first list tab contents
public list<Inspections_Item__c> getXYZDirectory() {
return [select  Id,Q_ID__c, Name,Mark__c,XYZ_Value__c,XYZ_Value_earned__c,Item_comments__c  from Inspections_Item__c  Where (section__c = ‘XYZ Directory’) and Inspection__c = :item.id ORDER BY Seq_No__c  ];
}
public list<Inspections_Item__c> getXYZWebsite() {
return [select  Id,Q_ID__c, Name,Mark__c,XYZ_Value__c,XYZ_Value_earned__c,Item_comments__c  from Inspections_Item__c  Where (section__c = ‘XYZ WEBSITE www.XYZ.com’) and Inspection__c = :item.id ORDER BY Seq_No__c  ];
}

<continues….. ending>

//catch-all tab for new sections or data with inaccurate section name

public list<Inspections_Item__c> getOtherItems() {
return [select  Id,Q_ID__c, Name,Mark__c,XYZ_Value__c,XYZ_Value_earned__c,section__c,Item_comments__c  from Inspections_Item__c Where (section__c <> ‘XYZ Directory’) and
(section__c <> ‘XYZ WEBSITE www.XYZ.com’) and (section__c <> ‘Membership Registration and Web Reservation’)  and
(section__c <> ‘XYZ Central Reservations’) and (section__c <> ‘Arrival’) and
(section__c <> ‘Bedroom’) and (section__c <> ‘Bathroom’) and  (section__c <> ‘Housekeeping Bathroom’) and (section__c <> ‘Housekeeping Bedroom’) and
(section__c <> ‘Telephone / Switchboard / Messages’) and (section__c <> ‘Concierge Services’) and
(section__c <> ‘Reception/Entrance hall’) and  (section__c <> ‘Lounge room/Seating area’) and (section__c <> ‘Staircases/Lifts/Corridors’) and  (section__c <> ‘Toilets’) and
(section__c <> ‘External Grounds’) and (section__c <> ‘Pool & Beach Service’) and (section__c <> ‘Leisure/Spa facilities’) and
(section__c <> ‘Main Restaurant’) and (section__c <> ‘Room Service’) and  (section__c <> ‘Bar/Lounge’) and  (section__c <> ‘Breakfast’) and
(section__c <> ‘Departure’) and   (section__c <> ‘XYZ Brand Values’) and
Inspection__c = :item.id ORDER BY Section__c, Seq_No__c  ];
}

}

Here is  the Visualforce edit page (note – fields not settled for final design):-

<apex:page standardController=”Inspection__c” id=”inspection” label=”inspection” setup=”false” extensions=”inspectionEdit” sidebar=”false”>
<apex:form >
        <apex:pageBlock >
             <apex:pageBlockSection title=”{!Inspection__c.Name} – Upload Inspection Files” columns=”3″>
        <apex:inputFile size=”60″ value=”{!attach.body}” filename=”{!attach.name}”/><br/>
        <apex:commandButton value=”Upload” action=”{!upload}”/>
</apex:pageblockSection>

            <apex:pageBlockSection title=”Inspection Details” columns=”2″>
            <apex:commandButton action=”{!save}” value=”Save Update”/>
                <apex:outputField value=”{!Inspection__c.Id}”   />                
                <apex:outputField value=”{!Inspection__c.Account__c}”   />
                <apex:outputField value=”{!Inspection__c.Contact__c}”/>
                <apex:outputField value=”{!Inspection__c.Number_or_name_of_room__c}”/>
                <apex:outputField value=”{!Inspection__c.Inspection_Date__c}”/>
                <apex:outputField value=”{!Inspection__c.Checkout_date__c}”/>
                <apex:outputField value=”{!Inspection__c.Total_Quality_Score__c}” />
                <apex:inputField value=”{!Inspection__c.Demo_Salesforce_Added__c}”/>
            </apex:pageblockSection></apex:pageBlock>
          <br/>
          Comments:<i> {!Inspection__c.Inspection_Comments__c}</i>
          <br/>
        <apex:pageBlock title=”Quality scores” >
            <apex:pageBlockSection title=”Overall Evaluation” columns=”2″>
                <apex:outputField value=”{!Inspection__c.Overall_Service__c}”   />
                <apex:outputField value=”{!Inspection__c.Overall_Staff__c}”   />
                <apex:outputField value=”{!Inspection__c.Overall_Accommodation__c}”   />
                <apex:outputField value=”{!Inspection__c.Overall_Food__c}”   />
                <apex:outputField value=”{!Inspection__c.Overall_Housekeeping__c}”   />       
            </apex:pageblockSection>
<apex:pageBlockSection title=”General Comments” columns=”1″>
                <apex:outputField value=”{!Inspection__c.Is_the_hotel_a_worthy_member_of_XYZ__c}”   />
                <apex:outputField value=”{!Inspection__c.Left_in_doubt_is_a_Small_Luxury_Hotel__c}”   />
                <apex:outputField value=”{!Inspection__c.Would_book_again__c}”   />
                <apex:outputField value=”{!Inspection__c.Would_encourage_selecting_another_XYZ__c}”   />
                <apex:outputField value=”{!Inspection__c.Confidence_in_Hotel__c}”   />    
                <apex:outputField value=”{!Inspection__c.Further_Comments__c  }”   />   
         </apex:pageblockSection>
</apex:pageblock>
</apex:form>
</apex:page>

And here is the InspectionEdit extension:-

public with sharing class inspectionEdit {
private final Inspection__c item;
// The extension constructor initializes the private member
// variable acct by using the getRecord method from the standard
// controller.
public inspectionEdit(ApexPages.StandardController stdController) {
this.item = (Inspection__c )stdController.getRecord();
}

public String getId() {
return  item.id ;
}

   public String parentId {get;set;}
    public Attachment attach {get;set;}

    {
        attach = new Attachment();
    }
    //When user clicks upload button on Visualforce Page, perform upload/insert
    //Redirect user to newly inserted document
    public ApexPages.Pagereference upload()
    {

        //This shows how to insert an Attachment
        attach.ParentId = item.id;
        insert attach;

        return new ApexPages.Standardcontroller(attach).view();  
    }
}

 

 

Comments Off on Visualforce is not hard to master…..
Categories: Salesforce