updated testcases, update code for account view page, modified code for attachments and comments for assigned user (#270)

This commit is contained in:
nikhila05 2019-08-14 14:16:43 +05:30 committed by GitHub
parent 46f1a1bbb0
commit 3bc30d3c95
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 2201 additions and 397 deletions

3
.gitignore vendored
View file

@ -16,4 +16,5 @@ ref.css.map
.coverage
crm/dev_settings\.py
env
docs/build
docs/build
celerybeat-schedule

View file

@ -1,5 +1,5 @@
from django import forms
from .models import Account
from .models import Account, Email
from common.models import Comment, Attachments, User
from leads.models import Lead
from contacts.models import Contact
@ -93,13 +93,72 @@ class AccountAttachmentForm(forms.ModelForm):
fields = ('attachment', 'account')
class EmailForm(forms.Form):
recipients = forms.CharField(max_length=500)
message_subject = forms.CharField(max_length=500)
message_body = forms.CharField(widget=forms.Textarea)
class EmailForm(forms.ModelForm):
recipients = forms.CharField()
# message_subject = forms.CharField(max_length=500)
# message_body = forms.CharField(widget=forms.Textarea)
# timezone = forms.CharField(max_length=200)
# scheduled_date_time = forms.DateTimeField()
# scheduled_later = forms.CharField(max_length=20)
def __init__(self, *args, **kwargs):
self.account_obj = kwargs.pop('account', False)
super(EmailForm, self).__init__(*args, **kwargs)
self.fields['message_subject'].widget.attrs['class'] = 'form-control'
self.fields['message_subject'].widget.attrs['required'] = True
self.fields['message_subject'].widget.attrs['placeholder'] = 'Email Subject'
# self.fields['message_subject'].widget.attrs['class'] = 'form-control'
# self.fields['message_subject'].widget.attrs['required'] = True
# self.fields['message_subject'].widget.attrs['placeholder'] = 'Email Subject'
# self.fields['recipient'].required = False
self.fields['from_email'].required = True
self.fields['message_subject'].required = True
self.fields['message_body'].required = True
self.fields['scheduled_date_time'].required = False
self.fields['scheduled_later'].required = False
self.fields['recipients'].required = True
self.fields['recipients'].query = self.account_obj.contacts.all()
# self.fields['recipients'].choices = list((contact.get('id'), contact.get('email'))
# for contact in self.account_obj.contacts.values('id', 'email'))
class Meta:
model = Email
fields = ['recipients', 'message_subject', 'from_email',
'message_body', 'timezone', 'scheduled_date_time', 'scheduled_later']
# def clean_recipients(self):
# recipients = self.cleaned_data.get('recipients')
# recipients = recipients.split(',')
# if len(recipients) == 0:
# raise forms.ValidationError('This field is required.')
# contacts = list(self.account_obj.contacts.values_list('id', flat=True))
# for recipient in recipients:
# if int(recipient) not in contacts:
# raise forms.ValidationError('{} is not a valid choice.'.format(recipient))
# return recipients
def clean_scheduled_date_time(self):
scheduled_date_time = self.cleaned_data.get('scheduled_date_time')
if self.data.get('scheduled_later') not in ['', None, False, 'false']:
if scheduled_date_time in ['', None]:
raise forms.ValidationError('This Field is required.')
if self.data.get('scheduled_later') == 'true':
if scheduled_date_time in ['', None]:
raise forms.ValidationError('This Field is required.')
return scheduled_date_time
def clean_message_body(self):
message_body = self.cleaned_data.get('message_body')
count = 0
for i in message_body:
if i == "{":
count += 1
elif i == "}":
count -= 1
if count < 0:
raise forms.ValidationError('Brackets do not match, Enter valid tags.')
if count != 0:
raise forms.ValidationError('Brackets do not match, Enter valid tags.')
return message_body

View file

@ -0,0 +1,70 @@
# Generated by Django 2.2.3 on 2019-08-09 11:29
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('contacts', '0003_merge_20190214_1427'),
('accounts', '0008_account_assigned_to'),
]
operations = [
migrations.RenameField(
model_name='email',
old_name='sent_at',
new_name='created_on',
),
migrations.RenameField(
model_name='email',
old_name='sender',
new_name='from_account',
),
migrations.RemoveField(
model_name='email',
name='recipient',
),
migrations.AddField(
model_name='email',
name='from_email',
field=models.EmailField(default=django.utils.timezone.now, max_length=254),
preserve_default=False,
),
migrations.AddField(
model_name='email',
name='recipients',
field=models.ManyToManyField(related_name='recieved_email', to='contacts.Contact'),
),
migrations.AddField(
model_name='email',
name='rendered_message_body',
field=models.TextField(null=True),
),
migrations.AddField(
model_name='email',
name='scheduled_date_time',
field=models.DateTimeField(null=True),
),
migrations.AddField(
model_name='email',
name='scheduled_later',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='email',
name='timezone',
field=models.CharField(default='UTC', max_length=100),
),
migrations.CreateModel(
name='EmailLog',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_sent', models.BooleanField(default=False)),
('contact', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='contact_email_log', to='contacts.Contact')),
('email', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='email_log', to='accounts.Email')),
],
),
]

View file

@ -118,20 +118,27 @@ class Account(models.Model):
class Email(models.Model):
sender = models.ForeignKey(
from_account = models.ForeignKey(
Account, related_name='sent_email', on_delete=models.SET_NULL, null=True)
recipient = models.ForeignKey(
Contact, related_name='recieved_email', on_delete=models.SET_NULL, null=True)
sent_at = models.DateTimeField(auto_now_add=True)
recipients = models.ManyToManyField(Contact, related_name='recieved_email')
message_subject = models.TextField(null=True)
message_body = models.TextField(null=True)
timezone = models.CharField(max_length=100, default='UTC')
scheduled_date_time = models.DateTimeField(null=True)
scheduled_later = models.BooleanField(default=False)
created_on = models.DateTimeField(auto_now_add=True)
from_email = models.EmailField()
rendered_message_body = models.TextField(null=True)
def __str__(self):
return self.message_body
return self.message_subject
def save(self, *args, **kwargs):
# prevent user from sending messages to themselves
if self.sender.email != self.recipient.email:
super(Email, self).save(*args, **kwargs)
class EmailLog(models.Model):
""" this model is used to track if the email is sent or not """
email = models.ForeignKey(Email, related_name='email_log', on_delete=models.SET_NULL, null=True)
contact = models.ForeignKey(Contact, related_name='contact_email_log', on_delete=models.SET_NULL, null=True)
is_sent = models.BooleanField(default=False)

View file

@ -1,34 +1,51 @@
from datetime import datetime
import pytz
from celery.task import task
from django.conf import settings
from django.core.mail import EmailMessage
from django.template.loader import render_to_string
from django.shortcuts import reverse
from django.template import Context, Template
from django.template.loader import render_to_string
from django.utils import timezone
from accounts.models import Account, Email
from accounts.models import Account, Email, EmailLog
from common.models import User
from common.utils import convert_to_custom_timezone
from contacts.models import Contact
@task
def send_email(subject, html_content, from_email=None,
recipients=[]):
account = Account.objects.filter(id=from_email).first()
if account:
contacts = recipients
for contact in contacts:
contact_obj = Contact.objects.filter(email=contact).first()
if contact_obj:
Email.objects.create(sender=account,
recipient=contact_obj,message_subject=subject,
message_body=html_content)
msg = EmailMessage(
subject,
html_content,
from_email=account.email,
to=[contact_obj.email, ]
)
msg.content_subtype = "html"
msg.send()
def send_email(email_obj_id):
email_obj = Email.objects.filter(id=email_obj_id).first()
if email_obj:
from_email = email_obj.from_email
contacts = email_obj.recipients.all()
for contact_obj in contacts:
if not EmailLog.objects.filter(email=email_obj, contact=contact_obj, is_sent=True).exists():
html = email_obj.message_body
context_data = {
'email': contact_obj.email if contact_obj.email else '',
'name': contact_obj.first_name if contact_obj.first_name else '' + ' ' + contact_obj.last_name if contact_obj.last_name else '',
}
try:
html_content = Template(html).render(Context(context_data))
subject = email_obj.message_subject
msg = EmailMessage(
subject,
html_content,
from_email=from_email,
to=[contact_obj.email, ]
)
msg.content_subtype = "html"
res = msg.send()
if res:
email_obj.rendered_message_body = html_content
email_obj.save()
EmailLog.objects.create(email=email_obj, contact=contact_obj, is_sent=True)
except Exception as e:
print(e)
pass
@ -59,3 +76,26 @@ def send_email_to_assigned_user(recipients, from_email, domain='demo.django-crm.
)
msg.content_subtype = "html"
msg.send()
@task
def send_scheduled_emails():
email_objs = Email.objects.filter(scheduled_later=True)
# TODO: modify this later , since models are updated
for each in email_objs:
scheduled_date_time = each.scheduled_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, to_utc=True)
if (
str(each.scheduled_date_time.date()) == str(sent_time.date()) and
str(scheduled_date_time.hour) == str(sent_time.hour) and
(str(scheduled_date_time.minute + 5) < str(sent_time.minute) or
str(scheduled_date_time.minute - 5) > str(sent_time.minute))
):
send_email.delay(each.id)

View file

