marketing.automation.record.activity
tree
diff --git a/mixin.py b/mixin.py
index a2c4807f91..9223db9731 100644
--- a/mixin.py
+++ b/mixin.py
@@ -1,6 +1,19 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
+from trytond.model import fields
+
class MarketingAutomationMixin:
__slots__ = ()
+
+ marketing_party = fields.Function(
+ fields.Many2One('party.party', "Marketing Party"),
+ 'get_marketing_party', searcher='search_marketing_party')
+
+ def get_marketing_party(self, name):
+ raise NotImplementedError
+
+ @classmethod
+ def search_marketing_party(cls, name, clause):
+ raise NotImplementedError
diff --git a/party.py b/party.py
index bf16fc5b20..06f4f01101 100644
--- a/party.py
+++ b/party.py
@@ -1,6 +1,7 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
+from trytond.model import ModelSQL, fields
from trytond.pool import PoolMeta
from .mixin import MarketingAutomationMixin
@@ -8,3 +9,40 @@ from .mixin import MarketingAutomationMixin
class Party(MarketingAutomationMixin, metaclass=PoolMeta):
__name__ = 'party.party'
+
+ marketing_scenario_unsubscribed = fields.Many2Many(
+ 'party.party-unsubscribed-marketing.automation.scenario',
+ 'party', 'scenario', "Marketing Automation Scenario Unsubscribed")
+
+ def get_marketing_party(self, name):
+ return self.id
+
+ @classmethod
+ def search_marketing_party(cls, name, clause):
+ operand, operator, value = clause[:3]
+ nested = operand.lstrip(name)
+ if not nested:
+ if operator.endswith('where'):
+ query = cls.search(value, order=[], query=True)
+ if operator.startswith('not'):
+ return [('id', 'not in', query)]
+ else:
+ return [('id', 'in', query)]
+ elif isinstance(value, str):
+ nested = 'rec_name'
+ else:
+ nested = 'id'
+ else:
+ nested = nested.lstrip('.')
+ return [(nested,) + tuple(clause[1:])]
+
+
+class PartyUnsubscribedScenario(ModelSQL):
+ "Party Unsubscribed Scenario"
+ __name__ = 'party.party-unsubscribed-marketing.automation.scenario'
+
+ party = fields.Many2One(
+ 'party.party', "Party", required=True, select=True, ondelete='CASCADE')
+ scenario = fields.Many2One(
+ 'marketing.automation.scenario', "Scenario",
+ required=True, ondelete='CASCADE')
diff --git a/party.xml b/party.xml
new file mode 100644
index 0000000000..e5c6732c93
--- /dev/null
+++ b/party.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+ party.party
+
+ party_form
+
+
+
diff --git a/sale.py b/sale.py
index b18ccdd751..0417943ecf 100644
--- a/sale.py
+++ b/sale.py
@@ -8,3 +8,11 @@ from .mixin import MarketingAutomationMixin
class Sale(MarketingAutomationMixin, metaclass=PoolMeta):
__name__ = 'sale.sale'
+
+ def get_marketing_party(self, name):
+ return self.party.id
+
+ @classmethod
+ def search_marketing_party(cls, name, clause):
+ nested = clause[0].lstrip(name)
+ return [('party' + nested,) + tuple(clause[1:])]
diff --git a/setup.py b/setup.py
index 3c6415ef33..fdf8103260 100755
--- a/setup.py
+++ b/setup.py
@@ -64,8 +64,9 @@ for dep in info.get('depends', []):
requires.append(get_require_version('trytond_%s' % dep))
requires.append(get_require_version('trytond'))
-tests_require = [get_require_version('proteus'),
- get_require_version('trytond_party')]
+tests_require = [
+ get_require_version('proteus'),
+ get_require_version('trytond_sale')]
dependency_links = []
if minor_version % 2:
dependency_links.append(
diff --git a/tests/email.html b/tests/email.html
index dbb04f540a..5a85030b39 100644
--- a/tests/email.html
+++ b/tests/email.html
@@ -4,7 +4,7 @@
Hello!
- Hello, ${record.name}!
+ Hello, ${record.rec_name}!
Here is a
diff --git a/tests/scenario_marketing_automation.rst b/tests/scenario_marketing_automation.rst
index 1f005b3848..442de099a5 100644
--- a/tests/scenario_marketing_automation.rst
+++ b/tests/scenario_marketing_automation.rst
@@ -17,10 +17,12 @@ Patch sendmail_transactional::
>>> from trytond.modules.marketing_automation import marketing_automation
>>> smtp_calls = patch.object(
... marketing_automation, 'sendmail_transactional').start()
+ >>> manager = patch.object(
+ ... marketing_automation, 'SMTPDataManager').start()
Activate modules::
- >>> config = activate_modules(['marketing_automation', 'party'])
+ >>> config = activate_modules('marketing_automation')
Create a party::
diff --git a/tests/scenario_marketing_automation_unsubscribable.rst b/tests/scenario_marketing_automation_unsubscribable.rst
new file mode 100644
index 0000000000..94c66d1346
--- /dev/null
+++ b/tests/scenario_marketing_automation_unsubscribable.rst
@@ -0,0 +1,110 @@
+Marketing Automation Unsubscribable Scenario
+============================================
+
+Imports::
+
+ >>> import datetime
+ >>> import re
+
+ >>> from proteus import Model, Wizard
+ >>> from proteus.config import get_config
+ >>> from trytond.modules.company.tests.tools import create_company
+ >>> from trytond.pyson import Eval, PYSONEncoder
+ >>> from trytond.tests.tools import activate_modules
+ >>> from trytond.tools import file_open
+
+Patch sendmail_transactional::
+
+ >>> from unittest.mock import patch
+ >>> from trytond.modules.marketing_automation import marketing_automation
+ >>> smtp_calls = patch.object(
+ ... marketing_automation, 'sendmail_transactional').start()
+ >>> manager = patch.object(
+ ... marketing_automation, 'SMTPDataManager').start()
+
+Activate modules::
+
+ >>> config = activate_modules(['marketing_automation', 'sale'])
+
+ >>> Activity = Model.get('marketing.automation.activity')
+ >>> Cron = Model.get('ir.cron')
+ >>> Party = Model.get('party.party')
+ >>> Record = Model.get('marketing.automation.record')
+ >>> Sale = Model.get('sale.sale')
+ >>> Scenario = Model.get('marketing.automation.scenario')
+
+ >>> cron_trigger, = Cron.find([
+ ... ('method', '=', 'marketing.automation.scenario|trigger'),
+ ... ])
+ >>> cron_process, = Cron.find([
+ ... ('method', '=', 'marketing.automation.record.activity|process'),
+ ... ])
+
+Create a party::
+
+ >>> party = Party()
+ >>> party.name = "Michael Scott"
+ >>> contact = party.contact_mechanisms.new()
+ >>> contact.type = 'email'
+ >>> contact.value = 'michael@example.com'
+ >>> party.save()
+
+Create company::
+
+ >>> _ = create_company()
+
+Create a sale::
+
+ >>> sale = Sale(party=party)
+ >>> sale.save()
+
+Create the running scenario::
+
+ >>> scenario = Scenario()
+ >>> scenario.name = "Sale Scenario"
+ >>> scenario.model = 'sale.sale'
+ >>> scenario.domain = '[["state", "=", "draft"]]'
+ >>> scenario.unsubscribable = True
+ >>> scenario.save()
+
+ >>> activity = scenario.activities.new()
+ >>> activity.name = "First E-Mail"
+ >>> activity.action = 'send_email'
+ >>> activity.email_title = "Pending Sale"
+ >>> activity.condition = PYSONEncoder().encode(
+ ... Eval('self', {}).get('active'))
+ >>> with file_open('marketing_automation/tests/email.html', mode='r') as fp:
+ ... activity.email_template = fp.read()
+
+ >>> scenario.click('run')
+
+Trigger scenario::
+
+ >>> cron_trigger.click('run_once')
+ >>> cron_process.click('run_once')
+
+ >>> record, = Record.find([])
+ >>> record.record == sale
+ True
+
+Block and unsubscribe::
+
+ >>> record.click('block')
+ >>> bool(record.blocked)
+ True
+ >>> party.reload()
+ >>> party.marketing_scenario_unsubscribed == [scenario]
+ True
+
+Create a new sale::
+
+ >>> sale = Sale(party=party)
+ >>> sale.save()
+
+Trigger scenario::
+
+ >>> cron_trigger.click('run_once')
+ >>> cron_process.click('run_once')
+
+ >>> Record.find([('blocked', '=', False)])
+ []
diff --git a/tests/test_module.py b/tests/test_module.py
index 9a156e3c1f..b0a4905e3f 100644
--- a/tests/test_module.py
+++ b/tests/test_module.py
@@ -7,6 +7,7 @@ from trytond.tests.test_tryton import ModuleTestCase
class MarketingAutomationTestCase(ModuleTestCase):
'Test Marketing Automation module'
module = 'marketing_automation'
+ extras = ['sale']
del ModuleTestCase
diff --git a/tryton.cfg b/tryton.cfg
index 6102eaee8a..6b6b36b9fb 100644
--- a/tryton.cfg
+++ b/tryton.cfg
@@ -2,12 +2,13 @@
version=6.5.0
depends:
ir
- res
marketing
+ party
+ res
web_shortener
extras_depend:
sale
- party
xml:
marketing_automation.xml
+ party.xml
message.xml
diff --git a/view/party_form.xml b/view/party_form.xml
new file mode 100644
index 0000000000..b6f5291469
--- /dev/null
+++ b/view/party_form.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
diff --git a/view/record_form.xml b/view/record_form.xml
index 47c68f04e4..8160508372 100644
--- a/view/record_form.xml
+++ b/view/record_form.xml
@@ -6,6 +6,8 @@ this repository contains the full copyright notices and license terms. -->
+
+
diff --git a/view/record_list.xml b/view/record_list.xml
index ccd4dd7b00..18a7767be0 100644
--- a/view/record_list.xml
+++ b/view/record_list.xml
@@ -5,4 +5,5 @@ this repository contains the full copyright notices and license terms. -->
+
diff --git a/view/scenario_form.xml b/view/scenario_form.xml
index 64e56e76ab..5076f5d36b 100644
--- a/view/scenario_form.xml
+++ b/view/scenario_form.xml
@@ -7,8 +7,11 @@ this repository contains the full copyright notices and license terms. -->
+
+
+
-
+
diff --git a/view/scenario_list.xml b/view/scenario_list.xml
index d6f01cfba9..de416da10f 100644
--- a/view/scenario_list.xml
+++ b/view/scenario_list.xml
@@ -4,6 +4,7 @@ this repository contains the full copyright notices and license terms. -->
+