📁 Showcase
Visit Sheet

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} &gt;= ${in:Details.FromDate}</stmt>
        <stmt>${f:Details.Date} &lt;= ${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'
        ]
      }
    }