@ -4,10 +4,19 @@
{% load thumbnail %}
{% block extralinks %}
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" />
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/selectize.js/0.12.6/css/selectize.bootstrap3.min.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/tempusdominus-bootstrap-4/5.0.0-alpha14/css/tempusdominus-bootstrap-4.min.css" />
<style>
.form-group label {
font-weight: 800;
}
#editor-container {
height: 20rem;
}
</style>
{% endblock %}
{% block content %}
@ -102,11 +111,11 @@
{% if open_accounts|length > 0 %}
<tr>
<th width="5%">ID</th>
<th width="15%">Name</th>
<th width="10%">Created By</th>
<th width="15%">City</th>
<th width="15%">State</th>
<th width="15%">Created On</th>
<th width="12%">Name</th>
<th width="8%">Created By</th>
<th width="5%">City</th>
<th width="5%">State</th>
<th width="10%">Created On</th>
<th width="10%">Tags</th>
<th width="10%">Actions</th>
</tr>
@ -161,6 +170,13 @@
{% endwith %}
</td>
<td class="actions">
{% if each_account.contacts.all %}
<a href="{% url 'accounts:create_mail' %}" class="btn btn-primary edit account_send_mail"
data-account="{{each_account.id}}" data-account-email="{{each_account.email}}"
data-toggle="modal" data-target="#send_email_to_contacts"><i
class="fas fa-paper-plane"></i>
</a>
{% endif %}
<a href="{% url 'accounts:view_account' each_account.id %}" class="btn btn-info edit"><i
class="fas fa-eye"></i></a>
<a href="{% url 'accounts:edit_account' each_account.id %}" class="btn btn-success edit"><i
@ -647,10 +663,151 @@
{% endfor %}
<div id="send_email_to_contacts" class="modal fade bd-example-modal-lg" tabindex="-1" role="dialog"
aria-labelledby="myLargeModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="col-md-12">
<form id="add_form_mail" method="POST" action="{% url 'accounts:create_mail' %}" 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">
Send Mail
</div>
<form>
<div class="row marl">
<div class="col-md-12">
<div class="form-group">
<label for="">From Email</label>
<!-- <input type="text" class="form-control" name="contacts_list" id="id_contacts"> -->
<!-- <select name="recipients" id="_id_recipients">
<option value="">email</option>
</select> -->
<input class="form-control" type="text" placeholder="From Email" name="from_email" id="set_from_email">
<span id="id_from_email"></span>
</div>
<span id="id_recipientsa"></span>
</div>
<div class="col-md-12">
<div class="form-group">
<label for="">Contacts</label>
<!-- <input type="text" class="form-control" name="contacts_list" id="id_contacts"> -->
<!-- <select name="recipients" id="_id_recipients">
<option value="">email</option>
</select> -->
<input type="text" placeholder="Contacts" name="recipients" id="_id_recipients">
</div>
<span id="id_recipients"></span>
</div>
<div class="col-md-12">
<div class="form-group">
<label for="">Subject</label>
<input type="text" class="form-control" name="message_subject" placeholder="Enter Subject">
</div>
<span id="id_message_subject"></span>
<input type="hidden" value="" name="account_id" id="mail_account_id">
</div>
<div class="col-md-12">
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label><input type="checkbox" name="scheduled_later" value="true" class="scheduled_later">
Schedule Later</label>
<span id="id_schedule_later"></span>
</div>
</div>
<div class="col-md-4 timezone-div" style="display: none;">
<div class="form-group">
<label>Timezone</label>
<select name="timezone" id="timezone"
class="form-control timezone-select js-example-basic-single">
<option value="">Select a Timezone</option>
{% for timezone in timezones %}
<option value="{{timezone.0}}"
{% if timezone.0 == settings_timezone %}selected{% endif %}>{{timezone.1}}</option>
{% endfor %}
</select>
<span id="id_timezone"></span>
</div>
</div>
<div class="col-md-4 timezone-div" style="display: none;">
<div class="form-group">
<label for="">Schedule Date Time</label><span class="error_marker" style="color:red">
*</span>
<div class="input-group date" id="datetimepicker1" data-target-input="nearest">
<input type="text" class="form-control datetimepicker-input"
data-target="#datetimepicker1" name="scheduled_date_time" />
<div class="input-group-append" data-target="#datetimepicker1"
data-toggle="datetimepicker">
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
</div>
</div>
<!-- <input type="text" class="form-control" name="schedule_date_time" value="" placeholder="Schedule Date Time"> -->
<span id="id_scheduled_date_time"></span>
</div>
</div>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label>Message</label>
<div id="editor-container"></div>
</div>
<input type="hidden" name="message_body" id="html_content">
<span id="id_message_body"></span>
</div>
<!-- <div class="col-md-12 text-center">
<pre id="myPrecious"></pre>
<div id="justText"></div>
<div id="justHtml"></div>
</div> -->
<div class="col-md-12">
<div class="heading">Personalization tags</div>
<p class="desc">The following tags can be used on both Plain text or HTML. Add these tags to
customize the email template, these tags will be replaced with the associated contact details
while the mails are sent.</p>
<ul>
<li>
<strong>Name: </strong>
<code>{% templatetag openvariable %} name {% templatetag closevariable %}</code>
</li>
<li>
<strong>Email: </strong>
<code>{% templatetag openvariable %} email {% templatetag closevariable %}</code>
</li>
<!-- <li>
<strong>Two digit day of the month (eg. 01 to 31):</strong>
<code>[currentdaynumber]</code>
</li> -->
</ul>
</div>
<div class="col-md-12 text-center">
<button type="submit" class="primary_btn mar_btm save_btn">Send</button>
<a href="#" class="primary_btn" data-dismiss="modal">Cancel</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block js_block %}
<script src="{% static 'js/ajaxForm.js' %}"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/selectize.js/0.12.6/js/standalone/selectize.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment-with-locales.min.js"></script>
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/tempusdominus-bootstrap-4/5.0.0-alpha14/js/tempusdominus-bootstrap-4.min.js"></script>
<script type="text/javascript">
$("#close-tab").click(function (e) {
$("#tab_status").val('Closed');
@ -662,37 +819,6 @@
window.location.href = url;
});
// $(".account_modal").click(function (e) {
// console.log('clicked')
// console.log(e)
// console.log($(this).attr('data-id'))
// $.ajax({
// url:`get-account-details/${$(this).attr('data-id')}/`,
// type: 'GET',
// success:function(data){
// console.log(data)
// console.log((data.data.fields))
// $('#modal_body_ajax').empty()
// for(key in data.data.fields){
// console.log(key, data.data.fields[key])
// $('#modal_body_ajax').append(
// "<div class=''><span>" + key + "</span>&nbsp;&nbsp;&nbsp;&nbsp;" + "<span>" + data.data.fields[key] + "</span>" + "</div>"
// )
// }
// // $('textarea#modal_data').val(JSON.stringify(data))
// $('#exampleModalCenter_account').modal()
// if(data.error){
// // TODO
// data.fields
// }
// else{
// }
// }
// })
// })
$("#open-tab").click(function (e) {
$("#tab_status").val('Open')
})
@ -759,5 +885,168 @@
$('#accounts_filter').attr("action", $(this).attr("href"));
$('#accounts_filter').submit();
});
$(".timezone-select").select2({
placeholder: "Select a Timezone",
});
$('.scheduled_later').on('change', function () {
// console.log('changed')
if ($('.check_delete:checked').length > 1) {
$('.timezone-div').show();
}
else {
$('.timezone-div').hide();
}
if ($(this).is(":checked")) {
$('.timezone-div').show();
} else {
$('.timezone-div').hide();
}
console.log($(this))
console.log($(this).is(':checked'))
})
$.fn.datetimepicker.Constructor.Default = $.extend({}, $.fn.datetimepicker.Constructor.Default, {
icons: {
time: 'fa fa-clock',
date: 'fa fa-calendar',
up: 'fa fa-arrow-up',
down: 'fa fa-arrow-down',
previous: 'fa fa-chevron-left',
next: 'fa fa-chevron-right',
today: 'far fa-calendar-check-o',
clear: 'fa fa-trash',
close: 'fa fa-times'
}
});
$('#datetimepicker1').datetimepicker({
format: "YYYY-MM-DD HH:mm"
});
var selectedAccountId = '';
var contactOptions = [];
// $("#id_contacts").select2();
$('.account_send_mail').click(function (e) {
// console.log($(this).attr('data-account'))
selectedAccountId = $(this).attr('data-account')
$('#mail_account_id').val(selectedAccountId)
$('#set_from_email').val($(this).attr('data-account-email'))
$.ajax({
url: "{% url 'accounts:get_contacts_for_account' %}",
type: 'POST',
dataType: 'json',
data: {
'account_id': selectedAccountId,
},
error: function (err) {
console.log(err)
},
success: function (res) {
contactOptions = [];
contactOptions = res;
$('#_id_recipients').selectize({
valueField: 'id',
labelField: 'name',
searchField: ['name', 'email'],
// options: [
// {email: 'brian@thirdroute.com', name: 'Brian Reavis'},
// {email: 'nikola@tesla.com', name: 'Nikola Tesla'},
// {email: 'someone@gmail.com'}
// ],
options: res,
render: {
item: function (item, escape) {
return '<div class="selectize_options_remove">' +
(item.name ? '<span class="name">' + escape(item.name) + '</span>' : '') +
(item.email ? '<span class="email">' + escape(item.email) + '</span>' : '') +
'</div>';
},
option: function (item, escape) {
var label = item.name || item.email;
var caption = item.name ? item.email : null;
return '<div class="selectize_options_remove">' +
'<span class="label">' + escape(label) + '</span>' +
(caption ? '<span class="caption">' + escape(caption) + '</span>' : '') +
'</div>';
}
},
});
}
});
})
$("#send_email_to_contacts").on('hidden.bs.modal', function () {
$('#_id_recipients')[0].selectize.destroy()
$('p.failure').remove();
$('#_id_recipients').val('')
})
$("#send_email_to_contacts").on('shown.bs.modal', function () {
});
var quill = new Quill('#editor-container', {
modules: {
toolbar: [
[{ header: [1, 2, false] }],
['bold', 'italic', 'underline'],
['code-block', 'link']
]
},
placeholder: 'Compose mail...',
theme: 'snow' // or 'bubble'
});
var preciousContent = document.getElementById('myPrecious');
var justTextContent = document.getElementById('justText');
var justHtmlContent = document.getElementById('justHtml');
quill.on('text-change', function () {
var delta = quill.getContents();
var text = quill.getText();
var justHtml = quill.root.innerHTML;
/*preciousContent.innerHTML = JSON.stringify(delta);
justTextContent.innerHTML = text;
justHtmlContent.innerHTML = justHtml;*/
$('#html_content').val(justHtml);
$('.ql-syntax').css({"background-color":"#23241f","color":"#f8f8f2","overflow":"visible","border-radius":"3px","white-space":"pre-wrap","margin-bottom":"5px","margin-top":"5px","padding":"5px 10px"})
});
$('.save_btn').click(function (e) {
e.preventDefault();
$("form#add_form_mail").submit()
})
$('form#add_form_mail').ajaxForm({
type: 'POST',
dataType: 'json',
url: $(this).attr('action'),
data: $(this).serialize(),
beforeSend: function (e) {
$('.ql-syntax').css({"background-color":"#23241f","color":"#f8f8f2","overflow":"visible","border-radius":"3px","white-space":"pre-wrap","margin-bottom":"5px","margin-top":"5px","padding":"5px 10px"})
},
success: function (data) {
console.log("data", data)
if (data.error == false) {
window.location = "{% url 'accounts: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 %}

View file

@ -3,8 +3,23 @@
{% load paginate %}
{% block extralinks %}
{% load thumbnail %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/selectize.js/0.12.6/css/selectize.bootstrap3.min.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/selectize.js/0.12.6/css/selectize.bootstrap3.min.css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" />
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/tempusdominus-bootstrap-4/5.0.0-alpha14/css/tempusdominus-bootstrap-4.min.css" />
{% endblock %}
{% block breadcrumb %}
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'accounts:list' %}">Accounts</a></li>
<li class="breadcrumb-item active">Create Mail</li>
</ol>
</nav>
{% endblock breadcrumb %}
{% block content %}
<!-- main_container starts here -->
<div class="main_container">
@ -36,10 +51,51 @@
<div class="col-md-12">
<div class="form-group">
<label for="">Subject</label>
<input type="text" class="form-control" name="message_subject" placeholder="Enter Subject">
<input type="text" class="form-control" name="message_subject" placeholder="Enter Subject">
</div>
<span id="id_message_subject"></span>
</div>
<div class="col-md-9">
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label><input type="checkbox" name="scheduled_later" value="true" class="scheduled_later">
Schedule Later</label>
<span id="id_schedule_later"></span>
</div>
</div>
<div class="col-md-4 timezone-div" style="display: none;">
<div class="form-group">
<label>Timezone</label>
<select name="timezone" id="timezone"
class="form-control timezone-select js-example-basic-single">
<option value="">Select a Timezone</option>
{% for timezone in timezones %}
<option value="{{timezone.0}}"
{% if timezone.0 == settings_timezone %}selected{% endif %}>{{timezone.1}}</option>
{% endfor %}
</select>
<span id="id_timezone"></span>
</div>
</div>
<div class="col-md-4 timezone-div" style="display: none;">
<div class="form-group">
<label for="">Schedule Date Time</label><span class="error_marker" style="color:red">
*</span>
<div class="input-group date" id="datetimepicker1" data-target-input="nearest">
<input type="text" class="form-control datetimepicker-input"
data-target="#datetimepicker1" name="scheduled_date_time" />
<div class="input-group-append" data-target="#datetimepicker1"
data-toggle="datetimepicker">
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
</div>
</div>
<!-- <input type="text" class="form-control" name="schedule_date_time" value="" placeholder="Schedule Date Time"> -->
<span id="id_scheduled_date_time"></span>
</div>
</div>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label>Message</label>
@ -53,9 +109,29 @@
<div id="justText"></div>
<div id="justHtml"></div>
</div> -->
<div class="col-md-12">
<div class="heading">Personalization tags</div>
<p class="desc">The following tags can be used on both Plain text or HTML. Add these tags to
customize the email template, these tags will be replaced with the associated contact details
while the mails are sent.</p>
<ul>
<li>
<strong>Name: </strong>
<code>{% templatetag openvariable %} name {% templatetag closevariable %}</code>
</li>
<li>
<strong>Email: </strong>
<code>{% templatetag openvariable %} email {% templatetag closevariable %}</code>
</li>
<!-- <li>
<strong>Two digit day of the month (eg. 01 to 31):</strong>
<code>[currentdaynumber]</code>
</li> -->
</ul>
</div>
<div class="col-md-12 text-center">
<button type="submit" class="primary_btn mar_btm save_btn">Send</button>
<a href="{% url 'accounts:create_mail' account_id %}" class="primary_btn">Cancel</a>
<a href="#" class="primary_btn">Cancel</a>
</div>
</div>
</form>
@ -71,64 +147,121 @@
{% block js_block %}
<script src="https://malsup.github.com/jquery.form.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/selectize.js/0.12.6/js/standalone/selectize.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script>
<!-- <script src="https://malsup.github.com/jquery.form.js"></script> -->
<script src="{% static 'js/ajaxForm.js' %}"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment-with-locales.min.js"></script>
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/tempusdominus-bootstrap-4/5.0.0-alpha14/js/tempusdominus-bootstrap-4.min.js"></script>
<script>
$(document).ready(function () {
// $('[name="schedule_later"]').on('change', function (e) {
// if ($(this).is(":checked")) {
// $('.timezone-div').show();
// } else {
// $('.timezone-div').hide();
// }
// console.log($(this))
// console.log($(this).is(':checked'))
// })
$(".timezone-select").select2({
placeholder: "Select a Timezone",
});
$('.scheduled_later').on('change', function () {
// console.log('changed')
if ($('.check_delete:checked').length > 1) {
$('.timezone-div').show();
}
else {
$('.timezone-div').hide();
}
if ($(this).is(":checked")) {
$('.timezone-div').show();
} else {
$('.timezone-div').hide();
}
console.log($(this))
console.log($(this).is(':checked'))
})
$.fn.datetimepicker.Constructor.Default = $.extend({}, $.fn.datetimepicker.Constructor.Default, {
icons: {
time: 'fa fa-clock',
date: 'fa fa-calendar',
up: 'fa fa-arrow-up',
down: 'fa fa-arrow-down',
previous: 'fa fa-chevron-left',
next: 'fa fa-chevron-right',
today: 'far fa-calendar-check-o',
clear: 'fa fa-trash',
close: 'fa fa-times'
}
});
$('#datetimepicker1').datetimepicker({
format: "YYYY-MM-DD HH:mm"
});
// $("#id_contacts").select2();
var REGEX_EMAIL = '([a-z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+/=?^_`{|}~-]+)*@' +
'(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)';
'(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)';
$('#_id_recipients').selectize({
persist: false,
maxItems: null,
valueField: 'email',
labelField: 'name',
searchField: ['name', 'email'],
// options: [
// {email: 'brian@thirdroute.com', name: 'Brian Reavis'},
// {email: 'nikola@tesla.com', name: 'Nikola Tesla'},
// {email: 'someone@gmail.com'}
// ],
options: {{contacts_list|safe}},
persist: false,
maxItems: null,
valueField: 'email',
labelField: 'name',
searchField: ['name', 'email'],
// options: [
// {email: 'brian@thirdroute.com', name: 'Brian Reavis'},
// {email: 'nikola@tesla.com', name: 'Nikola Tesla'},
// {email: 'someone@gmail.com'}
// ],
options: {{ contacts_list| safe}},
render: {
item: function(item, escape) {
return '<div>' +
(item.name ? '<span class="name">' + escape(item.name) + '</span>' : '') +
(item.email ? '<span class="email">' + escape(item.email) + '</span>' : '') +
'</div>';
item: function (item, escape) {
return '<div>' +
(item.name ? '<span class="name">' + escape(item.name) + '</span>' : '') +
(item.email ? '<span class="email">' + escape(item.email) + '</span>' : '') +
'</div>';
},
option: function (item, escape) {
var label = item.name || item.email;
var caption = item.name ? item.email : null;
return '<div>' +
'<span class="label">' + escape(label) + '</span>' +
(caption ? '<span class="caption">' + escape(caption) + '</span>' : '') +
'</div>';
}
},
option: function(item, escape) {
var label = item.name || item.email;
var caption = item.name ? item.email : null;
return '<div>' +
'<span class="label">' + escape(label) + '</span>' +
(caption ? '<span class="caption">' + escape(caption) + '</span>' : '') +
'</div>';
}
},
createFilter: function(input) {
var match, regex;
createFilter: function (input) {
var match, regex;
// email@address.com
regex = new RegExp('^' + REGEX_EMAIL + '$', 'i');
match = input.match(regex);
if (match) return !this.options.hasOwnProperty(match[0]);
// email@address.com
regex = new RegExp('^' + REGEX_EMAIL + '$', 'i');
match = input.match(regex);
if (match) return !this.options.hasOwnProperty(match[0]);
// name <email@address.com>
regex = new RegExp('^([^<]*)\<' + REGEX_EMAIL + '\>$' , 'i' ); match=input.match(regex); if (match) return
!this.options.hasOwnProperty(match[2]); return false; }, create: function(input) { if ((new RegExp('^' +
REGEX_EMAIL + '$' , 'i' )).test(input)) { return {email: input}; } var match=input.match(new RegExp('^([^<]*)\<'
+ REGEX_EMAIL + '\>$' , 'i' )); if (match) { return { email : match[2], name : $.trim(match[1]) }; }
alert('Invalid email address.'); return false; } });
// name <email@address.com>
regex = new RegExp('^([^<]*)\<' + REGEX_EMAIL + '\>$', 'i'); match = input.match(regex); if (match) return
!this.options.hasOwnProperty(match[2]); return false;
}, create: function (input) {
if ((new RegExp('^' +
REGEX_EMAIL + '$', 'i')).test(input)) { return { email: input }; } var match = input.match(new RegExp('^([^<]*)\<'
+ REGEX_EMAIL + '\>$', 'i')); if (match) { return { email: match[2], name: $.trim(match[1]) }; }
alert('Invalid email address.'); return false;
} });
});
var quill = new Quill('#editor-container', {
modules: {
toolbar: [
[{ header: [1, 2, false] }],
['bold', 'italic', 'underline'],
['image', 'code-block']
['code-block', 'link']
]
},
placeholder: 'Compose an epic...',
placeholder: 'Compose mail...',
theme: 'snow' // or 'bubble'
});

View file

@ -9,6 +9,10 @@
.opacity_block {
opacity: 0.2;
}
label {
font-weight: 600;
color: black;
}
</style>
{% endblock extralinks %}
@ -312,8 +316,8 @@
<a href="{% url 'contacts:add_contact' %}?view_account={{account_record.id}}"><span
class="fa fa-plus pull-right" style="color:black;"></span></a>
{% if contacts|length > 0 %}
<a href="{% url 'accounts:create_mail' account_record.id %}" style="color:black">
<span class="pull-right mr-2">Send Mail</span>
<!-- <a href="{% url 'accounts:create_mail' %}" style="color:black">
<span class="pull-right mr-2">Send Mail</span> -->
</a>
{% endif %}
</div>
@ -472,7 +476,7 @@
<div class="panel-heading-list card-title view-pad">
Tasks
<a href="{% url 'tasks:task_create' %}?view_account={{account_record.id}}"><span
class="fa fa-plus pull-right" style="color:black;"></span></a>
class="fa fa-plus pull-right" style="color:black;"></span></a>
</div>
<div class="table-responsive">
<table class="table table-bordered table-striped table-condensed">
@ -568,6 +572,57 @@
</div>
</div>
</form>
<form method='GET' class="accountdetails_row">
<div class="table_container_row row marl no-gutters">
<div class="col-md-12">
<div class="card">
<div class="card-body">
<div class="panel-heading-list card-title view-pad">
Sent Mails
<a href="#"><span></span></a>
</div>
<div class="table-responsive">
<table class="table table-bordered table-striped table-condensed">
{% if emails|length > 0 %}
<thead>
<tr>
<th width="5%" style="text-align:center;">S.No</th>
<th width="30%" style="text-align:center;">From mail</th>
<th width="20%" style="text-align:center;">Contact</th>
<th width="20%" style="text-align:center;">Subject</th>
<th width="25%" style="text-align:center;">Created On </th>
<th width="25%" style="text-align:center;">View </th>
</tr>
</thead>
{% endif %}
<tbody>
{% for email in emails %}
<tr id="invoice_{{invoice.id}}" class="text-center">
<td class="text-center" scope="row">{{ forloop.counter }}</td>
<td class="text-center">{{email.from_email}}</td>
<td class="text-center">{{email.recipients.count}}</td>
<td class="text-center">{{email.message_subject}}</td>
<td class="text-center">{{email.created_on}}</td>
<td class="text-center">
<a href="#" class="btn btn-primary get_email_info" style="color: white;" data-toggle="modal"
data-target="#email_info_ajax" data-account="{{email.id}}">View</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% ifequal emails|length 0 %}
<h6 class="text-center">No Emails Records Found</h6>
{% endifequal %}
</div>
</div>
<br clear="all">
</div>
</div>
</form>
</div>
<div class="overview_form_block row marl justify-content-center" id="loading_spinner"
style="z-index: 10; left: 0; right: 0; vertical-align: middle; position: absolute; margin-top: 15%;">
@ -578,6 +633,56 @@
</div>
</div>
</div>
<div class="modal fade bd-example-modal-lg" id="email_info_ajax" tabindex="-1" role="dialog"
aria-labelledby="myLargeModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="" id="">
<div class="" style="padding: 1em;">
<div class="col-md-12">
<div class="card" style="padding: 1em;">
<div class="card-body" id="datashow" style="margin: 0; padding: 0;">
<div class="row marl no-gutters">
<div class="col-md-12">
<div class="filter_col col-md-12">
<div class="form-group">
<label class="contact_field_label" for="id_title" data-name="title">Subject</label>
<div class="contact_field" id="mail_subject" data-name="name"> </div>
</div>
</div>
<div class="filter_col col-md-12">
<div class="form-group">
<label class="contact_field_label" for="id_status" data-name="status">Body</label>
<div class="contact_field" id="mail_body" data-name="status"> </div>
</div>
</div>
<div class="filter_col col-md-12">
<div class="form-group">
<label class="contact_field_label" for="id_status" data-name="status">Contacts</label>
<div class="contact_field" id="mail_contacts" data-name="status"> </div>
</div>
</div>
<div class="filter_col col-md-12">
<div class="form-group">
<label class="contact_field_label" for="id_created_on" data-name="created_on">Created
On</label>
<div class="contact_field" id="mail_created_on" data-name="created_on">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- ACCOUNTS VIEW ENDS -->
{% endblock content %}
{% block js_block %}
@ -760,5 +865,52 @@
}
}
})
</script>
<script>
$('.get_email_info').click(function (e) {
var email_account_id = $(this).attr('data-account')
$.ajax({
url: "{% url 'accounts:get_email_data_for_account' %}",
type: "POST",
dataType: 'json',
data: { 'email_account_id': email_account_id, },
success: function (data) {
if (data.error) {
alert('No Data found.')
} else {
$('#mail_subject').html(data.subject);
$('#mail_body').html(data.body);
d = new Date(data.created_on)
let options = {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
};
d = d.toLocaleString('en-us', options);
$('#mail_created_on').html(d);
// $('#mail_emails').html(data.contacts);
if (data.contacts.length >= 1) {
var contacts = data.contacts.map((value) => `<p>
${value.email}
</p>`)
$('#mail_contacts').html(contacts);
}
}
}
})
})
// $('#email_info_ajax').on('hidden.bs.modal', function () {
// // $('#mail_subject').html('');
// // $('#mail_body').html('');
// // $('#mail_emails').html('');
// // do something…
// })
</script>
{% endblock js_block %}

