# Sales Opportunity Modeling and Forms

Screenshots on this page are taken from the Chinese UI. Menu, field and button names in the text use the English UI labels.

In the previous steps, we defined the customer model and created its list, create and edit forms. Now we define sales opportunities and their related interaction records and tasks.

Based on the CRM System Design Document, the fields of the three models are as follows.

# Model Definition

# Interaction Records (InteractionRecords)

Field Name Type Nullable Options Remarks
name STRING No None Interaction subject
interactionType STRING No 电话, 会议, 邮件, 现场拜访, 视频会议, 其他 Interaction type
interactionDate LOCAL_DATE No None Interaction date
notes STRING Yes None Notes

# Tasks (Tasks)

Field Name Type Nullable Options Remarks
name STRING No None Task name
description STRING No None Task description
dueDate LOCAL_DATE No None Due date
status STRING No 待处理, 进行中, 已完成, 已取消 Task status

# Sales Opportunities (SalesOpportunities)

Field Name Type Nullable Options Reference Type Remarks
name STRING No None None Opportunity name
customer DOMAIN_OBJECT No None Customers Related customer
stage STRING No 初步接触, 需求分析, 方案设计, 投标, 谈判, 成交, 失效 None Opportunity stage
expectedAmount BIG_DECIMAL No None None Expected amount
expectedCloseDate LOCAL_DATE No None None Expected close date
interactionRecords DOMAIN_OBJECT_LIST Yes None InteractionRecords Interaction records
tasks DOMAIN_OBJECT_LIST Yes None Tasks Tasks

Notes:

  1. The customer field references the customer model defined in step 1. Its type is DOMAIN_OBJECT, and referenceDomain.shortName is Customers.
  2. A sales opportunity has a one-to-many relationship with interaction records and with tasks. This kind of relationship is configured with DOMAIN_OBJECT_LIST. By default, the platform maintains them as sub-tables in the sales opportunity forms.

# Create the Models with CSV

Append the following content to the two CSV files from step 1.

DomainClass_crm.csv (full content):

shortName(*),extInfo,createRoleRequirement.name,readRoleRequirement.name,updateRoleRequirement.name,deleteRoleRequirement.name
;; CRM tutorial: customers, sales opportunities, interaction records, tasks
Customers,,USER,USER,USER,
InteractionRecords,,USER,USER,USER,USER
Tasks,,USER,USER,USER,USER
SalesOpportunities,,USER,USER,USER,
1
2
3
4
5
6

Interaction records and tasks are created, modified and deleted in sub-tables, so all four permissions are granted to USER. As with customers, the delete permission of sales opportunities is left for step 4.

DomainClassField_crm.csv (appended after the customer fields):

;; Interaction records
InteractionRecords,name,STRING,,false,true,,
InteractionRecords,interactionType,STRING,,false,true,,"[""电话"",""会议"",""邮件"",""现场拜访"",""视频会议"",""其他""]"
InteractionRecords,interactionDate,LOCAL_DATE,,false,true,,
InteractionRecords,notes,STRING,,true,true,,

;; Tasks
Tasks,name,STRING,,false,true,,
Tasks,description,STRING,,false,true,,
Tasks,dueDate,LOCAL_DATE,,false,true,,
Tasks,status,STRING,,false,true,,"[""待处理"",""进行中"",""已完成"",""已取消""]"

;; Sales opportunities
SalesOpportunities,name,STRING,,false,true,,
SalesOpportunities,customer,DOMAIN_OBJECT,Customers,false,true,,
SalesOpportunities,stage,STRING,,false,true,,"[""初步接触"",""需求分析"",""方案设计"",""投标"",""谈判"",""成交"",""失效""]"
SalesOpportunities,expectedAmount,BIG_DECIMAL,,false,true,,
SalesOpportunities,expectedCloseDate,LOCAL_DATE,,false,true,,
SalesOpportunities,interactionRecords,DOMAIN_OBJECT_LIST,InteractionRecords,true,true,,
SalesOpportunities,tasks,DOMAIN_OBJECT_LIST,Tasks,true,true,,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

The referenced models (Customers, InteractionRecords, Tasks) are created by DomainClass_crm.csv, and DomainClass is always imported before DomainClassField, so the order of lines in the field file does not matter.

After restarting the backend service, three new models appear in Development > Classes:

CRM models in the domain model list

# Form Definition

As in step 2, create three forms for sales opportunities in System Config > Forms > Forms:

