Feedback on Rental Property Rental Agreement
@fabio.tudone Just some feedback on the Rental Smart Contract:
This system is fully supported with Node, Java and Daml, and I followed the instructions at:

GitHub - circlespainter/daml-rental: Ax example flat rental flow built on top...
Ax example flat rental flow built on top of the DAML smart contract platform - GitHub - circlespainter/daml-rental: Ax example flat rental flow built on top of the DAML smart contract platform
The instructions initially worked through Terminals [1…3], however the process failed at:
$ cd ui/
$ yarn start
The error from this was:
sh: react-scripts: command not found but there was more than that, however I did not think to save it, before solving it.
yarn start --errata
Solution, referring to sh: react-scripts
$ rm -f yarn.lock
$ sudo npm upgrade <- Should not use 'sudo' with Node, however Node.js
& NPM on Ubuntu by default does something ridiculously daft with the base
install, so you *have* to use 'sudo' on 'npm' commands due to /usr/bin/npm/node
permissions or else 'npm' will not execute for you 8-(
$ sudo npm install -g npm@latest
$ sudo npm install -g npm@7.9.0
$ sudo npm install --force <- Unsafe
$ sudo npm audit fix --force <- Unsafe
$ yarn start
Compiled successfully!
You can now view ui in the browser.
Local: http://localhost:3000
On Your Network: http://192.168.1.150:3000
Note that the development build is not optimized.
To create a production build, use npm run build.
Login into to http://localhost:3000/properties as the Authority party and from there, the user can do nothing further.
Is this due to the stated limit of:
At present only two choices are implemented and the rest are left out as an exercise to the reader
: Authority registers a property and Landlord puts it to rent; the rest of the steps have to be carried out using the DAML Navigator UI.
Or is there a further issue with my install? I want to get it working so I can extend the contract to include Maintenance requests and periodic inspections ← a pet hate of mine ![]()
Hi @quidagis and thanks for the feedback!
The instructions initially worked through Terminals [1…3], however the process failed at:
$ cd ui/ $ yarn startThe error from this was:
sh: react-scripts: command not foundbut there was more than that, however I did not think to save it, before solving it.
README.md (now updated) was missing a step there, it seems that running yarn before yarn start is enough to download the dependencies, including react-scripts.
Let me know if that works for you too.
Login into to
http://localhost:3000/propertiesas the Authority party and from there, the user can do nothing further.
All actions can be carried out through the Daml navigator but two of them (the ones mentioned in the post) are also supported by the React UI. I have updated the README.md with a short explanation about them.
I have also fixed an issue with the JSON request format there.
I want to get it working so I can extend the contract to include Maintenance requests and periodic inspections ← a pet hate of mine
Yo’re probably in good company ![]()
By the way, I still have an item in my “want to do” list about upgrading Daml Rental to the latest Daml version, I hope to get around to doing it soon.
In addition to discussing them in the forum, if you find room for improvements (or bugfixes), feel also free to open GitHub issues and/or send PRs!
This was your first post!! Welcome to the churn 
Re the update instructions, will do. I’ll run it again in the morning, and update you. It’s a great contract with significant scope to morph into other use cases.
I upgraded my Daml to 1.12.1.snapshot last week, no issues reported, but I did notice that your contract was squawking about Daml 1.11.1 
Again, nice job!
This was your first post!! Welcome to the churn
Oh that’s true, shame on me ![]()
Again, nice job!
Thank you and let me/us know how things are going, both with running Daml Rental and with your additions!
I’ll give you the short version of how I got this to work:
# Remove all current NPM packages, globally, from the system
$ npm ls -gp --depth=0 | awk -F/node_modules/ '{print $2}' | grep -vE '^(npm|)$' | xargs -r npm -g rm
$ npm ls
/home/quid
└── (empty)
$ git clone https://github.com/circlespainter/daml-rental.git
# Follow the instructions as per the README.md
Critical point, in relation to TERMINAL 4:
$ cd ui
$ yarn
$ yarn start
# There will be a warning:
# Browserslist: caniuse-lite is outdated. Please run next command `yarn upgrade`
# **DO NOT UPGRADE**, the 'daml-rental' app will break
Error from 'yarn upgrade'
#### Remove NPM Modules Globally
quid ~ $ npm ls -gp --depth=0 | awk -F/node_modules/ '{print $2}' | grep -vE '^(npm|)$' | xargs -r npm -g rm
quid ~ $ npm ls
/home/quid
└── (empty)
$ cd ~/Git
$ rm -rf daml-rental
rm: cannot remove 'daml-rental/ui/node_modules/@typescript-eslint/typescript-estree/node_modules/.bin/semver': Permission denied
rm: cannot remove 'daml-rental/ui/node_modules/@babel/plugin-transform-runtime/node_modules/.bin/semver': Permission denied
rm: cannot remove 'daml-rental/ui/node_modules/webpack/node_modules/.bin/acorn': Permission denied
rm: cannot remove 'daml-rental/ui/node_modules/react-scripts/node_modules/@babel/core/node_modules/.bin/semver': Permission denied
rm: cannot remove 'daml-rental/ui/node_modules/babel-preset-react-app/node_modules/.bin/semver': Permission denied
rm: cannot remove 'daml-rental/ui/node_modules/resolve-url-loader/node_modules/.bin/json5': Permission denied
rm: cannot remove 'daml-rental/ui/node_modules/fork-ts-checker-webpack-plugin/node_modules/.bin/semver': Permission denied
$ sudo rm -rf daml-rental
$ ls daml-rental
ls: cannot access 'daml-rental': No such file or directory
## Reinstall Daml Rental
$ git clone https://github.com/circlespainter/daml-rental.git
Cloning into 'daml-rental'...
remote: Enumerating objects: 135, done.
remote: Counting objects: 100% (135/135), done.
remote: Compressing objects: 100% (101/101), done.
remote: Total 135 (delta 48), reused 102 (delta 25), pack-reused 0
Receiving objects: 100% (135/135), 228.83 KiB | 1.05 MiB/s, done.
Resolving deltas: 100% (48/48), done.
$ cd daml-rental
$ clear && ls -al
total 40
drwxrwxr-x 6 quid quid 4096 Apr 14 13:55 .
drwxrwxr-x 12 quid quid 4096 Apr 14 13:55 ..
drwxrwxr-x 3 quid quid 4096 Apr 14 13:55 cleanup-trigger
drwxrwxr-x 8 quid quid 4096 Apr 14 13:55 .git
-rw-rw-r-- 1 quid quid 48 Apr 14 13:55 .gitignore
drwxrwxr-x 4 quid quid 4096 Apr 14 13:55 ledger
-rw-rw-r-- 1 quid quid 639 Apr 14 13:55 LICENSE
-rw-rw-r-- 1 quid quid 5019 Apr 14 13:55 README.md
drwxrwxr-x 4 quid quid 4096 Apr 14 13:55 ui
## [TERMINAL 4]
quid ~/Git/daml-rental $ cd ui
quid ~/Git/daml-rental/ui $ yarn
yarn install v1.22.5
[1/4] Resolving packages...
[2/4] Fetching packages...
info fsevents@2.1.2: The platform "linux" is incompatible with this module.
info "fsevents@2.1.2" is an optional dependency and failed compatibility check. Excluding it from installation.
info fsevents@1.2.9: The platform "linux" is incompatible with this module.
info "fsevents@1.2.9" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
warning " > @testing-library/user-event@7.2.1" has unmet peer dependency "@testing-library/dom@>=5".
warning " > material-table@1.56.1" has unmet peer dependency "@date-io/core@^1.3.6".
warning "material-table > @material-ui/pickers@3.2.9" has unmet peer dependency "@date-io/core@^1.3.6".
[4/4] Building fresh packages...
Done in 52.55s.
quid ~/Git/daml-rental/ui $ yarn start
Starting the development server...
Browserslist: caniuse-lite is outdated. Please run next command `yarn upgrade`
Compiled successfully!
You can now view ui in the browser.
Local: http://localhost:3000/
On Your Network: http://192.168.1.150:3000/
Note that the development build is not optimized.
To create a production build, use yarn build.
Starting the development server...
$ Ctrl-C
$ yarn upgrade
$ yarn start
## Console Error Output
TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received undefined
at new NodeError (node:internal/errors:329:5)
at validateString (node:internal/validators:129:11)
at Object.join (node:path:1081:7)
at noopServiceWorkerMiddleware (/home/quid/Git/daml-rental/ui/node_modules/react-dev-utils/noopServiceWorkerMiddleware.js:14:26)
at Layer.handle [as handle_request] (/home/quid/Git/daml-rental/ui/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/home/quid/Git/daml-rental/ui/node_modules/express/lib/router/index.js:317:13)
at /home/quid/Git/daml-rental/ui/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/home/quid/Git/daml-rental/ui/node_modules/express/lib/router/index.js:335:12)
at next (/home/quid/Git/daml-rental/ui/node_modules/express/lib/router/index.js:275:10)
at launchEditorMiddleware (/home/quid/Git/daml-rental/ui/node_modules/react-dev-utils/errorOverlayMiddleware.js:20:7)
at Layer.handle [as handle_request] (/home/quid/Git/daml-rental/ui/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/home/quid/Git/daml-rental/ui/node_modules/express/lib/router/index.js:317:13)
at /home/quid/Git/daml-rental/ui/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/home/quid/Git/daml-rental/ui/node_modules/express/lib/router/index.js:335:12)
at next (/home/quid/Git/daml-rental/ui/node_modules/express/lib/router/index.js:275:10)
at handleWebpackInternalMiddleware (/home/quid/Git/daml-rental/ui/node_modules/react-dev-utils/evalSourceMapMiddleware.js:42:7)
-----
Failed to compile.
## File Error Warning
## I edited Line 67, removed the {}, added (), no change
# File: /home/quid/Git/daml-rental/ui/src/App.tsx
TypeScript error in /home/quid/Git/daml-rental/ui/src/App.tsx(67,15):
No overload matches this call.
Overload 1 of 2, '(props: { component: ElementType<any>; } & { children: string | number | boolean | {} | ReactElement<any, string | ((props: any) => ReactElement<any, any> | null) | (new (props: any) => Component<...>)> | ReactNodeArray | ReactPortal; disableGutters?: boolean | undefined; fixed?: boolean | undefined; maxWidth?: false | ... 5 more ... | undefined; } & CommonProps<...> & Pick<...>): Element', gave the following error.
Type 'ReactNode' is not assignable to type 'string | number | boolean | {} | ReactElement<any, string | ((props: any) => ReactElement<any, any> | null) | (new (props: any) => Component<any, any, any>)> | ReactNodeArray | ReactPortal'.
Type 'undefined' is not assignable to type 'string | number | boolean | {} | ReactElement<any, string | ((props: any) => ReactElement<any, any> | null) | (new (props: any) => Component<any, any, any>)> | ReactNodeArray | ReactPortal'.
Overload 2 of 2, '(props: DefaultComponentProps<ContainerTypeMap<{}, "div">>): Element', gave the following error.
Type 'ReactNode' is not assignable to type 'string | number | boolean | {} | ReactElement<any, string | ((props: any) => ReactElement<any, any> | null) | (new (props: any) => Component<any, any, any>)> | ReactNodeArray | ReactPortal'.
Type 'undefined' is not assignable to type 'string | number | boolean | {} | ReactElement<any, string | ((props: any) => ReactElement<any, any> | null) | (new (props: any) => Component<any, any, any>)> | ReactNodeArray | ReactPortal'. TS2769
65 | <div>
66 | <Container maxWidth="xl" className={classes.container}>
> 67 | {props.children}
| ^
68 | </Container>
69 | </div>
70 | )
## Google Chrome Browser Error Output:
TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received undefined
at new NodeError (node:internal/errors:329:5)
at validateString (node:internal/validators:129:11)
at Object.join (node:path:1081:7)
at noopServiceWorkerMiddleware (/home/quid/Git/daml-rental/ui/node_modules/react-dev-utils/noopServiceWorkerMiddleware.js:14:26)
at Layer.handle [as handle_request] (/home/quid/Git/daml-rental/ui/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/home/quid/Git/daml-rental/ui/node_modules/express/lib/router/index.js:317:13)
at /home/quid/Git/daml-rental/ui/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/home/quid/Git/daml-rental/ui/node_modules/express/lib/router/index.js:335:12)
at next (/home/quid/Git/daml-rental/ui/node_modules/express/lib/router/index.js:275:10)
at launchEditorMiddleware (/home/quid/Git/daml-rental/ui/node_modules/react-dev-utils/errorOverlayMiddleware.js:20:7)
at Layer.handle [as handle_request] (/home/quid/Git/daml-rental/ui/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/home/quid/Git/daml-rental/ui/node_modules/express/lib/router/index.js:317:13)
at /home/quid/Git/daml-rental/ui/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/home/quid/Git/daml-rental/ui/node_modules/express/lib/router/index.js:335:12)
at next (/home/quid/Git/daml-rental/ui/node_modules/express/lib/router/index.js:275:10)
at handleWebpackInternalMiddleware (/home/quid/Git/daml-rental/ui/node_modules/react-dev-utils/evalSourceMapMiddleware.js:42:7)
## EOF
Otherwise, continue as per the instructions and it works great 
For those whom are interested:
Daml Rental app File Structure
daml_rental_fs_using_tree.daml (3.7 MB)
Anyone else who is interested in Rental Contracts and is happy to work together, to help Fabio extend this, please ping me.
Also @fabio.tudone I have forked the project, and submitted a very minor PR. More to follow.
EDIT: I removed the daml-rental-yarn error as a file, and included it as formatted code using the [Hide Details] function. I also went to do the same thing for the daml-rental/tree output but I received a 413 Error size too big from the blog.
I am reading through daml-rental/…/Rental.daml and I note that you seem to use the indentation of 4/8 spaces, not 2/4 as I have seen in most Daml code. As it is all aligned perfectly, it makes no functional difference but from the Daml language POV, is this OK?
I’m happy to do what your existing code does, but just wanted to ask.
From what I can see, the project seems to be driven off the …/ledger/daml/Rental.daml for functional actions but …/ui/src/tables for the presentation actions.
If that is correct, then to add in additional functionality say, a maintenance request by the current Tenant, the place to start would be the Rental.daml? I’ll just copy one of the current Templates and work my way through adding a maint_req in.
from the Daml language POV, is this OK?
The Daml compiler doesn’t care about how many spaces as long as:
- All the code that is part of the same block is indented at the same level, and
- Nested blocks are indented (at least one space) more than their parent.
So instead of 2/4/6/8 you could go for 1/5/18/19 and that would work just as well. Readers of your code may not be too happy though.
From what I can see, the project seems to be driven off the …/ledger/daml/Rental.daml for functional actions but …/ui/src/tables for the presentation actions.
If that is correct, then to add in additional functionality say, a maintenance request by the current Tenant, the place to start would be the Rental.daml? I’ll just copy one of the current Templates and work my way through adding a maint_req in.
I like to think about the business logic first, so I tend to first model it in Daml and then to think about the UI. This is also made quite easy by Daml Navigator, so my suggestion would be to try and do the same and see if it works for you.
On a bigger project the process is probably more bi-directional, as you’ll have UX-oriented sessions taking the business idea and thinking about user interaction and usability matters, business-oriented sessions thinking about the formal business model (probably directly in Daml) and then engineering work to refine and putting it all together. It is also probably much less linear, perhaps using design thinking techniques to challenge and refine the business idea itself.
Makes sense. As a non-Developer / Business type (Currently), in relation to the Daml Rental application, I’d do something similar as to you; model the business logic in Daml, and when it works, or fails less, I’d consider some type of UI front 
I usually use pseudo-code for any idea, flesh out the usual & critical use cases, then start to code using the language of choice. I did note that Daml removes a lot of those upfront decisions such as do I need to loop here? How do I handle messaging? If it fails, what happens?
Which is very handy and forgiving for a new user.
I am currently making a MaintenanceRequest template, and based on my years of renting I have a solid handle on that actual business process. I’ll do some testing tomorrow and see how it goes, then maybe a PR.
I am adding in another template titled ‘MaintenanceRequest’, and the logic flow from it is initially as follows:
Note: MaintenanceRequest is referred to as: Maint_Req
Process:
- Tenant contacts Agency about Maintenance Issue
- Agency confirms Maintenance issue
- If repair cost < $300, Agency contacts Provider
- If repair cost > $300, Agency contacts Landlord
- If Landlord approves Repair, Agency contacts Provider
- Provider contacts Tenant, arranges Access Time/Date
- Repair is actioned
- Tenant provides Feedback to Agency
- If Good, Agency close Maint_Req, Authorises payment
- If Bad, Agency visits Property, inspects, Provider remediates
- Agency inspects, closes Maint_Req, authorises payment
I reviewed the existing Rental.daml and
choose the template VisitScheduleRequest.
View 'template VisitScheduleRequest
template VisitScheduleRequest
with
-- Signatory
agency : Party
visitor : Party
landlord : Party
-- Controller
tenant : Party
-- Data
registerId : Text -- Carryover
where
-- Agreement
agreement
"A request by a visitor to schedule a visit with the tenant"
signatory
agency,
visitor,
landlord
controller agency can
VisitScheduleRequest_Abort : () do return () -- For cleanup trigger
controller tenant can
VisitScheduleRequest_Accept : ContractId Visited
do
create Visited with
-- Signatory
agency,
visitor,
landlord,
-- Data
registerId -- Carryover
VisitScheduleRequest_Reject : () do return ()
Modified it to new terms, but I think it can support almost the same method of making contact; setting a visit time, complete the visit, records the visit.
template MaintenanceRequest
template MaintenanceRequest
with
-- Signatory
landlord : Party
agency : Party
provider: Party
-- Controller
agency : Party
-- Data
registerId : Text -- Carryover
where
-- Agreement
agreement
"Test"
signatory
agency,
landlord,
provider
controller agency can
MaintenanceRequest_Withdraw : () do return () -- For cleanup trigger
controller agency can
MaintenanceRequest_Accept : ContractId Approved
do
create Approved with
-- Signatory
agency,
landlord,
provider,
-- Controller
agency,
-- Data
registerId -- Carryover
controller agency can
MaintenanceRequest_Refuse : ContractId Denied
do
create Denied with
-- Signatory
agency,
landlord,
-- Controller
agency,
-- Data
registerId -- Carryover
controller agency can
MaintenanceRequest_Schedule : ContractId Scheduled
do
create Scheduled with
-- Signatory
agency,
landlord,
provider,
-- Controller
agency,
-- Data
registerId -- Carryover
controller agency can
MaintenanceRequest_Active : ContractId Active
do
create Active with
-- Signatory
agency,
landlord,
provider,
-- Controller
agency
-- Data
registerId -- Carryover
controller agency can
MaintenanceRequest_Repaired : ContractId Complete -- repair done, not inspected
do
create Complete with
-- Signatory
agency,
landlord,
provider,
-- Controller
agency
-- Data
registerId -- Carryover
controller agency can
MaintenanceRequest_Processed : ContractId Finalised -- repair done, inspected, authorised payment
do
create Finalised with
-- Signatory
agency,
landlord,
provider,
-- Controller
agency
-- Data
registerId -- Carryover
-- END OF FILE
The current configuration produces this error:
New template error output
... truncated ...
Bound at: daml/Rental.daml:615:20-25
daml/Rental.daml:615:20-25
• In a lambda abstraction
File: daml/Rental.daml
Hidden: no
Range: 617:54-617:63
Source: typecheck
Severity: DsError
Message: daml/Rental.daml:617:55: error:Not in scope: type constructor or class ‘Finalised’
File: daml/Rental.daml
Hidden: no
Range: 617:54-617:63
Source: typecheck
Severity: DsError
Message: daml/Rental.daml:617:55: error:Not in scope: type constructor or class ‘Finalised’
File: daml/Rental.daml
Hidden: no
Range: 618:16-629:38
Source: typecheck
Severity: DsError
Message:
daml/Rental.daml:618:17: error:
• Conflicting definitions for ‘agency’
Bound at: daml/Rental.daml:(618,17)-(629,38)
daml/Rental.daml:(618,17)-(629,38)
• In a lambda abstraction
File: daml/Rental.daml
Hidden: no
Range: 619:27-619:36
Source: typecheck
Severity: DsError
Message: daml/Rental.daml:619:28: error:Not in scope: data constructor ‘Finalised’
File: daml/Rental.daml
Hidden: no
Range: 619:27-626:34
Source: typecheck
Severity: DsError
Message: daml/Rental.daml:619:28: error:duplicate field name ‘agency’ in record construction
ERROR: Creation of DAR file failed.
I went through every line, getting the indentation correct in the MaintenanceRequest template, I had it down to 1 single error, then tweaked something, and now this
… I think this is an error of function, not format.
Hi @quidagis, I can see 2 errors, one about Finalised not being found and one about a duplicate agency field. Could you commit and push your changes to a branch in your fork? This will make it easier to inspect them. Thanks!
Thank you for the feedback => Done.
Thanks! Here are some initial suggestions to fix it:
- A template parameter cannot be defined twice but you have two
agencyoccurrences there; you can just remove one of them. - The choices available to the same controller should be under the same
controller X canclause, i.e., you should not repeat the latter for the same controller. - All the contracts defined as result of choices (e.g. “Active”) should be defined as templates and I suggest to name them in such a way that they describe the process’ situation (e.g., “ActiveMaintenanceRequest”).
- As for the process itself, I encourage you to first draw a diagram that shows how the process flows, i.e., how the maintenance is transitioned from one situation to the next one and who can do that. Then, a straightforward way to translate it in daml is to create one template per situation and one choice per arrow, with the controller being the party that can action the arrow.
For example, “Tenant contacts Agency about Maintenance issue” really means, in “situational” terms, “the tenant (of a specific rental) creates a new maintenance request that is then actionable by that rental’s agency”.
You have already many daml definition elements in the more detailed rephrasing above: a tenant party that is the tenant of a specific rental can create a new contract of MaintenanceRequest template, for that same rental only (so perhaps exercising a choice on that rental is the natural starting point?), where the rental’s agency is the controller of the next action (the confirmation, in this case). And so on.
Is it useful to think about it in this way?
All the contracts defined as result of choices (e.g. “Active”) should be defined as templates and I suggest to name them in such a way that they describe the process’ situation (e.g., “ActiveMaintenanceRequest”).
Noted, thanks. That makes more sense than the variations using the base MaintenanceRequest template that I currently have ![]()
As for the process itself, I encourage you to first draw a diagram that shows how the process flows, i.e., how the maintenance is transitioned from one situation to the next one and who can do that. Then, a straightforward way to translate it in daml is to create one template per situation and one choice per arrow, with the controller being the party that can action the arrow.
Excellent advice. I noted that you had modelled it yourself previously, and since I prefer Words to Pictures, I just went straight to the list and tried to encode it using Daml ← fail ![]()
I think your suggestion is a good, longer term guide that I will incorporate into my work flow. I would assume that having such a large number of Templates, as matching the action items, give you a high degree of finite control on the overall process, and should reduce the scope for logic errors?
Given that MaintReq is one additional function that I can think of in Renting, the Rental.daml could expand considerably. In that case, how would you generally order the Templates? Alphabetically? Functionally and alphabetically? Or is it of no consequence to DAMLC?
I would assume that having such a large number of Templates, as matching the action items, give you a high degree of finite control on the overall process
I find it easy to think in terms of process states or, similarly, agreements/contracts (and you can think of templates as their “types” or “blueprints”, and the choices in them prescribe the possible actions or “options”) and there can be further mental models, I guess.
In the end, the current situation of a multi-party workflow is determined by the official (i.e., binding, as far as the parties are concerned) records that:
- describe the present state by providing information about it and its place in the process
- prescribe how it can be advanced, i.e. who can do what, when and at which conditions, potentially based on that information.
Note that templates, and the choices they define, play a role in both.
Given that MaintReq is one additional function that I can think of in Renting, the Rental.daml could expand considerably. In that case, how would you generally order the Templates? Alphabetically? Functionally and alphabetically? Or is it of no consequence to DAMLC?
It’s really a matter of readability, understandability and how easily it can be evolved even without a lot of memory/knowledge/context about it. You could also split it into different modules.
You could also split it into different modules.
It did not occur to do that, thanks, I will read further on this.
EDIT => Using Graphviz via a simple Dot file but tested using Viz.js
Logically it seems sound, but it just looks ‘off’ 
If anyone is interested, here is the code to make that image, using the Graphviz package installed via:
sudo apt install graphviz:
Maintenance_Request_Logic_Dot_Code
// Filename: daml_rental.dot
// Author: Quid Agis
// Version: 0.010
// Date: 03/05/2021
digraph Maintenance_Request {
subgraph cluster_0 {
style=filled;
color=lightgrey;
node [style=filled,color=white];
Maint_Req_Open -> Agency_Confirms -> Repair_Less_300 -> Agency_Approves -> Provider_Contacts -> Repair_Actioned -> Tenant_Feedback -> If_Positive -> Agency_Inspects -> Agency_Closes -> Maint_Req_Closed;
label = "Maintenance_Request";
}
subgraph cluster_1 {
node [style=filled];
Repair_Greater_300 -> Landlord_Approves ;
label = "> $300, Approval";
color=blue
}
subgraph cluster_2 {
node [style=filled];
Repair_Greater_300 -> Landlord_Refuses ;
label = "> $300, Refusal";
color=blue
}
subgraph cluster_3 {
node [style=filled];
Tenant_Feedback -> If_Negative -> Agency_Inspects -> Provider_Remediates -> Tenant_Feedback;
label = "Repaired, Negative feedback";
color=blue
}
Agency_Confirms -> Repair_Greater_300 ;
Landlord_Approves -> Agency_Approves ;
Landlord_Refuses -> Agency_Closes ;
}
Once the code is finalised, convert the code into an image:
# Convert
$ dot -Tpng -O daml_rental.dot
# View
$ xdg-open daml_rental.dot.png
Hi @quidagis, I am on Ubuntu 18.04 and can’t find graphiz; did you mean graphviz?
The graph nodes here seem to encode actions (with actors) rather than states (so choices rather than contracts, in daml parlance); I haven’t thought of that but I think this works too.
Did it help you with writing the process in daml? Looking forward to it!
Yes, Graphviz, thank you for seeing that. Errors corrected 
Hi @fabio.tudone I just tried out the Daml Rental app now, and it worked as per spec. Once I actually did yarn start. The potential upgrade issue with the caniuse-lite package still exists.
But the UI looks pretty clean now, nice one 
