Visit book
Backend
1. Variables
Alright, let’s start by creating a variable for customer classification — a set of text variable to represent this. This is going to help us categorize our customers later on.
// picker variable for customer type
ensure var VarSetOfTextCustomerType kind: setOfText
deploy: fixedOnDeploy
value: "<root>
<node key='keyDistributor'>Distributor</node>
<node key='keyDealer'>Dealer</node>
</root>"
2. VisitBook
Next up, we need a way to track the salesman’s daily visits. Let’s set up a spreadsheet called VisitBook. Nice and simple, right?
ensure spreadsheet: VisitBook
withFields: ["Date", "VisitedBy", "CustomerName", "MobileNumber", "CustomerType",
"GSTNo", "Location", "Image1", "Image2", "Remarks"]
ofTypes: [date, pickUser, text, mobileNumber, pickText,
text, location, camera, camera, paragraph]
removeRoleSet: [Owner]
insertRoleSet: [Owner, Salesman]
updateRoleSet: [Owner, Salesman]
readRoleSet: [Owner, $Manager]
Now, let’s configure the EntityVisit form. We’ll tweak its fields a bit so everything works just the way we need it.
ensure form EntityVisit
commentRoleSet: [Employee]
chatLabelField: Details.CustomerName
ensure section: Details
ensure field Date defaultValue: "now"
ensure field VisitedBy disabled: true roleDataSource: [$Self] defaultValue: $Self
ensure field CustomerType sourceVar: VarSetOfTextCustomerType defaultOptionId: "keyDealer"
ensure field Location captureUser: false captureMode: onInsert
ensure field Image1 label: "Image 1" showLabel: false
ensure field Image2 label: "Image 2" showLabel: false
ensure field GSTNo label: "GST No"
ensure field Remarks lineCount: 5
To make things easier later, let’s make these fields searchable and queryable. This will allow us to filter and search through the data when we need it.
ensure spreadsheet VisitBook
searchables: [Date, VisitedBy]
queryables: [CustomerName, MobileNumber, VisitedBy]
Alright, let’s make the data look nice by applying a ListLayout to the spreadsheet. This turns everything into a clean, organized list.
ensure layoutSpreadsheet ListLayout kind: list
filter.showSearchBar: true
firstLine.first.lineFields: [CustomerName]
firstLine.caption.lineFields: [CustomerType]
secondLine.first.lineFields: [MobileNumber]
secondLine.first.showLabels: true
thirdLine.first.lineFields: [Location]
thirdLine.first.showLabels: true
Now, let’s try something different. Apply the TableLayout to the data. This will display everything in rows and columns, which can be super helpful for reviewing lots of information at once.
ensure layoutSpreadsheet TableLayout kind: table
columnSizeSet: ["AutoSize"]
showCommentCount: true
showComps: [
Date,
VisitedBy,
CustomerName,
MobileNumber,
CustomerType,
Location,
Remarks
]
3. Reports
a. ReportVisit
• Input form
Now, it’s time to create the FilterVisit form. This form is going to help us filter the data for the ReportVisit report.
ensure form FilterVisit
ensure section: Details
ensure field LabelFilterDate kind: label
label: "Filter Date"
bold: true
textPattern: "Filter Date"
ensure field FromDate kind: date defaultValue: "startOfMonth"
ensure field ToDate kind: date defaultValue: "endOfMonth"
ensure field Divider kind: divider
ensure field LabelFilterEmployee kind: label
label: "Filter Employee"
bold: true
textPattern: "Filter Employee"
ensure field Employee kind: pickUser roleDataSource: [Employee]
• Output form
Next, we need the OutputVisit form. This is where the data from the ReportVisit report will show up once it’s ready.
ensure form OutputVisit
ensure grid: VisitSet
ensure field Date kind: date defaultValue: "now"
ensure field VisitedBy kind: pickUser roleDataSource: [Employee]
ensure field VisitRowId kind: rowId
ensure field CustomerName kind: text
ensure field MobileNumber kind: mobileNumber
ensure field CustomerType kind: pickText sourceVar: VarSetOfTextCustomerType
ensure field GSTNo kind: text label: "GST No"
ensure field Location kind: location
ensure field Remarks kind: paragraph
ensure field MapLineStroke kind: lineStroke defaultValue: solid
ensure field MapPinShape kind: pinShape defaultValue: pin
ensure field UserColor kind: color
• Output layout
Let’s go ahead and apply the TableLayout to the VisitSet grid. This will make sure the data shows up in a neat, tabular format.
// change context to VisitSet grid
ensure grid: VisitSet
ensure layoutGrid TableLayout kind: table columnSizeSet: ["AutoSize"]
showComps: [
Date,
VisitedBy,
CustomerName,
MobileNumber,
CustomerType,
GSTNo,
Location,
Remarks
]
Now, if you want to try something a little different, let’s apply the MapLayout to the VisitSet grid. This will show the data on a map, which can be really cool depending on what you’re working with.
// use location fields of grid to show map layout
ensure layoutGrid MapLayout kind: map
locationField: Location
colorField: UserColor
shapeField: MapPinShape
groupByField: VisitedBy
strokeField: MapLineStroke
Let’s go back to the OutputVisit form and apply the right layouts so everything looks good when we display the report.
// change context to OutputVisit
ensure form OutputVisit
// add map layout
ensure layout Map kind: content
direction: horizontal
renderingMode: fullScreen
flexCenter.gridLayouts: [VisitSet.MapLayout]
// add table layout
ensure layout Table kind: content
direction: vertical
allowToSwitchLayouts: [Map]
flexCenter.gridLayouts: [VisitSet.TableLayout]
// let user toggle between Map and Table layouts
ensure layout Map
allowToSwitchLayouts: [Table]
• Output formulas
Now, we need a formula to calculate the UserColor. This will assign a specific color to each user on the map, helping to visually differentiate them.
ensure formula ColorFormula
assignToField: VisitSet.UserColor
formula: "getUserColor(${f:VisitSet.VisitedBy})"
• Report
Next, create a condition variable. This will allow us to filter the data from the VisitBook spreadsheet based on what we need.
ensure var VarFilterConditionReportVisit kind: condition
deploy: fixedOnDeploy
inputForm: FilterVisit
sourceForm: EntityVisit
value: "<root>
<and>
<and>
<stmt>${f:Details.Date} >= ${in:Details.FromDate}</stmt>
<stmt>${f:Details.Date} <= ${in:Details.ToDate}</stmt>
</and>
<or>
<and>
<stmt>${in:Details.Employee} has value</stmt>
<stmt>${f:Details.VisitedBy} == ${in:Details.Employee}</stmt>
</and>
<stmt>${in:Details.Employee} has no value</stmt>
</or>
</and>
</root>"
Let’s set up the mapping variable now. This will map the data from the source form to the target form in the ReportVisit report.
// mapping variable between spreadsheet form and output form
ensure var VarMappingVisit kind: mapping
deploy: fixedOnDeploy
fromForm: EntityVisit
toForm: OutputVisit
toGrid: VisitSet
fieldMappingMap: {
'map': {
'${f:Details.Date}': 'VisitSet.Date',
'${f:Details.VisitedBy}': 'VisitSet.VisitedBy',
'${f:Details.CustomerName}': 'VisitSet.CustomerName',
'${f:Details.MobileNumber}': 'VisitSet.MobileNumber',
'${f:Details.CustomerType}': 'VisitSet.CustomerType',
'${f:Details.GSTNo}': 'VisitSet.GSTNo',
'${f:Details.Location}': 'VisitSet.Location',
'${f:Details.Remarks}': 'VisitSet.Remarks',
'${f:SysRowId}': 'VisitSet.VisitRowId'
}
}
Now, create the OutputVisitInfo form. This form will display the details from the ReportVisitInfo report once it’s generated.
ensure report ReportVisit kind: spreadsheet
inputForm: FilterVisit
outputForm: OutputVisit
fromSpreadsheet: VisitBook
filterConditionVar: VarFilterConditionReportVisit
outputFormMappingVar: VarMappingVisit
b. ReportVisitInfo
• Input form
Output form OutputVisit of ReportVisit is the input form for ReportVisitInfo.
• Output form
Now, create the OutputVisitInfo form. This form will display the details from the ReportVisitInfo report once it’s generated.
ensure form OutputVisitInfo
ensure section: Details
ensure field CustomerType kind: pickText
sourceVar: VarSetOfTextCustomerType
ensure field Customer kind: text
ensure field VisitedBy kind: pickUser roleDataSource: [Employee]
ensure field Address kind: location
ensure field Remarks kind: paragraph
ensure layout ReportLayout kind: content
displayLabel: "Visit Info"
direction: vertical
start.fields: [CustomerType, Customer, Address, Remarks]
• Report
Next, create the ReportVisitInfo report. This will pull up detailed customer info for anyone visited by a salesman, and we’ll see it right on the map.
ensure report ReportVisitInfo kind: query
label: "Visit Info"
inputForm: OutputVisit
outputForm: OutputVisitInfo
fromSpreadsheets: [VisitBook]
neoQL: "select
${ss:VisitBook.Details.CustomerType} as ${out:Details.CustomerType},
${ss:VisitBook.Details.CustomerName} as ${out:Details.Customer},
${ss:VisitBook.Details.VisitedBy} as ${out:Details.VisitedBy},
${ss:VisitBook.Details.Location} as ${out:Details.Address},
${ss:VisitBook.Details.Remarks} as ${out:Details.Remarks}
from ${ss}
where ${ctx:row.type} = ${ss:VisitBook}
and ${ctx:row.id} = ${arg:selectedGridId.selectedGridRowId.VisitRowId}"
Frontend
1. Actions
a. AddVisit
Let’s implement the AddVisit action. This action will let us insert a new visit record into the VisitBook spreadsheet.
ensure action AddVisit kind: rowInsert
icon: "AddLocationRounded"
spreadsheet: VisitBook
sendMessageToInbox: true
b. ReportVisit
Let’s add the ReportVisit action to track daily sales visits — all shown visually on the map.
ensure action ReportVisit kind: report
icon: "MapRounded"
report: ReportVisit
outputFormContentLayout: Map
sendMessageToInbox: false
c. ReportVisitInfo
Now, we need to implement the ReportVisitInfo action. This will display detailed customer information for everyone visited by a salesman.
ensure action ReportVisitInfo kind: report
label: "Visit Info"
report: ReportVisitInfo
outputFormContentLayout: ReportLayout
sendMessageToInbox: false
d. EditVisitSheet
Next, let’s set up the EditVisitSheet action. This will allow us to modify existing visit records within the VisitBook spreadsheet.
ensure action EditVisitSheet kind: spreadsheetEditor
icon: "EditNoteRounded"
spreadsheet: VisitBook
layoutSpreadsheet: TableLayout
2. Attach Actions
Now, let’s link the ReportVisitInfo action to the OutputVisit form, so the report data shows up correctly.
ensure form OutputVisit
ensure composite VisitSet
actionPermission: {
'ReportVisitInfo': {
'showMessageToolTip': true,
'roles': [
'Employee'
]
}
}
3. Group actions
Finally, let’s group all these actions into a MyVisits section. This will make them easier to find and use whenever you need them.
ensure group MyVisits
pinnedActions: [AddVisit, ReportVisit]
pinnedActionSetMobile: [AddVisit]
actionPermission: {
'AddVisit': {
'menuGroup': '1',
'roles': [
'Salesman'
]
},
'EditVisitSheet': {
'menuGroup': '2',
'roles': [
'Owner'
]
},
'ReportVisit': {
'menuGroup': '3',
'roles': [
'Owner'
]
}
}