Type Name Label Object type
LIST List of SalesOpportunities 销售机会列表 Sales Opportunities
CREATE Create SalesOpportunities 新建销售机会 Sales Opportunities
UPDATE Update SalesOpportunities 编辑销售机会 Sales Opportunities

TIP

The Object type drop-down shows the display names of the models. When a dynamic model is created, its display name is generated by splitting the model name at camel-case boundaries, so SalesOpportunities is shown as Sales Opportunities and InteractionRecords as Interaction Records.

The three sales opportunity forms

Then create a top-level menu in System Config > UI > Menus: Name SalesOpportunities, Type Form, Label 销售机会, Icon FundOutlined, Form List of SalesOpportunities, Display sequence 6.

Interaction records and tasks have a one-to-many relationship with sales opportunities and are shown as sub-tables in the sales opportunity create and edit forms. The columns of a sub-table come from the child object's LIST form; if the child object has no LIST form, the sub-table area keeps showing the loading state.

So you also need to create forms for interaction records and tasks (this tutorial also creates CREATE and UPDATE forms so that these two kinds of data can be maintained separately later):

Type Name Label Object type
LIST List of InteractionRecords 互动记录列表 Interaction Records
CREATE Create InteractionRecords 新建互动记录 Interaction Records
UPDATE Update InteractionRecords 编辑互动记录 Interaction Records
LIST List of Tasks 任务列表 Tasks
CREATE Create Tasks 新建任务 Tasks
UPDATE Update Tasks 编辑任务 Tasks

Finally, append the 9 forms created in this step to DynamicForm_crm.csv to set their access permissions (together with the 3 lines from step 2, the file has 12 lines), then restart the backend service:

name(*),accessRequirement.name
;; CRM tutorial: add access permissions to the forms created in the UI (the UI has no such field)
List of Customers,USER
Create Customers,USER
Update Customers,USER
List of SalesOpportunities,USER
Create SalesOpportunities,USER
Update SalesOpportunities,USER
List of InteractionRecords,USER
Create InteractionRecords,USER
Update InteractionRecords,USER
List of Tasks,USER
Create Tasks,USER
Update Tasks,USER
1
2
3
4
5
6
7
8
9
10
11
12
13
14

TIP

This file looks up existing forms by name through name(*), and accessRequirement.name references a role requirement by name. If a form has not been created in the UI yet, its line fails to import (the CSV lacks required fields such as the form type, so a new form cannot be created), the import status of this file is not success, and the whole file is imported again on the next restart. Create the forms first, then edit the CSV.

Now click 销售机会 on the left, then click Create. Above each sub-table there is a Create button; click it to add a row to the sub-table, fill it in, and click Save on that row:

Create a sales opportunity and enter interaction records and tasks in the sub-tables

The extra "0" at the end of the operation column of each sub-table row in the screenshot is a UI issue in the current version and does not affect usage; the customer drop-down shows customer IDs, for reasons explained below.

Click Save / Close. The sales opportunity is saved together with the interaction records and tasks in the sub-tables.

After saving, go back to the list. You will find that the customer column shows the customer's ID rather than the customer's name, and the options of the customer drop-down in the create form are IDs as well:

Customers shown as IDs

This is because the platform does not know which field of the customer represents a customer. The labelField in the domain model's extended information (extInfo) specifies that field.

Known issue in 1.0.0-beta18: do not make the change below yet

In 1.0.0-beta18, after labelField is set on a dynamic model, the backend falls into infinite recursion when a list page that references it (here the 销售机会 list) loads data, and the list stays in the loading state. Until this issue is fixed, do not set labelField on a model that is referenced by other dynamic models. If you have already set it, change the extInfo cell to NULL and import again to clear it. Customers are still shown as IDs in the later screenshots of this tutorial.

Once the issue is fixed, change the extInfo of the Customers line in DomainClass_crm.csv to the value below, and customers will be shown by name:

Customers,"{""labelField"": ""name""}",USER,USER,USER,
1

You can also click Update on the Customers row in Development > Classes and enter {"labelField": "name"} in Extend information.

TIP

If the same record has been changed both in the UI and in CSV, the import treats it as a conflict and does not overwrite the change made in the UI. See "Conflict Detection and Overwrite" in Data Import for details. It is therefore best to maintain each configuration item in one place only: either CSV only or the UI only.

# Further Reading

Next, we will grant the delete permission on customers and sales opportunities and bind the batch delete action. Continue with the Object Delete Permission chapter.

Our platform is the Muyan low-code platform, designed to help users build information systems quickly. To learn more about our platform, visit Muyan Low-Code Platform (opens new window).

Last Updated: 9/24/2026, 2:27:35 PM