New changes (#218)
* added models contacts, contactlists, campaigns for marketing module & completed crud operations for email templates model in marketing, added contact list crud operations, sample csv file added for reference, updated actions icons in all list pages, fixed issue of getting closed leads in accounts create/edit pages * completed crud operations to use email templates model, displaying contact list contacts and faile/invalid contacts, completed changes for new campaign, removed firstname and lastname as required and added title as required in leads create page, completed changes for Campaign email send with attachment
This commit is contained in:
parent
88ae667245
commit
e2772eca8a
30 changed files with 1821 additions and 1157 deletions
|
|
@ -1,23 +1,36 @@
|
|||
from django.test import TestCase
|
||||
from cases.models import Case
|
||||
from accounts.models import Account
|
||||
from common.models import User, Comment, Attachments
|
||||
from accounts.models import Account, Tags
|
||||
from common.models import User, Comment, Attachments, Address
|
||||
from django.urls import reverse
|
||||
from leads.models import Lead
|
||||
from contacts.models import Contact
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
|
||||
|
||||
class AccountCreateTest(object):
|
||||
|
||||
def setUp(self):
|
||||
self.user = User.objects.create(
|
||||
first_name="mike", username='mike', email='u@mp.com', role='ADMIN')
|
||||
self.user.set_password('mike2293')
|
||||
self.user.save()
|
||||
|
||||
self.user1 = User.objects.create(
|
||||
first_name="mp",
|
||||
username='mp',
|
||||
email='mp@micropyramid.com',
|
||||
role="USER")
|
||||
self.user1.set_password('mp')
|
||||
self.user1.save()
|
||||
|
||||
self.account = Account.objects.create(
|
||||
name="mike", email="mike@micropyramid.com", phone="8333855552",
|
||||
billing_address_line="", billing_street="KPHB",
|
||||
billing_city="New York",
|
||||
billing_state="usa", billing_postcode="500073",
|
||||
billing_country="IN",
|
||||
website="www.mike.com", created_by=self.user,
|
||||
website="www.mike.com", created_by=self.user, status="open",
|
||||
industry="SOFTWARE", description="Yes.. Testing Done")
|
||||
self.case = Case.objects.create(
|
||||
name="raghu", case_type="Problem",
|
||||
|
|
@ -33,9 +46,40 @@ class AccountCreateTest(object):
|
|||
created_by=self.user, account=self.account
|
||||
)
|
||||
self.client.login(email='u@mp.com', password='mike2293')
|
||||
self.lead = Lead.objects.create(title="LeadCreation",
|
||||
first_name="Alisa",
|
||||
last_name="k",
|
||||
email="Alisak1993@gmail.com",
|
||||
address_line="",
|
||||
street="Arcade enclave colony",
|
||||
city="NewTown",
|
||||
state="California",
|
||||
postcode="5079",
|
||||
country="AD",
|
||||
website="www.gmail.com",
|
||||
status="assigned",
|
||||
source="Call",
|
||||
opportunity_amount="700",
|
||||
description="Iam an Lead",
|
||||
created_by=self.user)
|
||||
self.lead.assigned_to.add(self.user)
|
||||
self.address = Address.objects.create(
|
||||
street="5th phase",
|
||||
city="Orlando",
|
||||
state="Florida",
|
||||
postcode=502279, country="AD")
|
||||
|
||||
self.contact = Contact.objects.create(
|
||||
first_name="contact",
|
||||
email="contact@gmail.com",
|
||||
phone="12345",
|
||||
address=self.address,
|
||||
description="contact",
|
||||
created_by=self.user)
|
||||
|
||||
|
||||
class AccountsCreateTestCase(AccountCreateTest, TestCase):
|
||||
|
||||
def test_account_create_url(self):
|
||||
response = self.client.get('/accounts/create/', {
|
||||
'name': "mike", 'email': "mike@micropyramid.com",
|
||||
|
|
@ -87,12 +131,14 @@ class AccountsListTestCase(AccountCreateTest, TestCase):
|
|||
|
||||
|
||||
class AccountsCountTestCase(AccountCreateTest, TestCase):
|
||||
|
||||
def test_accounts_list_count(self):
|
||||
count = Account.objects.all().count()
|
||||
self.assertEqual(count, 1)
|
||||
|
||||
|
||||
class AccountsViewTestCase(AccountCreateTest, TestCase):
|
||||
|
||||
def test_accounts_view(self):
|
||||
self.accounts = Account.objects.all()
|
||||
response = self.client.get(
|
||||
|
|
@ -102,6 +148,7 @@ class AccountsViewTestCase(AccountCreateTest, TestCase):
|
|||
|
||||
|
||||
class AccountsRemoveTestCase(AccountCreateTest, TestCase):
|
||||
|
||||
def test_accounts_remove(self):
|
||||
response = self.client.get(
|
||||
'/accounts/' + str(self.account.id) + '/delete/')
|
||||
|
|
@ -114,6 +161,7 @@ class AccountsRemoveTestCase(AccountCreateTest, TestCase):
|
|||
|
||||
|
||||
class AccountsUpdateUrlTestCase(AccountCreateTest, TestCase):
|
||||
|
||||
def test_accounts_update(self):
|
||||
response = self.client.get(
|
||||
'/accounts/' + str(self.account.id) + '/edit/', {
|
||||
|
|
@ -187,14 +235,25 @@ class AccountModelTest(AccountCreateTest, TestCase):
|
|||
|
||||
|
||||
class CommentTestCase(AccountCreateTest, TestCase):
|
||||
|
||||
def test_comment_add(self):
|
||||
response = self.client.post(
|
||||
'/accounts/comment/add/', {'accountid': self.account.id})
|
||||
'/accounts/comment/add/', {'accountid': self.account.id,
|
||||
'comment': 'comment'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_comment_create(self):
|
||||
|
||||
response = self.client.post(
|
||||
'/accounts/comment/add/', {'accountid': self.account.id,
|
||||
'comment': 'comment'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_comment_edit(self):
|
||||
self.client.login(email='mp@micropyramid.com', password='mp')
|
||||
response = self.client.post(
|
||||
'/accounts/comment/edit/', {'commentid': self.comment.id})
|
||||
'/accounts/comment/edit/', {'commentid': self.comment.id,
|
||||
'comment': 'comment'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# def test_comment_valid(self):
|
||||
|
|
@ -210,16 +269,27 @@ class CommentTestCase(AccountCreateTest, TestCase):
|
|||
'/accounts/comment/remove/', {'comment_id': self.comment.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_comment_deletion(self):
|
||||
self.client.login(email='mp@micropyramid.com', password='mp')
|
||||
response = self.client.post(
|
||||
'/accounts/comment/remove/', {'comment_id': self.comment.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class AttachmentTestCase(AccountCreateTest, TestCase):
|
||||
|
||||
def test_attachment_add(self):
|
||||
self.client.login(email='mp@micropyramid.com', password='mp')
|
||||
response = self.client.post(
|
||||
'/accounts/attachment/add/', {'accountid': self.account.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_attachment_valid(self):
|
||||
upload_file = open('static/images/user.png', 'rb')
|
||||
response = self.client.post(
|
||||
'/accounts/attachment/add/', {'accountid': self.account.id})
|
||||
'/accounts/attachment/add/', {'accountid': self.account.id,
|
||||
'attachment': SimpleUploadedFile(
|
||||
upload_file.name, upload_file.read())})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_attachment_delete(self):
|
||||
|
|
@ -227,3 +297,52 @@ class AttachmentTestCase(AccountCreateTest, TestCase):
|
|||
'/accounts/attachment/remove/',
|
||||
{'attachment_id': self.attachment.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_attachment_deletion(self):
|
||||
self.client.login(email='mp@micropyramid.com', password='mp')
|
||||
response = self.client.post(
|
||||
'/accounts/attachment/remove/',
|
||||
{'attachment_id': self.attachment.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class TagCreateTest(object):
|
||||
|
||||
def setUp(self):
|
||||
self.tag = Tags.objects.create(
|
||||
name="tag", slug="tag1")
|
||||
|
||||
|
||||
class TagModelTest(TagCreateTest, TestCase):
|
||||
|
||||
def test_string_representation(self):
|
||||
tag = Tags(name="tag", slug="tag1")
|
||||
self.assertEqual(str(self.tag.name), tag.name)
|
||||
|
||||
|
||||
class TestCreateLeadPostView(AccountCreateTest, TestCase):
|
||||
|
||||
def test_create_lead_post_status(self):
|
||||
upload_file = open('static/images/user.png', 'rb')
|
||||
response = self.client.post(reverse(
|
||||
'accounts:new_account'), {"name": "mike",
|
||||
"email": "mike@micropyramid.com",
|
||||
"phone": "+91-833-385-5552",
|
||||
"billing_address_line": "sddsv",
|
||||
"billing_street": "KPHB",
|
||||
"billing_city": "New York",
|
||||
"billing_state": "usa",
|
||||
"billing_postcode": "500073",
|
||||
"billing_country": "IN",
|
||||
"website": "www.mike.com",
|
||||
"created_by": self.user,
|
||||
"status": "open",
|
||||
"industry": "SOFTWARE",
|
||||
"description": "Yes.. Testing Done",
|
||||
"lead": str(self.lead.id),
|
||||
'contacts': str(self.contact.id),
|
||||
'tags': 'tag1',
|
||||
'account_attachment': SimpleUploadedFile(
|
||||
upload_file.name, upload_file.read())
|
||||
})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
|
|
|||
|
|
@ -4,9 +4,12 @@ from contacts.models import Contact
|
|||
from accounts.models import Account
|
||||
from common.models import Address, Comment, Attachments
|
||||
from common.models import User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
|
||||
|
||||
class CaseCreation(object):
|
||||
|
||||
def setUp(self):
|
||||
self.address = Address.objects.create(
|
||||
street="6th phase",
|
||||
|
|
@ -21,6 +24,13 @@ class CaseCreation(object):
|
|||
role="ADMIN")
|
||||
self.user.set_password('robert')
|
||||
self.user.save()
|
||||
self.user1 = User.objects.create(
|
||||
first_name="mp",
|
||||
username='mp',
|
||||
email='mp@micropyramid.com',
|
||||
role="USER")
|
||||
self.user1.set_password('mp')
|
||||
self.user1.save()
|
||||
self.client.login(email='r@mp.com', password='robert')
|
||||
|
||||
self.client.login(email='r@mp.com', password='robert')
|
||||
|
|
@ -56,9 +66,12 @@ class CaseCreation(object):
|
|||
attachment='image.png', case=self.case,
|
||||
created_by=self.user, account=self.account
|
||||
)
|
||||
self.content_type = ContentType.objects.create(
|
||||
app_label="cases1", model="case")
|
||||
|
||||
|
||||
class CaseViewTestCase(CaseCreation, TestCase):
|
||||
|
||||
def test_list_cases(self):
|
||||
self.cases = Case.objects.all()
|
||||
response = self.client.get('/cases/list/')
|
||||
|
|
@ -78,12 +91,16 @@ class CaseViewTestCase(CaseCreation, TestCase):
|
|||
|
||||
|
||||
class CaseCreationUrlTestCase(CaseCreation, TestCase):
|
||||
|
||||
def test_create_cases(self):
|
||||
upload_file = open('static/images/user.png', 'rb')
|
||||
response = self.client.post('/cases/create/', {
|
||||
'name': 'new case', 'case_type': 'Problem',
|
||||
'status': 'New',
|
||||
'account': self.account, 'contacts': [self.contacts.id],
|
||||
'priority': "Low", 'description': "something"
|
||||
'name': 'new case', 'closed_on': '2019-03-14',
|
||||
'status': 'New', 'priority': "Low",
|
||||
'created_by': self.user, "assigned_to": str(self.user.id),
|
||||
'contacts': str(self.contacts.id),
|
||||
'case_attachment': SimpleUploadedFile(
|
||||
upload_file.name, upload_file.read())
|
||||
})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
|
@ -99,8 +116,13 @@ class CaseCreationUrlTestCase(CaseCreation, TestCase):
|
|||
# self.assertTemplateUsed(response, 'create_cases.html')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_create_case_get_request(self):
|
||||
response = self.client.get('/cases/create/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class CaseShowTestCase(CaseCreation, TestCase):
|
||||
|
||||
def test_show_case(self):
|
||||
response = self.client.get('/cases/' + str(self.case.id) + '/view/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
|
@ -116,6 +138,7 @@ class CaseShowTestCase(CaseCreation, TestCase):
|
|||
|
||||
|
||||
class CaseRemoveTestCase(CaseCreation, TestCase):
|
||||
|
||||
def test_case_deletion_show_case(self):
|
||||
response = self.client.get('/cases/' + str(self.case.id) + '/remove/')
|
||||
self.assertEqual(response['location'], '/cases/list/')
|
||||
|
|
@ -128,15 +151,22 @@ class CaseRemoveTestCase(CaseCreation, TestCase):
|
|||
|
||||
|
||||
class CaseUpdateTestCase(CaseCreation, TestCase):
|
||||
|
||||
def test_update_case_view(self):
|
||||
response = self.client.get(
|
||||
'/cases/' + str(self.case.id) + '/edit_case/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_case_update(self):
|
||||
response = self.client.post(
|
||||
'/cases/' + str(self.case.id) + '/edit_case/',
|
||||
{'hiddenval': self.case.id})
|
||||
upload_file = open('static/images/user.png', 'rb')
|
||||
response = self.client.post('/cases/' + str(self.case.id) + '/edit_case/', {
|
||||
'name': 'new case', 'closed_on': '2019-03-14',
|
||||
'status': 'New', 'priority': "Low",
|
||||
'created_by': self.user, "assigned_to": str(self.user.id),
|
||||
'contacts': str(self.contacts.id),
|
||||
'case_attachment': SimpleUploadedFile(
|
||||
upload_file.name, upload_file.read())
|
||||
})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_case_update_html(self):
|
||||
|
|
@ -185,10 +215,17 @@ class CaseFormTestCase(CaseCreation, TestCase):
|
|||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_comment_add(self):
|
||||
self.client.login(email='mp@micropyramid.com', password='mp')
|
||||
response = self.client.post(
|
||||
'/cases/comment/add/', {'caseid': self.case.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_comment_form_valid(self):
|
||||
response = self.client.post(
|
||||
'/cases/comment/add/', {'caseid': self.case.id, 'comment': 'comment'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
def test_comment_edit(self):
|
||||
response = self.client.post(
|
||||
'/cases/comment/edit/', {'commentid': self.comment.id})
|
||||
|
|
@ -201,7 +238,18 @@ class CaseFormTestCase(CaseCreation, TestCase):
|
|||
|
||||
|
||||
class AttachmentTestCase(CaseCreation, TestCase):
|
||||
|
||||
def test_attachment_add(self):
|
||||
upload_file = open('static/images/user.png', 'rb')
|
||||
response = self.client.post(
|
||||
'/cases/attachment/add/', {'caseid': self.case.id,
|
||||
'attachment': SimpleUploadedFile(
|
||||
upload_file.name, upload_file.read())
|
||||
})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_attachment_creation(self):
|
||||
self.client.login(email='mp@micropyramid.com', password='mp')
|
||||
response = self.client.post(
|
||||
'/cases/attachment/add/', {'caseid': self.case.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
|
@ -211,8 +259,15 @@ class AttachmentTestCase(CaseCreation, TestCase):
|
|||
'/cases/attachment/remove/', {'attachment_id': self.attachment.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_attachment_deletion(self):
|
||||
self.client.login(email='mp@micropyramid.com', password='mp')
|
||||
response = self.client.post(
|
||||
'/cases/attachment/remove/', {'attachment_id': self.attachment.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class SelectViewTestCase(CaseCreation, TestCase):
|
||||
|
||||
def test_select_contact(self):
|
||||
response = self.client.get('/cases/select_contacts/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
|
|
|||
114
common/tests.py
114
common/tests.py
|
|
@ -1,11 +1,13 @@
|
|||
from django.test import TestCase
|
||||
from django.test import Client
|
||||
|
||||
from django.urls import reverse
|
||||
from common.models import User, Document
|
||||
from common.forms import *
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
|
||||
|
||||
class ObjectsCreation(object):
|
||||
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
# self.user = User.objects.create(first_name="admin",
|
||||
|
|
@ -33,13 +35,19 @@ class ObjectsCreation(object):
|
|||
self.user.set_password('paul123')
|
||||
self.user.save()
|
||||
# self.user
|
||||
user_login = self.client.login(
|
||||
self.client.login(
|
||||
username='admin@micropyramid.com', password='admin123')
|
||||
self.document = Document.objects.create(
|
||||
title="abc", document_file="1.png", created_by=self.user)
|
||||
|
||||
self.comment = Comment.objects.create(
|
||||
comment='testikd', user=self.user,
|
||||
commented_by=self.user
|
||||
)
|
||||
|
||||
|
||||
class TestHomePage(ObjectsCreation, TestCase):
|
||||
|
||||
def test_home_page(self):
|
||||
response = self.client.get('/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
|
@ -80,6 +88,7 @@ class UserCreateTestCase(ObjectsCreation, TestCase):
|
|||
|
||||
|
||||
class PasswordChangeTestCase(ObjectsCreation, TestCase):
|
||||
|
||||
def test_password_change(self):
|
||||
self.client.login(username="admin@micropyramid.com",
|
||||
password="admin123")
|
||||
|
|
@ -98,6 +107,14 @@ class PasswordChangeTestCase(ObjectsCreation, TestCase):
|
|||
response = self.client.post(url, data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def change_passsword_by_admin(self):
|
||||
print(self.user, "madhu", self.user.role)
|
||||
response = self.client.post(reverse('common:change_passsword_by_admin'), {
|
||||
'useer_id': self.user.id,
|
||||
'new_passwoord': "admin123"
|
||||
})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
|
||||
class ForgotPasswordViewTestCase(ObjectsCreation, TestCase):
|
||||
|
||||
|
|
@ -109,7 +126,8 @@ class ForgotPasswordViewTestCase(ObjectsCreation, TestCase):
|
|||
|
||||
class LoginViewTestCase(ObjectsCreation, TestCase):
|
||||
# user2 = User.objects.create(first_name="paul", username='paul', email='paul@micropyramid.com',
|
||||
# is_staff=True, is_admin=True, is_superuser=True, is_active=False)
|
||||
# is_staff=True, is_admin=True, is_superuser=True, is_active=False)
|
||||
|
||||
def test_login_post(self):
|
||||
self.client.logout()
|
||||
data = {"email": "admin@micropyramid.com", "password": "admin123"}
|
||||
|
|
@ -147,6 +165,7 @@ class LoginViewTestCase(ObjectsCreation, TestCase):
|
|||
|
||||
|
||||
class UserTestCase(ObjectsCreation, TestCase):
|
||||
|
||||
def test_user_create_url(self):
|
||||
response = self.client.get('/users/create/', {
|
||||
'first_name': 'micheal',
|
||||
|
|
@ -188,6 +207,7 @@ class UserListTestCase(ObjectsCreation, TestCase):
|
|||
|
||||
|
||||
class UserRemoveTestCase(ObjectsCreation, TestCase):
|
||||
|
||||
def test_users_remove(self):
|
||||
response = self.client.get('/users/' + str(self.user.id) + '/delete/')
|
||||
self.assertEqual(response['location'], '/users/list/')
|
||||
|
|
@ -199,6 +219,7 @@ class UserRemoveTestCase(ObjectsCreation, TestCase):
|
|||
|
||||
|
||||
class UserUpdateTestCase(ObjectsCreation, TestCase):
|
||||
|
||||
def test_users_update(self):
|
||||
response = self.client.get('/users/' + str(self.user.id) + '/edit/', {
|
||||
'first_name': "admin",
|
||||
|
|
@ -239,6 +260,7 @@ class UserUpdateTestCase(ObjectsCreation, TestCase):
|
|||
|
||||
|
||||
class ProfileViewTestCase(ObjectsCreation, TestCase):
|
||||
|
||||
def test_profile_view(self):
|
||||
url = "/profile/"
|
||||
response = self.client.get(url)
|
||||
|
|
@ -254,6 +276,7 @@ class ProfileViewTestCase(ObjectsCreation, TestCase):
|
|||
|
||||
|
||||
class UserDetailView(ObjectsCreation, TestCase):
|
||||
|
||||
def test_user_detail(self):
|
||||
url = "/users/" + str(self.user.id) + "/view/"
|
||||
response = self.client.get(url)
|
||||
|
|
@ -261,6 +284,7 @@ class UserDetailView(ObjectsCreation, TestCase):
|
|||
|
||||
|
||||
class DocumentDetailView(ObjectsCreation, TestCase):
|
||||
|
||||
def test_document_detail(self):
|
||||
url = "/documents/" + str(self.document.id) + "/view/"
|
||||
repsonse = self.client.get(url)
|
||||
|
|
@ -271,12 +295,8 @@ class DocumentDetailView(ObjectsCreation, TestCase):
|
|||
|
||||
|
||||
class CreateCommentFile(TestCase):
|
||||
|
||||
def test_invalid_user_form(self):
|
||||
fields = ['email',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'username', 'role',
|
||||
'profile_pic']
|
||||
user1 = User.objects.create(username='robert',
|
||||
first_name='robert',
|
||||
last_name='clark',
|
||||
|
|
@ -295,3 +315,81 @@ class CreateCommentFile(TestCase):
|
|||
self.assertEqual(len(userr.password), 3)
|
||||
self.assertFalse(form.is_valid())
|
||||
# self.assertTrue(form.)
|
||||
|
||||
|
||||
class CommentTestCase(ObjectsCreation, TestCase):
|
||||
|
||||
def test_comment_add(self):
|
||||
response = self.client.post(
|
||||
'/comment/add/', {'userid': self.user.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_comment_edit(self):
|
||||
response = self.client.post(reverse('common:edit_comment',
|
||||
kwargs={'pk': self.comment.id}))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
resp = self.client.post(reverse('common:edit_comment',
|
||||
kwargs={'pk': self.comment.id}), {'comment': 'hello123'})
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
def test_comment_delete(self):
|
||||
response = self.client.post(
|
||||
'/comment/remove/', {'comment_id': self.comment.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_form_valid(self):
|
||||
response = self.client.post(
|
||||
'/comment/add/', {'userid': self.user.id,
|
||||
'comment': 'hello'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class DocumentTestCase(ObjectsCreation, TestCase):
|
||||
|
||||
def test_document_add(self):
|
||||
response = self.client.post(
|
||||
'/documents/create/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_document_create(self):
|
||||
response = self.client.post(
|
||||
'/documents/create/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_document_edit(self):
|
||||
upload_file = open('static/images/user.png', 'rb')
|
||||
data = {'title': "doc",
|
||||
'created_by': self.user,
|
||||
'document_file': SimpleUploadedFile(
|
||||
upload_file.name, upload_file.read()),
|
||||
'status': 'active',
|
||||
'shared_to': str(self.user.id)}
|
||||
response = self.client.get(reverse('common:edit_doc',
|
||||
kwargs={'pk': self.document.id}), data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_document_valid(self):
|
||||
upload_file = open('static/images/user.png', 'rb')
|
||||
response = self.client.post(
|
||||
'/documents/create/', {'title': "doc",
|
||||
'created_by': self.user,
|
||||
'document_file': SimpleUploadedFile(
|
||||
upload_file.name, upload_file.read()),
|
||||
'status': 'active',
|
||||
'shared_to': str(self.user.id)})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_document_delete(self):
|
||||
response = self.client.get(reverse('common:remove_doc',
|
||||
kwargs={'pk': self.document.id}))
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_document_list_view(self):
|
||||
response = self.client.get(reverse('common:doc_list'))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = self.client.post(reverse('common:doc_list'), {
|
||||
'doc_name': 'doc',
|
||||
'status': 'active',
|
||||
"shared_to": str(self.user.id)
|
||||
})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
|
|
|||
|
|
@ -4,9 +4,13 @@ from common.models import Address, User, Comment, Attachments
|
|||
from cases.models import Case
|
||||
from django.test import Client
|
||||
from django.urls import reverse
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from contacts.forms import ContactAttachmentForm
|
||||
from accounts.models import Account
|
||||
|
||||
|
||||
class ContactObjectsCreation(object):
|
||||
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
self.user = User.objects.create(
|
||||
|
|
@ -50,6 +54,7 @@ class ContactObjectsCreation(object):
|
|||
|
||||
|
||||
class ContactObjectsCreation_Count(ContactObjectsCreation, TestCase):
|
||||
|
||||
def test_contact_object_creation(self):
|
||||
c = Contact.objects.count()
|
||||
con = Contact.objects.filter(id=self.contact.id)
|
||||
|
|
@ -77,6 +82,7 @@ class ContactViewsTestCase(ContactObjectsCreation, TestCase):
|
|||
self.assertTemplateUsed(response, 'contacts.html')
|
||||
|
||||
def test_contacts_create(self):
|
||||
upload_file = open('static/images/user.png', 'rb')
|
||||
response = self.client.post('/contacts/create/', {
|
||||
'first_name': 'contact',
|
||||
'last_name': 'george',
|
||||
|
|
@ -84,7 +90,45 @@ class ContactViewsTestCase(ContactObjectsCreation, TestCase):
|
|||
'phone': '+917898901234',
|
||||
'address': self.address.id,
|
||||
'description': 'contact',
|
||||
'created_by': self.user})
|
||||
'created_by': self.user,
|
||||
'assigned_to': str(self.user.id),
|
||||
'contact_attachment': SimpleUploadedFile(
|
||||
upload_file.name, upload_file.read())
|
||||
})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_contact_create(self):
|
||||
upload_file = open('static/images/user.png', 'rb')
|
||||
response = self.client.post('/contacts/create/', {
|
||||
'first_name': 'contact',
|
||||
'last_name': 'george',
|
||||
'email': 'meg@gmail.com',
|
||||
'phone': '+917898901234',
|
||||
'address': self.address.id,
|
||||
'description': 'contact',
|
||||
'created_by': self.user,
|
||||
'assigned_to': str(self.user.id),
|
||||
'contact_attachment': SimpleUploadedFile(
|
||||
upload_file.name, upload_file.read())
|
||||
})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_update_contact(self):
|
||||
upload_file = open('static/images/user.png', 'rb')
|
||||
url = '/contacts/' + str(self.contact.id) + '/edit/'
|
||||
data = {
|
||||
'first_name': 'contact',
|
||||
'last_name': 'george',
|
||||
'email': 'meg@gmail.com',
|
||||
'phone': '+917898901234',
|
||||
'address': self.address.id,
|
||||
'description': 'contact',
|
||||
'created_by': self.user,
|
||||
'assigned_to': str(self.user.id),
|
||||
'contact_attachment': SimpleUploadedFile(
|
||||
upload_file.name, upload_file.read())
|
||||
}
|
||||
response = self.client.post(url, data)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_contacts_create_html(self):
|
||||
|
|
@ -166,6 +210,7 @@ class ContactsListTestCase(ContactObjectsCreation, TestCase):
|
|||
|
||||
|
||||
class CommentTestCase(ContactObjectsCreation, TestCase):
|
||||
|
||||
def test_comment_add(self):
|
||||
response = self.client.post(
|
||||
'/contacts/comment/add/', {'contactid': self.contact.id})
|
||||
|
|
@ -199,14 +244,18 @@ class CommentTestCase(ContactObjectsCreation, TestCase):
|
|||
|
||||
|
||||
class AttachmentTestCase(ContactObjectsCreation, TestCase):
|
||||
|
||||
def test_attachment_add(self):
|
||||
response = self.client.post(
|
||||
'/contacts/attachment/add/', {'contactid': self.contact.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_attachment_valid(self):
|
||||
upload_file = open('static/images/user.png', 'rb')
|
||||
response = self.client.post(
|
||||
'/contacts/attachment/add/', {'contactid': self.contact.id})
|
||||
'/contacts/attachment/add/', {'contactid': self.contact.id,
|
||||
'attachment': SimpleUploadedFile(
|
||||
upload_file.name, upload_file.read())})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_attachment_delete(self):
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
from celery.schedules import crontab
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
|
@ -107,7 +108,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'Asia/Calcutta'
|
||||
TIME_ZONE = 'Asia/Kolkata'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
|
|
@ -219,6 +220,21 @@ DEFAULT_FROM_EMAIL = 'no-reply@django-crm.micropyramid.com'
|
|||
CELERY_BROKER_URL = 'redis://localhost:6379'
|
||||
CELERY_RESULT_BACKEND = 'redis://localhost:6379'
|
||||
|
||||
CELERY_BEAT_SCHEDULE = {
|
||||
"runs-campaign-for-every-thiry-minutes": {
|
||||
"task": "marketing.tasks.run_all_campaigns",
|
||||
"schedule": crontab(minute=30, hour='*')
|
||||
},
|
||||
"runs-campaign-for-every-five-minutes": {
|
||||
"task": "marketing.tasks.list_all_bounces_unsubscribes",
|
||||
"schedule": crontab(minute='*/5')
|
||||
},
|
||||
"runs-scheduled-campaigns-for-every-one-hour": {
|
||||
"task": "marketing.tasks.send_scheduled_campaigns",
|
||||
"schedule": crontab(hour='*/1')
|
||||
},
|
||||
}
|
||||
|
||||
MAIL_SENDER = 'AMAZON'
|
||||
INACTIVE_MAIL_SENDER = 'MANDRILL'
|
||||
|
||||
|
|
@ -236,6 +252,8 @@ MANDRILL_API_KEY = os.getenv('MANDRILL_API_KEY', '')
|
|||
|
||||
ADMIN_EMAIL = "admin@micropyramid.com"
|
||||
|
||||
URL_FOR_LINKS = "http://djangocrm.com"
|
||||
|
||||
try:
|
||||
from .dev_settings import *
|
||||
except ImportError:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from django.contrib.auth import views
|
||||
from django.urls import include, path
|
||||
from common.views import handler404, handler500
|
||||
|
|
@ -20,5 +22,9 @@ urlpatterns = [
|
|||
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
urlpatterns = urlpatterns + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
|
||||
handler404 = handler404
|
||||
handler500 = handler500
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ class LeadForm(forms.ModelForm):
|
|||
self.fields['email'].required = True
|
||||
self.fields['assigned_to'].queryset = assigned_users
|
||||
self.fields['assigned_to'].required = False
|
||||
self.fields['first_name'].required = False
|
||||
self.fields['last_name'].required = False
|
||||
self.fields['title'].required = True
|
||||
for key, value in self.fields.items():
|
||||
if key == 'phone':
|
||||
value.widget.attrs['placeholder'] =\
|
||||
|
|
|
|||
29
leads/migrations/0008_auto_20190315_1503.py
Normal file
29
leads/migrations/0008_auto_20190315_1503.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Generated by Django 2.1.7 on 2019-03-15 09:33
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('leads', '0007_auto_20190306_1226'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='lead',
|
||||
name='first_name',
|
||||
field=models.CharField(max_length=255, null=True, verbose_name='First name'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='lead',
|
||||
name='last_name',
|
||||
field=models.CharField(max_length=255, null=True, verbose_name='Last name'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='lead',
|
||||
name='title',
|
||||
field=models.CharField(default='title', max_length=64, verbose_name='Title'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
|
|
@ -13,9 +13,9 @@ from contacts.models import Contact
|
|||
class Lead(models.Model):
|
||||
title = models.CharField(
|
||||
pgettext_lazy("Treatment Pronouns for the customer",
|
||||
"Title"), max_length=64, blank=True, null=True)
|
||||
first_name = models.CharField(("First name"), max_length=255)
|
||||
last_name = models.CharField(("Last name"), max_length=255)
|
||||
"Title"), max_length=64)
|
||||
first_name = models.CharField(("First name"), null=True, max_length=255)
|
||||
last_name = models.CharField(("Last name"), null=True, max_length=255)
|
||||
email = models.EmailField(null=True, blank=True)
|
||||
phone = PhoneNumberField(null=True, blank=True)
|
||||
status = models.CharField(
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@
|
|||
{% if open_leads|length > 0 %}
|
||||
<tr>
|
||||
<th width="5%">ID</th>
|
||||
<th width="10%">Name</th>
|
||||
<th width="10%">Title</th>
|
||||
<th width="8%">Created By</th>
|
||||
<th width="10%">Source</th>
|
||||
<th width="10%">Status</th>
|
||||
|
|
@ -133,7 +133,8 @@
|
|||
{% for lead in open_leads %}
|
||||
<tr class="text-center">
|
||||
<td scope="row">{{ forloop.counter }}</td>
|
||||
<td><a href="{% url 'leads:view_lead' lead.id %}">{{ lead.first_name }} {{ lead.last_name }}</a></td>
|
||||
<td><a href="{% url 'leads:view_lead' lead.id %}">
|
||||
{{ lead.title }} </a></td>
|
||||
<td>
|
||||
{% if lead.created_by.profile_pic %}
|
||||
{% thumbnail lead.created_by.profile_pic "40x40" crop="center" as im %}
|
||||
|
|
@ -207,7 +208,7 @@
|
|||
{% if close_leads|length > 0 %}
|
||||
<tr>
|
||||
<th width="5%">ID</th>
|
||||
<th width="10%">Name</th>
|
||||
<th width="10%">Title</th>
|
||||
<th width="10%">Source</th>
|
||||
<th width="10%">Status</th>
|
||||
<th width="20%">Assigned User</th>
|
||||
|
|
@ -226,7 +227,8 @@
|
|||
{% for lead in close_leads %}
|
||||
<tr class="text-center">
|
||||
<td scope="row">{{ forloop.counter }}</td>
|
||||
<td><a href="{% url 'leads:view_lead' lead.id %}">{{ lead.first_name }} {{ lead.last_name }}</a></td>
|
||||
<td><a href="{% url 'leads:view_lead' lead.id %}">
|
||||
{{ lead.title }}</a></td>
|
||||
<td>{{ lead.source }}</td>
|
||||
<td>{{ lead.get_status_display }}</td>
|
||||
<td>
|
||||
|
|
|
|||
|
|
@ -37,11 +37,12 @@
|
|||
<div class="filter_col col-md-12" id="iname">
|
||||
<div class="form-group" >
|
||||
<label class="lead_field_label" for="id_name" data-name="name">Name</label>
|
||||
<div class="lead_field" id="lead_name" data-name="name">{{ lead_record.first_name }} {{ lead_record.last_name }}</div>
|
||||
<div class="lead_field" id="lead_name" data-name="name"> {% if lead_record.first_name %}
|
||||
{{ lead_record.first_name }}{% endif %} {% if lead_record.last_name %}
|
||||
{{ lead_record.last_name }}{% endif %}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter_col col-md-12">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="lead_field_label" for="id_contact_account" data-name="name">Account Name</label>
|
||||
{% if lead_record.account_name %}
|
||||
|
|
|
|||
152
leads/tests.py
152
leads/tests.py
|
|
@ -1,13 +1,16 @@
|
|||
from django.test import TestCase, Client
|
||||
from cases.models import Case
|
||||
from leads.models import Lead
|
||||
from common.models import User, Comment, Attachments
|
||||
from common.models import User, Comment, Attachments, APISettings
|
||||
from accounts.models import Account
|
||||
from leads.tasks import *
|
||||
from leads.forms import *
|
||||
from django.urls import reverse
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
|
||||
|
||||
class TestLeadModel(object):
|
||||
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
|
||||
|
|
@ -16,6 +19,14 @@ class TestLeadModel(object):
|
|||
self.user.set_password('jorge2293')
|
||||
self.user.save()
|
||||
|
||||
self.user1 = User.objects.create(
|
||||
first_name="mp",
|
||||
username='mp',
|
||||
email='mp@micropyramid.com',
|
||||
role="USER")
|
||||
self.user1.set_password('mp')
|
||||
self.user1.save()
|
||||
|
||||
self.client.login(username='j@mp.com', password='jorge2293')
|
||||
|
||||
self.account = Account.objects.create(name="account",
|
||||
|
|
@ -36,7 +47,7 @@ class TestLeadModel(object):
|
|||
first_name="Alisa",
|
||||
last_name="k",
|
||||
email="Alisak1993@gmail.com",
|
||||
address_line="",
|
||||
address_line="hyd",
|
||||
street="Arcade enclave colony",
|
||||
city="NewTown",
|
||||
state="California",
|
||||
|
|
@ -47,7 +58,10 @@ class TestLeadModel(object):
|
|||
source="Call",
|
||||
opportunity_amount="700",
|
||||
description="Iam an Lead",
|
||||
created_by=self.user)
|
||||
created_by=self.user,
|
||||
account_name="account",
|
||||
phone="+91-123-456-7890")
|
||||
self.lead.assigned_to.add(self.user)
|
||||
self.case = Case.objects.create(
|
||||
name="Rose", case_type="Problem",
|
||||
status="New",
|
||||
|
|
@ -60,9 +74,12 @@ class TestLeadModel(object):
|
|||
attachment='image.png',
|
||||
case=self.case, created_by=self.user,
|
||||
account=self.account)
|
||||
self.api_seetings = APISettings.objects.create(
|
||||
title="api", apikey="api", created_by=self.user)
|
||||
|
||||
|
||||
class LeadsPostrequestTestCase(TestLeadModel, TestCase):
|
||||
|
||||
def test_valid_postrequesttestcase_date(self):
|
||||
data = {'title': 'LeadCreation', 'first_name': "micky",
|
||||
'last_name': "Alisa", 'email': "Alisamicky1993@gmail.com",
|
||||
|
|
@ -95,6 +112,7 @@ class LeadsPostrequestTestCase(TestLeadModel, TestCase):
|
|||
|
||||
|
||||
class LeadsCreateUrlTestCase(TestLeadModel, TestCase):
|
||||
|
||||
def test_leads_create_url(self):
|
||||
response = self.client.post('/leads/create/', {
|
||||
'title': 'LeadCreation',
|
||||
|
|
@ -128,8 +146,21 @@ class LeadsCreateUrlTestCase(TestLeadModel, TestCase):
|
|||
# self.assertTemplateUsed(response, 'create_lead.html')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_leads_create_with_status_converted(self):
|
||||
response = self.client.post('/leads/create/', {
|
||||
'title': 'LeadCreation', 'name': "micky",
|
||||
'email': "Alisamicky1993@gmail.com", 'account': self.account,
|
||||
'address_line': "", 'account_name': 'account1',
|
||||
'street': "Arcade enclave colony", 'city': "NewTown",
|
||||
'state': "California", 'postcode': "579", 'country': "AD",
|
||||
'website': "www.gmail.com", 'status': "converted",
|
||||
"source": "Call", 'opportunity_amount': "700",
|
||||
'description': "Iam an Lead", 'created_by': self.user})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class LeadsEditUrlTestCase(TestLeadModel, TestCase):
|
||||
|
||||
def test_leads_editurl(self):
|
||||
response = self.client.get('/leads/' + str(self.lead.id) + '/edit/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
|
@ -177,6 +208,7 @@ class LeadListTestCase(TestLeadModel, TestCase):
|
|||
|
||||
|
||||
class GetLeadsViewTestCase(TestLeadModel, TestCase):
|
||||
|
||||
def test_get_lead(self):
|
||||
url = '/leads/get/list/'
|
||||
response = self.client.get(url)
|
||||
|
|
@ -215,6 +247,7 @@ class UpdateLeadTestCase(TestLeadModel, TestCase):
|
|||
|
||||
|
||||
class LeadDetailTestCase(TestLeadModel, TestCase):
|
||||
|
||||
def test_lead_detail(self):
|
||||
url = '/leads/' + str(self.lead.id) + '/view/'
|
||||
response = self.client.get(url, {'status': ''})
|
||||
|
|
@ -222,12 +255,25 @@ class LeadDetailTestCase(TestLeadModel, TestCase):
|
|||
|
||||
|
||||
class CommentTestCase(TestLeadModel, TestCase):
|
||||
|
||||
def test_comment_add(self):
|
||||
self.client.login(email='mp@micropyramid.com', password='mp')
|
||||
response = self.client.post(
|
||||
'/leads/comment/add/', {'leadid': self.lead.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_comment_create(self):
|
||||
response = self.client.post(
|
||||
'/leads/comment/add/', {'leadid': self.lead.id, 'comment': "comment"})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_comment_edit(self):
|
||||
response = self.client.post(
|
||||
'/leads/comment/edit/', {'commentid': self.comment.id, 'comment': "comment"})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_comment_update(self):
|
||||
self.client.login(email='mp@micropyramid.com', password='mp')
|
||||
response = self.client.post(
|
||||
'/leads/comment/edit/', {'commentid': self.comment.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
|
@ -237,9 +283,17 @@ class CommentTestCase(TestLeadModel, TestCase):
|
|||
'/leads/comment/remove/', {'comment_id': self.comment.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_comment_deletion(self):
|
||||
self.client.login(email='mp@micropyramid.com', password='mp')
|
||||
response = self.client.post(
|
||||
'/leads/comment/remove/', {'comment_id': self.comment.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class AttachmentTestCase(TestLeadModel, TestCase):
|
||||
|
||||
def test_attachment_add(self):
|
||||
self.client.login(email='mp@micropyramid.com', password='mp')
|
||||
response = self.client.post(
|
||||
'/leads/attachment/add/', {'leadid': self.lead.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
|
@ -249,8 +303,23 @@ class AttachmentTestCase(TestLeadModel, TestCase):
|
|||
'/leads/attachment/remove/', {'attachment_id': self.attachment.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_attachment_deletion(self):
|
||||
self.client.login(email='mp@micropyramid.com', password='mp')
|
||||
response = self.client.post(
|
||||
'/leads/attachment/remove/', {'attachment_id': self.attachment.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_attachment_valid(self):
|
||||
upload_file = open('static/images/user.png', 'rb')
|
||||
response = self.client.post(
|
||||
'/leads/attachment/add/', {'leadid': self.lead.id,
|
||||
'attachment': SimpleUploadedFile(
|
||||
upload_file.name, upload_file.read())})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class TestTemplates(TestLeadModel, TestCase):
|
||||
|
||||
def test_lead_list_view(self):
|
||||
resp = self.client.post('/leads/list/', {'name': 'trevor',
|
||||
'tag': "123",
|
||||
|
|
@ -266,6 +335,83 @@ class TestTemplates(TestLeadModel, TestCase):
|
|||
|
||||
|
||||
class TestConvertLeadView(TestLeadModel, TestCase):
|
||||
|
||||
def test_get_fun(self):
|
||||
resp = self.client.get('/leads/' + str(self.lead.id) + '/convert/')
|
||||
self.assertEqual(resp.status_code, 302)
|
||||
|
||||
|
||||
class TestCreateLeadPostView(TestLeadModel, TestCase):
|
||||
|
||||
def test_create_lead_post_status(self):
|
||||
upload_file = open('static/images/user.png', 'rb')
|
||||
response = self.client.post(reverse(
|
||||
'leads:add_lead'), {'first_name': 'm',
|
||||
'last_name': 's',
|
||||
"created_by": self.user,
|
||||
'status': 'converted',
|
||||
'description': 'wgrgre',
|
||||
'website': 'www.mike.com',
|
||||
'phone': '+91-123-456-7890',
|
||||
'email': 'a@gmail.com',
|
||||
'account_name': 'account',
|
||||
'address_line': self.lead.address_line,
|
||||
'street': self.lead.street,
|
||||
'city': self.lead.city,
|
||||
'state': self.lead.state,
|
||||
'postcode': self.lead.postcode,
|
||||
'country': self.lead.country,
|
||||
'lead_attachment': SimpleUploadedFile(
|
||||
upload_file.name, upload_file.read()),
|
||||
'assigned_to': str(self.user.id),
|
||||
'tags': 'tag'
|
||||
})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_create_lead_get_request(self):
|
||||
response = self.client.get(reverse('leads:add_lead'))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_update_lead_post_status(self):
|
||||
upload_file = open('static/images/user.png', 'rb')
|
||||
response = self.client.post('/leads/' + str(self.lead.id) + '/edit/',
|
||||
{'first_name': 'm',
|
||||
'last_name': 's',
|
||||
"created_by": self.user,
|
||||
'status': 'converted',
|
||||
'description': 'wgrgre',
|
||||
'website': 'www.mike.com',
|
||||
'phone': '+91-123-456-7890',
|
||||
'email': 'a@gmail.com',
|
||||
'account_name': 'account',
|
||||
'address_line': self.lead.address_line,
|
||||
'street': self.lead.street,
|
||||
'city': self.lead.city,
|
||||
'state': self.lead.state,
|
||||
'postcode': self.lead.postcode,
|
||||
'country': self.lead.country,
|
||||
'lead_attachment': SimpleUploadedFile(
|
||||
upload_file.name, upload_file.read()),
|
||||
'assigned_to': str(self.user.id),
|
||||
'tags': 'tag'
|
||||
})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_lead_convert(self):
|
||||
response = self.client.get('/leads/' + str(self.lead.id) + '/convert/')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
|
||||
class TestLeadDetailView(TestLeadModel, TestCase):
|
||||
|
||||
def test_lead_detail_view(self):
|
||||
response = self.client.get(
|
||||
reverse('leads:view_lead', kwargs={'pk': self.lead.id}))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class TestLeadFromSite(TestLeadModel, TestCase):
|
||||
|
||||
def create_lead_from_site(self):
|
||||
response = self.client.post('/leads/create/from-site/', {'apikey': self.api_seetings.apikey})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
|
|
|||
|
|
@ -10,9 +10,12 @@ from marketing.models import (
|
|||
EmailTemplate, Campaign, Tag
|
||||
)
|
||||
|
||||
email_regex = '^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$'
|
||||
|
||||
|
||||
def csv_doc_validate(document):
|
||||
temp_row = []
|
||||
invalid_row = []
|
||||
reader = csv.reader((document.read().decode("utf-8")).splitlines())
|
||||
# csv_headers = ["first name", "last name", "email"]
|
||||
csv_headers = ["first name", "email"]
|
||||
|
|
@ -20,6 +23,7 @@ def csv_doc_validate(document):
|
|||
required_headers = ["first name", "email"]
|
||||
for y_index, row in enumerate(reader):
|
||||
each = {}
|
||||
invalid_each = {}
|
||||
if y_index == 0:
|
||||
csv_headers = [header_name.lower()
|
||||
for header_name in row if header_name]
|
||||
|
|
@ -40,15 +44,23 @@ def csv_doc_validate(document):
|
|||
continue
|
||||
if csv_headers[x_index] in required_headers:
|
||||
if not cell_value:
|
||||
message = 'Missing required value %s for row %s' % (
|
||||
csv_headers[x_index], y_index + 1)
|
||||
return {"error": True, "message": message}
|
||||
# message = 'Missing required value %s for row %s' % (
|
||||
# csv_headers[x_index], y_index + 1)
|
||||
# return {"error": True, "message": message}
|
||||
invalid_each[csv_headers[x_index]] = cell_value
|
||||
else:
|
||||
if csv_headers[x_index] == "email":
|
||||
if re.match(email_regex, cell_value) is None:
|
||||
invalid_each[csv_headers[x_index]] = cell_value
|
||||
each[csv_headers[x_index]] = cell_value
|
||||
temp_row.append(each)
|
||||
return {"error": False, "validated_rows": temp_row}
|
||||
if invalid_each:
|
||||
invalid_row.append(each)
|
||||
else:
|
||||
temp_row.append(each)
|
||||
return {"error": False, "validated_rows": temp_row, "invalid_rows": invalid_row}
|
||||
|
||||
|
||||
def get_validated_rows(wb, sheet_name, validated_rows):
|
||||
def get_validated_rows(wb, sheet_name, validated_rows, invalid_rows):
|
||||
# headers = ["first name", "last name", "email"]
|
||||
# required_headers = ["first name", "last name", "email"]
|
||||
headers = ["first name", "email"]
|
||||
|
|
@ -68,6 +80,7 @@ def get_validated_rows(wb, sheet_name, validated_rows):
|
|||
continue
|
||||
else:
|
||||
temp_row = []
|
||||
invalid_row = []
|
||||
for x_index, cell in enumerate(row):
|
||||
try:
|
||||
headers[x_index]
|
||||
|
|
@ -75,29 +88,28 @@ def get_validated_rows(wb, sheet_name, validated_rows):
|
|||
continue
|
||||
if headers[x_index] in required_headers:
|
||||
if not cell.value:
|
||||
message = 'Missing required \
|
||||
value %s for row %s in sheet %s'\
|
||||
% (headers[x_index], y_index + 1, sheet_name)
|
||||
return {"error": True, "message": message}
|
||||
# elif x_index == 0:
|
||||
# cell_value = is_valid_date_format(cell.value)
|
||||
# if not cell_value:
|
||||
# message = 'Invalid date format(format:yyyy-mm-dd) \
|
||||
# %s for row %s in sheet %s' % (headers[x_index], y_index + 1, sheet_name)
|
||||
# return {"error": True, "message": message}
|
||||
# message = 'Missing required \
|
||||
# value %s for row %s in sheet %s'\
|
||||
# % (headers[x_index], y_index + 1, sheet_name)
|
||||
# return {"error": True, "message": message}
|
||||
invalid_row.append(headers[x_index])
|
||||
temp_row.append(cell.value)
|
||||
if len(temp_row) >= len(required_headers):
|
||||
validated_rows.append(temp_row)
|
||||
return validated_rows
|
||||
if len(invalid_row) > 0:
|
||||
invalid_rows.append(temp_row)
|
||||
else:
|
||||
if len(temp_row) >= len(required_headers):
|
||||
validated_rows.append(temp_row)
|
||||
return validated_rows, invalid_rows
|
||||
|
||||
|
||||
def xls_doc_validate(document):
|
||||
wb = openpyxl.load_workbook(document)
|
||||
sheets = wb.get_sheet_names()
|
||||
validated_rows = []
|
||||
invalid_rows = []
|
||||
for sheet_name in sheets:
|
||||
get_validated_rows(wb, sheet_name, validated_rows)
|
||||
return {"error": False, "validated_rows": validated_rows}
|
||||
validated_rows, invalid_rows = get_validated_rows(wb, sheet_name, validated_rows, invalid_rows)
|
||||
return {"error": False, "validated_rows": validated_rows, "invalid_rows": invalid_rows}
|
||||
|
||||
|
||||
def import_document_validator(document):
|
||||
|
|
@ -141,7 +153,8 @@ class ContactListForm(forms.ModelForm):
|
|||
if data.get("error"):
|
||||
raise forms.ValidationError(data.get("message"))
|
||||
else:
|
||||
self.validated_rows = data.get("validated_rows")
|
||||
self.validated_rows = data.get("validated_rows", [])
|
||||
self.invalid_rows = data.get("invalid_rows", [])
|
||||
return document
|
||||
|
||||
def clean_tags(self):
|
||||
|
|
@ -265,7 +278,7 @@ class SendCampaignForm(forms.ModelForm):
|
|||
|
||||
class Meta:
|
||||
model = Campaign
|
||||
fields = ['title', 'subject', 'html', 'email_template']
|
||||
fields = ['title', 'subject', 'html', 'email_template', 'attachment']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SendCampaignForm, self).__init__(*args, **kwargs)
|
||||
|
|
@ -278,11 +291,11 @@ class SendCampaignForm(forms.ModelForm):
|
|||
def clean_contact_list(self):
|
||||
contact_list = self.cleaned_data.get("contact_list")
|
||||
if not contact_list or contact_list == '[]' or \
|
||||
json.loads(contact_list) == []:
|
||||
contact_list == []:
|
||||
raise forms.ValidationError(
|
||||
"Please choose any of the Contact List")
|
||||
else:
|
||||
for each in json.loads(contact_list):
|
||||
for each in contact_list:
|
||||
if not ContactList.objects.filter(id=each).first():
|
||||
raise forms.ValidationError(
|
||||
"Please choose a valid Contact List")
|
||||
|
|
|
|||
24
marketing/migrations/0004_auto_20190315_1443.py
Normal file
24
marketing/migrations/0004_auto_20190315_1443.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# Generated by Django 2.1.7 on 2019-03-15 09:13
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('marketing', '0003_failedcontact'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='contact',
|
||||
name='contact_number',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, validators=[django.core.validators.RegexValidator(message="Phone number must be entered in the format: '+999999999'. Up to 20 digits allowed.", regex='^\\+?1?\\d{9,15}$')]),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='failedcontact',
|
||||
name='contact_number',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, validators=[django.core.validators.RegexValidator(message="Phone number must be entered in the format: '+999999999'. Up to 20 digits allowed.", regex='^\\+?1?\\d{9,15}$')]),
|
||||
),
|
||||
]
|
||||
18
marketing/migrations/0005_campaign_timezone.py
Normal file
18
marketing/migrations/0005_campaign_timezone.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.1.7 on 2019-03-18 07:00
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('marketing', '0004_auto_20190315_1443'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='campaign',
|
||||
name='timezone',
|
||||
field=models.CharField(default='UTC', max_length=100),
|
||||
),
|
||||
]
|
||||
19
marketing/migrations/0006_campaign_attachment.py
Normal file
19
marketing/migrations/0006_campaign_attachment.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 2.1.7 on 2019-03-19 10:29
|
||||
|
||||
from django.db import migrations, models
|
||||
import marketing.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('marketing', '0005_campaign_timezone'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='campaign',
|
||||
name='attachment',
|
||||
field=models.FileField(blank=True, max_length=1000, null=True, upload_to=marketing.models.get_campaign_attachment_path),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
import os
|
||||
from datetime import datetime, timedelta
|
||||
from django.db import models
|
||||
from django.utils.timesince import timesince
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.validators import RegexValidator
|
||||
from django.dispatch import receiver
|
||||
from django.db.models import Sum
|
||||
from django.template.defaultfilters import slugify
|
||||
from common.models import User
|
||||
from common.utils import convert_to_custom_timezone
|
||||
|
||||
|
||||
class Tag(models.Model):
|
||||
|
|
@ -150,6 +153,13 @@ class FailedContact(models.Model):
|
|||
return self.email
|
||||
|
||||
|
||||
def get_campaign_attachment_path(self, filename):
|
||||
file_split = filename.split('.')
|
||||
file_extension = file_split[-1]
|
||||
path = "%s_%s" % (file_split[0], str(datetime.now()))
|
||||
return "campaigns/attachment/" + slugify(path) + "." + file_extension
|
||||
|
||||
|
||||
class Campaign(models.Model):
|
||||
STATUS_CHOICES = (
|
||||
('Scheduled', 'Scheduled'),
|
||||
|
|
@ -170,6 +180,7 @@ class Campaign(models.Model):
|
|||
email_template = models.ForeignKey(
|
||||
EmailTemplate, blank=True, null=True, on_delete=models.SET_NULL)
|
||||
schedule_date_time = models.DateTimeField(blank=True, null=True)
|
||||
timezone = models.CharField(max_length=100, default='UTC')
|
||||
reply_to_email = models.EmailField(blank=True, null=True)
|
||||
subject = models.CharField(max_length=5000)
|
||||
html = models.TextField()
|
||||
|
|
@ -182,6 +193,8 @@ class Campaign(models.Model):
|
|||
bounced = models.IntegerField(default='0')
|
||||
status = models.CharField(
|
||||
default="Preparing", choices=STATUS_CHOICES, max_length=20)
|
||||
attachment = models.FileField(
|
||||
max_length=1000, upload_to=get_campaign_attachment_path, blank=True, null=True)
|
||||
|
||||
@property
|
||||
def no_of_unsubscribers(self):
|
||||
|
|
@ -209,6 +222,27 @@ class Campaign(models.Model):
|
|||
def created_on_format(self):
|
||||
return self.created_on.strftime('%b %d, %Y %I:%M %p')
|
||||
|
||||
@property
|
||||
def sent_on_format(self):
|
||||
if self.schedule_date_time:
|
||||
c_schedule_date_time = convert_to_custom_timezone(self.schedule_date_time, self.timezone)
|
||||
return c_schedule_date_time.strftime('%b %d, %Y %I:%M %p')
|
||||
else:
|
||||
c_created_on = convert_to_custom_timezone(self.created_on, self.timezone)
|
||||
return c_created_on.strftime('%b %d, %Y %I:%M %p')
|
||||
|
||||
|
||||
@receiver(models.signals.pre_delete, sender=Campaign)
|
||||
def comment_attachments_delete(sender, instance, **kwargs):
|
||||
attachment = instance.attachment
|
||||
if attachment:
|
||||
try:
|
||||
if os.path.isfile(attachment.path):
|
||||
os.remove(attachment.path)
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Link(models.Model):
|
||||
campaign = models.ForeignKey(
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@ import datetime
|
|||
import hashlib
|
||||
import pytz
|
||||
import requests
|
||||
from mimetypes import MimeTypes
|
||||
from celery.task import task
|
||||
from django.conf import settings
|
||||
from django.core.mail import EmailMessage
|
||||
from django.template import Template, Context
|
||||
from common.utils import convert_to_custom_timezone
|
||||
from marketing.models import Contact, ContactList, Campaign, CampaignLog
|
||||
from marketing.models import Contact, FailedContact, ContactList, Campaign, CampaignLog
|
||||
|
||||
|
||||
@task
|
||||
|
|
@ -26,7 +27,7 @@ def campaign_click(request):
|
|||
|
||||
|
||||
@task
|
||||
def upload_csv_file(data, user, contact_lists):
|
||||
def upload_csv_file(data, invalid_data, user, contact_lists):
|
||||
for each in data:
|
||||
contact = Contact.objects.filter(email=each['email']).first()
|
||||
if not contact:
|
||||
|
|
@ -45,6 +46,24 @@ def upload_csv_file(data, user, contact_lists):
|
|||
for contact_list in contact_lists:
|
||||
contact.contact_list.add(ContactList.objects.get(id=int(contact_list)))
|
||||
|
||||
for each in invalid_data:
|
||||
contact = FailedContact.objects.filter(email=each['email']).first()
|
||||
if not contact:
|
||||
contact = FailedContact.objects.create(
|
||||
email=each['email'], created_by_id=user,
|
||||
name=each['first name'])
|
||||
if each.get('company name', None):
|
||||
contact.company_name = each['company name']
|
||||
if each.get('last name', None):
|
||||
contact.last_name = each['last name']
|
||||
if each.get('city', None):
|
||||
contact.city = each['city']
|
||||
if each.get("state", None):
|
||||
contact.state = each['state']
|
||||
contact.save()
|
||||
for contact_list in contact_lists:
|
||||
contact.contact_list.add(ContactList.objects.get(id=int(contact_list)))
|
||||
|
||||
|
||||
def send_campaign_mail(subject, content, from_email, to_email, bcc, reply_to, attachments):
|
||||
msg = EmailMessage(
|
||||
|
|
@ -76,13 +95,18 @@ def get_campaign_message_id(campaign):
|
|||
def run_campaign(campaign):
|
||||
try:
|
||||
campaign = Campaign.objects.get(id=campaign)
|
||||
attachments = []
|
||||
if campaign.attachment:
|
||||
file_path = campaign.attachment.path
|
||||
file_name = file_path.split("/")[-1]
|
||||
content = open(file_path, 'rb').read()
|
||||
mime = MimeTypes()
|
||||
mime_type = mime.guess_type(file_path)
|
||||
attachments.append((file_name, content, mime_type[0]))
|
||||
subject = campaign.subject
|
||||
# project_settings = Settings.objects.filter().first()
|
||||
|
||||
contacts = []
|
||||
for each_list in campaign.contact_lists.all():
|
||||
contacts = Contact.objects.filter(contact_list__in=[each_list])
|
||||
# contacts = campaign.contacts.filter(Q(is_bounced=False) | Q(is_unsubscribed=False)).distinct()
|
||||
contacts = Contact.objects.filter(
|
||||
contact_list__in=[each_list for each_list in campaign.contact_lists.all()])
|
||||
default_html = campaign.html_processed
|
||||
for each_contact in contacts:
|
||||
html = default_html
|
||||
|
|
@ -94,18 +118,15 @@ def run_campaign(campaign):
|
|||
message_id = get_campaign_message_id(campaign_log)
|
||||
campaign_log.message_id = message_id
|
||||
campaign_log.save()
|
||||
domain_name = settings.SUBDOMAIN_NAME + '.' + project_settings.domain if project_settings else ''
|
||||
# domain_name = 'micro.peeljobs.com'
|
||||
domain_name = 'django-crm.com'
|
||||
if campaign.from_email is not None:
|
||||
from_email = campaign.from_email
|
||||
else:
|
||||
from_email = campaign.created_by.email
|
||||
reply_to_email = str(from_email) + ' <' + str(message_id + '@' + domain_name + '') + '>'
|
||||
if not (each_contact.is_bounced or each_contact.is_unsubscribed):
|
||||
# link = '<img src="https://micropyramid.localtunnel.me/track-email/' + \
|
||||
# str(campaign_log.id) + '/" height="1" width="1"</img>'
|
||||
domain_url = '%s%s' % (settings.SCHEME, settings.HOST_URL)
|
||||
link = '<img src="' + domain_url + '/track-email/' + \
|
||||
domain_url = settings.URL_FOR_LINKS
|
||||
link = '<img src="' + domain_url + '/m/cm/track-email/' + \
|
||||
str(campaign_log.id) + '/contact/' + str(each_contact.id) + '/" height="1" width="1" />'
|
||||
names_dict = {'company_name': each_contact.company_name if each_contact.company_name else '',
|
||||
'last_name': each_contact.last_name if each_contact.last_name else '',
|
||||
|
|
@ -117,13 +138,9 @@ def run_campaign(campaign):
|
|||
html = Template(html).render(Context(names_dict))
|
||||
mail_html = html + link
|
||||
from_email = str(campaign.from_name) + "<" + str(campaign.from_email) + '>'
|
||||
# text_content = re.sub(r'<(.*?)>', '', html)
|
||||
# ReplyMail.objects.create(campaign_log=campaign_log,
|
||||
# reply_from=campaign.created_by.email,
|
||||
# text=text_content,
|
||||
# sent_on=datetime.datetime.now())
|
||||
to_email = [each_contact.email]
|
||||
send_campaign_mail(
|
||||
subject, mail_html, from_email, [each_contact.email], [], [reply_to_email], [])
|
||||
subject, mail_html, from_email, to_email, [], [reply_to_email], attachments)
|
||||
except Exception as e:
|
||||
print (e)
|
||||
pass
|
||||
|
|
@ -161,14 +178,16 @@ def send_scheduled_campaigns():
|
|||
from datetime import datetime
|
||||
campaigns = Campaign.objects.filter(schedule_date_time__isnull=False)
|
||||
for each in campaigns:
|
||||
sent_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
sent_time = datetime.strptime(sent_time, '%Y-%m-%d %H:%M:%S')
|
||||
local_tz = pytz.timezone('UTC')
|
||||
schedule_date_time_hour = str(each.schedule_date_time.time()).replace(':00:00', '')
|
||||
schedule_date_time = each.schedule_date_time
|
||||
|
||||
sent_time = datetime.now().strftime('%Y-%m-%d %H:%M')
|
||||
sent_time = datetime.strptime(sent_time, '%Y-%m-%d %H:%M')
|
||||
local_tz = pytz.timezone(settings.TIME_ZONE)
|
||||
sent_time = local_tz.localize(sent_time)
|
||||
sent_time = convert_to_custom_timezone(sent_time, each.timezone)
|
||||
sent_time = convert_to_custom_timezone(sent_time, each.timezone, to_utc=True)
|
||||
|
||||
if (
|
||||
str(each.schedule_date_time.date()) == str(sent_time.date()) and
|
||||
str(schedule_date_time_hour) == str(sent_time.hour)
|
||||
str(schedule_date_time.hour) == str(sent_time.hour)
|
||||
):
|
||||
run_campaign(each)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class TestTemplates(TestMarketingModel, TestCase):
|
|||
url4 = '/m/cl/lists/'
|
||||
url5 = '/m/cl/list/new/'
|
||||
url6 = '/m/cl/list/cnew/'
|
||||
url7 = '/m/cl/list/detail/'
|
||||
# url7 = '/m/cl/list/detail/'
|
||||
url8 = '/m/et/list/'
|
||||
url9 = '/m/et/new/'
|
||||
# url10 = '/m/et/edit/'
|
||||
|
|
@ -42,7 +42,7 @@ class TestTemplates(TestMarketingModel, TestCase):
|
|||
resp4 = self.client.get(url4)
|
||||
resp5 = self.client.get(url5)
|
||||
resp6 = self.client.get(url6)
|
||||
resp7 = self.client.get(url7)
|
||||
# resp7 = self.client.get(url7)
|
||||
resp8 = self.client.get(url8)
|
||||
resp9 = self.client.get(url9)
|
||||
# resp10 = self.client.get(url10)
|
||||
|
|
@ -57,7 +57,7 @@ class TestTemplates(TestMarketingModel, TestCase):
|
|||
self.assertEqual(resp4.status_code, 200)
|
||||
self.assertEqual(resp5.status_code, 200)
|
||||
self.assertEqual(resp6.status_code, 200)
|
||||
self.assertEqual(resp7.status_code, 200)
|
||||
# self.assertEqual(resp7.status_code, 200)
|
||||
self.assertEqual(resp8.status_code, 200)
|
||||
self.assertEqual(resp9.status_code, 200)
|
||||
# self.assertEqual(resp10.status_code, 200)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ from .views import (
|
|||
dashboard, contact_lists, contacts_list, contact_list_new, contacts_list_new, contact_list_detail, edit_contact,
|
||||
email_template_list, email_template_new, email_template_edit, email_template_delete,
|
||||
email_template_detail, campaign_list, campaign_new, campaign_edit, campaign_details, campaign_delete,
|
||||
edit_contact_list, delete_contact_list
|
||||
edit_contact_list, delete_contact_list, failed_contact_list_detail, failed_contact_list_download_delete,
|
||||
campaign_link_click, campaign_open
|
||||
)
|
||||
|
||||
app_name = 'marketing'
|
||||
|
|
@ -15,10 +16,13 @@ urlpatterns = [
|
|||
path('cl/edit_contact/', edit_contact, name='edit_contact'),
|
||||
path('cl/lists/', contacts_list, name='contacts_list'),
|
||||
path('cl/list/new/', contact_list_new, name='contact_list_new'),
|
||||
path('cl/list/edit/<int:pk>/', edit_contact_list, name='edit_contact_list'),
|
||||
path('cl/list/delete/<int:pk>/', delete_contact_list, name='delete_contact_list'),
|
||||
path('cl/list/cnew/', contacts_list_new, name='contacts_list_new'),
|
||||
path('cl/list/detail/', contact_list_detail, name='contact_list_detail'),
|
||||
path('cl/list/<int:pk>/detail/', contact_list_detail, name='contact_list_detail'),
|
||||
path('cl/list/<int:pk>/failed/', failed_contact_list_detail, name='failed_contact_list_detail'),
|
||||
path('cl/list/<int:pk>/failed/download/',
|
||||
failed_contact_list_download_delete, name='failed_contact_list_download_delete'),
|
||||
path('cl/list/<int:pk>/edit/', edit_contact_list, name='edit_contact_list'),
|
||||
path('cl/list/<int:pk>/delete/', delete_contact_list, name='delete_contact_list'),
|
||||
|
||||
path('et/list/', email_template_list, name='email_template_list'),
|
||||
path('et/new/', email_template_new, name='email_template_new'),
|
||||
|
|
@ -30,5 +34,7 @@ urlpatterns = [
|
|||
path('cm/new/', campaign_new, name='campaign_new'),
|
||||
path('cm/<int:pk>/edit/', campaign_edit, name='campaign_edit'),
|
||||
path('cm/<int:pk>/details/', campaign_details, name='campaign_details'),
|
||||
path('cm/<int:pk>/delete/', campaign_delete, name='campaign_delete')
|
||||
path('cm/<int:pk>/delete/', campaign_delete, name='campaign_delete'),
|
||||
path('cm/link/<int:link_id>/e/<int:email_id>/', campaign_link_click, name='campaign_link_click'),
|
||||
path('cm/track-email/<int:campaign_log_id>/contact/<int:email_id>/', campaign_open, name='campaign_open'),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,19 +1,23 @@
|
|||
import csv
|
||||
import json
|
||||
import pytz
|
||||
import lxml
|
||||
import lxml.html
|
||||
from lxml.cssselect import CSSSelector
|
||||
from django.conf import Settings
|
||||
from datetime import datetime as ddatetime
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from django.db.models import Q
|
||||
from django.http.response import (JsonResponse,
|
||||
HttpResponse, HttpResponseRedirect)
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.urls import reverse_lazy, reverse
|
||||
from common import status
|
||||
from common.utils import convert_to_custom_timezone
|
||||
from marketing.models import (
|
||||
Tag, ContactList, Contact, EmailTemplate, Campaign, CampaignLog, Link
|
||||
Tag, ContactList, Contact, EmailTemplate, Campaign, CampaignLog, Link, CampaignLinkClick,
|
||||
CampaignOpen
|
||||
)
|
||||
from marketing.forms import ContactListForm, ContactForm, EmailTemplateForm, SendCampaignForm
|
||||
from marketing.tasks import upload_csv_file, run_campaign
|
||||
|
|
@ -97,7 +101,7 @@ def contact_list_new(request):
|
|||
instance.tags.add(tag)
|
||||
if request.FILES.get('contacts_file'):
|
||||
upload_csv_file.delay(
|
||||
form.validated_rows, request.user.id, [instance.id])
|
||||
form.validated_rows, form.invalid_rows, request.user.id, [instance.id])
|
||||
|
||||
return JsonResponse({'error': False,
|
||||
'data': form.data},
|
||||
|
|
@ -144,7 +148,7 @@ def edit_contact_list(request, pk):
|
|||
instance.tags.add(tag)
|
||||
if request.FILES.get('contacts_file'):
|
||||
upload_csv_file.delay(
|
||||
form.validated_rows, request.user.id, [instance.id])
|
||||
form.validated_rows, form.invalid_rows, request.user.id, [instance.id])
|
||||
|
||||
return JsonResponse({'error': False,
|
||||
'data': form.data}, status=status.HTTP_200_OK)
|
||||
|
|
@ -215,8 +219,38 @@ def edit_contact(request):
|
|||
|
||||
|
||||
@login_required(login_url='/login')
|
||||
def contact_list_detail(request):
|
||||
return render(request, 'marketing/lists/detail.html')
|
||||
def contact_list_detail(request, pk):
|
||||
contact_list = get_object_or_404(ContactList, pk=pk)
|
||||
contacts_list = contact_list.contacts.all()
|
||||
data = {'contact_list': contact_list, "contacts_list": contacts_list}
|
||||
return render(request, 'marketing/lists/detail.html', data)
|
||||
|
||||
|
||||
@login_required(login_url='/login')
|
||||
def failed_contact_list_detail(request, pk):
|
||||
contact_list = get_object_or_404(ContactList, pk=pk)
|
||||
failed_contacts_list = contact_list.failed_contacts.all()
|
||||
data = {'contact_list': contact_list, "failed_contacts_list": failed_contacts_list}
|
||||
return render(request, 'marketing/lists/failed_detail.html', data)
|
||||
|
||||
|
||||
@login_required(login_url='/login')
|
||||
def failed_contact_list_download_delete(request, pk):
|
||||
contact_list = get_object_or_404(ContactList, pk=pk)
|
||||
failed_contacts_list = contact_list.failed_contacts.all()
|
||||
if failed_contacts_list.count() > 0:
|
||||
response = HttpResponse(content_type='text/csv')
|
||||
response['Content-Disposition'] = 'attachment; filename="failed_contacts.csv"'
|
||||
writer = csv.writer(response)
|
||||
writer.writerow(["company name", "email", "first name", "last name", "city", "state"])
|
||||
|
||||
for contact in failed_contacts_list:
|
||||
writer.writerow([
|
||||
contact.company_name, contact.email, contact.name, contact.last_name, contact.city, contact.state])
|
||||
failed_contacts_list.delete()
|
||||
return response
|
||||
else:
|
||||
return HttpResponseRedirect(reverse('marketing:contact_lists', kwargs={"pk": pk}))
|
||||
|
||||
|
||||
@login_required(login_url='/login')
|
||||
|
|
@ -336,23 +370,25 @@ def campaign_new(request):
|
|||
# contact_list__id__in=json.loads(request.GET.get('contact_list'))).distinct()
|
||||
|
||||
data = {
|
||||
'contact_lists': contact_lists, 'email_templates': email_templates, "timezones": TIMEZONE_CHOICES}
|
||||
'contact_lists': contact_lists, 'email_templates': email_templates,
|
||||
"timezones": TIMEZONE_CHOICES, "settings_timezone": settings.TIME_ZONE}
|
||||
# return JsonResponse(data, status=status.HTTP_200_OK)
|
||||
return render(request, 'marketing/campaign/new.html', data)
|
||||
else:
|
||||
print ("request.POST", request.POST)
|
||||
form = SendCampaignForm(request.POST)
|
||||
form = SendCampaignForm(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
instance = form.save(commit=False)
|
||||
instance.created_by = request.user
|
||||
if request.POST['from_email']:
|
||||
if request.POST.get('from_email'):
|
||||
instance.from_email = request.POST['from_email']
|
||||
if request.POST['from_name']:
|
||||
if request.POST.get('from_name'):
|
||||
instance.from_name = request.POST['from_name']
|
||||
if request.POST['reply_to_email']:
|
||||
if request.POST.get('reply_to_email'):
|
||||
instance.reply_to_email = request.POST['reply_to_email']
|
||||
# if request.FILES.get('attachment'):
|
||||
# instance.attachment = request.FILES.get('attachment')
|
||||
instance.save()
|
||||
for each in json.loads(request.POST['contact_list']):
|
||||
for each in request.POST.getlist('contact_list'):
|
||||
instance.contact_lists.add(ContactList.objects.get(id=each))
|
||||
# contacts = Contact.objects.filter(contact_list__in=json.loads(request.POST['contact_list'])).distinct()
|
||||
# for each_contact in contacts:
|
||||
|
|
@ -373,33 +409,31 @@ def campaign_new(request):
|
|||
links = set(llist)
|
||||
|
||||
# Replace Links with new One
|
||||
domain_url = '%s://%s' % (request.scheme, request.META['HTTP_HOST'])
|
||||
# domain_url = '%s://%s' % (request.scheme, request.META['HTTP_HOST'])
|
||||
domain_url = settings.URL_FOR_LINKS
|
||||
if Link.objects.filter(campaign_id=camp.id):
|
||||
Link.objects.filter(campaign_id=camp.id).delete()
|
||||
for l in links:
|
||||
link = Link.objects.create(campaign_id=camp.id, original=l)
|
||||
html = html.replace(
|
||||
'href="' + l + '"', 'href="' + domain_url + '/a/' + str(link.id) + '/e/{{email_id}}"')
|
||||
'href="' + l + '"', 'href="' + domain_url + '/m/cm/link/' + str(link.id) + '/e/{{email_id}}"')
|
||||
camp.html_processed = html
|
||||
camp.sent_status = "scheduled"
|
||||
camp.save()
|
||||
|
||||
if request.POST['schedule_later'] == 'false':
|
||||
run_campaign.delay(instance.id)
|
||||
else:
|
||||
if request.POST.get('schedule_later', None) == 'true':
|
||||
schedule_date_time = request.POST.get('schedule_date_time', '')
|
||||
# user_timezone = request.POST.get('timezone', '')
|
||||
# if user_timezone and schedule_date_time:
|
||||
# print ("schedule_date_time", schedule_date_time)
|
||||
# print ("timezone", user_timezone)
|
||||
# start_time_object = ddatetime.strptime(schedule_date_time, '%Y-%m-%d %H:%M:%S')
|
||||
# schedule_date_time = convert_to_custom_timezone(start_time_object, user_timezone, to_utc=True)
|
||||
# print ("After", schedule_date_time)
|
||||
user_timezone = request.POST.get('timezone', '')
|
||||
if user_timezone and schedule_date_time:
|
||||
start_time_object = ddatetime.strptime(schedule_date_time, '%Y-%m-%d %H:%M')
|
||||
schedule_date_time = convert_to_custom_timezone(start_time_object, user_timezone, to_utc=True)
|
||||
instance.schedule_date_time = schedule_date_time
|
||||
# instance.timezone = request.POST['timezone']
|
||||
instance.save()
|
||||
instance.timezone = user_timezone
|
||||
instance.save()
|
||||
else:
|
||||
run_campaign.delay(instance.id)
|
||||
|
||||
return JsonResponse({'error': True, 'data': form.data}, status=status.HTTP_201_CREATED)
|
||||
return JsonResponse({'error': False, 'data': form.data}, status=status.HTTP_201_CREATED)
|
||||
return JsonResponse({'error': True, 'errors': form.errors}, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
|
|
@ -409,10 +443,186 @@ def campaign_edit(request):
|
|||
|
||||
|
||||
@login_required(login_url='/login')
|
||||
def campaign_details(request):
|
||||
return render(request, 'marketing/campaign/details.html')
|
||||
def campaign_details(request, pk):
|
||||
try:
|
||||
campaign = Campaign.objects.get(pk=pk)
|
||||
except Campaign.DoesNotExist:
|
||||
return redirect(reverse('marketing:campaign_list'))
|
||||
|
||||
contact_lists = campaign.contact_lists.all()
|
||||
if (request.user.is_admin or request.user.is_superuser):
|
||||
contact_lists = contact_lists
|
||||
else:
|
||||
contact_lists = contact_lists.filter(
|
||||
is_public=True, created_by=request.user)
|
||||
|
||||
contacts = Contact.objects.filter(contact_list__in=contact_lists)
|
||||
contact_list_ids = campaign.contact_lists.all().values_list('id', flat=True)
|
||||
|
||||
contacts = Contact.objects.filter(contact_list__id__in=contact_list_ids)
|
||||
|
||||
all_contacts = contacts
|
||||
bounced_contacts = contacts.filter(is_bounced=True)
|
||||
unsubscribe_contacts = contacts.filter(
|
||||
is_unsubscribed=True)
|
||||
read_contacts = campaign.marketing_links.filter(Q(clicks__gt=0))
|
||||
# read_contacts =
|
||||
sent_contacts = contacts.exclude(
|
||||
Q(is_bounced=True) | Q(is_unsubscribed=True))
|
||||
if request.POST:
|
||||
contacts = contacts.filter(Q(name__icontains=request.POST['search']))
|
||||
|
||||
tab = request.GET.get('tab', 'contacts')
|
||||
per_page = request.GET.get("per_page", 10)
|
||||
page = request.GET.get('page', 1)
|
||||
|
||||
contacts_paginator = Paginator(contacts.order_by('id'), per_page)
|
||||
if (tab == "contacts"):
|
||||
page = page
|
||||
else:
|
||||
page = 1
|
||||
try:
|
||||
contacts = contacts_paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
contacts = contacts_paginator.page(1)
|
||||
except EmptyPage:
|
||||
contacts = contacts_paginator.page(contacts_paginator.num_pages)
|
||||
|
||||
unsubscribe_contacts_paginator = Paginator(unsubscribe_contacts.order_by('id'), per_page)
|
||||
if (tab == "unsubscribe_contacts"):
|
||||
page = page
|
||||
else:
|
||||
page = 1
|
||||
|
||||
try:
|
||||
unsubscribe_contacts = unsubscribe_contacts_paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
unsubscribe_contacts = unsubscribe_contacts_paginator.page(1)
|
||||
except EmptyPage:
|
||||
unsubscribe_contacts = unsubscribe_contacts_paginator.page(unsubscribe_contacts_paginator.num_pages)
|
||||
|
||||
bounced_contacts_paginator = Paginator(bounced_contacts.order_by('id'), per_page)
|
||||
if (tab == "bounced_contacts"):
|
||||
page = page
|
||||
else:
|
||||
page = 1
|
||||
|
||||
try:
|
||||
bounced_contacts = bounced_contacts_paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
bounced_contacts = bounced_contacts_paginator.page(1)
|
||||
except EmptyPage:
|
||||
bounced_contacts = bounced_contacts_paginator.page(bounced_contacts_paginator.num_pages)
|
||||
|
||||
sent_contacts_paginator = Paginator(sent_contacts.order_by('id'), per_page)
|
||||
if (tab == "sent_contacts"):
|
||||
page = page
|
||||
else:
|
||||
page = 1
|
||||
|
||||
try:
|
||||
sent_contacts = sent_contacts_paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
sent_contacts = sent_contacts_paginator.page(1)
|
||||
except EmptyPage:
|
||||
sent_contacts = sent_contacts_paginator.page(sent_contacts_paginator.num_pages)
|
||||
|
||||
all_contacts_paginator = Paginator(all_contacts.order_by('id'), per_page)
|
||||
if (tab == "all_contacts"):
|
||||
page = page
|
||||
else:
|
||||
page = 1
|
||||
|
||||
try:
|
||||
all_contacts = all_contacts_paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
all_contacts = all_contacts_paginator.page(1)
|
||||
except EmptyPage:
|
||||
all_contacts = all_contacts_paginator.page(all_contacts_paginator.num_pages)
|
||||
|
||||
read_contacts_paginator = Paginator(read_contacts.order_by('id'), per_page)
|
||||
if (tab == "read_contacts"):
|
||||
page = page
|
||||
else:
|
||||
page = 1
|
||||
|
||||
try:
|
||||
read_contacts = read_contacts_paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
read_contacts = read_contacts_paginator.page(1)
|
||||
except EmptyPage:
|
||||
read_contacts = read_contacts_paginator.page(read_contacts_paginator.num_pages)
|
||||
|
||||
data = {'contact_lists': contact_lists, "campaign": campaign,
|
||||
"contacts": contacts, 'unsubscribe_contacts': unsubscribe_contacts,
|
||||
'bounced_contacts': bounced_contacts, 'read_contacts': read_contacts,
|
||||
'sent_contacts': sent_contacts, 'all_contacts': all_contacts, 'tab': tab}
|
||||
|
||||
return render(request, 'marketing/campaign/details.html', data)
|
||||
|
||||
|
||||
@login_required(login_url='/login')
|
||||
def campaign_delete(request):
|
||||
return render(request, 'marketing/campaign/details.html')
|
||||
def campaign_delete(request, pk):
|
||||
try:
|
||||
campaign = Campaign.objects.get(id=pk)
|
||||
campaign.delete()
|
||||
redirect_url = reverse('marketing:campaign_list')
|
||||
except Campaign.DoesNotExist:
|
||||
redirect_url = reverse('marketing:campaign_list')
|
||||
return redirect(redirect_url)
|
||||
|
||||
|
||||
def campaign_link_click(request, link_id, email_id):
|
||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
|
||||
if x_forwarded_for:
|
||||
ipaddress = x_forwarded_for.split(',')[-1].strip()
|
||||
else:
|
||||
ipaddress = request.META.get('REMOTE_ADDR')
|
||||
|
||||
link = Link.objects.filter(id=link_id).first()
|
||||
contact = Contact.objects.filter(id=email_id).first()
|
||||
if link and contact:
|
||||
campaign_link_click = CampaignLinkClick.objects.filter(
|
||||
link=link, campaign=link.campaign, contact=contact).first()
|
||||
if not campaign_link_click:
|
||||
campaign_link_click = CampaignLinkClick.objects.create(
|
||||
link=link, campaign=link.campaign, contact=contact, ip_address=ipaddress)
|
||||
link.unique += 1
|
||||
else:
|
||||
campaign_link_click.ip_address = ipaddress
|
||||
link.clicks += 1
|
||||
link.save()
|
||||
campaign_link_click.save()
|
||||
url = link.original
|
||||
else:
|
||||
url = settings.URL_FOR_LINKS
|
||||
return redirect(url)
|
||||
|
||||
|
||||
def campaign_open(request, campaign_log_id, email_id):
|
||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
|
||||
if x_forwarded_for:
|
||||
ipaddress = x_forwarded_for.split(',')[-1].strip()
|
||||
else:
|
||||
ipaddress = request.META.get('REMOTE_ADDR')
|
||||
|
||||
campaign_log = CampaignLog.objects.filter(id=campaign_log_id).first()
|
||||
contact = Contact.objects.filter(id=email_id).first()
|
||||
image_data = settings.STATIC_URL + 'images/'
|
||||
image_data = open(settings.STATIC_ROOT + '/images/company.png', 'rb').read()
|
||||
if campaign_log and contact:
|
||||
campaign_open = CampaignOpen.objects.filter(
|
||||
campaign=campaign_log.campaign, contact=contact).first()
|
||||
if not campaign_open:
|
||||
campaign_open = CampaignOpen.objects.create(
|
||||
campaign=campaign_log.campaign, contact=contact, ip_address=ipaddress)
|
||||
campaign_log.campaign.opens_unique += 1
|
||||
else:
|
||||
campaign_open.ip_address = ipaddress
|
||||
campaign_log.campaign.opens += 1
|
||||
campaign_log.campaign.status = "Sent"
|
||||
campaign_log.campaign.save()
|
||||
campaign_open.save()
|
||||
return HttpResponse(image_data, content_type="image/png")
|
||||
|
|
|
|||
|
|
@ -4,12 +4,14 @@ from accounts.models import Account
|
|||
from contacts.models import Contact
|
||||
from common.models import User, Comment, Attachments, Address
|
||||
from cases.models import Case
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
|
||||
|
||||
# Create your tests here.
|
||||
|
||||
|
||||
class OpportunityModel(object):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
self.user = User.objects.create(
|
||||
|
|
@ -47,6 +49,7 @@ class OpportunityModel(object):
|
|||
closed_on="2016-05-04",
|
||||
description="hgfdxc",
|
||||
created_by=self.user)
|
||||
self.opportunity.assigned_to.add(self.user)
|
||||
self.case = Case.objects.create(
|
||||
name="raghu", case_type="Problem", status="New", account=self.account,
|
||||
priority="Low", description="something",
|
||||
|
|
@ -58,6 +61,7 @@ class OpportunityModel(object):
|
|||
|
||||
|
||||
class OpportunityCreateTestCase(OpportunityModel, TestCase):
|
||||
|
||||
def test_opportunity_create(self):
|
||||
response = self.client.get('/opportunities/create/', {
|
||||
'name': "meghan", 'amount': "478",
|
||||
|
|
@ -67,8 +71,14 @@ class OpportunityCreateTestCase(OpportunityModel, TestCase):
|
|||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_opportunity_create_post(self):
|
||||
upload_file = open('static/images/user.png', 'rb')
|
||||
url = '/opportunities/create/'
|
||||
data = {'name': "micky", 'amount': "500", 'stage': "CLOSED WON"}
|
||||
data = {'name': "micky", 'amount': "500", 'stage': "CLOSED WON",
|
||||
'assigned_to': str(self.user.id),
|
||||
'contacts': str(self.contacts.id),
|
||||
'tags': 'tag',
|
||||
'oppurtunity_attachment': SimpleUploadedFile(
|
||||
upload_file.name, upload_file.read())}
|
||||
response = self.client.post(url, data)
|
||||
# self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
|
@ -113,16 +123,24 @@ class EditOpportunityTestCase(OpportunityModel, TestCase):
|
|||
'stage': "negotiation/review",
|
||||
'lead_source': "Call", 'probability': "58",
|
||||
'closed_on': "2016-05-04", 'description': "hgfdxc"})
|
||||
resp = self.client.post( '/opportunities/' + str(self.opportunity.id) + '/edit/', **{'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'})
|
||||
resp = self.client.post('/opportunities/' + str(self.opportunity.id) +
|
||||
'/edit/', **{'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'})
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
resp1 = self.client.post( '/opportunities/create/', **{'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'})
|
||||
resp1 = self.client.post(
|
||||
'/opportunities/create/', **{'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'})
|
||||
self.assertEqual(resp1.status_code, 200)
|
||||
|
||||
def test_update_opportunity(self):
|
||||
upload_file = open('static/images/user.png', 'rb')
|
||||
url = '/opportunities/' + str(self.opportunity.id) + '/edit/'
|
||||
data = {
|
||||
'name': "meghan", 'amount': "478", 'stage': "QUALIFICATION",
|
||||
'probability': "58", 'closed_on': "2016-05-04", 'description': "hgfdxc"}
|
||||
'probability': "58", 'closed_on': "2016-05-04",
|
||||
'description': "hgfdxc",
|
||||
'tags': 'tag', 'assigned_to': str(self.user.id),
|
||||
'contacts': str(self.contacts.id),
|
||||
'oppurtunity_attachment': SimpleUploadedFile(
|
||||
upload_file.name, upload_file.read())}
|
||||
response = self.client.post(url, data)
|
||||
# self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
|
@ -137,6 +155,7 @@ class EditOpportunityTestCase(OpportunityModel, TestCase):
|
|||
|
||||
|
||||
class OpportunityListView(OpportunityModel, TestCase):
|
||||
|
||||
def test_opportunity_list(self):
|
||||
self.opportunity = Opportunity.objects.all()
|
||||
response = self.client.get('/opportunities/list/')
|
||||
|
|
@ -164,6 +183,7 @@ class OpportunityListView(OpportunityModel, TestCase):
|
|||
|
||||
|
||||
class ContactGetViewTestCase(OpportunityModel, TestCase):
|
||||
|
||||
def test_get_contact(self):
|
||||
url = '/opportunities/contacts/'
|
||||
response = self.client.get(url)
|
||||
|
|
@ -173,6 +193,7 @@ class ContactGetViewTestCase(OpportunityModel, TestCase):
|
|||
|
||||
|
||||
class CommentTestCase(OpportunityModel, TestCase):
|
||||
|
||||
def test_comment_add(self):
|
||||
response = self.client.post(
|
||||
'/opportunities/comment/add/', {'opportunityid': self.opportunity.id})
|
||||
|
|
@ -188,11 +209,21 @@ class CommentTestCase(OpportunityModel, TestCase):
|
|||
'/opportunities/comment/remove/', {'comment_id': self.comment.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_comment_form_valid(self):
|
||||
response = self.client.post(
|
||||
'/opportunities/comment/add/', {'opportunityid': self.opportunity.id,
|
||||
'comment': 'hello'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class AttachmentTestCase(OpportunityModel, TestCase):
|
||||
|
||||
def test_attachment_add(self):
|
||||
upload_file = open('static/images/user.png', 'rb')
|
||||
url = "/opportunities/attachment/add/"
|
||||
data = {'opportunityid': self.opportunity.id}
|
||||
data = {'opportunityid': self.opportunity.id,
|
||||
'attachment': SimpleUploadedFile(
|
||||
upload_file.name, upload_file.read())}
|
||||
response = self.client.post(url, data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
|
@ -204,9 +235,9 @@ class AttachmentTestCase(OpportunityModel, TestCase):
|
|||
|
||||
|
||||
class TestGetOpportunitiesView(OpportunityModel, TestCase):
|
||||
|
||||
def test_get_page(self):
|
||||
url = "/opportunities/get/list/"
|
||||
resp = self.client.get(url)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertIsNotNone(resp.context['opportunities'])
|
||||
|
||||
|
|
|
|||
BIN
static/images/company.png
Normal file
BIN
static/images/company.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
File diff suppressed because it is too large
Load diff
|
|
@ -31,7 +31,7 @@
|
|||
<td scope="row"><span class="badge badge-primary">Primary</span> <a href="{% url 'marketing:campaign_details' campaign.id %}">{{ campaign.title }} </a></td>
|
||||
<td>5478</td>
|
||||
<td>
|
||||
{{ campaign.created_on }}
|
||||
{{ campaign.sent_on_format }}
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-secondary">20.6%</span> 125 Opened
|
||||
|
|
@ -40,7 +40,10 @@
|
|||
<span class="badge badge-secondary">15.5%</span> 78 Clicked
|
||||
</td>
|
||||
<td class="actions">
|
||||
<a href="{% url 'marketing:campaign_edit' campaign.id %}" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
|
||||
<a href="{% url 'marketing:campaign_details' campaign.id %}" class="btn btn-success view"><i class="fas fa-eye"></i></a>
|
||||
<!--
|
||||
<a href="{% url 'marketing:campaign_edit' campaign.id %}" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
|
||||
-->
|
||||
<a href="{% url 'marketing:campaign_delete' campaign.id %}" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -49,116 +52,6 @@
|
|||
<td colspan="6"> No records found!</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<!--
|
||||
<tr style="text-align:center;">
|
||||
<td scope="row"><span class="badge badge-primary">Primary</span> <a href="#">Sample scheduled newsletter </a></td>
|
||||
<td>5478</td>
|
||||
<td>
|
||||
Tue, Nov 22, 2016, 12:00AM
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-secondary">20.6%</span> 125 Opened
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-secondary">15.5%</span> 78 Clicked
|
||||
</td>
|
||||
<td class="actions">
|
||||
<a href="/contacts/1/edit/" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
|
||||
<a href="/contacts/1/edit/" class="btn btn-info duplicate"><i class="fas fa-copy"></i></a>
|
||||
<a href="/contacts/1/delete/" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="text-align:center;">
|
||||
<td scope="row"><span class="badge badge-primary">Primary</span> <a href="#">Sample scheduled newsletter </a></td>
|
||||
<td>5478</td>
|
||||
<td>
|
||||
Tue, Nov 22, 2016, 12:00AM
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-secondary">20.6%</span> 125 Opened
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-secondary">15.5%</span> 78 Clicked
|
||||
</td>
|
||||
<td class="actions">
|
||||
<a href="/contacts/1/edit/" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
|
||||
<a href="/contacts/1/edit/" class="btn btn-info duplicate"><i class="fas fa-copy"></i></a>
|
||||
<a href="/contacts/1/delete/" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="text-align:center;">
|
||||
<td scope="row"><span class="badge badge-primary">Primary</span> <a href="#">Sample scheduled newsletter </a></td>
|
||||
<td>5478</td>
|
||||
<td>
|
||||
Tue, Nov 22, 2016, 12:00AM
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-secondary">20.6%</span> 125 Opened
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-secondary">15.5%</span> 78 Clicked
|
||||
</td>
|
||||
<td class="actions">
|
||||
<a href="/contacts/1/edit/" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
|
||||
<a href="/contacts/1/edit/" class="btn btn-info duplicate"><i class="fas fa-copy"></i></a>
|
||||
<a href="/contacts/1/delete/" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="text-align:center;">
|
||||
<td scope="row"><span class="badge badge-primary">Primary</span> <a href="#">Sample scheduled newsletter </a></td>
|
||||
<td>5478</td>
|
||||
<td>
|
||||
Tue, Nov 22, 2016, 12:00AM
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-secondary">20.6%</span> 125 Opened
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-secondary">15.5%</span> 78 Clicked
|
||||
</td>
|
||||
<td class="actions">
|
||||
<a href="/contacts/1/edit/" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
|
||||
<a href="/contacts/1/edit/" class="btn btn-info duplicate"><i class="fas fa-copy"></i></a>
|
||||
<a href="/contacts/1/delete/" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="text-align:center;">
|
||||
<td scope="row"><span class="badge badge-primary">Primary</span> <a href="#">Sample scheduled newsletter </a></td>
|
||||
<td>5478</td>
|
||||
<td>
|
||||
Tue, Nov 22, 2016, 12:00AM
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-secondary">20.6%</span> 125 Opened
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-secondary">15.5%</span> 78 Clicked
|
||||
</td>
|
||||
<td class="actions">
|
||||
<a href="/contacts/1/edit/" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
|
||||
<a href="/contacts/1/edit/" class="btn btn-info duplicate"><i class="fas fa-copy"></i></a>
|
||||
<a href="/contacts/1/delete/" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="text-align:center;">
|
||||
<td scope="row"><span class="badge badge-primary">Primary</span> <a href="#">Sample scheduled newsletter </a></td>
|
||||
<td>5478</td>
|
||||
<td>
|
||||
Tue, Nov 22, 2016, 12:00AM
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-secondary">20.6%</span> 125 Opened
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-secondary">15.5%</span> 78 Clicked
|
||||
</td>
|
||||
<td class="actions">
|
||||
<a href="/contacts/1/edit/" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
|
||||
<a href="/contacts/1/edit/" class="btn btn-info duplicate"><i class="fas fa-copy"></i></a>
|
||||
<a href="/contacts/1/delete/" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
-->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
@ -168,4 +61,4 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -6,280 +6,59 @@
|
|||
<div class="marketing_pages">
|
||||
<div class="row marl">
|
||||
<div class="col-md-12">
|
||||
<form id="add_form" method="POST" action='' novalidate>
|
||||
<div class="overview_form_block row marl justify-content-center">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="card-title text-center">
|
||||
Create Campaign
|
||||
</div>
|
||||
<form>
|
||||
<div class="row marl">
|
||||
<div class="col-md-12">
|
||||
<div class="form-group">
|
||||
<label>Title</label>
|
||||
<input type="text" name="title" class="form-control" placeholder="Title of this campaign">
|
||||
<small class="form-text text-muted">Set a title for this campaign?</small>
|
||||
<span id="id_title"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label>Select Template</label>
|
||||
<select class="form-control" name="email_template">
|
||||
<option value="">Select a template</option>
|
||||
{% for email_template in email_templates %}
|
||||
<option value="{{email_template.id}}">{{ email_template.title }}</option>
|
||||
{% endfor %}
|
||||
<!-- <option value="one">First template</option>
|
||||
<option value="two">Second template</option>
|
||||
<option value="three">Third template</option>
|
||||
<option value="four">Fourth template</option> -->
|
||||
</select>
|
||||
<span id="id_email_template"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label>Subject</label>
|
||||
<input type="text" name="subject" class="form-control" placeholder="Subject of this Campaign">
|
||||
<!-- <small class="form-text text-muted">Set a title for this campaign?</small> -->
|
||||
<span id="id_subject"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label>From Name</label>
|
||||
<input type="text" class="form-control" placeholder="John Doe" name="from_name">
|
||||
<span id="id_from_name"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label>From Email</label>
|
||||
<input type="email" class="form-control" placeholder="support@mp.com" name="from_email">
|
||||
<span id="id_from_email"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label><input type="checkbox" name="reply_to_crm" value="true"> Reply To CRM</label>
|
||||
<span id="id_reply_to_crm"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Reply to email</label>
|
||||
<input type="email" class="form-control" placeholder="replyto@mp.com" name="reply_to_email">
|
||||
<span id="id_reply_to_email"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Query String</label>
|
||||
<div class="well">
|
||||
Optionally append a query string to all links in your email newsletter. A good use case is Google Analytics tracking. Don't include '?' in your query string.
|
||||
</div>
|
||||
<input type="email" class="form-control" placeholder="url">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Attachments</label>
|
||||
<input type="file" class="form-control" name="attachment">
|
||||
<small class="form-text text-muted">Allowed file types: jpeg, jpg, gif, png, pdf, zip</small>
|
||||
<span id="id_attachment"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Track Opens</label>
|
||||
<button type="button" class="btn btn-toggle" data-toggle="button" aria-pressed="false" autocomplete="off">
|
||||
<div class="handle"></div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Track Clicks</label>
|
||||
<button type="button" class="btn btn-toggle" data-toggle="button" aria-pressed="false" autocomplete="off">
|
||||
<div class="handle"></div>
|
||||
</button>
|
||||
</div><br>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="primary_btn mar_btm save_btn">Save Campaign</button>
|
||||
<a href="{% url 'marketing:campaign_list' %}" class="seconadry_btn">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label><input type="checkbox" name="schedule_later" value="true"> Schedule Later</label>
|
||||
<span id="id_schedule_later"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label>Timezone</label>
|
||||
<select name="timezone" id="timezone" class="form-control">
|
||||
<option value="">Select a Timezone</option>
|
||||
{% for timezone in timezones %}
|
||||
<option value="{{timezone.0}}">{{timezone.1}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<span id="id_timezone"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="">Schedule Date Time</label>
|
||||
<input type="datetime" class="form-control" name="schedule_date_time" value="" placeholder="Schedule Date Time">
|
||||
<span id="id_schedule_date_time"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="form-group">
|
||||
<label>Plain Text</label>
|
||||
<textarea class="form-control" rows="3" name="plain_html"></textarea>
|
||||
<span id="id_plain_html"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Html Code</label>
|
||||
<div id="editor-container">
|
||||
</div>
|
||||
<span class="hint">Use the following tags in your subject, plain text or HTML code and they'll automatically be formatted when your campaign is sent. For web version and unsubscribe tags, you can style them with inline CSS.</span>
|
||||
<input type="hidden" name="html" id="html_content">
|
||||
<span id="id_html"></span>
|
||||
</div>
|
||||
<div class="col-md-12 no-padding">
|
||||
<div class="row help_container">
|
||||
<div class="col-md-6">
|
||||
<div class="heading">Essential tags (HTML only)</div>
|
||||
<p class="desc">The following tags can only be used on the HTML version</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Webversion link: </strong>
|
||||
<code>
|
||||
<webversion>View web version<webversion>
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Unsubscribe link: </strong>
|
||||
<code>
|
||||
<unsubscribe>Unsubscribe here</unsubscribe>
|
||||
</code>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="heading">Essential tags</div>
|
||||
<p class="desc">The following tags can only be used on the HTML version</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Webversion link: </strong>
|
||||
<code>
|
||||
<webversion>View web version<webversion>
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Unsubscribe link: </strong>
|
||||
<code>
|
||||
<unsubscribe>Unsubscribe here</unsubscribe>
|
||||
</code>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="heading">Personalization tags</div>
|
||||
<p class="desc">The following tags can be used on both Plain text or HTML version</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Name: </strong>
|
||||
<code>[Name,fallback=]</code>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Email: </strong>
|
||||
<code>[Email]</code>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Two digit day of the month (eg. 01 to 31):</strong>
|
||||
<code>[currentdaynumber]</code>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="heading">Essential tags</div>
|
||||
<p class="desc">The following tags can only be used on the HTML version</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Name: </strong>
|
||||
<code>[Name,fallback=]</code>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Email: </strong>
|
||||
<code>[Email]</code>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Two digit day of the month (eg. 01 to 31):</strong>
|
||||
<code>[currentdaynumber]</code>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="table_container_row no_mar_top">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="card-title text-right">
|
||||
<span class="float-left">Campaigns - {{ campaigns_list|length }}</span>
|
||||
<a href="{% url 'marketing:campaign_new' %}" class="primary_btn">Add New Campaign</a>
|
||||
</div>
|
||||
<div class="table-responsive table-bordered ">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="25%">Campaign</th>
|
||||
<th width="10%">Receipents</th>
|
||||
<th width="20%">Sent</th>
|
||||
<th width="15%">Unique Opens</th>
|
||||
<th width="15%">Unique Clicks</th>
|
||||
<th width="10%">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for campaign in campaigns_list %}
|
||||
<tr style="text-align:center;">
|
||||
<td scope="row"><span class="badge badge-primary">Primary</span> <a href="{% url 'marketing:campaign_details' campaign.id %}">{{ campaign.title }} </a></td>
|
||||
<td>5478</td>
|
||||
<td>
|
||||
{{ campaign.sent_on_format }}
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-secondary">20.6%</span> 125 Opened
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-secondary">15.5%</span> 78 Clicked
|
||||
</td>
|
||||
<td class="actions">
|
||||
<a href="{% url 'marketing:campaign_details' campaign.id %}" class="btn btn-success view"><i class="fas fa-eye"></i></a>
|
||||
<!--
|
||||
<a href="{% url 'marketing:campaign_edit' campaign.id %}" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
|
||||
-->
|
||||
<a href="{% url 'marketing:campaign_delete' campaign.id %}" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="6"> No records found!</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block js_block %}
|
||||
<script src="https://malsup.github.com/jquery.form.js"></script>
|
||||
<script>
|
||||
var quill = new Quill('#editor-container', {
|
||||
modules: {
|
||||
toolbar: [
|
||||
[{ header: [1, 2, false] }],
|
||||
['bold', 'italic', 'underline'],
|
||||
['image', 'code-block']
|
||||
]
|
||||
},
|
||||
placeholder: 'Compose an epic...',
|
||||
theme: 'snow' // or 'bubble'
|
||||
});
|
||||
|
||||
quill.on('text-change', function() {
|
||||
var justHtml = quill.root.innerHTML;
|
||||
$('#html_content').val(justHtml);
|
||||
});
|
||||
|
||||
|
||||
$('.save_btn').click(function(e){
|
||||
e.preventDefault();
|
||||
$("form#add_form").submit()
|
||||
})
|
||||
|
||||
$('form#add_form').ajaxForm({
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
url: $(this).attr('action'),
|
||||
data: $(this).serialize(),
|
||||
success: function(data) {
|
||||
console.log("data", data)
|
||||
if (data.error == false) {
|
||||
window.location = "{% url 'marketing:campaign_list' %}";
|
||||
} else {
|
||||
/*$(document).ajaxStop($.unblockUI);*/
|
||||
// $.unblockUI();
|
||||
$('p.failure').remove();
|
||||
for (var key in data.errors) {
|
||||
$('#id_' + key).html('<p class="error failure"> *' + data.errors[key] + '</p>');
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function(xhr, errmsg, err) {
|
||||
console.log('error data', errmsg, err)
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock js_block %}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
{% extends 'marketing/base.html' %}
|
||||
{% load staticfiles %}
|
||||
{% load paginate %}
|
||||
{% block content %}
|
||||
{{ block.super }}
|
||||
<div class="marketing_pages">
|
||||
<div class="row marl">
|
||||
<div class="col-md-12">
|
||||
<div class="table_container_row no_mar_top">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="card-title text-right">
|
||||
<span class="float-left">{{ contact_list.name }} - {{ contacts_list|length }}</span>
|
||||
{% if contact_list.failed_contacts.all|length > 0 %}
|
||||
<a href="{% url 'marketing:failed_contact_list_detail' contact_list.id %}" class="primary_btn">Failed Contacts</a>
|
||||
{% endif %}
|
||||
<a href="#" class="primary_btn">Add New Contact</a>
|
||||
</div>
|
||||
<div class="table-responsive table-bordered ">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="5%">ID</th>
|
||||
<th width="20%">Name</th>
|
||||
<th width="20%">Email</th>
|
||||
<th width="10%">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for contact in contacts_list %}
|
||||
<tr style="text-align:center;">
|
||||
<td scope="row">{{ forloop.counter }}</td>
|
||||
<td>{{ contact.name }}</td>
|
||||
<td>{{ contact.email }}</td>
|
||||
<td class="actions">
|
||||
<a href="#" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
|
||||
<!-- <a href="/contacts/1/edit/" class="btn btn-info duplicate"><i class="fas fa-copy"></i></a> -->
|
||||
<a href="#" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr style="text-align:center;">
|
||||
<td colspan="6">No records found!</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
55
templates/marketing/lists/failed_detail.html
Normal file
55
templates/marketing/lists/failed_detail.html
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
{% extends 'marketing/base.html' %}
|
||||
{% load staticfiles %}
|
||||
{% load paginate %}
|
||||
{% block content %}
|
||||
{{ block.super }}
|
||||
<div class="marketing_pages">
|
||||
<div class="row marl">
|
||||
<div class="col-md-12">
|
||||
<div class="table_container_row no_mar_top">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="card-title text-right">
|
||||
<span class="float-left">{{ contact_list.name }} - Failed: {{ failed_contacts_list|length }}</span>
|
||||
{% if contact_list.contacts.all|length > 0 %}
|
||||
<a href="{% url 'marketing:contact_list_detail' contact_list.id %}" class="primary_btn">Contacts</a>
|
||||
{% endif %}
|
||||
<a href="{% url 'marketing:failed_contact_list_download_delete' contact_list.id %}" class="primary_btn">Download & Delete</a>
|
||||
</div>
|
||||
<div class="table-responsive table-bordered ">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="5%">ID</th>
|
||||
<th width="20%">Name</th>
|
||||
<th width="20%">Email</th>
|
||||
<th width="10%">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for contact in failed_contacts_list %}
|
||||
<tr style="text-align:center;">
|
||||
<td scope="row">{{ forloop.counter }}</td>
|
||||
<td>{{ contact.name }}</td>
|
||||
<td>{{ contact.email }}</td>
|
||||
<td class="actions">
|
||||
<a href="#" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
|
||||
<!-- <a href="/contacts/1/edit/" class="btn btn-info duplicate"><i class="fas fa-copy"></i></a> -->
|
||||
<a href="#" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr style="text-align:center;">
|
||||
<td colspan="6">No records found!</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
{% for contact_list in contact_lists %}
|
||||
<tr style="text-align:center;">
|
||||
<td scope="row">{{ forloop.counter }}</td>
|
||||
<td>{{ contact_list.name }}</td>
|
||||
<td><a href="{% url 'marketing:contact_list_detail' contact_list.id %}">{{ contact_list.name }}</td>
|
||||
<td>{{ contact_list.created_by }}</td>
|
||||
<td>
|
||||
{{ contact_list.updated_on }}
|
||||
|
|
@ -157,4 +157,4 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -2,95 +2,94 @@
|
|||
{% load staticfiles %}
|
||||
{% load paginate %}
|
||||
{% block extralinks %}
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/jquery-tagsinput/1.3.6/jquery.tagsinput.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/jquery-tagsinput/1.3.6/jquery.tagsinput.min.css" rel="stylesheet">
|
||||
{% endblock extralinks %}
|
||||
{% block content %}
|
||||
{{ block.super }}
|
||||
<div class="marketing_pages">
|
||||
<div class="row marl">
|
||||
<div class="col-md-12">
|
||||
<form id="add_form" method="post" action="." enctype="multipart/form-data" novalidate>
|
||||
<div class="overview_form_block row marl justify-content-center">
|
||||
<div class="col-md-5">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="card-title text-center">
|
||||
{% if contact_list %}Edit{% else %}Create{% endif %} Contact List
|
||||
</div>
|
||||
<div class="row marl justify-content-center">
|
||||
<div class="filter_col col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="required">Name <span class="error">*</span></label>
|
||||
<input type="text" class="form-control" name="name" value="{% if contact_list %}{{contact_list.name}}{% endif %}" placeholder="Enter Contacts Name">
|
||||
<span id="id_name"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter_col col-md-12">
|
||||
<div class="form-group">
|
||||
<label>Contacts File </label>
|
||||
<input type="file" class="form-control" name="contacts_file">
|
||||
<span id="id_contacts_file"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter_col col-md-12">
|
||||
<div class="form-group">
|
||||
<label>Tags</label>
|
||||
<div class="txt-box-div" id="tag-div">
|
||||
<input type="text" name="tags" id="tags_1" class="tags" value="{% if contact_list %}{% for tag in contact_list.tags.all %}{{tag.name}}, {% endfor %}{% endif %}" />
|
||||
</div>
|
||||
<span id="id_tags"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter_col col-md-12">
|
||||
<div class="demo_clist">
|
||||
<a href="#">Demo file <i class="fas fa-download"></i> </a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="row marl buttons_row text-center form_btn_row">
|
||||
<button type="submit" class="btn primary_btn save_btn">Save</button>
|
||||
<a href="{% url 'marketing:contact_lists' %}" class="btn seconadry_btn">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ block.super }}
|
||||
<div class="marketing_pages">
|
||||
<div class="row marl">
|
||||
<div class="col-md-12">
|
||||
<form id="add_form" method="post" action="." enctype="multipart/form-data" novalidate>
|
||||
<div class="overview_form_block row marl justify-content-center">
|
||||
<div class="col-md-5">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="card-title text-center">
|
||||
{% if contact_list %}Edit{% else %}Create{% endif %} Contact List
|
||||
</div>
|
||||
<div class="row marl justify-content-center">
|
||||
<div class="filter_col col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="required">Name <span class="error">*</span></label>
|
||||
<input type="text" class="form-control" name="name" value="{% if contact_list %}{{contact_list.name}}{% endif %}" placeholder="Enter Contact List Name">
|
||||
<span id="id_name"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter_col col-md-12">
|
||||
<div class="form-group">
|
||||
<label>Contacts File </label>
|
||||
<input type="file" class="form-control" name="contacts_file">
|
||||
<span id="id_contacts_file"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter_col col-md-12">
|
||||
<div class="form-group">
|
||||
<label>Tags</label>
|
||||
<div class="txt-box-div" id="tag-div">
|
||||
<input type="text" name="tags" id="tags_1" class="tags" value="{% if contact_list %}{% for tag in contact_list.tags.all %}{{tag.name}}, {% endfor %}{% endif %}" />
|
||||
</div>
|
||||
<span id="id_tags"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter_col col-md-12">
|
||||
<div class="demo_clist">
|
||||
<a href="#">Demo file <i class="fas fa-download"></i> </a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="row marl buttons_row text-center form_btn_row">
|
||||
<button type="submit" class="btn primary_btn save_btn">Save</button>
|
||||
<a href="{% url 'marketing:contact_lists' %}" class="btn seconadry_btn">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block js_block %}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-tagsinput/1.3.6/jquery.tagsinput.min.js"></script>
|
||||
<script src="https://malsup.github.com/jquery.form.js"></script>
|
||||
<script>
|
||||
// $(document).ajaxStart($.blockUI).ajaxStop($.unblockUI);
|
||||
$('.save_btn').click(function(e){
|
||||
e.preventDefault();
|
||||
$("form#add_form").submit()
|
||||
})
|
||||
/*$.blockUI({
|
||||
message: '<h1>Please wait ...</h1>',
|
||||
css: {
|
||||
border: 'none',
|
||||
padding: '15px',
|
||||
backgroundColor: '#000',
|
||||
'-webkit-border-radius': '10px',
|
||||
'-moz-border-radius': '10px',
|
||||
opacity: .5,
|
||||
color: '#fff'
|
||||
}
|
||||
});*/
|
||||
// setTimeout($.unblockUI, 2000);
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-tagsinput/1.3.6/jquery.tagsinput.min.js"></script>
|
||||
<script src="https://malsup.github.com/jquery.form.js"></script>
|
||||
<script>
|
||||
// $(document).ajaxStart($.blockUI).ajaxStop($.unblockUI);
|
||||
$('.save_btn').click(function(e) {
|
||||
e.preventDefault();
|
||||
$("form#add_form").submit()
|
||||
})
|
||||
/*$.blockUI({
|
||||
message: '<h1>Please wait ...</h1>',
|
||||
css: {
|
||||
border: 'none',
|
||||
padding: '15px',
|
||||
backgroundColor: '#000',
|
||||
'-webkit-border-radius': '10px',
|
||||
'-moz-border-radius': '10px',
|
||||
opacity: .5,
|
||||
color: '#fff'
|
||||
}
|
||||
});*/
|
||||
// setTimeout($.unblockUI, 2000);
|
||||
$('form#add_form').ajaxForm({
|
||||
type: 'POST',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
url: $(this).attr('action'),
|
||||
data: $(this).serialize(),
|
||||
success: function(data) {
|
||||
console.log("data", data)
|
||||
if (data.error == false) {
|
||||
window.location = "{% url 'marketing:contact_lists' %}";
|
||||
} else {
|
||||
|
|
@ -103,13 +102,12 @@
|
|||
}
|
||||
},
|
||||
error: function(xhr, errmsg, err) {
|
||||
console.log('error data', errmsg, err)
|
||||
console.log('error data', errmsg, err)
|
||||
}
|
||||
});
|
||||
|
||||
$(function() {
|
||||
$('#tags_1').tagsInput({width:'auto'});
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock js_block %}
|
||||
$(function() {
|
||||
$('#tags_1').tagsInput({ width: 'auto' });
|
||||
});
|
||||
</script>
|
||||
{% endblock js_block %}
|
||||
|
|
|
|||
Loading…
Reference in a new issue