View file

@ -1,11 +1,13 @@
from django.test import TestCase
from cases.models import Case
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
from django.test import TestCase
from django.urls import reverse
from django.utils import timezone
from accounts.models import Account, Tags, Email
from cases.models import Case
from common.models import Address, Attachments, Comment, User
from contacts.models import Contact
from leads.models import Lead
from teams.models import Teams
@ -49,21 +51,11 @@ class AccountCreateTest(object):
)
self.client.login(email='johnAccount@example.com', password='password')
self.lead = Lead.objects.create(title="LeadCreation",
first_name="john lead",
last_name="doe",
email="johnLead@example.com",
address_line="",
street="street name",
city="city name",
state="state",
postcode="5079",
country="IN",
website="www.example.com",
status="assigned",
source="Call",
opportunity_amount="700",
description="lead description",
created_by=self.user)
first_name="john lead",last_name="doe",email="johnLead@example.com",
address_line="",street="street name",city="city name",
state="state",postcode="5079",country="IN",
website="www.example.com",status="assigned",source="Call",
opportunity_amount="700",description="lead description",created_by=self.user)
self.lead.assigned_to.add(self.user)
self.address = Address.objects.create(
street="street number",
@ -346,25 +338,25 @@ class TestCreateLeadPostView(AccountCreateTest, TestCase):
upload_file = open('static/images/user.png', 'rb')
response = self.client.post(reverse(
'accounts:new_account'), {"name": "janeLead",
"email": "janeLead@example.com",
"phone": "+911234567891",
"billing_address_line": "address line",
"billing_street": "street name",
"billing_city": "city name",
"billing_state": "usa",
"billing_postcode": "1234",
"billing_country": "IN",
"website": "www.example.com",
"created_by": self.user,
"status": "open",
"industry": "SOFTWARE",
"description": "Test description",
"lead": str(self.lead.id),
'contacts': str(self.contact.id),
'tags': 'tag1',
'account_attachment': SimpleUploadedFile(
upload_file.name, upload_file.read())
},
"email": "janeLead@example.com",
"phone": "+911234567891",
"billing_address_line": "address line",
"billing_street": "street name",
"billing_city": "city name",
"billing_state": "usa",
"billing_postcode": "1234",
"billing_country": "IN",
"website": "www.example.com",
"created_by": self.user,
"status": "open",
"industry": "SOFTWARE",
"description": "Test description",
"lead": str(self.lead.id),
'contacts': str(self.contact.id),
'tags': 'tag1',
'account_attachment': SimpleUploadedFile(
upload_file.name, upload_file.read())
},
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, 200)
@ -401,9 +393,90 @@ class test_account_forms(AccountCreateTest, TestCase):
response = self.client.get(reverse('accounts:new_account'))
self.assertEqual(200, response.status_code)
response = self.client.get(reverse('accounts:create_mail', args=(self.account.id,)))
response = self.client.get(reverse('accounts:create_mail') + '?account_id={}'.format(self.account.id))
self.assertEqual(200, response.status_code)
data = {
'account_id': self.account.id,
'message_body': 'message body {{email}}',
'message_subject':'message subject',
'recipients':[self.contact.id, self.contact_user1.id],
'scheduled_date_time': timezone.now(),
}
response = self.client.post(reverse('accounts:create_mail'), data)
self.assertEqual(200, response.status_code)
data = {
'account_id': self.account.id,
'message_body': 'message body {{email}',
'message_subject':'message subject',
'recipients':[self.contact.id, self.contact_user1.id],
'scheduled_date_time': '',
'scheduled_later': 'true',
}
response = self.client.post(reverse('accounts:create_mail'), data)
self.assertEqual(200, response.status_code)
data = {
'account_id': self.account.id,
'message_body': 'message body {{email}}}',
'message_subject':'message subject',
'recipients':[self.contact.id, self.contact_user1.id],
'scheduled_date_time': timezone.now().strftime('%Y-%m-%d %H:%M'),
}
response = self.client.post(reverse('accounts:create_mail'), data)
self.assertEqual(200, response.status_code)
data = {
'account_id': self.account.id,
'message_body': 'message body {{email}}',
'message_subject':'message subject',
'recipients':[self.contact.id, self.contact_user1.id],
'scheduled_date_time': timezone.now().strftime('%Y-%m-%d %H:%M'),
'scheduled_later':'true',
}
response = self.client.post(reverse('accounts:create_mail'), data)
self.assertEqual(200, response.status_code)
data = {
'account_id': self.account.id,
'message_body': 'message body {{email}}',
'message_subject':'message subject',
'recipients':[self.contact.id, self.contact_user1.id],
'from_email': 'jane@doe.com',
'timezone':'UTC',
}
response = self.client.post(reverse('accounts:create_mail'), data)
self.assertEqual(200, response.status_code)
data = {
'account_id': 0,
'message_body': 'message body {{email}}',
'message_subject':'message subject',
'recipients':[self.contact.id, self.contact_user1.id],
'from_email': 'jane@doe.com',
'timezone':'UTC',
}
response = self.client.post(reverse('accounts:create_mail'), data)
self.assertEqual(200, response.status_code)
data = {
'account_id': self.account.id,
'message_body': 'message body {{email}}',
'message_subject':'message subject',
'recipients':[self.contact.id, self.contact_user1.id],
'from_email': 'jane@doe.com',
'timezone':'UTC',
'scheduled_later': 'true',
'scheduled_date_time': timezone.now().strftime('%Y-%m-%d %H:%M'),
}
response = self.client.post(reverse('accounts:create_mail'), data)
self.assertEqual(200, response.status_code)
class test_account_models(AccountCreateTest, TestCase):
def test_account_model(self):
@ -545,3 +618,186 @@ class test_account_views_list(AccountCreateTest, TestCase):
'teams': [self.team_account.id,]},
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(200, response.status_code)
self.account_edit = Account.objects.create(
name="account edit", email="johndoe@example.com", phone="123456789",
billing_address_line="", billing_street="street name",
billing_city="city name",
billing_state="state", billing_postcode="1234",
billing_country="US",
website="www.example.como", created_by=self.user, status="open",
industry="SOFTWARE", description="Testing")
self.account_by_user = Account.objects.create(
name="account edit", email="johndoe@example.com", phone="123456789",
billing_address_line="", billing_street="street name",
billing_city="city name",
billing_state="state", billing_postcode="1234",
billing_country="US",
website="www.example.como", created_by=self.user, status="open",
industry="SOFTWARE", description="Testing")
upload_file = open('static/images/user.png', 'rb')
response = self.client.post(reverse('accounts:edit_account', args=(self.account_edit.id,)), {
'name': "account", 'email': "johndoe@example.com",
'phone': "+91-123-456-7894",
'billing_address_line': "address line",
'billing_street': "billing street",
'billing_city': "billing city",
'billing_state': "state",
'billing_postcode': "1234",
'billing_country': "IN",
'website': "www.example.com",
'industry': "SOFTWARE", 'description': "Testing",
'contacts':[self.contact_user1.id,],
'tags': self.tag_name.name + ', another tag edit',
'assigned_to' : [self.user.id, ],
'teams': [self.team_account.id,],
'savenewform': 'true',
'account_attachment': SimpleUploadedFile(
upload_file.name, upload_file.read())})
self.assertEqual(response.status_code, 302)
response = self.client.post(reverse('accounts:edit_account', args=(self.account_edit.id,)), {
'name': "account", 'email': "johndoe@example.com",
'phone': "+91-123-456-7894",
'billing_address_line': "address line",
'billing_street': "billing street",
'billing_city': "billing city",
'billing_state': "state",
'billing_postcode': "1234",
'billing_country': "IN",
'website': "www.example.com",
'industry': "SOFTWARE", 'description': "Testing",
'contacts':[self.contact_user1.id,],
'tags': self.tag_name.name + ', another tag edit',
'teams': [self.team_account.id,],
'savenewform': 'true',},
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, 200)
response = self.client.post(reverse('accounts:edit_account', args=(self.account_edit.id,)), {
'name': "account", 'email': "johndoe@example.com",
'phone': "+91-123-456 ",
'billing_address_line': "address line",
'billing_street': "billing street",
'billing_city': "billing city",
'billing_state': "state",
'billing_postcode': "1234",
'billing_country': "IN",
'website': "www.example.com",
'industry': "SOFTWARE", 'description': "Testing",
'contacts':[self.contact_user1.id,],
'tags': self.tag_name.name + ', another tag edit',
'teams': [self.team_account.id,],
'savenewform': 'true',},
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, 200)
self.client.logout()
self.client.login(email='janeAccount@example.com', password='password')
response = self.client.get(reverse('accounts:edit_account', args=(self.account_by_user.id,)), {})
self.assertEqual(403, response.status_code)
self.account_by_user1 = Account.objects.create(
name="account edit", email="johndoe@example.com", phone="123456789",
billing_address_line="", billing_street="street name",
billing_city="city name",
billing_state="state", billing_postcode="1234",
billing_country="US",
website="www.example.como", created_by=self.user1, status="open",
industry="SOFTWARE", description="Testing")
response = self.client.get(reverse('accounts:edit_account', args=(self.account_by_user1.id,)), {})
self.assertEqual(200, response.status_code)
response = self.client.get(reverse('accounts:remove_account', args=(self.account_by_user.id,)), {})
self.assertEqual(403, response.status_code)
response = self.client.post(reverse('accounts:add_comment'), {'accountid':self.account_by_user.id})
self.assertEqual(200, response.status_code)
response = self.client.post(reverse('accounts:edit_comment'), {'commentid':self.comment.id})
self.assertEqual(200, response.status_code)
self.client.logout()
self.client.login(email='johnAccount@example.com', password='password')
response = self.client.post(reverse('accounts:edit_comment'), {'commentid':self.comment.id, 'comment':''})
self.assertEqual(200, response.status_code)
self.client.logout()
self.client.login(email='janeAccount@example.com', password='password')
response = self.client.post(reverse('accounts:remove_comment'), {'comment_id':self.comment.id, 'comment':''})
self.assertEqual(200, response.status_code)
self.client.logout()
self.client.login(email='johnAccount@example.com', password='password')
response = self.client.post(reverse('accounts:add_attachment'), {'accountid':self.account.id, 'comment':''})
self.assertEqual(200, response.status_code)
email_str = Email.objects.create(message_subject='message subject', message_body='message body')
self.assertEqual(str(email_str), 'message subject')
response = self.client.post(reverse('accounts:get_contacts_for_account'), {
'account_id':self.account.id
})
self.assertEqual(200, response.status_code)
self.account.contacts.add(self.contact.id, self.contact_user1.id)
response = self.client.post(reverse('accounts:get_contacts_for_account'), {
'account_id':self.account.id
})
self.assertEqual(200, response.status_code)
response = self.client.get(reverse('accounts:get_contacts_for_account'), {
'account_id':self.account.id
})
self.assertEqual(200, response.status_code)
response = self.client.post(reverse('accounts:get_email_data_for_account'), {
'email_account_id':email_str.id
})
self.assertEqual(200, response.status_code)
response = self.client.get(reverse('accounts:get_email_data_for_account'), {
'email_account_id':email_str.id
})
self.assertEqual(200, response.status_code)
class TestAccountUserMentions(AccountCreateTest, TestCase):
def test_account_views(self):
self.user_created_by = User.objects.create(
first_name="jane",
username='janeAccountCreatedBy',
email='janeAccountCreatedBy@example.com',
role="USER",
has_sales_access=True)
self.user_created_by.set_password('password')
self.user_created_by.save()
self.user_assigned_to = User.objects.create(
first_name="jane",
username='janeAccountUserAssigned',
email='janeAccountUserAssigned@example.com',
role="USER",
has_sales_access=True)
self.user_assigned_to.set_password('password')
self.user_assigned_to.save()
self.account = Account.objects.create(
name="john doe acc created by", email="johndoe@example.com", phone="123456789",
billing_address_line="", billing_street="street name",
billing_city="city name",
billing_state="state", billing_postcode="1234",
billing_country="US",
website="www.example.como", created_by=self.user_created_by, status="open",
industry="SOFTWARE", description="Testing")
self.account.assigned_to.add(self.user_assigned_to.id)
self.client.logout()
self.client.login(email='janeAccountCreatedBy@example.com', password='password')
response = self.client.get(reverse('accounts:view_account', args=(self.account.id,)))
self.assertEqual(200, response.status_code)
self.client.logout()
self.client.login(email='janeAccountUserAssigned@example.com', password='password')
response = self.client.get(reverse('accounts:view_account', args=(self.account.id,)))
self.assertEqual(200, response.status_code)

