# 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:
- The
customerfield references the customer model defined in step 1. Its type isDOMAIN_OBJECT, andreferenceDomain.shortNameisCustomers. - 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,
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,,
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:
# 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.
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.
# Maintain Related Data in Sub-tables
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
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:
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.
# Optimize the Display of the Related Customer
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:
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,
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).