Configuration¶
Attention
If you have configured MS-inbox to create helpdesk limeobjects, please be aware. Since we are adding configuration in custom endpoint shared with MS-inbox and decorators to Custom Limeobject Helpdesk you will not be able to create helpdesk limobjects from email until both MS-inbox and Communication-flow is fully configured.
Create a custom endpoint for handling the incoming emails¶
-
Follow the instructions of MS Inbox Configuration for adding a custom endpoint for handling incoming emails.
-
Add handling for communication to the same handle function created above.
# TODO: Add this import at the top of the file
import limepkg_communication_flow.ms_inbox.behaviours as comm_behaviours
# This is the function from above MS Inbox step
def _handle_email_message(app: LimeApplication, email_data: dict) -> dict:
# Code for processing email in MS Inbox....
# TODO: Just before committing the unit of work add this row.
# The helpdesk input variable will most likely come from the
# process_email() function call, like this:
# helpdesk = inbox_behaviours.process_email(app, email_item, uow)
# defined in the inbox configuration examples
comm_behaviours.parse_email_as_communication(helpdesk, uow, email_item)
return uow.commit()
Custom Limeobject Helpdesk¶
There's some code that should be run for the helpdesk's custom limeobject for creating follower and such. To do so you need to add a decorator to your custom limeobject for helpdesk.
import logging
# TODO: Add decorator import
import limepkg_communication_flow.decorators as communication_flow_decorators
from lime_type.limeobjects import LimeObject
logger = logging.getLogger(__name__)
# TODO: Add this row before your custom limeobject class
@communication_flow_decorators.dress_helpdesk()
class Helpdesk(LimeObject):
# This may contain functions like before_update, after_update and so on
pass
def register_limeobject_classes(register_class):
# TODO: May need to change "helpdesk" according to your table name
register_class("helpdesk", Helpdesk)
(Optional) Auto reply with communication¶
Attention
If you already have configured auto reply for MS-inbox please make sure to remove the Auto reply configuration in Lime Inbox before adding this to avoid duplicate auto replies.
Info
send_system_message is using functions from MS-inbox. From email is based on the standard behaviour functions for MS Inbox. Available merge codes and subject will be as documented here MS-Inbox - Configure auto reply.
-
To send and log an auto reply add the following code in your Custom Limeobject Helpdesk.
Bad practice
Adding a new unit of work in after_update in custom limeobject is considered a bad practice and should be avoided.
This is an old implementation, and will be replaced by the new Email integration
import logging
import limepkg_communication_flow.decorators as communication_flow_decorators
# TODO: Add new traml imports
import limepkg_communication_flow.traml as comm_traml
import limepkg_transactional_message_library.traml as traml_lib
from lime_type.limeobjects import LimeObject
from limepkg_dynamic_checklist import checklist_owner
logger = logging.getLogger(__name__)
# This is a part from the Custom Limeobject Helpdesk from the step above
# in the after_update add the call to _send_traml_message
# if you are missing after_update in your 'Custom Limeobject Helpdesk' copy entire after_update
# only add the "_send_traml_message call" at the end of your after_update
@communication_flow_decorators.dress_helpdesk()
class Helpdesk(LimeObject):
def after_update(self, unsaved_self, **kwargs):
super().after_update(unsaved_self, **kwargs)
# TODO: Add this call in the end of after_update
_send_traml_message(self, unsaved_self)
def _send_traml_message(helpdesk: LimeObject, unsaved_helpdesk: LimeObject):
if not unsaved_helpdesk.is_new:
return
app = helpdesk.application
traml = traml_lib.TramlClient(app)
# Step 1: Fetch template
try:
# TODO: Change to the template the customer will use
traml_template = traml.get_mail_template_by_name(
"limepkg-communication-flow-autoreply-template"
)
except traml_lib.models.TramlError as e:
logger.warning(f"Failed to fetch template due to {e}")
return
try:
# TODO: Change to whatever default email should be used
message_meta = comm_traml.get_system_message_meta(
helpdesk,
"[email protected]",
)
# TODO: add any potential customer specific merge_codes
merge_codes = {
**message_meta.merge_codes,
# Optional to add custom merge_codes
"$$my.custom.merge_code$$": "This is just for fun",
}
recipients = message_meta.all_recipients
if not recipients:
return
# Step 2: Create communication for sent autoreply
uow = app.unit_of_work()
idx_comm = comm_traml.create_system_communication(
helpdesk=helpdesk,
traml_template=traml_template,
recipients=recipients,
from_email=message_meta.from_email,
merge_codes=merge_codes,
uow=uow,
)
result = uow.commit()
communication: LimeObject = result.get(idx_comm)
# Step 3: Send traml email
for recipient in recipients:
email_model = traml_lib.models.Email(
external_id=recipient.get_external_id(communication.id),
# TODO: Change to whatever the customer wants
from_name="Lime Helpdesk",
from_email=message_meta.from_email,
recipient_name=recipient.name,
recipient_email=recipient.email,
subject=message_meta.subject,
merge_codes=merge_codes,
exclude_totaloptouts=True,
)
email_model.set_autoreply_headers()
if recipient.internet_message_id:
email_model.set_in_reply_to_headers(
recipient.internet_message_id
)
traml.send_transactionmail_by_template(
template_id=traml_template.id,
message=email_model,
)
except traml_lib.models.TramlError as e:
logger.warning(
f"Failed to send autoreply for: {message_meta.subject} due to: {e}"
)
# TODO: Handle TramlError
# (Remove if you want it to fail on error)
raise
def register_limeobject_classes(register_class):
register_class("helpdesk", Helpdesk)
Lime Admin¶
Add-on config¶
Go To: Add-ons
=> Communication flow
The only things you have to configure is which limetypes it's supposed to use.
Here is an example of a standard setup:
- Set communication automatically as read, when entering communication-feed? => If
True
, helpdesk will automatically be marked as read. - Property where datetime is saved for latest unread communication => datetime field where datetime for latest unread communication is saved. For visualization in table view update the table view as described here.
Info
By default all files from communication flow are copied to document on helpdesk. You must set the optionkey, see below. However you can disable this and communication flow files will only be accessible in communication flow.
- Optionkey for documents created from sys_communicationdocument => The optionkey for documents created from sys_communicationdocument.
- Disable copy document => Set to
True
if you don't want documents from communication flow to be copied to documents.
Views config¶
Info
The limeobjects sys_communication, sys_follower, sys_communicationdocument and sys_communicationfollower are intended to be hidden in your solution, meaning that no views should be configured for these.
Communication Flow¶
To be able to see the communication flow in the web client you need to add it as a custom tab on the relevant limetype i.e. helpdesk
.
Go To: Views
=> Relevant Limetype
Scroll down to Tabs
and add the following:
- Title =>
limepkg_communication_flow.communication.feed.tab.title
- Is translation key =>
True
- Icon =>
message
- Color =>
<empty>
(Nothing) - Web component - Name =>
lwc-limepkg-communication-flow-root
- Web component - Properties =>
{}
Mark as read/unread communication¶
To add a button on helpdesk where the user can mark a communication as read or unread.
Go To: Views
=> Relevant Limetype
=> Card
Scroll down to Web components
and add the following:
- Name =>
lwc-limepkg-communication-flow-unread-communication-card-view
- Properties =>
{}
Visualize unread communication¶
Unread communication is by default a datetime field and to visualize unread/new communications in the table view we need to add a web-component in the table view.
Go To: Views
=> Helpdesk limetype
=> Table
Find the datetime field used to save unread communications and add the following to Component:
- Name =>
lwc-limepkg-communication-flow-unread-communication-table-view
- Properties =>
{}
Communication flow template¶
Go To: Views
=> Communication flow template
Info
Please note the web-component, lwc-limepkg-communication-flow-template-config, on field template. Template is visualized and editable through an editor to support formatting. Data is saved as html.
Paste the following json.
{
"general": {
"primaryTitle": [
{
"property": "name"
}
],
"icon": "template",
"color": "rgb(var(--color-blue-default))",
"create": true,
"inlineCreate": false,
"bulkDeleteObjects": true,
"views": [
{
"view": {
"props": {},
"name": "limec-table-view"
},
"title": "Table",
"icon": "insert_table"
}
],
"globalTablesMenu": true
},
"card": {
"sections": [
{
"collapsed": true,
"layout": {
"columns": 5,
"dense": true
},
"controls": [
{
"layout": {
"colSpan": 4
},
"component": {
"props": {}
},
"property": "name",
"required": true
},
{
"layout": {},
"component": {
"props": {}
},
"property": "inactive"
}
],
"title": "untitled"
},
{
"collapsed": false,
"layout": {
"columns": 5,
"dense": true
},
"controls": [
{
"layout": {
"colSpan": 5
},
"component": {
"props": {},
"name": "lwc-limepkg-communication-flow-template-config"
},
"property": "template"
}
],
"title": "Template"
}
]
},
"list": {
"header": [
{
"property": "name"
}
]
},
"search": {
"header": [
{
"property": "name"
}
]
},
"table": {
"columns": [
{
"isDefault": true,
"component": {
"props": {}
},
"property": "name"
},
{
"isDefault": true,
"component": {
"props": {}
},
"property": "inactive"
}
],
"actions": []
}
}
Text Template Configuration¶
Go To: Add-ons
=> Communication flow
=> Text Template Configuration
- Use text templates => Set to
True
to enable text templates in Communication flow editor.
Info
Even if Use text templates is True at least one template must be active in order for the template picker to be visible in Communication flow editor.
If you would like to group the templates add an Option
or Relation (field)
field on limetype sys_communicationtemplate
. Don't forget to add it to the webclient card view as well. For option field text will be used as group name and for relation field descriptive will be used as group name. Name and group name will be searchable when using text templates in the composer.
- Group by => Select the field you have created in sys_communicationtemplate.
Setting policies on templates¶
It is possible to use policies on the text templates, both on table and field. So you can easily set the appropriate policies on both the field and editor.
Create filter to highlight tickets with undelivered email¶
Sometimes outgoing email are not delivered due to different reasons, where a bounce is the most common culprit. To help users understand which tickets have messages that have bounced create the following filter on the Helpdesk table by opening the advanced filter editor and copy paste the JSON below. Save the filter as Tickets with undelivered emails past 7 days
. It is also recommended to create an Infotile connected to this filter.
Tickets with undelivered emails past 7 days¶
{
"op": "AND",
"exp": [
{
"key": "sys_communication.sys_communicationfollower.sys_communication.sys_communicationfollower.traml_delivery_description",
"op": "!",
"exp": ""
},
{
"op": "AND",
"exp": [
{
"key": "sys_communication.sys_communicationfollower.traml_send_date",
"op": ">=",
"exp": "$previous_day(7)"
},
{
"key": "sys_communication.sys_communicationfollower.traml_send_date",
"op": "<=",
"exp": "$today"
}
]
}
]
}
Application Config¶
Froala¶
The editor in Communication flow is using the third-party FROALA WYSIWYG Editor. So you need to add the Lime internal Froala license key to application_config.yaml
(or application config in CAFE).
Locally the application config will have the following structure:
solution-x:
secrets:
froala:
license_key: <froala-license-key>
Froala License Key
froala-license-key
can be found here
Inline image handling¶
Communication flow will try to avoid saving images as documents that are smaller in size (filesize). For example small thumbnails in email signatures. They will still be stored and rendered in the message, just not considered an attachment in other contexts
By default that size limit is set to 25KB, but can be changed in the application config like this:
solution-x:
config:
limepkg_communication_flow:
inline_image_size_limit: 25600 # Size in Bytes (25 * 1024B) => 25KB
If in cloud
Exclude the solution-x part and only add config:...
Configure TRAML for sending the e-mails¶
If you use the standard behaviour functions for MS Inbox. We will store the subscription e-mail on the helpdesk in a hidden field that can be used as "sender" when sending the e-mail. That way you can have multiple inboxes setup and the correct e-mail address will be used for each communication sent.
All sendouts are done in the customer's own solution, but we have some help functions/classes that you can use for setting it up easily. We use TRAML, so you need to setup a Lime Marketing account for the customer for this to be used. This is normally already setup for the Auto-responses in Lime Inbox.
Add webhook subscriptions in Lime Marketing¶
This so that we in the communication flow can see if an email bounced or is sent/delivered without issue.
Go to Integrations>Webhook subscriptions
, and create a new webhook subscription with the following information:
- Name:
Lime CRM Communication flow
- URL:
https://<server>/<limeenvironment>/limepkg-communication-flow/traml/webhook/
- AppId:
<Leave empty>
- Secret:
<Leave empty>
- Send batched webhooks:
OFF
- Headers:
x-api-key: <API-key for the lime crm communication-flow user, that you have to create and generate an API-key for in LISA>
- Subscribe to all webhooks:
Chose specific
-
Turn on webhooks for:
transactionmail.delivered
transactionmail.bounced
transactionmail.sent
transactionmail.not_sent
Warning
Spaces matter! Don't remove the one after
x-api-key:
Custom Limeobject for sys_communication¶
Create a custom limeobject for sys_communication and add the logic in after_update
.
This is just an example and you may change it to whatever you'd like.
There's also some code that should be run for the communication's custom limeobject to automatically update the related helpdesk. To do so you need to add a decorator to your custom limeobject for communication.
Recommended!
Want to create a history note for every incoming and outgoing communication see (Optional) Create a summarizing history note when sending and receiving emails.
import logging
# TODO: Add typing imports
from typing import Optional
# TODO: Add traml and decorator imports
import limepkg_communication_flow.decorators as communication_flow_decorators
import limepkg_communication_flow.traml as comm_traml
import limepkg_transactional_message_library.traml as traml_lib
from lime_type.limeobjects import LimeObject
logger = logging.getLogger(__name__)
@communication_flow_decorators.sys_communication()
class Communication(LimeObject):
_message_meta: Optional[comm_traml.CommunicationMeta] = None
@property
def message_meta(self) -> comm_traml.CommunicationMeta:
if not self._message_meta:
# The second parameter is a default fallback
# if the helpdesk wouldn't contain a subscription_email.
# an example of that would be if a user in
# lime created a helpdesk manually in the client
# TODO: Change Default E-mail to
# the customers default helpdesk inbox
self._message_meta = comm_traml.get_communication_message(
self, "[email protected]"
)
return self._message_meta
def after_update(self, unsaved_self, **kwargs):
super().after_update(unsaved_self, **kwargs)
_send_traml_message(self, unsaved_self)
def _send_traml_message(
communication: Communication,
unsaved_communication: Communication,
):
if not unsaved_communication.is_new:
# Only send e-mail if communication is new
return
elif communication.properties.source.value.key != "lime":
# Only send e-mail if communication is created from communication feed
return
elif communication.properties.system_message.value:
# if auto reply(system_message) we have already sent an email
# with system_message (helpdesk custom limeobject)
return
try:
traml = traml_lib.TramlClient(app=communication.application)
# TODO: Change to whatever the customer wants
from_name = "Lime Helpdesk"
# TODO: Change to the template the customer will use
template = traml.get_mail_template_by_name(
"limepkg-communication-flow-communication-template"
)
except traml_lib.models.TramlError as e:
logger.warning(f"Failed to fetch template due to {e}")
return
message_meta = communication.message_meta
for recipient in message_meta.all_recipients:
try:
message = traml_lib.models.Email(
external_id=recipient.get_external_id(communication.id),
from_name=from_name,
from_email=message_meta.from_email,
recipient_name=recipient.name,
recipient_email=recipient.email,
subject=message_meta.subject,
attachments=[
traml_lib.models.Attachment.from_lime_file(a.file)
for a in message_meta.attachments
],
merge_codes=message_meta.merge_codes,
exclude_totaloptouts=True,
)
if recipient.internet_message_id:
message.set_in_reply_to_headers(recipient.internet_message_id)
traml.send_transactionmail_by_template(
template_id=template.id,
message=message,
)
except traml_lib.models.TramlError as e:
logger.warning(
"Failed to send e-mail for sys_communication "
f"with ID: {communication.id} due to {e}"
)
def register_limeobject_classes(register_class):
register_class("sys_communication", Communication)
The subject
will be set to the helpdesk title and then the helpdesk number
inside square brackets for example: My title of helpdesk [abc123]
There are some default merge_codes
that will be added automatically:
Name | Description |
---|---|
$$helpdesknumber$$ |
The helpdesk number of the related helpdesk |
$$sender.name$$ |
The name of the follower writing the communication message (will fallback to the e-mail if no name is given) |
$$sender.email$$ |
The email of the follower writing the communication message |
$$communication.body$$ |
The body of the communication message that trigger the sendout |
$$answer_above_prefix$$ |
The prefix tag for answer above this line. Imported from ms-inbox |
$$answer_above_suffix$$ |
The suffix tag for answer above this line. Imported from ms-inbox |
(Optional) Add previous communications to the TRAML email¶
It is possible to fetch previous communications and add them as a merge code and send to TRAML.
See below for an example:¶
Warning
This example customization includes ALL earlier communication, meaning that it will always include email history, even new followers in the conversation will be able to read earlier emails from all followers.
If you want to exclude some communications, for example internal ones you have to make your own adjustments.
Info
The code below expects you to have at least version v3.9.0 of Communication flow
If needed it can be modified to work for older versions, but we always recommend to upgrade if possible.
Do the following steps in your sys_communication
custom limeobject file
1. Add these imports
import datetime
from typing import Dict, List, Optional
import babel.dates
import lime_errors
import limepkg_communication_flow.communication_items as communication_items
from lime_type import OptionProperty
2. Add these functions
def _fetch_previous_communications(communication_obj: Communication) -> str:
# TODO: Change (Or empty) to customer name
customer_company_name = "The Customer Name"
# Make sure that the relation property here is called "helpdesk"
app: LimeApplication = communication_obj.application
helpdesk: LimeObject = communication_obj.properties.helpdesk.fetch()
if not helpdesk:
return ""
communication_data = communication_items.fetch_communications(
app=app,
owner_id=helpdesk.id,
owner_limetype=helpdesk.limetype.name,
skip_attachments=True,
skip_recipients=True,
)
grouped_followers: Dict[str, dict] = {
follower["follower_id"]: follower
for follower in communication_data.get("followers") or []
}
communication_bodies: List[str] = []
for communication in communication_data.get("communications") or []:
if communication["_id"] == communication_obj.id:
continue # Skip current communication
sent_date: Optional[datetime.datetime] = communication[
"communication_timestamp"
]
helpdesk_history = communication["body"]
sender_title = (
_format_sender(
app=app,
sender=grouped_followers.get(communication["sys_follower"]),
company_name=customer_company_name,
)
or "-"
)
# Add sender information
communication_content = (
'<p style="'
" font-family: 'Lucida Grande', 'Lucida Sans Unicode',"
" 'Lucida Sans', Verdana, Tahoma, sans-serif;"
" font-size: small;"
" margin-bottom: 0px;"
" margin-top: 0px;"
" padding: 0px;"
" opacity: 0.8;"
'">'
f"{sender_title}"
"</p>"
)
# Add sent date
communication_content += (
'<p style="'
" font-family: 'Lucida Grande', 'Lucida Sans Unicode',"
" 'Lucida Sans', Verdana, Tahoma, sans-serif;"
" font-size: smaller;"
" margin-bottom: 15px;"
" margin-top: 0;"
" padding: 0;"
" opacity: 0.7;"
f'">{_format_datetime(app.language, sent_date) or "-"}</p>'
)
communication_content += f"<div>{helpdesk_history}</div>"
communication_bodies.append(communication_content)
helpdesk_history = '<p style="border-top: 1px dotted rgb(197, 197, 197)"/>'.join(
communication_bodies
)
return (
'<div style="width: 100%; margin: 5px">'
'<p style="border-top: 1px dotted rgb(197, 197, 197)"/>'
f"{helpdesk_history}"
"</div>"
)
def _format_sender(
app: LimeApplication,
sender: dict,
company_name: Optional[str] = None,
) -> Optional[str]:
if not sender:
return
sender_title = ""
sender_subtitle = ""
sender_name = sender.get("name")
sender_email = sender.get("email")
role = sender.get("role")
if role == "autoreply":
# If auto-reply => Use Option text
sender_subtitle = company_name
role_prop: OptionProperty = app.limetypes.sys_follower.properties.role
try:
sender_title = role_prop.get_by_key(role).text
except lime_errors.NotFoundError:
sender_title = role
elif role == "agent":
# If from Agent => Skip e-mail AND Add company name
sender_title = sender_name
sender_subtitle = company_name
else:
# Else add name and email
if sender_name == sender_email:
sender_title = sender_name
else:
sender_title = f"{sender_name} <{sender_email}>"
if not sender_title:
return ""
parts = [f"<strong>{sender_title}</strong>"]
if sender_subtitle:
parts.append(f"<span>({sender_subtitle})</span>")
return " ".join(parts)
def _format_datetime(lang: str, date: Optional[datetime.datetime]) -> str:
if not isinstance(date, datetime.datetime):
return ""
try:
return babel.dates.format_datetime(date, locale=lang)
except Exception:
return babel.dates.format_datetime(date, locale="en")
3. Add the this merge code to your other merge codes before your looping through recipients
merge_codes = {
**message_meta.merge_codes,
"$$helpdesk.history$$": _fetch_previous_communications(communication),
}
# for recipient in ... comes here
4. Pass the new merge codes to the traml mail
In the regular template you probably set the merge_codes in the traml mail like this:
merge_codes=message_meta.merge_codes
Now you should change so that it passes the merge_codes
from step 3 like this:
merge_codes=merge_codes,
5. Go through TODO:s
Make sure to search for TODO:
in the file and follow each instruction.
After your done, remove the comment.
6. Add new merge code to your TRAML template
You have a template in TRAML that you use for the communication TRAML email in Lime Marketing.
Make sure to add the merge code $$helpdesk.history$$
at the end of the template.
Result¶
(Optional but recommended) Create a summarizing history note when sending and receiving emails.¶
Info
Communication Flow and the feed is intended to be the place for all email communications. That means that the history feed on a ticket should not have the messages. Instead, the history notes should be the internal log on the ticket – where a user will manually log an actual comment on the ticket – stating more concise and specific on what has been said, done and promised. If a user wants to dig deeper, they can always see the complete conversation in the Communication Flow tab – but history should not be littered with long emails. At the same time the history feed comes with technical limitations, it can’t display the formatting and potential pictures in the emails.
This custom lime object extends sys_communication
documented above. It will create a history note when receiving or sending an email through the communication flow. It will highlight who sent and to whom, connecting all relations of the helpdesk object to the history object. Please note that this custom lime object uses limepkg-base-solution-helpers
to handle the relation attachments, thus it needs to be added to your solution by running poetry add limepkg-base-solution-helpers
. Also please note that this custom lime object also contains the configuration done at the previous step Custom Limeobject for sys_communication
1. Add these imports
import limepkg_base_solution_helpers.common as base_helpers
2. Add this function
def _create_history(
communication: Communication,
unsaved_communication: Communication,
):
if not unsaved_communication.is_new:
return
message_meta = communication.message_meta
if not message_meta.from_follower:
return
try:
from_limeobject = message_meta.from_follower.follower_limeobject
except lime_errors.NotFoundError:
from_limeobject = None
app: LimeApplication = communication.application
uow = app.unit_of_work()
helpdesk = communication.properties.helpdesk.fetch()
com_source = communication.properties.source.value.key
history: LimeObject = app.limetypes.history()
uow.add(history)
base_helpers.relate_all_relations(
helpdesk,
history,
uow,
exclude_relations=[
"person",
"coworker",
],
)
if com_source == "lime":
recipient_names = ", ".join([r.name for r in message_meta.all_recipients])
note = (
f"**{message_meta.from_follower.name}** "
f"sent an email to **{recipient_names}**"
)
type_key = "sentemail"
if from_limeobject:
uow.add(from_limeobject)
history.properties.coworker.attach(from_limeobject)
person = helpdesk.properties.person.fetch()
person_email = person.properties.email.value if person else None
is_person_recipient = person_email and any(
(
recipient.email == person_email
for recipient in message_meta.customer_recipients
)
)
if is_person_recipient:
uow.add(person)
history.properties.person.attach(person)
elif com_source == "email":
type_key = "receivedemail"
note = f"Received email from **{message_meta.from_follower.name}**"
coworker = helpdesk.properties.coworker.fetch()
if coworker:
uow.add(coworker)
history.properties.coworker.attach(coworker)
if from_limeobject and from_limeobject.limetype.name == "person":
uow.add(from_limeobject)
history.properties.person.attach(from_limeobject)
elif com_source == "autoreply":
recipient_names = ", ".join([r.name for r in message_meta.all_recipients])
note = f"Autoreply sent to **{recipient_names}**"
type_key = "sentemail"
if history.properties.coworker.value:
history.properties.coworker.detach()
history.properties.type.set_by_key(type_key)
history.properties.note.value = note
history.properties.helpdesk.attach(helpdesk)
uow.add(helpdesk)
return uow.commit()
3. Add function call to above function in after_update
Bad practice
Adding a new unit of work in after_update in custom limeobject is considered a bad practice and should be avoided.
This is an old implementation, and will be replaced by the new Email integration
class Communication(LimeObject):
def after_update(self, unsaved_self, **kwargs):
super().after_update(unsaved_self, **kwargs)
_create_history(self, unsaved_self) # TODO: Add this row
_send_traml_message(self, unsaved_self)
Images of the history notes¶
See attached screenshot of it looks on a company and a helpdesk ticket