View file

@ -2,7 +2,8 @@ from django.urls import path
from accounts.views import (
AccountsListView, CreateAccountView, AccountDetailView, AccountUpdateView,
AccountDeleteView, AddCommentView, UpdateCommentView, DeleteCommentView,
AddAttachmentView, DeleteAttachmentsView, create_mail, #get_account_details
AddAttachmentView, DeleteAttachmentsView, create_mail, # get_account_details,
get_contacts_for_account, get_email_data_for_account
)
app_name = 'accounts'
@ -23,8 +24,11 @@ urlpatterns = [
name="add_attachment"),
path('attachment/remove/', DeleteAttachmentsView.as_view(),
name="remove_attachment"),
path('create-mail/<int:account_id>/', create_mail,
name="create_mail"),
# path('get-account-details/<int:account_id>/', get_account_details, name="get_account_details"),
path('create-mail', create_mail, name="create_mail"),
path('get_contacts_for_account/', get_contacts_for_account,
name="get_contacts_for_account"),
path('get_email_data_for_account/', get_email_data_for_account,
name="get_email_data_for_account"),
# path('get-account-details/<int:account_id>/', get_account_details, name="get_account_details"),
]

View file

@ -1,29 +1,35 @@
import pytz
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.views.generic import (
CreateView, UpdateView, DetailView, TemplateView, View, DeleteView, FormView)
from accounts.forms import AccountForm, AccountCommentForm, \
AccountAttachmentForm, EmailForm
from accounts.models import Account, Tags, Email
from common.models import User, Comment, Attachments
from common.utils import INDCHOICES, COUNTRIES, \
CURRENCY_CODES, CASE_TYPE, PRIORITY_CHOICE, STATUS_CHOICE
from contacts.models import Contact
from opportunity.models import Opportunity, STAGES, SOURCES
from cases.models import Case
from django.urls import reverse_lazy, reverse
from leads.models import Lead
from django.contrib.sites.shortcuts import get_current_site
from django.core.exceptions import PermissionDenied
from django.db.models import Q
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse, reverse_lazy
from django.views.generic import (CreateView, DeleteView, DetailView, FormView,
TemplateView, UpdateView, View)
from accounts.forms import (AccountAttachmentForm, AccountCommentForm,
AccountForm, EmailForm)
from accounts.models import Account, Email, Tags
from accounts.tasks import send_email, send_email_to_assigned_user
from cases.models import Case
from common.access_decorators_mixins import (MarketingAccessRequiredMixin,
SalesAccessRequiredMixin,
marketing_access_required,
sales_access_required)
from common.models import Attachments, Comment, User
from common.tasks import send_email_user_mentions
from django.contrib.sites.shortcuts import get_current_site
from common.access_decorators_mixins import (
sales_access_required, marketing_access_required, SalesAccessRequiredMixin, MarketingAccessRequiredMixin)
from common.utils import (CASE_TYPE, COUNTRIES, CURRENCY_CODES, INDCHOICES,
PRIORITY_CHOICE, STATUS_CHOICE)
from contacts.models import Contact
from leads.models import Lead
from opportunity.models import SOURCES, STAGES, Opportunity
from teams.models import Teams
class AccountsListView(SalesAccessRequiredMixin, LoginRequiredMixin, TemplateView):
model = Account
context_object_name = "accounts_list"
@ -87,6 +93,10 @@ class AccountsListView(SalesAccessRequiredMixin, LoginRequiredMixin, TemplateVie
if self.request.POST.get('tab_status'):
tab_status = self.request.POST.get('tab_status')
context['tab_status'] = tab_status
TIMEZONE_CHOICES = [(tz, tz) for tz in pytz.common_timezones]
context["timezones"] = TIMEZONE_CHOICES
context["settings_timezone"] = settings.TIME_ZONE
return context
def get(self, request, *args, **kwargs):
@ -195,7 +205,7 @@ class CreateAccountView(SalesAccessRequiredMixin, LoginRequiredMixin, CreateView
context["industries"] = INDCHOICES
context["countries"] = COUNTRIES
# context["contact_count"] = Contact.objects.count()
if self.request.user.role == 'ADMIN':
if self.request.user.role == 'ADMIN' or self.request.user.is_superuser:
context["leads"] = Lead.objects.exclude(
status__in=['converted', 'closed'])
context["contacts"] = Contact.objects.all()
@ -259,6 +269,7 @@ class AccountDetailView(SalesAccessRequiredMixin, LoginRequiredMixin, DetailView
'comment_permission': comment_permission,
'tasks':account_record.accounts_tasks.all(),
'invoices':account_record.accounts_invoices.all(),
'emails':account_record.sent_email.all(),
'users_mention': users_mention,
})
return context
@ -552,32 +563,75 @@ class DeleteAttachmentsView(LoginRequiredMixin, View):
return JsonResponse(data)
@login_required
def create_mail(request, account_id):
def create_mail(request):
if request.method == 'GET':
account = get_object_or_404(Account, pk=account_id)
account = get_object_or_404(Account, pk=request.GET.get('account_id'))
contacts_list = list(account.contacts.all().values('email'))
email_form = EmailForm()
return render(request, 'create_mail_accounts.html', {'account_id': account_id,
'contacts_list': contacts_list, 'email_form': email_form})
TIMEZONE_CHOICES = [(tz, tz) for tz in pytz.common_timezones]
email_form = EmailForm(account=account)
return render(request, 'create_mail_accounts.html', {'account_id': request.GET.get('account_id'),
'contacts_list': contacts_list, 'email_form': email_form,
"timezones": TIMEZONE_CHOICES, "settings_timezone": settings.TIME_ZONE})
if request.method == 'POST':
form = EmailForm(request.POST)
if form.is_valid():
send_email.delay(form.data.get('message_subject'), form.data.get('message_body'),
from_email=account_id, recipients=form.data.get('recipients').split(','))
account = Account.objects.filter(id=request.POST.get('account_id')).first()
if account:
form = EmailForm(request.POST, account=account)
if form.is_valid():
contacts = form.data.get('recipients').split(',')
email_obj = Email.objects.create(
from_account=account,
message_body=form.cleaned_data.get('message_body'),
message_subject=form.cleaned_data.get('message_subject'),
from_email=form.cleaned_data.get('from_email'),
timezone=form.cleaned_data.get('timezone'),
)
email_obj.recipients.add(*contacts)
if request.POST.get('scheduled_later') != 'true':
send_email.delay(email_obj.id)
else:
email_obj.scheduled_later = True
email_obj.scheduled_date_time = form.cleaned_data.get('scheduled_date_time')
email_obj.save()
return JsonResponse({'error': False})
return JsonResponse({'error': False})
else:
return JsonResponse({'error': True, 'errors': form.errors})
else:
return JsonResponse({'error': True, 'errors': form.errors})
return JsonResponse({'error':True, 'message': "Account does not exist."})
# @login_required
# def get_account_details(request, account_id):
# from django.core import serializers
# import json
# fields = ['name', 'email', 'phone', 'industry', 'billing_address_line', 'billing_street', 'billing_city',
# 'billing_state', 'billing_postcode', 'billing_country', 'website', 'description',
# 'created_by__email', 'created_on', 'tags__name', 'status', 'contacts__email', 'assigned_to__email']
# data = serializers.serialize('json', Account.objects.filter(id=account_id), fields=fields)
# return JsonResponse({'data': json.loads(data)[0]})
# @sales_access_required
# def create_mail(request, pk):
# account_obj = get_object_or_404(Account, pk=pk)
# if request.method == 'GET':
# context = {}
# context['account_obj'] = account_obj
# return render(request, 'create_email_for_contacts.html')
def get_contacts_for_account(request):
account_id = request.POST.get('account_id')
if account_id:
account_obj = Account.objects.filter(pk=account_id).first()
if account_obj:
if account_obj.contacts.all():
data = list(account_obj.contacts.values('id', 'email'))
import json
return HttpResponse(json.dumps(data))
return JsonResponse({})
return JsonResponse({})
def get_email_data_for_account(request):
email_obj = Email.objects.filter(id=request.POST.get('email_account_id')).first()
if email_obj:
ctx = {}
ctx['subject'] = email_obj.message_subject
ctx['body'] = email_obj.message_body
ctx['created_on'] = email_obj.created_on
ctx['contacts'] = list(email_obj.recipients.values('email'))
ctx['error'] = False
return JsonResponse(ctx)
else:
return JsonResponse({'error': True, 'data' : 'No emails found.'})

View file

@ -1,15 +1,16 @@
from django.test import TestCase
from cases.models import Case
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
from planner.models import Event, Reminder
from django.test import TestCase
from django.urls import reverse
from django.utils.encoding import force_text
from accounts.models import Account
from cases.models import Case
from common.models import Address, Attachments, Comment, User
from contacts.models import Contact
from planner.models import Event, Reminder
from teams.models import Teams
class CaseCreation(object):
@ -436,6 +437,192 @@ class TestCasesListViewForUser(CaseCreation, TestCase):
response = self.client.post(reverse('cases:close_case'), {'case_id':self.case.id})
self.assertEqual(response.status_code, 403)
self.client.login(email='johnDoeCase@example.com', password='password')
self.team_case = Teams.objects.create(name='dev team case')
self.team_case.users.add(self.user1.id)
response = self.client.post(reverse('cases:add_case') + '?view_account={}'.format(self.account.id), {
'name':'team case',
'status':'New',
'priority':'Low',
'closed_on':'2019-03-14',
'teams':self.team_case.id,
'from_account':self.account.id
})
self.assertEqual(200, response.status_code)
response = self.client.get(reverse('cases:add_case') + '?view_account={}'.format(self.account.id))
self.assertEqual(200, response.status_code)
self.case_without_account = Case.objects.create(
name="case_without_account", case_type="Problem", status="New",
priority="Low", description="something",
created_by=self.user, closed_on="2016-05-04")
response = self.client.get(reverse('cases:view_case', args=(self.case_without_account.id,)))
self.assertEqual(200, response.status_code)
self.account_user = Account.objects.create(
name="account name",
email="account@example.com", phone="12345",
billing_address_line="",
billing_street="street",
billing_city="city",
billing_postcode="1234",
billing_country='US',
website="www.example.com", description="account",
created_by=self.usermp)
self.account_user.assigned_to.add(self.usermp.id)
self.case_user = Case.objects.create(
name="case user", case_type="Problem", status="New",
priority="Low", description="something",
account=self.account_user,
created_by=self.usermp, closed_on="2016-05-04")
self.usermp.has_sales_access = True
self.usermp.save()
self.client.logout()
self.client.login(username='joedoeCase7@user.com', password='password')
response = self.client.get(reverse('cases:view_case', args=(self.case_user.id,)))
self.assertEqual(200, response.status_code)
self.case_user_1 = Case.objects.create(
name="case user authorization", case_type="Problem", status="New",
priority="Low", description="something",
created_by=self.user, closed_on="2016-05-04")
self.case_user_1.assigned_to.add(self.user1, self.user)
self.case_user_1.save()
response = self.client.get(reverse('cases:view_case', args=(self.case_user_1.id,)))
self.assertEqual(403, response.status_code)
self.client.logout()
self.client.login(username='joedoeCase7@user.com', password='password')
response = self.client.get(reverse('cases:edit_case', args=(self.case_user.id,)))
self.assertEqual(200, response.status_code)
self.client.login(email='johnDoeCase@example.com', password='password')
data = {
'name':"case user", 'case_type':"Problem", 'status':"New",
'priority':"Low", 'description':"something",
'created_by':self.usermp, 'closed_on':"2016-05-04",
'teams': self.team_case.id,
'from_account':self.account.id,
}
response = self.client.post(reverse('cases:edit_case', args=(self.case_user.id,)), data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(200, response.status_code)
response = self.client.get(reverse('cases:edit_case', args=(self.case_user.id,)) + '?view_account={}'.format(self.account.id))
self.assertEqual(200, response.status_code)
self.client.logout()
self.client.login(username='joedoeCase7@user.com', password='password')
response = self.client.get(reverse('cases:edit_case', args=(self.case.id,)))
self.assertEqual(403, response.status_code)
response = self.client.get(reverse('cases:remove_case', args=(self.case_user_1.id,)) + '?view_account={}'.format(self.account.id))
self.assertEqual(403, response.status_code)
self.client.logout()
self.client.login(email='johnDoeCase@example.com', password='password')
response = self.client.get(reverse('cases:remove_case', args=(self.case_user.id,)) + '?view_account={}'.format(self.account.id))
self.assertEqual(302, response.status_code)
self.case_user = Case.objects.create(
name="case user", case_type="Problem", status="New",
priority="Low", description="something",
account=self.account_user,
created_by=self.usermp, closed_on="2016-05-04")
self.client.logout()
self.client.login(email='johnDoeCase@example.com', password='password')
response = self.client.post(reverse('cases:remove_case', args=(self.case_user.id,)) + '?view_account={}'.format(self.account.id),
{'case_id': self.case_user.id}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(200, response.status_code)
self.case_user = Case.objects.create(
name="case user", case_type="Problem", status="New",
priority="Low", description="something",
account=self.account_user,
created_by=self.usermp, closed_on="2016-05-04")
self.client.logout()
self.client.login(username='joedoeCase7@user.com', password='password')
response = self.client.post(reverse('cases:remove_case', args=(self.case.id,)) + '?view_account={}'.format(self.account.id),
{'case_id': self.case.id})
self.assertEqual(403, response.status_code)
data = {
'comment':'comment update',
'caseid': self.case.id,
'commentid': self.comment.id
}
response = self.client.post(reverse('cases:edit_comment'), data)
self.assertEqual(200, response.status_code)
response = self.client.post(reverse('cases:close_case') + '?view_account={}'.format(self.account.id),
{'case_id': self.case.id})
self.assertEqual(403, response.status_code)
data = {
'caseid': self.case.id,
'comment': ''
}
response = self.client.post(reverse('cases:add_comment'), data)
self.assertEqual(200, response.status_code)
self.client.logout()
self.client.login(email='johnDoeCase@example.com', password='password')
response = self.client.get(reverse('cases:select_contacts') + '?account={}'.format(self.account.id))
self.assertEqual(200, response.status_code)
data = {
'caseid': self.case.id,
'comment': ''
}
response = self.client.post(reverse('cases:add_comment'), data)
self.assertEqual(200, response.status_code)
data = {
'comment':'comment update',
'caseid': self.case.id,
'commentid': self.comment.id
}
response = self.client.post(reverse('cases:edit_comment'), data)
self.assertEqual(200, response.status_code)
data = {
'comment':'',
'caseid': self.case.id,
'commentid': self.comment.id
}
response = self.client.post(reverse('cases:edit_comment'), data)
self.assertEqual(200, response.status_code)
self.client.logout()
self.client.login(username='joedoeCase7@user.com', password='password')
data = {
'comment_id': self.comment.id
}
response = self.client.post(reverse('cases:remove_comment'), data)
self.assertEqual(200, response.status_code)
self.client.logout()
self.client.login(email='johnDoeCase@example.com', password='password')
data = {
'caseid': self.case.id,
'attachment':'',
}
response = self.client.post(reverse('cases:add_attachment'), data)
self.assertEqual(200, response.status_code)
# def test_comment_add_error(self):
# self.client.login(email='mpmp@micropyramid.com', password='mp')
# response = self.client.post(reverse('cases:add_comment'), {})
@ -448,5 +635,3 @@ class TestCasesListViewForUser(CaseCreation, TestCase):
# self.client.login(email='mpmp1@micropyramid.com', password='mp')
# response = self.client.post(reverse('cases:edit_comment'), {})
# self.assertJSONEqual(force_text(response.content), {'error': "You don't have permission to edit this comment."})

View file

@ -361,10 +361,10 @@ class RemoveCaseView(SalesAccessRequiredMixin, LoginRequiredMixin, View):
self.request.user == self.object.created_by
):
self.object.delete()
if request.GET.get('view_account'):
account = request.GET.get('view_account')
return redirect("accounts:view_account", pk=account)
return redirect("cases:list")
if request.GET.get('view_account'):
account = request.GET.get('view_account')
return redirect("accounts:view_account", pk=account)
return redirect("cases:list")
raise PermissionDenied
def post(self, request, *args, **kwargs):

View file

@ -7,6 +7,7 @@
<!-- filter_row starts here -->
<div class="row marl">
<div class="col-lg-12 text-right">
<span class="d-inline"><a href="{% url 'marketing:list_all_emails_for_campaigns' %}" class="btn primary_btn">Contacts</a></span>
<span class="d-inline"><a href="{% url 'common:add_api_settings'%}" class="btn primary_btn"><i class="fa fa-plus"></i> Add New Setting</a></span>
</div>
</div>

View file

@ -430,6 +430,7 @@ class PasswordResetView(PasswordResetView):
form_class = PasswordResetEmailForm
email_template_name = 'registration/password_reset_email.html'
html_email_template_name = 'registration/password_reset_email.html'
from_email = 'no-reply@' + settings.EMAIL_HOST_USER + '.com'
@login_required

View file

@ -1,14 +1,15 @@
from django.test import TestCase
from contacts.models import Contact
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
from django.test import Client, TestCase
from django.urls import reverse
from django.utils.encoding import force_text
from accounts.models import Account
from cases.models import Case
from common.models import Address, Attachments, Comment, User
from contacts.forms import ContactAttachmentForm
from contacts.models import Contact
from teams.models import Teams
class ContactObjectsCreation(object):
@ -56,7 +57,10 @@ class ContactObjectsCreation(object):
first_name="janeUser@example.com",
username='janeUserContact',
email="janeUser@example.com",
role="USER")
role="USER",
has_sales_access=True)
self.user_contacts_mp.set_password('password')
self.user_contacts_mp.save()
self.client.login(username='johnContact@example.com', password='password')
@ -344,7 +348,9 @@ class TestContactCreateContact(ContactObjectsCreation, TestCase):
first_name="janeDoe@example.com",
username='janeDoe',
email="janeDoe@example.com",
role="USER")
role="USER",
is_active=True,
has_sales_access=True)
self.usermp1.set_password('password')
self.usermp1.save()
self.contact = Contact.objects.create(
@ -390,3 +396,145 @@ class TestContactCreateContact(ContactObjectsCreation, TestCase):
'/contacts/attachment/remove/', {'attachment_id': self.attachment.id})
self.assertJSONEqual(force_text(response.content), {
'error': "You don't have permission to delete this attachment."})
class TestContactViews(ContactObjectsCreation, TestCase):
def test_create_contact(self):
self.client.logout()
self.client.login(username='janeUser@example.com', password='password')
response = self.client.get(reverse('contacts:list'),{})
self.assertEqual(200, response.status_code)
self.client.logout()
self.client.login(username='johnContact@example.com', password='password')
self.account_by_user = Account.objects.create(
name="account edit", email="johndoe@example.com", phone="123456789",
billing_address_line="", billing_street="street name",
billing_city="city name",
billing_state="state", billing_postcode="1234",
billing_country="US",
website="www.example.como", created_by=self.user, status="open",
industry="SOFTWARE", description="Testing")
self.teams_contacts = Teams.objects.create(name='teams contacts')
self.teams_contacts.users.add(self.user)
data = {
'first_name':'first name',
'last_name':'last name',
'phone':'+91-123-456-7854',
'email':'example@user.com',
'teams':[self.teams_contacts.id,]
}
response = self.client.post(reverse('contacts:add_contact') + '?view_account={}&address_form='.format(self.account_by_user.id), data)
self.assertEqual(302, response.status_code)
data = {
'first_name':'first name',
'last_name':'last name',
'phone':'+91-123-456-7858',
'email':'example@mail.com',
'teams':[self.teams_contacts.id,]
}
response = self.client.post(reverse('contacts:add_contact') + '?view_account={}'.format(self.account_by_user.id), data,
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(200, response.status_code)
data = {
'first_name':'first name',
'last_name':'last name',
'phone':'+91-123-456-7854',
'email':'example@user',
'teams':[self.teams_contacts.id,]
}
response = self.client.post(reverse('contacts:add_contact') + '?view_account={}'.format(self.account_by_user.id), data,
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(200, response.status_code)
response = self.client.get(reverse('contacts:add_contact') + '?address_form=')
self.assertEqual(200, response.status_code)
response = self.client.post(reverse('contacts:edit_contact', args=(self.contact.id,)),{
'first_name':'contact',
'last_name':'contact@example.com',
'phone':'+91-123-456-7856',
'email':'contact@example.com',
'teams':[self.teams_contacts.id,]
})
self.assertEqual(302, response.status_code)
self.user_contacts_mp = User.objects.create(
first_name="joeUser@contact.com",
username='joeUser@contact.com',
email="joeUser@contact.com",
role="USER",
has_sales_access=True)
self.user_contacts_mp.set_password('password')
self.user_contacts_mp.save()
self.client.logout()
self.client.login(username='joeUser@contact.com', password='password')
response = self.client.get(reverse('contacts:view_contact', args=(self.contact.id,)))
self.assertEqual(403, response.status_code)
self.client.logout()
self.client.login(username='janeUser@example.com', password='password')
response = self.client.get(reverse('contacts:view_contact', args=(self.contact.id,)))
# self.assertEqual(403, response.status_code)
self.client.logout()
self.client.login(username='johnContact@example.com', password='password')
self.teams_contacts.users.add(self.user)
data = {
'first_name':'contact',
'last_name':'contact',
'phone':'+91-123-456-7852',
'email':'contact@example.com',
'teams':[self.teams_contacts.id,]
}
response = self.client.post(reverse('contacts:edit_contact', args=(self.contact.id,)) + '?from_account={}'.format(self.account_by_user.id), data)
self.assertEqual(302, response.status_code)
response = self.client.post(reverse('contacts:edit_contact', args=(self.contact.id,)) + '?from_account={}&address_form='.format(self.account_by_user.id), data,
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(200, response.status_code)
data = {
'first_name':'contact',
'last_name':'contact',
'phone':'+91-123-456',
'email':'contact@example',
'teams':[self.teams_contacts.id,]
}
response = self.client.post(reverse('contacts:edit_contact', args=(self.contact.id,)) + '?from_account={}'.format(self.account_by_user.id), data,
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(200, response.status_code)
response = self.client.post(reverse('contacts:edit_contact', args=(self.contact.id,)) + '?from_account={}'.format(self.account_by_user.id), data,)
self.assertEqual(200, response.status_code)
self.client.logout()
self.client.login(username='johnContact@example.com', password='password')
self.contact_delete = Contact.objects.create(
first_name="contact",
email="contact_delete@example.com",
phone="12345",
address=self.address,
description="contact",
created_by=self.user)
response = self.client.post(reverse('contacts:remove_contact', args=(self.contact_delete.id,)), {'pk': self.contact_delete.id},
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(200, response.status_code)
self.contact_edit = Contact.objects.create(
first_name="contact",
email="contact_edit@example.com",
phone="12345",
description="contact",
created_by=self.user_contacts_mp)
self.client.logout()
self.client.login(username='joeUser@contact.com', password='password')
response = self.client.get(reverse('contacts:view_contact', args=(self.contact_edit.id,)), {'pk': self.contact_edit.id},
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(200, response.status_code)
response = self.client.get(reverse('contacts:add_contact'))
self.assertEqual(200, response.status_code)

View file

@ -89,8 +89,6 @@ class CreateContactView(SalesAccessRequiredMixin, LoginRequiredMixin, CreateView
def dispatch(self, request, *args, **kwargs):
if self.request.user.role == 'ADMIN' or self.request.user.is_superuser:
self.users = User.objects.filter(is_active=True).order_by('email')
elif request.user.google.all():
self.users = []
else:
self.users = User.objects.filter(role='ADMIN').order_by('email')
return super(CreateContactView, self).dispatch(
@ -251,8 +249,6 @@ class UpdateContactView(SalesAccessRequiredMixin, LoginRequiredMixin, UpdateView
def dispatch(self, request, *args, **kwargs):
if self.request.user.role == 'ADMIN' or self.request.user.is_superuser:
self.users = User.objects.filter(is_active=True).order_by('email')
elif request.user.google.all():
self.users = []
else:
self.users = User.objects.filter(role='ADMIN').order_by('email')
return super(UpdateContactView, self).dispatch(

View file

@ -237,6 +237,10 @@ CELERY_BEAT_SCHEDULE = {
"runs-scheduled-campaigns-for-every-one-hour": {
"task": "marketing.tasks.send_scheduled_campaigns",
"schedule": crontab(hour='*/1')
},
"runs-scheduled-emails-for-accounts-every-five-minutes": {
"task": "accounts.tasks.send_scheduled_emails",
"schedule": crontab(minute='*/1')
}
}

View file

@ -138,60 +138,6 @@ def csv_doc_validate(document):
return {"error": False, "validated_rows": temp_row, "invalid_rows": invalid_row, "headers":csv_headers,
"failed_leads_csv": failed_leads_csv}
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 = ["title"]
required_headers = ["title"]
work_sheet = wb.get_sheet_by_name(name=sheet_name)
for y_index, row in enumerate(work_sheet.iter_rows()):
if y_index == 0:
missing_headers = set(required_headers) - \
set([str(cell.value).lower() for cell in row])
if missing_headers:
missing_headers_str = ', '.join(missing_headers)
message = "Missing headers: %s %s" % (
missing_headers_str, sheet_name)
return {"error": True, "message": message}
continue
elif not ''.join(str(cell.value) for cell in row):
continue
else:
temp_row = []
invalid_row = []
for x_index, cell in enumerate(row):
try:
headers[x_index]
except IndexError:
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}
invalid_row.append(headers[x_index])
temp_row.append(cell.value)
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:
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):
try:
# dialect = csv.Sniffer().sniff(document.read(1024).decode("ascii"))
@ -199,11 +145,7 @@ def import_document_validator(document):
return csv_doc_validate(document)
except Exception as e:
print(e)
try:
return xls_doc_validate(document)
except Exception as e:
print(e)
return {"error": True, "message": "Not a valid CSV/XLS file"}
return {"error": True, "message": "Not a valid CSV file"}
class LeadListForm(forms.Form):
@ -212,12 +154,12 @@ class LeadListForm(forms.Form):
def __init__(self, *args, **kwargs):
super(LeadListForm, self).__init__(*args, **kwargs)
self.fields['leads_file'].widget.attrs.update({
"accept": ".csv,.xls,.xlsx,.xlsm,.xlsb,.xml",
"accept": ".csv",
})
self.fields['leads_file'].required = True
if self.data.get('leads_file'):
self.fields['leads_file'].widget.attrs.update({
"accept": ".csv,.xls,.xlsx,.xlsm,.xlsb,.xml",
"accept": ".csv",
})
def clean_leads_file(self):

View file

@ -1,14 +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, APISettings
from accounts.models import Account, Tags
from leads.tasks import *
from leads.forms import *
from django.urls import reverse
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import Client, TestCase
from django.urls import reverse
from django.utils.encoding import force_text
from accounts.models import Account, Tags
from cases.models import Case
from common.models import APISettings, Attachments, Comment, User
from leads.forms import *
from leads.models import Lead
from leads.tasks import *
from teams.models import Teams
class TestLeadModel(object):
@ -32,7 +34,8 @@ class TestLeadModel(object):
first_name="janeLead2",
username='janeLead2',
email='janeLead2@example.com',
role="USER")
role="USER",
has_sales_access=True)
self.user2.set_password('password')
self.user2.save()
@ -40,7 +43,8 @@ class TestLeadModel(object):
first_name="janeLead3",
username='janeLead3',
email='janeLead3@example.com',
role="USER")
role="USER",
has_sales_access=True)
self.user3.set_password('password')
self.user3.save()
@ -82,6 +86,7 @@ class TestLeadModel(object):
self.lead1 = Lead.objects.create(
title="jane doe lead", created_by=self.user3)
self.lead.assigned_to.add(self.user)
self.lead.assigned_to.add(self.user2)
self.lead.assigned_to.add(self.user3)
self.case = Case.objects.create(
name="john case", case_type="Problem",
@ -513,7 +518,7 @@ class TestLeadDetailView1(TestLeadModel, TestCase):
def test_lead_detail_view(self):
self.client.login(username='janeLead2@example.com', password='password')
resp = self.client.get(reverse('leads:view_lead', args=(self.lead.id,)))
self.assertEqual(resp.status_code, 403)
self.assertEqual(resp.status_code, 200)
self.client.logout()
@ -536,25 +541,170 @@ class TestCommentAddResponse(TestLeadModel, TestCase):
self.assertJSONEqual(force_text(response.content), {"error": ["This field is required."]})
# # class TestAddAttachmentView(TestLeadModel, TestCase):
def test_file_upload_for_leads(self):
self.client.logout()
self.client.login(email='johnLead@example.com', password="password")
# # def test_convert_lead_view(self):
# # response = self.client.post(reverse('leads:add_attachment'),{})
# # self.assertEqual(response.status_code, 200)
# # self.assertJSONEqual(
# # str(response.content, encoding='utf8'),
# # {'status': 'success'}
# # )
file_content = [
'title,first name,last name,website,phone,email,address\n',
'lead1,john,doe,www.example.com,911234567890,user1@email.com,address for lead1\n',
'lead2,jane,doe,www.website.com,911234567891,user2@email.com,address for lead2\n',
'lead3,joe,doe,www.test.com,911234567892,user3@email.com,address for lead3\n',
'lead4,john,doe,www.sample.com,911234567893,user4@email.com,address for lead4\n',
]
file_content = bytes(''.join(file_content), 'utf-8')
data = {'leads_file': SimpleUploadedFile('file name', file_content)}
response = self.client.post(reverse('leads:upload_lead_csv_file'), data)
self.assertEqual(201, response.status_code)
file_content_missing_headers = [
',first name,last name,website,phone,,address\n',
'lead1,john,doe,www.example.com,911234567890,user1@email.com,address for lead1\n',
'lead2,jane,doe,www.website.com,911234567891,user2@email.com,address for lead2\n',
'lead3,joe,doe,www.test.com,911234567892,user3@email.com,address for lead3\n',
'lead4,john,doe,www.sample.com,911234567893,user4@email.com,address for lead4\n',
]
file_content_missing_headers = bytes(''.join(file_content_missing_headers), 'utf-8')
data = {'leads_file': SimpleUploadedFile('file name', file_content_missing_headers)}
response = self.client.post(reverse('leads:upload_lead_csv_file'), data)
# # class TestAddLeadViewFormError(TestLeadModel, TestCase):
# for invalid lead titles
file_content = [
'title,first name,last name,website,phone,email,address\n',
',john,doe,www.example.com,911234567890,user1@email.com,address for lead1\n',
',jane,doe,www.website.com,911234567891,user2@email.com,address for lead2\n',
',joe,doe,www.test.com,911234567892,user3@email.com,address for lead3\n',
',john,doe,www.sample.com,911234567893,user4@email.com,address for lead4\n',
]
file_content = bytes(''.join(file_content), 'utf-8')
data = {'leads_file': SimpleUploadedFile('file name', file_content)}
response = self.client.post(reverse('leads:upload_lead_csv_file'), data)
self.assertEqual(200, response.status_code)
# # def test_add_lead_view_form_error(self):
# # response = self.client.post(reverse('leads:add_lead'),{})
# # # self.assertEqual(response.status_code, 200)
# # print(response.json())
# # print(dir(response))
# # self.assertJSONEqual(
# # str(response.json(), encoding='utf8'),
# # {'status': 'success'}
# # )
# for invalid email
file_content = [
'title,first name,last name,website,phone,email,address\n',
'lead1,john,doe,www.example.com,911234567890,user1@email.com,address for lead1\n',
'lead2,jane,doe,www.website.com,911234567891,user2@email.com,address for lead2\n',
'lead3,joe,doe,www.test.com,911234567892,useremail.com,address for lead3\n',
'lead4,john,doe,www.sample.com,911234567893,user4@email,address for lead4\n',
]
file_content = bytes(''.join(file_content), 'utf-8')
data = {'leads_file': SimpleUploadedFile('file name', file_content)}
response = self.client.post(reverse('leads:upload_lead_csv_file'), data)
self.assertEqual(201, response.status_code)
# for invalid file type
data = {'leads_file': b''}
response = self.client.post(reverse('leads:upload_lead_csv_file'), data)
self.assertEqual(200, response.status_code)
file_content_missing_headers = [
',first name,last name,website,phone,,address\n',
'lead1,john,doe,www.example.com,911234567890,user1@email,address for lead1\n',
'lead2,jane,doe,www.website.com,911234567891,user2@email.com,address for lead2\n',
'lead3,joe,doe,www.test.com,911234567892,user3@email.com,address for lead3\n',
'lead4,john,doe,www.sample.com,911234567893,user4@email,address for lead4\n',
]
file_content_missing_headers = bytes(''.join(file_content_missing_headers), 'utf-8')
data = {'leads_file': SimpleUploadedFile('file name', file_content_missing_headers)}
response = self.client.post(reverse('leads:upload_lead_csv_file'), data)
response = self.client.post(reverse('leads:list') + '?tag=0', {'city': 'city'})
self.assertEqual(response.status_code, 200)
self.teams_leads = Teams.objects.create(name='teams leads')
self.teams_leads.users.add(self.user1)
response = self.client.post(reverse('leads:add_lead'), {'title': 'new lead title', 'teams': [self.teams_leads.id, ],
'savenewform':'true'})
self.assertEqual(response.status_code, 200)
response = self.client.post(reverse('leads:add_lead'), {'title': ''})
self.assertEqual(response.status_code, 200)
self.client.logout()
self.client.login(email='janeLead3@example.com', password="password")
response = self.client.get(reverse('leads:view_lead', args=(self.lead_1.id,)))
self.assertEqual(response.status_code, 403)
response = self.client.get(reverse('leads:view_lead', args=(self.lead1.id,)))
self.assertEqual(response.status_code, 200)
self.client.logout()
self.client.login(email='janeLead2@example.com', password="password")
self.lead_user = Lead.objects.create(title="jane doe lead 2", created_by=self.user2)
response = self.client.get(reverse('leads:view_lead', args=(self.lead_user.id,)))
self.assertEqual(response.status_code, 200)
self.client.logout()
self.client.login(email='johnLead@example.com', password="password")
response = self.client.get(reverse('leads:edit_lead', args=(self.lead_1.id,)) + '?status=converted')
self.assertEqual(response.status_code, 200)
response = self.client.get(reverse('leads:edit_lead', args=(self.lead_1.id,)) + '?status=c')
self.assertEqual(response.status_code, 200)
response = self.client.get(reverse('leads:edit_lead', args=(self.lead_1.id,)) + '?status=converted')
self.assertEqual(response.status_code, 200)
self.teams_leads.users.add(self.user3)
data = {
'title': self.lead_1.title,
'teams': [self.teams_leads.id, ],
'status':'converted',
'account_name': 'lead_conversion',
'email':'account@lead.com'
}
comment = Comment.objects.create(comment='comment text lead', lead=self.lead_1, commented_by=self.user)
response = self.client.post(reverse('leads:edit_lead', args=(self.lead_1.id,)) + '?status=converted', data)
self.assertEqual(response.status_code, 200)
self.client.logout()
self.client.login(email='janeLead3@example.com', password="password")
response = self.client.get(reverse('leads:remove_lead', args=(self.lead_1.id,)))
self.assertEqual(response.status_code, 403)
self.client.logout()
self.client.login(email='johnLead@example.com', password="password")
self.lead.email = None
self.lead.save()
response = self.client.get(reverse('leads:leads_convert', args=(self.lead.id,)) + '?status=converted')
self.assertEqual(response.status_code, 302)
self.tag_for_lead = Tags.objects.create(name='tag for lead')
data = {
'tags':'tag1 lead,tag2 lead,tag for lead'
}
response = self.client.post(reverse('leads:update_lead_tags', args=(self.lead.id,)), data)
self.assertEqual(response.status_code, 302)
self.client.logout()
self.client.login(email='janeLead3@example.com', password="password")
response = self.client.post(reverse('leads:update_lead_tags', args=(self.lead.id,)), data)
self.assertEqual(response.status_code, 403)
self.client.logout()
self.client.login(email='johnLead@example.com', password="password")
data = {
'lead': self.lead.id,
'tag': self.tag_for_lead.id
}
response = self.client.post(reverse('leads:remove_lead_tag'), data)
self.assertEqual(response.status_code, 200)
self.client.logout()
self.client.login(email='janeLead3@example.com', password="password")
response = self.client.post(reverse('leads:remove_lead_tag'), data)
self.assertEqual(response.status_code, 200)
response = self.client.get(reverse('leads:sample_lead_file'))
sample_data = [
'title,first name,last name,website,phone,email,address\n',
'lead1,john,doe,www.example.com,911234567890,user1@email.com,address for lead1\n',
'lead2,jane,doe,www.website.com,911234567891,user2@email.com,address for lead2\n',
'lead3,joe,doe,www.test.com,911234567892,user3@email.com,address for lead3\n',
'lead4,john,doe,www.sample.com,911234567893,user4@email.com,address for lead4\n',
]
content = bytes(''.join(sample_data), 'utf-8')
self.assertEqual(response.content, content)

View file

@ -302,8 +302,6 @@ def update_lead(request, pk):
users = []
if request.user.role == 'ADMIN' or request.user.is_superuser:
users = User.objects.filter(is_active=True).order_by('email')
elif request.user.google.all():
users = []
else:
users = User.objects.filter(role='ADMIN').order_by('email')
status = request.GET.get('status', None)

View file

@ -1,13 +1,15 @@
from django.test import TestCase
from opportunity.models import Opportunity
from accounts.models import Account, Tags
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
from django.shortcuts import reverse
from django.test import TestCase
from django.utils.encoding import force_text
from accounts.models import Account, Tags
from cases.models import Case
from common.models import Address, Attachments, Comment, User
from contacts.models import Contact
from opportunity.models import Opportunity
from teams.models import Teams
# Create your tests here.
@ -29,6 +31,24 @@ class OpportunityModel(object):
self.user1.set_password('password')
self.user1.save()
self.user2 = User.objects.create(
first_name="john doe",
username='jane doe opportunity',
email='janeDoeOpp@example.com',
role="USER",
has_sales_access=True)
self.user2.set_password('password')
self.user2.save()
self.user3 = User.objects.create(
first_name="john doe",
username='joe doe opportunity',
email='joeDoeOpp@example.com',
role="USER",
has_sales_access=True)
self.user3.set_password('password')
self.user3.save()
self.address = Address.objects.create(
street="", city="city name", postcode="1234",
country='IN')
@ -55,10 +75,23 @@ class OpportunityModel(object):
self.opportunity = Opportunity.objects.create(
name="jane opportunity", amount="478",
stage="negotiation/review", lead_source="Call", probability="58",
closed_on="2016-05-04",
closed_on="2016-05-04", account=self.account,
description="opportunity description",
created_by=self.user)
self.opportunity.assigned_to.add(self.user)
self.opportunity_1 = Opportunity.objects.create(
name="new opportunity", amount="478",
stage="negotiation/review", probability="92",
closed_on="2016-05-04", description="opportunity description",
created_by=self.user)
self.opportunity_2 = Opportunity.objects.create(
name="joe opportunity", amount="478",
stage="negotiation/review", probability="92",
closed_on="2016-05-04", description="opportunity description",
created_by=self.user3)
self.case = Case.objects.create(
name="case name", case_type="Problem", status="New", account=self.account,
priority="Low", description="case description",
@ -467,4 +500,83 @@ class AttachmentTestCaseError(OpportunityModel, TestCase):
def test_attachment_add(self):
url = "/opportunities/attachment/add/"
response = self.client.post(url, {'opportunityid': self.opportunity.id})
self.assertJSONEqual(force_text(response.content),{"error": ["This field is required."]})
self.assertJSONEqual(force_text(response.content),{"error": ["This field is required."]})
response = self.client.get(reverse('opportunity:list') + '?tag={}'.format(self.tag_1.id))
self.assertEqual(200, response.status_code)
response = self.client.post(reverse('opportunity:list') + '?tag={}'.format(self.tag_1.id), {
'tag': self.tag_1.id,
})
self.assertEqual(200, response.status_code)
self.team_opp = Teams.objects.create(name='opp team')
self.team_opp.users.add(self.user1.id)
self.client.logout()
self.client.login(email='janeOpp@example.com', password='password')
data = {
'name': 'opportunity teams',
'stage': 'QUALIFICATION',
'teams': self.team_opp.id,
'savenewform':'true',
}
response = self.client.post(reverse('opportunity:save'), data)
self.assertEqual(200, response.status_code)
response = self.client.get(reverse('opportunity:save') + '?view_account={}'.format(self.account.id))
self.assertEqual(200, response.status_code)
response = self.client.get(reverse('opportunity:opp_view', args=(self.opportunity_1.id,)))
self.assertEqual(200, response.status_code)
self.client.logout()
self.client.login(email='janeDoeOpp@example.com', password='password')
response = self.client.get(reverse('opportunity:opp_view', args=(self.opportunity.id,)))
self.assertEqual(403, response.status_code)
self.client.logout()
self.client.login(email='joeDoeOpp@example.com', password='password')
response = self.client.get(reverse('opportunity:opp_view', args=(self.opportunity_2.id,)))
self.assertEqual(200, response.status_code)
self.client.logout()
self.client.login(email='janeOpp@example.com', password='password')
data = {
'name': 'opportunity teams edit',
'stage': 'QUALIFICATION',
'teams': self.team_opp.id,
'savenewform':'true',
'from_account': self.account.id,
}
response = self.client.post(reverse('opportunity:opp_edit', args=(self.opportunity_2.id,)), data)
self.assertEqual(200, response.status_code)
response = self.client.get(reverse('opportunity:opp_edit', args=(self.opportunity_2.id,)) + '?view_account={}'.format(self.account.id))
self.assertEqual(200, response.status_code)
self.client.logout()
self.client.login(email='joeDoeOpp@example.com', password='password')
response = self.client.get(reverse('opportunity:opp_edit', args=(self.opportunity_1.id,)), data)
self.assertEqual(403, response.status_code)
self.client.logout()
self.client.login(email='janeOpp@example.com', password='password')
response = self.client.get(reverse('opportunity:opp_remove', args=(self.opportunity_1.id,)), {
'pk':self.opportunity_1.id,
}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.client.logout()
self.client.login(email='joeDoeOpp@example.com', password='password')
response = self.client.post(reverse('opportunity:opp_remove', args=(self.opportunity.id,)) + '?view_account={}'.format(self.account.id), {
'pk':self.opportunity.id})
self.assertEqual(403, response.status_code)
self.client.logout()
self.client.login(email='janeOpp@example.com', password='password')
response = self.client.post(reverse('opportunity:opp_remove', args=(self.opportunity_2.id,)) + '?view_account={}'.format(self.account.id), {
'pk':self.opportunity_2.id})
self.assertEqual(302, response.status_code)
response = self.client.get(reverse('opportunity:contacts') + '?account={}'.format(self.account.id))
self.assertEqual(200, response.status_code)

View file

@ -1,55 +1,19 @@
amqp==2.5.0
arrow==0.14.2
atomicwrites==1.3.0
attrs==19.1.0
Babel==2.7.0
billiard==3.6.0.0
boto==2.49.0
boto3==1.9.94
botocore==1.12.196
celery==4.3.0
certifi==2019.6.16
chardet==3.0.4
codacy-coverage==1.3.11
coverage==4.5.3
cssselect==1.0.3
Django==2.2.3
django-appconf==1.0.3
django-compressor==2.2
django-phonenumber-field==2.2.0
Django
celery
django-simple-pagination==1.3
django-storages==1.7.1
docutils==0.14
et-xmlfile==1.0.1
idna==2.8
importlib-metadata==0.18
jdcal==1.4.1
jmespath==0.9.4
kombu==4.6.3
lxml==4.3.2
more-itertools==7.2.0
openpyxl==2.6.0
pdfkit==0.6.1
django-compressor
django-phonenumber-field==2.2.0
django-storages
psycopg2-binary
arrow
phonenumbers==8.10.4
Pillow==5.4.1
pluggy==0.12.0
psycopg2-binary==2.7.7
py==1.8.0
pytest==4.1.1
pytest-cov==2.6.1
pytest-django==3.4.5
python-dateutil==2.8.0
pytz==2018.9
raven==6.10.0
rcssmin==1.0.6
redis==3.2.1
requests==2.22.0
rjsmin==1.0.12
s3transfer==0.2.1
six==1.12.0
sorl-thumbnail==12.5.0
sqlparse==0.3.0
urllib3==1.25.3
vine==1.3.0
xlrd==1.2.0
zipp==0.5.2
requests
boto3
lxml
cssselect
openpyxl
xlrd
pdfkit
redis
coverage
raven

View file

@ -1,3 +1,242 @@
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase
from django.urls import reverse
# Create your tests here.
from accounts.models import Account
from common.models import User
from tasks.models import Task
from teams.models import Teams
class TaskCreateTest(object):
def setUp(self):
self.user = User.objects.create(
first_name="johnTask", username='johnDoeTask', email='johnTask@example.com', role='ADMIN')
self.user.set_password('password')
self.user.save()
self.user1 = User.objects.create(
first_name="janeTask",
username='janeDoeTask',
email='janeDoeTask@example.com',
role="USER",
has_sales_access=True)
self.user1.set_password('password')
self.user1.save()
self.user2 = User.objects.create(
first_name="joeTask",
username='joeTask',
email='joeTask@example.com',
role="USER",
has_sales_access=True)
self.user2.set_password('password')
self.user2.save()
# self.user3 = User.objects.create(
# first_name="joeTask",
# username='joeDoeTask',
# email='joeDoeTask@example.com',
# role="USER",
# has_sales_access=True)
# self.user3.set_password('password')
# self.user3.save()
self.team_dev = Teams.objects.create(name='task teams')
self.team_dev.users.add(self.user2.id)
self.account = Account.objects.create(
name="john task", email="johntasks@example.com", phone="123456789",
billing_address_line="", billing_street="street name",
billing_city="city name",
billing_state="state", billing_postcode="1234",
billing_country="US",
website="www.example.como", created_by=self.user, status="open",
industry="SOFTWARE", description="Testing")
self.task = Task.objects.create(
title='first task', status='New', priority='Medium')
self.task.created_by = self.user
self.task.assigned_to.add(self.user1)
self.task.account = self.account
self.task.save()
self.account.assigned_to.add(self.user1)
self.task_1 = Task.objects.create(
title='second task', status='New', priority='Medium')
self.task_1.created_by = self.user
self.task_1.assigned_to.add(self.user1)
self.task_1.save()
self.task_2 = Task.objects.create(
title='second task', status='New', priority='Medium')
self.task_2.created_by = self.user1
self.task_2.save()
class TaskListTestCase(TaskCreateTest, TestCase):
def test_tasks_list(self):
self.client.login(email='johnTask@example.com', password='password')
response = self.client.get(reverse('tasks:tasks_list'))
self.assertEqual(response.status_code, 200)
data = {
'task_title': 'title',
'status': 'New',
'priority': 'Medium'
}
response = self.client.post(reverse('tasks:tasks_list'), data)
self.assertEqual(response.status_code, 200)
self.client.login(email='janeDoeTask@example.com', password='password')
response = self.client.get(reverse('tasks:tasks_list'))
self.assertEqual(response.status_code, 200)
response = self.client.post(reverse('tasks:tasks_list'), data)
self.assertEqual(response.status_code, 200)
class TaskCreateTestCase(TaskCreateTest, TestCase):
def test_task_create(self):
self.client.login(email='johnTask@example.com', password='password')
response = self.client.get(reverse('tasks:task_create'))
self.assertEqual(response.status_code, 200)
self.client.login(email='janeDoeTask@example.com', password='password')
response = self.client.get(reverse('tasks:task_create'))
self.assertEqual(response.status_code, 200)
self.client.login(email='johnTask@example.com', password='password')
data = {
'title': '',
'status': 'New',
'priority': 'Medium'
}
response = self.client.post(reverse('tasks:task_create'), data)
self.assertEqual(response.status_code, 200)
data = {
'title': 'task title',
'status': 'New',
'priority': 'Medium',
'teams': self.team_dev.id,
'from_account': self.account.id,
}
response = self.client.post(reverse('tasks:task_create'), data)
self.assertEqual(response.status_code, 200)
class TaskDetailTestCase(TaskCreateTest, TestCase):
def test_task_detail(self):
self.client.login(email='johnTask@example.com', password='password')
response = self.client.get(
reverse('tasks:task_detail', args=(self.task.id,)))
self.assertEqual(response.status_code, 200)
response = self.client.get(
reverse('tasks:task_detail', args=(self.task_1.id,)))
self.assertEqual(response.status_code, 200)
self.client.login(email='janeDoeTask@example.com', password='password')
response = self.client.get(
reverse('tasks:task_detail', args=(self.task.id,)))
self.assertEqual(response.status_code, 200)
response = self.client.get(
reverse('tasks:task_detail', args=(self.task_2.id,)))
self.assertEqual(response.status_code, 200)
self.client.login(email='joeTask@example.com', password='password')
response = self.client.get(
reverse('tasks:task_detail', args=(self.task.id,)))
self.assertEqual(response.status_code, 403)
class TaskEditTestCase(TaskCreateTest, TestCase):
def test_task_Edit(self):
self.client.login(email='johnTask@example.com', password='password')
response = self.client.get(
reverse('tasks:task_edit', args=(self.task.id,)))
self.assertEqual(response.status_code, 200)
self.client.login(email='janeDoeTask@example.com', password='password')
response = self.client.get(
reverse('tasks:task_edit', args=(self.task.id,)))
self.assertEqual(response.status_code, 403)
response = self.client.get(
reverse('tasks:task_edit', args=(self.task_2.id,)))
self.assertEqual(response.status_code, 200)
self.client.login(email='johnTask@example.com', password='password')
data = {
'title': '',
'status': 'New',
'priority': 'Medium'
}
response = self.client.post(
reverse('tasks:task_edit', args=(self.task.id,)), data)
self.assertEqual(response.status_code, 200)
data = {
'title': 'task title',
'status': 'New',
'priority': 'High',
'teams': self.team_dev.id,
'from_account': self.account.id,
}
response = self.client.post(
reverse('tasks:task_edit', args=(self.task.id,)), data)
self.assertEqual(response.status_code, 200)
class TaskDeleteTestCase(TaskCreateTest, TestCase):
def test_task_delete(self):
self.client.login(email='janeDoeTask@example.com', password='password')
response = self.client.get(
reverse('tasks:task_delete', args=(self.task.id,)))
self.assertEqual(response.status_code, 403)
self.client.login(email='johnTask@example.com', password='password')
response = self.client.get(
reverse('tasks:task_delete', args=(self.task.id,)))
self.assertEqual(response.status_code, 302)
response = self.client.get(
reverse('tasks:task_delete', args=(self.task_1.id,)) + '?view_account={}'.format(self.account.id))
self.assertEqual(response.status_code, 302)
# class AddCommentTestCase(TaskCreateTest, TestCase):
# def test_task_delete(self):
# self.client.login(email='johnTask@example.com', password='password')
# data = {
# 'comment': '',
# 'task_id': self.task.id,
# }
# response = self.client.post(
# reverse('tasks:add_comment'), data)
# self.assertEqual(response.status_code, 200)
# data = {
# 'comment': 'test comment',
# 'task_id': self.task.id,
# }
# response = self.client.post(
# reverse('tasks:add_comment'), data)
# self.assertEqual(response.status_code, 200)
# self.client.login(email='janeDoeTask@example.com', password='password')
# response = self.client.post(
# reverse('tasks:add_comment'))
# self.assertEqual(response.status_code, 200)

View file

@ -8,9 +8,11 @@ from django.db.models import Q
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, redirect, render, reverse
from django.views.generic import (CreateView, DeleteView, DetailView, FormView,
TemplateView, UpdateView, View)
TemplateView, UpdateView, View)
from accounts.models import Account
from common.access_decorators_mixins import (MarketingAccessRequiredMixin,
SalesAccessRequiredMixin, marketing_access_required, sales_access_required)
from common.models import Attachments, Comment, User
from common.tasks import send_email_user_mentions
from contacts.models import Contact
@ -18,8 +20,6 @@ from tasks.celery_tasks import send_email
from tasks.forms import TaskAttachmentForm, TaskCommentForm, TaskForm
from tasks.models import Task
from tasks.utils import *
from common.access_decorators_mixins import (
sales_access_required, marketing_access_required, SalesAccessRequiredMixin, MarketingAccessRequiredMixin)
from teams.models import Teams

View file

@ -11,7 +11,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Django CRM</title>
<link href="https://fonts.googleapis.com/css?family=Muli" rel="stylesheet">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/css/bootstrap-datetimepicker.min.css" />
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
@ -115,7 +115,6 @@
{% if request.user.role == "ADMIN" or request.user.is_superuser %}
<a class="dropdown-item" href="{% url 'common:users_list' %}">Users</a>
<a class="dropdown-item" href="{% url 'common:api_settings' %}">Settings</a>
<a class="dropdown-item" href="{% url 'marketing:list_all_emails_for_campaigns' %}">Contacts</a>
{% endif %}
{% if request.user.email != "admin@micropyramid.com" %}
<a class="dropdown-item" href="/change-password/">Change Password</a>
@ -143,7 +142,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment-with-locales.min.js"></script>
<script defer src="https://use.fontawesome.com/releases/v5.7.1/js/all.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/js/bootstrap-datetimepicker.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/js/select2.min.js"></script>