1
|
|
|
# Copyright 2013 Mathias WOLFF |
2
|
|
|
# This file is part of pyfreebilling. |
3
|
|
|
# |
4
|
|
|
# pyfreebilling is free software: you can redistribute it and/or modify |
5
|
|
|
# it under the terms of the GNU General Public License as published by |
6
|
|
|
# the Free Software Foundation, either version 3 of the License, or |
7
|
|
|
# (at your option) any later version. |
8
|
|
|
# |
9
|
|
|
# pyfreebilling is distributed in the hope that it will be useful, |
10
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
11
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12
|
|
|
# GNU General Public License for more details. |
13
|
|
|
# |
14
|
|
|
# You should have received a copy of the GNU General Public License |
15
|
|
|
# along with pyfreebilling. If not, see <http://www.gnu.org/licenses/> |
16
|
|
|
|
17
|
|
|
from django.db import models |
18
|
|
|
from django.db.models import permalink, Sum, Avg, Count, Max, Min |
19
|
|
|
from django.core.validators import EMPTY_VALUES |
20
|
|
|
from django.core.exceptions import ValidationError |
21
|
|
|
from django.contrib.auth.models import AbstractUser |
22
|
|
|
from django.conf import settings |
23
|
|
|
from django.contrib.contenttypes import generic |
24
|
|
|
from django.contrib.comments.models import Comment |
25
|
|
|
from django.contrib.contenttypes.models import ContentType |
26
|
|
|
from django.contrib.contenttypes.generic import GenericRelation |
27
|
|
|
from django.utils.translation import ugettext_lazy as _ |
28
|
|
|
from django.utils.html import format_html |
29
|
|
|
|
30
|
|
|
import datetime |
31
|
|
|
import qsstats |
32
|
|
|
import vatnumber |
33
|
|
|
|
34
|
|
|
from django_iban.fields import IBANField, SWIFTBICField |
35
|
|
|
|
36
|
|
|
import decimal |
37
|
|
|
|
38
|
|
|
import math |
39
|
|
|
|
40
|
|
|
from django_countries.fields import CountryField |
41
|
|
|
|
42
|
|
|
from netaddr import IPNetwork, AddrFormatError |
43
|
|
|
|
44
|
|
|
import re |
45
|
|
|
|
46
|
|
|
from currencies.models import Currency |
47
|
|
|
|
48
|
|
|
from pyfreebill.validators import validate_cidr |
49
|
|
|
|
50
|
|
|
# CustomUser -- Django 1.6 |
51
|
|
|
# class CustomUser(AbstractUser): |
52
|
|
|
# keyboard_shortcuts = models.BooleanField(default=True) |
53
|
|
|
|
54
|
|
|
# Finance |
55
|
|
|
from django.core.exceptions import ValidationError |
56
|
|
|
|
57
|
|
|
def check_vat(value): |
58
|
|
|
if value != "" and not vatnumber.check_vat(value): |
59
|
|
|
raise ValidationError(u"%s is not a valid VAT number" % value) |
60
|
|
|
|
61
|
|
|
class Company(models.Model): |
62
|
|
|
"""Company model.""" |
63
|
|
|
name = models.CharField(_(u'name'), |
64
|
|
|
max_length=200, |
65
|
|
|
unique=True) |
66
|
|
|
nickname = models.CharField(_('nickname'), |
67
|
|
|
max_length=50, |
68
|
|
|
blank=True, |
69
|
|
|
null=True) |
70
|
|
|
slug = models.SlugField(_('slug'), |
71
|
|
|
max_length=50, |
72
|
|
|
unique=True) |
73
|
|
|
about = models.CharField(_(u'about'), |
74
|
|
|
max_length=250, |
75
|
|
|
blank=True, |
76
|
|
|
null=True) |
77
|
|
|
phone_number = GenericRelation(u'PhoneNumber') |
78
|
|
|
email_address = GenericRelation(u'EmailAddress') |
79
|
|
|
web_site = GenericRelation(u'WebSite') |
80
|
|
|
street_address = GenericRelation(u'StreetAddress') |
81
|
|
|
note = GenericRelation(Comment, object_id_field='object_pk') |
82
|
|
|
account_number = models.IntegerField(_(u"Account number"), |
83
|
|
|
blank=True, |
84
|
|
|
null=True) |
85
|
|
|
vat = models.BooleanField(_(u"VAT Applicable / Not applicable"), |
86
|
|
|
default=False, |
87
|
|
|
help_text=_(u"if checked, VAT is applicable.")) |
88
|
|
|
vat_number = models.CharField(_(u"VAT number"), |
89
|
|
|
max_length=30, |
90
|
|
|
blank=True, |
91
|
|
|
validators=[check_vat]) |
92
|
|
|
vat_number_validated = models.BooleanField(_(u"VAT Vies Validated."), |
93
|
|
|
default=False, |
94
|
|
|
help_text=_(u"If on, it means that VAT is " |
95
|
|
|
u"validated through <a target='_blank' " |
96
|
|
|
u"href='http://ec.europa.eu/taxation_customs/vies/vatRequest.html'>Vies</a>.")) |
97
|
|
|
swift_bic = SWIFTBICField(_(u"SWIFT BIC bank account number"), |
98
|
|
|
blank=True, |
99
|
|
|
null=True) |
100
|
|
|
iban = IBANField(_(u"IBAN bank account number"), |
101
|
|
|
blank=True, |
102
|
|
|
null=True) |
103
|
|
|
prepaid = models.BooleanField(_(u"Prepaid / Postpaid"), |
104
|
|
|
default=True, |
105
|
|
|
help_text=_(u"If checked, this account customer is prepaid.")) |
106
|
|
|
credit_limit = models.DecimalField(_(u'credit limit'), |
107
|
|
|
max_digits=12, |
108
|
|
|
decimal_places=4, |
109
|
|
|
default=0, |
110
|
|
|
help_text=_(u"Credit limit for postpaid account.")) |
111
|
|
|
low_credit_alert = models.DecimalField(_(u'low credit level alert'), |
112
|
|
|
max_digits=12, |
113
|
|
|
decimal_places=4, |
114
|
|
|
default="10", |
115
|
|
|
help_text=_(u"Low credit limit alert.")) |
116
|
|
|
low_credit_alert_sent = models.BooleanField(_(u"low credit alert ON"), |
117
|
|
|
default=False) |
118
|
|
|
account_blocked_alert_sent = models.BooleanField(_(u"Customer account blocked - low balance - ON"), |
119
|
|
|
default=False) |
120
|
|
|
email_alert = models.EmailField(_('alert email address'), |
121
|
|
|
blank=True, |
122
|
|
|
null=True) |
123
|
|
|
customer_balance = models.DecimalField(_(u'customer balance'), |
124
|
|
|
max_digits=12, |
125
|
|
|
decimal_places=6, |
126
|
|
|
default=0, |
127
|
|
|
help_text=_(u"Actual customer balance.")) |
128
|
|
|
cb_currency = models.ForeignKey(Currency, |
129
|
|
|
verbose_name=_(u"Currency")) |
130
|
|
|
supplier_balance = models.DecimalField(_(u'supplier balance'), |
131
|
|
|
max_digits=12, |
132
|
|
|
decimal_places=6, |
133
|
|
|
default=0, |
134
|
|
|
help_text=_(u"Actual supplier balance.")) |
135
|
|
|
max_calls = models.PositiveIntegerField(_(u'max simultaneous calls'), |
136
|
|
|
default=1, |
137
|
|
|
help_text=_(u"maximum simultaneous calls allowed for this customer account.")) |
138
|
|
|
calls_per_second = models.PositiveIntegerField(_(u'max calls per second'), |
139
|
|
|
default=10, |
140
|
|
|
help_text=_(u"maximum calls per seconds allowed for this customer account.")) |
141
|
|
|
BILLING_CYCLE_CHOICES = ( |
142
|
|
|
('w', _(u'weekly')), |
143
|
|
|
('m', _(u'monthly')), |
144
|
|
|
) |
145
|
|
|
billing_cycle = models.CharField(_(u'billing cycle'), |
146
|
|
|
max_length=10, |
147
|
|
|
choices=BILLING_CYCLE_CHOICES, |
148
|
|
|
default='m', |
149
|
|
|
help_text=_(u"billinng cycle for invoice generation.")) |
150
|
|
|
customer_enabled = models.BooleanField(_(u"Customer Enabled / Disabled"), |
151
|
|
|
default=True) |
152
|
|
|
supplier_enabled = models.BooleanField(_(u"Supplier Enabled / Disabled"), |
153
|
|
|
default=True) |
154
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
155
|
|
|
auto_now_add=True) |
156
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
157
|
|
|
auto_now=True) |
158
|
|
|
|
159
|
|
|
class Meta: |
160
|
|
|
db_table = 'company' |
161
|
|
|
ordering = ('name',) |
162
|
|
|
verbose_name = _(u"Company") |
163
|
|
|
verbose_name_plural = _(u"Companies") |
164
|
|
|
|
165
|
|
|
def clean(self): |
166
|
|
|
if self.vat_number: |
167
|
|
|
try: |
168
|
|
|
vatnumber.check_vies(self.vat_number) |
169
|
|
|
except Exception, e: |
170
|
|
|
raise ValidationError("""Wrong VAT number - validation made throw VIES services. %s""") % e |
171
|
|
|
|
172
|
|
|
def save(self, *args, **kwargs): |
173
|
|
|
if self.vat_number: |
174
|
|
|
try: |
175
|
|
|
self.vat_number_validated = vatnumber.check_vies(self.vat_number) |
176
|
|
|
except: |
177
|
|
|
self.vat_number_validated = False |
178
|
|
|
else: |
179
|
|
|
self.vat_number_validated = False |
180
|
|
|
|
181
|
|
|
super(Company, self).save(*args, **kwargs) |
182
|
|
|
|
183
|
|
|
def __unicode__(self): |
184
|
|
|
return u"%s" % self.name |
185
|
|
|
|
186
|
|
|
def colored_name(self): |
187
|
|
|
if self.customer_enabled == False and self.supplier_enabled == False: |
188
|
|
|
color = "red" |
189
|
|
|
elif self.customer_enabled == False and self.supplier_enabled == True: |
190
|
|
|
color = "orange" |
191
|
|
|
elif self.customer_enabled == True and self.supplier_enabled == False: |
192
|
|
|
color = "purple" |
193
|
|
|
else: |
194
|
|
|
color = "green" |
195
|
|
|
return " <span style=color:%s>%s</span>" % (color, self.name) |
196
|
|
|
colored_name.allow_tags = True |
197
|
|
|
|
198
|
|
|
def balance_history(self): |
199
|
|
|
html = '<span><a href="/extranet/pyfreebill/companybalancehistory/?company__id__exact={0}" class="btn btn-inverse btn-mini">Balance history <i class="icon-plus-sign"></i></a></span>' |
200
|
|
|
return format_html(html, (self.id)) |
201
|
|
|
balance_history.allow_tags = True |
202
|
|
|
balance_history.short_description = 'balance history' |
203
|
|
|
|
204
|
|
|
|
205
|
|
|
class Person(models.Model): |
206
|
|
|
"""Person model.""" |
207
|
|
|
first_name = models.CharField(_('first name'), |
208
|
|
|
max_length=100) |
209
|
|
|
last_name = models.CharField(_('last name'), |
210
|
|
|
max_length=200) |
211
|
|
|
middle_name = models.CharField(_('middle name'), |
212
|
|
|
max_length=200, |
213
|
|
|
blank=True, |
214
|
|
|
null=True) |
215
|
|
|
suffix = models.CharField(_('suffix'), |
216
|
|
|
max_length=50, |
217
|
|
|
blank=True, |
218
|
|
|
null=True) |
219
|
|
|
nickname = models.CharField(_('nickname'), |
220
|
|
|
max_length=100, |
221
|
|
|
blank=True) |
222
|
|
|
slug = models.SlugField(_('slug'), |
223
|
|
|
max_length=50, |
224
|
|
|
unique=True) |
225
|
|
|
title = models.CharField(_('title'), |
226
|
|
|
max_length=200, |
227
|
|
|
blank=True) |
228
|
|
|
company = models.ForeignKey(Company, |
229
|
|
|
blank=True, |
230
|
|
|
null=True) |
231
|
|
|
about = models.TextField(_('about'), |
232
|
|
|
blank=True) |
233
|
|
|
user = models.OneToOneField(settings.AUTH_USER_MODEL, |
234
|
|
|
blank=True, |
235
|
|
|
null=True, |
236
|
|
|
verbose_name=_('user')) |
237
|
|
|
phone_number = GenericRelation('PhoneNumber') |
238
|
|
|
email_address = GenericRelation('EmailAddress') |
239
|
|
|
instant_messenger = GenericRelation('InstantMessenger') |
240
|
|
|
special_date = GenericRelation('SpecialDate') |
241
|
|
|
note = GenericRelation(Comment, |
242
|
|
|
object_id_field='object_pk') |
243
|
|
|
date_added = models.DateTimeField(_('date added'), |
244
|
|
|
auto_now_add=True) |
245
|
|
|
date_modified = models.DateTimeField(_('date modified'), |
246
|
|
|
auto_now=True) |
247
|
|
|
|
248
|
|
|
class Meta: |
249
|
|
|
db_table = 'contacts_people' |
250
|
|
|
ordering = ('last_name', 'first_name') |
251
|
|
|
verbose_name = _('person') |
252
|
|
|
verbose_name_plural = _('people') |
253
|
|
|
|
254
|
|
|
def __unicode__(self): |
255
|
|
|
return self.fullname |
256
|
|
|
|
257
|
|
|
@property |
258
|
|
|
def fullname(self): |
259
|
|
|
return u"%s %s" % (self.first_name, self.last_name) |
260
|
|
|
|
261
|
|
|
|
262
|
|
View Code Duplication |
class Group(models.Model): |
|
|
|
|
263
|
|
|
"""Group model.""" |
264
|
|
|
name = models.CharField(_('name'), |
265
|
|
|
max_length=200, |
266
|
|
|
unique=True) |
267
|
|
|
slug = models.SlugField(_('slug'), |
268
|
|
|
max_length=50, |
269
|
|
|
unique=True) |
270
|
|
|
about = models.TextField(_('about'), |
271
|
|
|
blank=True) |
272
|
|
|
people = models.ManyToManyField(Person, |
273
|
|
|
verbose_name='people', |
274
|
|
|
blank=True, |
275
|
|
|
null=True) |
276
|
|
|
companies = models.ManyToManyField(Company, |
277
|
|
|
verbose_name='companies', |
278
|
|
|
blank=True, |
279
|
|
|
null=True) |
280
|
|
|
date_added = models.DateTimeField(_('date added'), |
281
|
|
|
auto_now_add=True) |
282
|
|
|
date_modified = models.DateTimeField(_('date modified'), |
283
|
|
|
auto_now=True) |
284
|
|
|
|
285
|
|
|
class Meta: |
286
|
|
|
db_table = 'contacts_groups' |
287
|
|
|
ordering = ('name',) |
288
|
|
|
verbose_name = _('group') |
289
|
|
|
verbose_name_plural = _('groups') |
290
|
|
|
|
291
|
|
|
def __unicode__(self): |
292
|
|
|
return u"%s" % self.name |
293
|
|
|
|
294
|
|
|
PHONE_LOCATION_CHOICES = ( |
295
|
|
|
('work', _('Work')), |
296
|
|
|
('mobile', _('Mobile')), |
297
|
|
|
('fax', _('Fax')), |
298
|
|
|
('pager', _('Pager')), |
299
|
|
|
('home', _('Home')), |
300
|
|
|
('other', _('Other')), |
301
|
|
|
) |
302
|
|
|
|
303
|
|
|
|
304
|
|
|
class PhoneNumber(models.Model): |
305
|
|
|
"""Phone Number model.""" |
306
|
|
|
content_type = models.ForeignKey(ContentType, |
307
|
|
|
limit_choices_to={'app_label': 'contacts'}) |
308
|
|
|
object_id = models.IntegerField(db_index=True) |
309
|
|
|
content_object = generic.GenericForeignKey() |
310
|
|
|
phone_number = models.CharField(_('number'), |
311
|
|
|
max_length=50) |
312
|
|
|
location = models.CharField(_('location'), |
313
|
|
|
max_length=6, |
314
|
|
|
choices=PHONE_LOCATION_CHOICES, |
315
|
|
|
default='work') |
316
|
|
|
date_added = models.DateTimeField(_('date added'), |
317
|
|
|
auto_now_add=True) |
318
|
|
|
date_modified = models.DateTimeField(_('date modified'), |
319
|
|
|
auto_now=True) |
320
|
|
|
|
321
|
|
|
def __unicode__(self): |
322
|
|
|
return u"%s (%s)" % (self.phone_number, self.location) |
323
|
|
|
|
324
|
|
|
class Meta: |
325
|
|
|
db_table = 'contacts_phone_numbers' |
326
|
|
|
verbose_name = 'phone number' |
327
|
|
|
verbose_name_plural = 'phone numbers' |
328
|
|
|
|
329
|
|
|
LOCATION_CHOICES = ( |
330
|
|
|
('work', _('Work')), |
331
|
|
|
('home', _('Home')), |
332
|
|
|
('mobile', _('Mobile')), |
333
|
|
|
('fax', _('Fax')), |
334
|
|
|
('person', _('Personal')), |
335
|
|
|
('other', _('Other')) |
336
|
|
|
) |
337
|
|
|
|
338
|
|
|
|
339
|
|
|
class EmailAddress(models.Model): |
340
|
|
|
content_type = models.ForeignKey(ContentType, |
341
|
|
|
limit_choices_to={'app_label': 'contacts'}) |
342
|
|
|
object_id = models.IntegerField(db_index=True) |
343
|
|
|
content_object = generic.GenericForeignKey() |
344
|
|
|
email_address = models.EmailField(_('email address')) |
345
|
|
|
location = models.CharField(_('location'), |
346
|
|
|
max_length=6, |
347
|
|
|
choices=LOCATION_CHOICES, |
348
|
|
|
default='work') |
349
|
|
|
date_added = models.DateTimeField(_('date added'), |
350
|
|
|
auto_now_add=True) |
351
|
|
|
date_modified = models.DateTimeField(_('date modified'), |
352
|
|
|
auto_now=True) |
353
|
|
|
|
354
|
|
|
def __unicode__(self): |
355
|
|
|
return u"%s (%s)" % (self.email_address, self.location) |
356
|
|
|
|
357
|
|
|
class Meta: |
358
|
|
|
db_table = 'contacts_email_addresses' |
359
|
|
|
verbose_name = 'email address' |
360
|
|
|
verbose_name_plural = 'email addresses' |
361
|
|
|
|
362
|
|
|
IM_SERVICE_CHOICES = ( |
363
|
|
|
('aim', 'AIM'), |
364
|
|
|
('msn', 'MSN'), |
365
|
|
|
('icq', 'ICQ'), |
366
|
|
|
('jabber', 'Jabber'), |
367
|
|
|
('yahoo', 'Yahoo'), |
368
|
|
|
('skype', 'Skype'), |
369
|
|
|
('qq', 'QQ'), |
370
|
|
|
('sametime', 'Sametime'), |
371
|
|
|
('gadu-gadu', 'Gadu-Gadu'), |
372
|
|
|
('google-talk', 'Google Talk'), |
373
|
|
|
('twitter', 'Twitter'), |
374
|
|
|
('other', _('Other')) |
375
|
|
|
) |
376
|
|
|
|
377
|
|
|
|
378
|
|
|
class InstantMessenger(models.Model): |
379
|
|
|
content_type = models.ForeignKey(ContentType, |
380
|
|
|
limit_choices_to={'app_label': 'contacts'}) |
381
|
|
|
object_id = models.IntegerField(db_index=True) |
382
|
|
|
content_object = generic.GenericForeignKey() |
383
|
|
|
im_account = models.CharField(_('im account'), |
384
|
|
|
max_length=100) |
385
|
|
|
location = models.CharField(_('location'), |
386
|
|
|
max_length=6, |
387
|
|
|
choices=LOCATION_CHOICES, |
388
|
|
|
default='work') |
389
|
|
|
service = models.CharField(_('service'), |
390
|
|
|
max_length=11, |
391
|
|
|
choices=IM_SERVICE_CHOICES, |
392
|
|
|
default='jabber') |
393
|
|
|
date_added = models.DateTimeField(_('date added'), |
394
|
|
|
auto_now_add=True) |
395
|
|
|
date_modified = models.DateTimeField(_('date modified'), |
396
|
|
|
auto_now=True) |
397
|
|
|
|
398
|
|
|
def __unicode__(self): |
399
|
|
|
return u"%s (%s)" % (self.im_account, self.location) |
400
|
|
|
|
401
|
|
|
class Meta: |
402
|
|
|
db_table = 'contacts_instant_messengers' |
403
|
|
|
verbose_name = 'instant messenger' |
404
|
|
|
verbose_name_plural = 'instant messengers' |
405
|
|
|
|
406
|
|
|
|
407
|
|
View Code Duplication |
class WebSite(models.Model): |
|
|
|
|
408
|
|
|
content_type = models.ForeignKey(ContentType, |
409
|
|
|
limit_choices_to={'app_label': 'contacts'}) |
410
|
|
|
object_id = models.IntegerField(db_index=True) |
411
|
|
|
content_object = generic.GenericForeignKey() |
412
|
|
|
url = models.URLField(_('URL')) |
413
|
|
|
location = models.CharField(_('location'), |
414
|
|
|
max_length=6, |
415
|
|
|
choices=LOCATION_CHOICES, |
416
|
|
|
default='work') |
417
|
|
|
date_added = models.DateTimeField(_('date added'), |
418
|
|
|
auto_now_add=True) |
419
|
|
|
date_modified = models.DateTimeField(_('date modified'), |
420
|
|
|
auto_now=True) |
421
|
|
|
|
422
|
|
|
def __unicode__(self): |
423
|
|
|
return u"%s (%s)" % (self.url, self.location) |
424
|
|
|
|
425
|
|
|
class Meta: |
426
|
|
|
db_table = 'contacts_web_sites' |
427
|
|
|
verbose_name = _('web site') |
428
|
|
|
verbose_name_plural = _('web sites') |
429
|
|
|
|
430
|
|
|
def get_absolute_url(self): |
431
|
|
|
return u"%s?web_site=%s" % (self.content_object.get_absolute_url(), self.pk) |
432
|
|
|
|
433
|
|
|
|
434
|
|
|
class StreetAddress(models.Model): |
435
|
|
|
content_type = models.ForeignKey(ContentType, |
436
|
|
|
limit_choices_to={'app_label': 'contacts'}) |
437
|
|
|
object_id = models.IntegerField(db_index=True) |
438
|
|
|
content_object = generic.GenericForeignKey() |
439
|
|
|
street = models.TextField(_('street'), |
440
|
|
|
blank=True) |
441
|
|
|
city = models.CharField(_('city'), |
442
|
|
|
max_length=200, |
443
|
|
|
blank=True) |
444
|
|
|
province = models.CharField(_('province'), |
445
|
|
|
max_length=200, |
446
|
|
|
blank=True) |
447
|
|
|
postal_code = models.CharField(_('postal code'), |
448
|
|
|
max_length=10, |
449
|
|
|
blank=True) |
450
|
|
|
country = CountryField(_('country')) |
451
|
|
|
location = models.CharField(_('location'), |
452
|
|
|
max_length=6, |
453
|
|
|
choices=LOCATION_CHOICES, |
454
|
|
|
default='work') |
455
|
|
|
date_added = models.DateTimeField(_('date added'), |
456
|
|
|
auto_now_add=True) |
457
|
|
|
date_modified = models.DateTimeField(_('date modified'), |
458
|
|
|
auto_now=True) |
459
|
|
|
|
460
|
|
|
def __unicode__(self): |
461
|
|
|
return u"%s (%s)" % (self.city, self.location) |
462
|
|
|
|
463
|
|
|
class Meta: |
464
|
|
|
db_table = 'contacts_street_addresses' |
465
|
|
|
verbose_name = _('street address') |
466
|
|
|
verbose_name_plural = _('street addresses') |
467
|
|
|
|
468
|
|
|
|
469
|
|
View Code Duplication |
class SpecialDate(models.Model): |
|
|
|
|
470
|
|
|
content_type = models.ForeignKey(ContentType, |
471
|
|
|
limit_choices_to={'app_label': 'contacts'}) |
472
|
|
|
object_id = models.IntegerField(db_index=True) |
473
|
|
|
content_object = generic.GenericForeignKey() |
474
|
|
|
occasion = models.TextField(_('occasion'), |
475
|
|
|
max_length=200) |
476
|
|
|
date = models.DateField(_('date')) |
477
|
|
|
every_year = models.BooleanField(_('every year'), |
478
|
|
|
default=True) |
479
|
|
|
date_added = models.DateTimeField(_('date added'), |
480
|
|
|
auto_now_add=True) |
481
|
|
|
date_modified = models.DateTimeField(_('date modified'), |
482
|
|
|
auto_now=True) |
483
|
|
|
|
484
|
|
|
def __unicode__(self): |
485
|
|
|
return u"%s: %s" % (self.occasion, self.date) |
486
|
|
|
|
487
|
|
|
class Meta: |
488
|
|
|
db_table = 'contacts_special_dates' |
489
|
|
|
verbose_name = _('special date') |
490
|
|
|
verbose_name_plural = _('special dates') |
491
|
|
|
|
492
|
|
|
|
493
|
|
|
class CompanyBalanceHistory(models.Model): |
494
|
|
|
""" Company balance history Model """ |
495
|
|
|
company = models.ForeignKey(Company, |
496
|
|
|
verbose_name=_(u"company")) |
497
|
|
|
amount_debited = models.DecimalField(_(u'amount debited'), |
498
|
|
|
max_digits=12, |
499
|
|
|
decimal_places=4) |
500
|
|
|
amount_refund = models.DecimalField(_(u'amount refund'), |
501
|
|
|
max_digits=12, |
502
|
|
|
decimal_places=4) |
503
|
|
|
customer_balance = models.DecimalField(_(u'customer balance'), |
504
|
|
|
max_digits=12, |
505
|
|
|
decimal_places=4, |
506
|
|
|
default=0, |
507
|
|
|
help_text=_(u"""Resulting customer |
508
|
|
|
balance.""")) |
509
|
|
|
supplier_balance = models.DecimalField(_(u'provider balance'), |
510
|
|
|
max_digits=12, |
511
|
|
|
decimal_places=4, |
512
|
|
|
default=0, |
513
|
|
|
help_text=_(u"""Resulting provider |
514
|
|
|
balance.""")) |
515
|
|
|
OPERATION_TYPE_CHOICES = ( |
516
|
|
|
('customer', _(u"operation on customer account")), |
517
|
|
|
('provider', _(u"operation on provider account")), |
518
|
|
|
) |
519
|
|
|
operation_type = models.CharField(_(u"operation type"), |
520
|
|
|
max_length=10, |
521
|
|
|
choices=OPERATION_TYPE_CHOICES, |
522
|
|
|
default='customer') |
523
|
|
|
reference = models.CharField(_(u'public description'), |
524
|
|
|
max_length=255, |
525
|
|
|
blank=True) |
526
|
|
|
description = models.CharField(_(u'internal description'), |
527
|
|
|
max_length=255, |
528
|
|
|
blank=True) |
529
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
530
|
|
|
auto_now_add=True) |
531
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
532
|
|
|
auto_now=True) |
533
|
|
|
|
534
|
|
|
class Meta: |
535
|
|
|
db_table = 'company_balance_history' |
536
|
|
|
ordering = ('company', 'date_added') |
537
|
|
|
verbose_name = _(u'Company balance history') |
538
|
|
|
verbose_name_plural = _(u'Company balance history') |
539
|
|
|
|
540
|
|
|
def __unicode__(self): |
541
|
|
|
return u"%s %s %s %s" % (self.company, |
542
|
|
|
self.amount_debited, |
543
|
|
|
self.amount_refund, |
544
|
|
|
self.operation_type) |
545
|
|
|
|
546
|
|
|
|
547
|
|
|
class CustomerDirectory(models.Model): |
548
|
|
|
""" Customer Directory Model """ |
549
|
|
|
company = models.ForeignKey(Company, |
550
|
|
|
verbose_name=_(u"company")) |
551
|
|
|
registration = models.BooleanField(_(u"Registration"), |
552
|
|
|
default=False, |
553
|
|
|
help_text=_(u"""Is registration needed |
554
|
|
|
for calling ? True, the phone needs to |
555
|
|
|
register with correct username/password. |
556
|
|
|
If false, you must specify a CIDR in SIP |
557
|
|
|
IP CIDR !""")) |
558
|
|
|
password = models.CharField(_(u"password"), |
559
|
|
|
max_length=100, |
560
|
|
|
blank=True, |
561
|
|
|
help_text=_(u"""It's recommended to use strong |
562
|
|
|
passwords for the endpoint.""")) |
563
|
|
|
description = models.TextField(_(u'description'), |
564
|
|
|
blank=True) |
565
|
|
|
name = models.CharField(_(u"SIP username"), |
566
|
|
|
max_length=50, |
567
|
|
|
unique=True, |
568
|
|
|
help_text=_(u"Ex.: customer SIP username, etc...")) |
569
|
|
|
rtp_ip = models.CharField(_(u"RTP IP CIDR"), |
570
|
|
|
max_length=100, |
571
|
|
|
default="auto", |
572
|
|
|
help_text=_(u"""Internal IP address/mask to bind |
573
|
|
|
to for RTP. Format : CIDR Ex. 192.168.1.0/32""")) |
574
|
|
|
sip_ip = models.CharField(_(u"SIP IP CIDR"), |
575
|
|
|
max_length=100, |
576
|
|
|
null=True, |
577
|
|
|
blank=True, |
578
|
|
|
validators=[validate_cidr], |
579
|
|
|
help_text=_(u"""Internal IP address/mask to bind |
580
|
|
|
to for SIP. Format : CIDR. Ex. 192.168.1.0/32 |
581
|
|
|
""")) |
582
|
|
|
sip_port = models.PositiveIntegerField(_(u"SIP port"), |
583
|
|
|
default=5060) |
584
|
|
|
max_calls = models.PositiveIntegerField(_(u'max calls'), |
585
|
|
|
default=1, |
586
|
|
|
help_text=_(u"""max simultaneous |
587
|
|
|
calls allowed for this customer |
588
|
|
|
account.""")) |
589
|
|
|
calls_per_second = models.PositiveIntegerField(_(u'max calls per second'), |
590
|
|
|
default=10, |
591
|
|
|
help_text=_(u"""maximum |
592
|
|
|
calls per second allowed for |
593
|
|
|
this customer account.""")) |
594
|
|
|
log_auth_failures = models.BooleanField(_(u"log auth failures"), |
595
|
|
|
default=False, |
596
|
|
|
help_text=_(u"""It true, the server |
597
|
|
|
will log authentication failures. |
598
|
|
|
Required for Fail2ban.""")) |
599
|
|
|
MULTIPLE_CODECS_CHOICES = ( |
600
|
|
|
("PCMA,PCMU,G729", _(u"PCMA,PCMU,G729")), |
601
|
|
|
("PCMU,PCMA,G729", _(u"PCMU,PCMA,G729")), |
602
|
|
|
("G729,PCMA,PCMU", _(u"G729,PCMA,PCMU")), |
603
|
|
|
("G729,PCMU,PCMA", _(u"G729,PCMU,PCMA")), |
604
|
|
|
("PCMA,G729", _(u"PCMA,G729")), |
605
|
|
|
("PCMU,G729", _(u"PCMU,G729")), |
606
|
|
|
("G729,PCMA", _(u"G729,PCMA")), |
607
|
|
|
("G729,PCMU", _(u"G729,PCMU")), |
608
|
|
|
("PCMA,PCMU", _(u"PCMA,PCMU")), |
609
|
|
|
("PCMU,PCMA", _(u"PCMU,PCMA")), |
610
|
|
|
("G722,PCMA,PCMU", _(u"G722,PCMA,PCMU")), |
611
|
|
|
("G722,PCMU,PCMA", _(u"G722,PCMU,PCMA")), |
612
|
|
|
("G722", _(u"G722")), |
613
|
|
|
("G729", _(u"G729")), |
614
|
|
|
("PCMU", _(u"PCMU")), |
615
|
|
|
("PCMA", _(u"PCMA")), |
616
|
|
|
("ALL", _(u"ALL")), |
617
|
|
|
) |
618
|
|
|
codecs = models.CharField(_(u"Codecs"), |
619
|
|
|
max_length=100, |
620
|
|
|
default="ALL", |
621
|
|
|
choices=MULTIPLE_CODECS_CHOICES, |
622
|
|
|
help_text=_(u"""Codecs allowed - beware about |
623
|
|
|
order, 1st has high priority """)) |
624
|
|
|
MULTIPLE_REG_CHOICES = ( |
625
|
|
|
("call-id", _(u"Call-id")), |
626
|
|
|
("contact", _(u"Contact")), |
627
|
|
|
("false", _(u"False")), |
628
|
|
|
("true", _(u"True"))) |
629
|
|
|
multiple_registrations = models.CharField(_(u"multiple registrations"), |
630
|
|
|
max_length=100, |
631
|
|
|
default="false", |
632
|
|
|
choices=MULTIPLE_REG_CHOICES, |
633
|
|
|
help_text=_(u"""Used to allow to |
634
|
|
|
call one extension and ring |
635
|
|
|
several phones.""")) |
636
|
|
|
outbound_caller_id_name = models.CharField(_(u"CallerID name"), |
637
|
|
|
max_length=50, |
638
|
|
|
blank=True, |
639
|
|
|
help_text=_(u"""Caller ID name |
640
|
|
|
sent to provider on outbound |
641
|
|
|
calls.""")) |
642
|
|
|
outbound_caller_id_number = models.CharField(_(u"""CallerID |
643
|
|
|
num"""), |
644
|
|
|
max_length=80, |
645
|
|
|
blank=True, |
646
|
|
|
help_text=_(u"""Caller ID |
647
|
|
|
number sent to provider on |
648
|
|
|
outbound calls.""")) |
649
|
|
|
IEM_CHOICES = ( |
650
|
|
|
("false", _(u"false")), |
651
|
|
|
("true", _(u"true")), |
652
|
|
|
("ring_ready", _(u"ring_ready"))) |
653
|
|
|
ignore_early_media = models.CharField(_(u"Ignore early media"), |
654
|
|
|
max_length=20, |
655
|
|
|
default="false", |
656
|
|
|
choices=IEM_CHOICES, |
657
|
|
|
help_text=_(u"""Controls if the call |
658
|
|
|
returns on early media |
659
|
|
|
or not. Default is false. |
660
|
|
|
Setting the value to |
661
|
|
|
"ring_ready" will work |
662
|
|
|
the same as |
663
|
|
|
ignore_early_media=true |
664
|
|
|
but also send a SIP 180 |
665
|
|
|
to the inbound leg when |
666
|
|
|
the first SIP 183 is |
667
|
|
|
caught. |
668
|
|
|
""")) |
669
|
|
|
enabled = models.BooleanField(_(u"Enabled / Disabled"), |
670
|
|
|
default=True) |
671
|
|
|
fake_ring = models.BooleanField(_(u"Fake ring"), |
672
|
|
|
default=False, |
673
|
|
|
help_text=_(u"""Fake ring : Enabled / |
674
|
|
|
Disabled - Send a fake ring to the |
675
|
|
|
caller.""")) |
676
|
|
|
cli_debug = models.BooleanField(_(u"CLI debug"), |
677
|
|
|
default=False, |
678
|
|
|
help_text=_(u"""CLI debug : Enabled / |
679
|
|
|
Disabled - Permit to see all debug |
680
|
|
|
messages on cli.""")) |
681
|
|
|
vmd = models.BooleanField(_(u"Voicemail detection : Enabled / Disabled"), |
682
|
|
|
default=False, |
683
|
|
|
help_text=_(u"""Be carefull with this option, as |
684
|
|
|
it takes a lot of ressources !.""")) |
685
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
686
|
|
|
auto_now_add=True) |
687
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
688
|
|
|
auto_now=True) |
689
|
|
|
|
690
|
|
|
class Meta: |
691
|
|
|
db_table = 'customer_directory' |
692
|
|
|
ordering = ('company', 'name') |
693
|
|
|
verbose_name = _(u'Customer sip account') |
694
|
|
|
verbose_name_plural = _(u'Customer sip accounts') |
695
|
|
|
|
696
|
|
|
def __unicode__(self): |
697
|
|
|
return "%s (%s:%s)" % (self.name, self.sip_ip, self.sip_port) |
698
|
|
|
|
699
|
|
|
def clean(self): |
700
|
|
|
if (self.registration and |
701
|
|
|
(self.password is None or self.password == '')): |
702
|
|
|
raise ValidationError("""You have to specify a password if you |
703
|
|
|
want to allow registration""") |
704
|
|
|
if (self.registration is False and |
705
|
|
|
(self.sip_ip is None or self.sip_ip == '')): |
706
|
|
|
raise ValidationError("""You must specify a SIP IP CIDR if you do |
707
|
|
|
not want to use registration""") |
708
|
|
|
if self.registration and self.password: |
709
|
|
|
# in future use https://github.com/dstufft/django-passwords ? |
710
|
|
|
MIN_LENGTH = 8 |
711
|
|
|
if len(self.password) < MIN_LENGTH: |
712
|
|
|
raise ValidationError("""The password must be at least %d |
713
|
|
|
characters long.""" % MIN_LENGTH) |
714
|
|
|
first_isalpha = self.password[0].isalpha() |
715
|
|
|
if all(c.isalpha() == first_isalpha for c in self.password): |
716
|
|
|
raise ValidationError("""The new password must contain |
717
|
|
|
at least one letter and at least |
718
|
|
|
one digit""") |
719
|
|
|
if self.sip_ip: |
720
|
|
|
m = re.search('/32$', self.sip_ip) |
721
|
|
|
if m: |
722
|
|
|
pass |
723
|
|
|
elif len(IPNetwork(self.sip_ip)) == 1: |
724
|
|
|
self.sip_ip = str(self.sip_ip) + str('/32') |
725
|
|
|
# add name check no space ... |
726
|
|
|
|
727
|
|
|
# Caller ID list |
728
|
|
|
|
729
|
|
|
|
730
|
|
|
class CalleridPrefixList(models.Model): |
731
|
|
|
""" CallerID List """ |
732
|
|
|
name = models.CharField(_(u'name'), |
733
|
|
|
max_length=128, |
734
|
|
|
unique=True) |
735
|
|
|
description = models.TextField(_(u'description'), |
736
|
|
|
blank=True) |
737
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
738
|
|
|
auto_now_add=True) |
739
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
740
|
|
|
auto_now=True) |
741
|
|
|
|
742
|
|
|
class Meta: |
743
|
|
|
db_table = 'callerid_prefix_list' |
744
|
|
|
ordering = ('name',) |
745
|
|
|
verbose_name = _(u'CallerID prefix list') |
746
|
|
|
verbose_name_plural = _(u'CallerID prefix lists') |
747
|
|
|
|
748
|
|
|
def __unicode__(self): |
749
|
|
|
return u"%s" % self.name |
750
|
|
|
|
751
|
|
|
def prefix(self): |
752
|
|
|
html = '<span><a href="/extranet/pyfreebill/calleridprefix/?calleridprefixlist__id__exact={0}" class="btn btn-inverse btn-mini">Prefix <i class="icon-plus-sign"></i></a></span>' |
753
|
|
|
return format_html(html, (self.id)) |
754
|
|
|
prefix.allow_tags = True |
755
|
|
|
prefix.short_description = 'prefix' |
756
|
|
|
|
757
|
|
|
|
758
|
|
View Code Duplication |
class CalleridPrefix(models.Model): |
|
|
|
|
759
|
|
|
""" Customer Rates Model """ |
760
|
|
|
calleridprefixlist = models.ForeignKey(CalleridPrefixList, |
761
|
|
|
verbose_name=_(u"callerid prefix list")) |
762
|
|
|
prefix = models.CharField(_(u'numeric prefix'), |
763
|
|
|
max_length=30, |
764
|
|
|
db_index=True) |
765
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
766
|
|
|
auto_now_add=True) |
767
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
768
|
|
|
auto_now=True) |
769
|
|
|
|
770
|
|
|
class Meta: |
771
|
|
|
db_table = 'caller_id_prefix' |
772
|
|
|
ordering = ('calleridprefixlist', 'prefix') |
773
|
|
|
unique_together = ("calleridprefixlist", "prefix") |
774
|
|
|
verbose_name = _(u'Callerid prefix') |
775
|
|
|
verbose_name_plural = _(u'Callerid prefix') |
776
|
|
|
|
777
|
|
|
def __unicode__(self): |
778
|
|
|
return u"%s" % self.prefix |
779
|
|
|
|
780
|
|
|
# Provider Rates |
781
|
|
|
|
782
|
|
|
|
783
|
|
|
class ProviderTariff(models.Model): |
784
|
|
|
""" Provider tariff """ |
785
|
|
|
name = models.CharField(_(u"name"), |
786
|
|
|
max_length=128) |
787
|
|
|
carrier = models.ForeignKey(Company, |
788
|
|
|
verbose_name=_(u"Provider"), |
789
|
|
|
limit_choices_to={'supplier_enabled': True}) |
790
|
|
|
currency = models.ForeignKey(Currency, |
791
|
|
|
verbose_name=_(u"Currency")) |
792
|
|
|
lead_strip = models.CharField(_(u'lead strip'), |
793
|
|
|
blank=True, |
794
|
|
|
default='', |
795
|
|
|
max_length=15) |
796
|
|
|
tail_strip = models.CharField(_(u'tail strip'), |
797
|
|
|
blank=True, |
798
|
|
|
default='', |
799
|
|
|
max_length=15) |
800
|
|
|
prefix = models.CharField(_(u'prefix'), |
801
|
|
|
blank=True, |
802
|
|
|
default='', |
803
|
|
|
max_length=15) |
804
|
|
|
suffix = models.CharField(_(u'suffix'), |
805
|
|
|
blank=True, |
806
|
|
|
default='', |
807
|
|
|
max_length=15) |
808
|
|
|
description = models.TextField(_(u'description'), |
809
|
|
|
blank=True) |
810
|
|
|
CALLERID_FILTER_CHOICES = ( |
811
|
|
|
('1', _(u"No filter")), |
812
|
|
|
('2', _(u"Prefix authorized")), |
813
|
|
|
('3', _(u"Prefix prohibited")), |
814
|
|
|
) |
815
|
|
|
callerid_filter = models.CharField(_(u"CallerID Prefix filter"), |
816
|
|
|
max_length=2, |
817
|
|
|
choices=CALLERID_FILTER_CHOICES, |
818
|
|
|
default='1') |
819
|
|
|
callerid_list = models.ForeignKey(CalleridPrefixList, |
820
|
|
|
verbose_name=_(u"CallerID prefix List"), |
821
|
|
|
blank=True, |
822
|
|
|
null=True) |
823
|
|
|
date_start = models.DateTimeField() |
824
|
|
|
date_end = models.DateTimeField() |
825
|
|
|
quality = models.IntegerField(_(u'quality'), |
826
|
|
|
blank=True, |
827
|
|
|
default='100', |
828
|
|
|
help_text=_(u"Order by quality.")) |
829
|
|
|
reliability = models.IntegerField(_(u'reliability'), |
830
|
|
|
blank=True, |
831
|
|
|
default='100', |
832
|
|
|
help_text=_(u"Order by reliability.")) |
833
|
|
|
cid = models.CharField(_(u'cid'), |
834
|
|
|
blank=True, |
835
|
|
|
default='', |
836
|
|
|
max_length=25, |
837
|
|
|
help_text=_(u"Regex to modify CallerID number.")) |
838
|
|
|
enabled = models.BooleanField(_(u"Enabled / Disabled"), |
839
|
|
|
default=True) |
840
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
841
|
|
|
auto_now_add=True) |
842
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
843
|
|
|
auto_now=True) |
844
|
|
|
|
845
|
|
|
class Meta: |
846
|
|
|
db_table = 'provider_tariff' |
847
|
|
|
ordering = ('enabled', |
848
|
|
|
'quality', |
849
|
|
|
'reliability') |
850
|
|
|
verbose_name = _(u'provider ratecard') |
851
|
|
|
verbose_name_plural = _(u'provider ratecards') |
852
|
|
|
|
853
|
|
|
def __unicode__(self): |
854
|
|
|
return u"%s" % self.name |
855
|
|
|
|
856
|
|
|
def rates(self): |
857
|
|
|
html = '<span><a href="/extranet/pyfreebill/providerrates/?provider_tariff__id__exact={0}" class="btn btn-inverse btn-mini">Rates <i class="icon-plus-sign"></i></a></span>' |
858
|
|
|
return format_html(html, (self.id)) |
859
|
|
|
rates.allow_tags = True |
860
|
|
|
rates.short_description = 'rates' |
861
|
|
|
|
862
|
|
|
|
863
|
|
|
class ProviderRates(models.Model): |
864
|
|
|
""" Provider Rates Model """ |
865
|
|
|
destination = models.CharField(_(u'destination'), |
866
|
|
|
blank=True, |
867
|
|
|
default='', |
868
|
|
|
null=True, |
869
|
|
|
max_length=128, |
870
|
|
|
db_index=True) |
871
|
|
|
digits = models.CharField(_(u'numeric prefix'), |
872
|
|
|
max_length=30, |
873
|
|
|
db_index=True) |
874
|
|
|
cost_rate = models.DecimalField(_(u'Cost rate'), |
875
|
|
|
max_digits=11, |
876
|
|
|
decimal_places=5) |
877
|
|
|
block_min_duration = models.IntegerField(_(u'block min duration'), |
878
|
|
|
default=1) |
879
|
|
|
init_block = models.DecimalField(_(u'Init block rate'), |
880
|
|
|
max_digits=11, |
881
|
|
|
decimal_places=5, |
882
|
|
|
default=0) |
883
|
|
|
provider_tariff = models.ForeignKey(ProviderTariff) |
884
|
|
|
date_start = models.DateTimeField() |
885
|
|
|
date_end = models.DateTimeField() |
886
|
|
|
enabled = models.BooleanField(_(u"Enabled / Disabled"), default=True) |
887
|
|
|
date_added = models.DateTimeField(_(u'date added'), auto_now_add=True) |
888
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), auto_now=True) |
889
|
|
|
|
890
|
|
|
class Meta: |
891
|
|
|
db_table = 'provider_rates' |
892
|
|
|
ordering = ('enabled', 'provider_tariff', 'digits') |
893
|
|
|
index_together = [ |
894
|
|
|
["provider_tariff", "digits", "enabled"], |
895
|
|
|
] |
896
|
|
|
unique_together = ("digits", "provider_tariff") |
897
|
|
|
verbose_name = _(u'provider rate') |
898
|
|
|
verbose_name_plural = _(u'provider rates') |
899
|
|
|
|
900
|
|
|
def __unicode__(self): |
901
|
|
|
return u"%s %s %s " % (self.digits, |
902
|
|
|
self.cost_rate, |
903
|
|
|
self.provider_tariff) |
904
|
|
|
|
905
|
|
|
def set_bar(self, value): |
906
|
|
|
self.bar = value |
907
|
|
|
simple_import_methods = ('set_bar',) |
908
|
|
|
|
909
|
|
|
|
910
|
|
|
# LCR |
911
|
|
|
|
912
|
|
|
|
913
|
|
View Code Duplication |
class LCRGroup(models.Model): |
|
|
|
|
914
|
|
|
""" LCR group model """ |
915
|
|
|
name = models.CharField(_(u"name"), |
916
|
|
|
max_length=128, |
917
|
|
|
unique=True) |
918
|
|
|
description = models.TextField(_(u'description'), |
919
|
|
|
blank=True) |
920
|
|
|
LCR_TYPE_CHOICES = ( |
921
|
|
|
('p', _(u"lower price")), |
922
|
|
|
('q', _(u"best quality")), |
923
|
|
|
('r', _(u"best reliability")), |
924
|
|
|
('l', _(u"load balance")), |
925
|
|
|
) |
926
|
|
|
lcrtype = models.CharField(_(u"lcr type"), |
927
|
|
|
max_length=10, |
928
|
|
|
choices=LCR_TYPE_CHOICES, |
929
|
|
|
default='p') |
930
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
931
|
|
|
auto_now_add=True) |
932
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
933
|
|
|
auto_now=True) |
934
|
|
|
|
935
|
|
|
class Meta: |
936
|
|
|
db_table = 'lcr_group' |
937
|
|
|
ordering = ('name',) |
938
|
|
|
verbose_name = _(u'LCR') |
939
|
|
|
verbose_name_plural = _(u'LCRs') |
940
|
|
|
|
941
|
|
|
def __unicode__(self): |
942
|
|
|
return u"%s %s " % (self.name, self.lcrtype) |
943
|
|
|
|
944
|
|
|
|
945
|
|
View Code Duplication |
class LCRProviders(models.Model): |
|
|
|
|
946
|
|
|
""" LCR group model """ |
947
|
|
|
lcr = models.ForeignKey(LCRGroup, |
948
|
|
|
verbose_name=_(u"LCR")) |
949
|
|
|
provider_tariff = models.ForeignKey(ProviderTariff, |
950
|
|
|
verbose_name=_(u"Provider tariff")) |
951
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
952
|
|
|
auto_now_add=True) |
953
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
954
|
|
|
auto_now=True) |
955
|
|
|
|
956
|
|
|
class Meta: |
957
|
|
|
db_table = 'lcr_providers' |
958
|
|
|
verbose_name = _(u'LCR provider') |
959
|
|
|
verbose_name_plural = _(u'LCR providers') |
960
|
|
|
|
961
|
|
|
def __unicode__(self): |
962
|
|
|
return u"%s - %s " % (self.lcr, self.provider_tariff) |
963
|
|
|
|
964
|
|
|
def rates(self): |
965
|
|
|
html = '<span><a href="/extranet/pyfreebill/providerrates/?provider_tariff__id__exact={0}" class="btn btn-inverse btn-mini">Rates <i class="icon-plus-sign"></i></a></span>' |
966
|
|
|
return format_html(html, (self.provider_tariff)) |
967
|
|
|
rates.allow_tags = True |
968
|
|
|
rates.short_description = 'rates' |
969
|
|
|
|
970
|
|
|
|
971
|
|
|
# Ratecard |
972
|
|
|
|
973
|
|
|
|
974
|
|
|
class RateCard(models.Model): |
975
|
|
|
""" RateCard Model """ |
976
|
|
|
name = models.CharField(_(u'name'), |
977
|
|
|
max_length=128, |
978
|
|
|
unique=True) |
979
|
|
|
description = models.TextField(_(u'description'), |
980
|
|
|
blank=True) |
981
|
|
|
currency = models.ForeignKey(Currency, |
982
|
|
|
verbose_name=_(u"Currency")) |
983
|
|
|
lcrgroup = models.ForeignKey(LCRGroup, |
984
|
|
|
verbose_name=_(u"lcr")) |
985
|
|
|
CALLERID_FILTER_CHOICES = ( |
986
|
|
|
('1', _(u"No filter")), |
987
|
|
|
('2', _(u"Prefix authorized")), |
988
|
|
|
('3', _(u"Prefix prohibited")), |
989
|
|
|
) |
990
|
|
|
callerid_filter = models.CharField(_(u"CallerID Prefix filter"), |
991
|
|
|
max_length=2, |
992
|
|
|
choices=CALLERID_FILTER_CHOICES, |
993
|
|
|
default='1') |
994
|
|
|
callerid_list = models.ForeignKey(CalleridPrefixList, |
995
|
|
|
verbose_name=_(u"CallerID prefix List"), |
996
|
|
|
blank=True, |
997
|
|
|
null=True) |
998
|
|
|
enabled = models.BooleanField(_(u"Enabled / Disabled"), |
999
|
|
|
default=True) |
1000
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
1001
|
|
|
auto_now_add=True) |
1002
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
1003
|
|
|
auto_now=True) |
1004
|
|
|
|
1005
|
|
|
class Meta: |
1006
|
|
|
db_table = 'ratecard' |
1007
|
|
|
ordering = ('name', 'enabled') |
1008
|
|
|
verbose_name = _(u'Customer ratecard') |
1009
|
|
|
verbose_name_plural = _(u'Customer ratecards') |
1010
|
|
|
|
1011
|
|
|
def __unicode__(self): |
1012
|
|
|
return u"%s" % self.name |
1013
|
|
|
|
1014
|
|
|
def rates(self): |
1015
|
|
|
html = '<span><a href="/extranet/pyfreebill/customerrates/?ratecard__id__exact={0}" class="btn btn-inverse btn-mini">Rates <i class="icon-plus-sign"></i></a></span>' |
1016
|
|
|
return format_html(html, (self.id)) |
1017
|
|
|
rates.allow_tags = True |
1018
|
|
|
rates.short_description = 'Rates' |
1019
|
|
|
|
1020
|
|
|
def lcr(self): |
1021
|
|
|
html = '<span><a href="/extranet/pyfreebill/lcrgroup/{0}/" class="btn btn-inverse btn-mini">LCR <i class="icon-plus-sign"></i></a></span>' |
1022
|
|
|
return format_html(html, (self.lcrgroup.pk)) |
1023
|
|
|
lcr.allow_tags = True |
1024
|
|
|
lcr.short_description = 'lcr' |
1025
|
|
|
|
1026
|
|
|
|
1027
|
|
|
class CustomerRates(models.Model): |
1028
|
|
|
""" Customer Rates Model """ |
1029
|
|
|
ratecard = models.ForeignKey(RateCard, |
1030
|
|
|
verbose_name=_(u"ratecard")) |
1031
|
|
|
destination = models.CharField(_(u'destination'), |
1032
|
|
|
blank=True, |
1033
|
|
|
default='', |
1034
|
|
|
null=True, |
1035
|
|
|
max_length=128, |
1036
|
|
|
db_index=True) |
1037
|
|
|
prefix = models.CharField(_(u'numeric prefix'), |
1038
|
|
|
max_length=30, |
1039
|
|
|
db_index=True) |
1040
|
|
|
rate = models.DecimalField(_(u'sell rate'), |
1041
|
|
|
max_digits=11, |
1042
|
|
|
decimal_places=5, |
1043
|
|
|
help_text=_(u"to block the prefix, put -1")) |
1044
|
|
|
block_min_duration = models.IntegerField(_(u'block min duration'), |
1045
|
|
|
default=1) |
1046
|
|
|
init_block = models.DecimalField(_(u'Init block rate'), |
1047
|
|
|
max_digits=11, |
1048
|
|
|
decimal_places=5, |
1049
|
|
|
default=0) |
1050
|
|
|
date_start = models.DateTimeField() |
1051
|
|
|
date_end = models.DateTimeField() |
1052
|
|
|
enabled = models.BooleanField(_(u"Enabled"), |
1053
|
|
|
default=True) |
1054
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
1055
|
|
|
auto_now_add=True) |
1056
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
1057
|
|
|
auto_now=True) |
1058
|
|
|
|
1059
|
|
|
class Meta: |
1060
|
|
|
db_table = 'customer_rates' |
1061
|
|
|
ordering = ('ratecard', 'prefix', 'enabled') |
1062
|
|
|
unique_together = ("ratecard", "prefix") |
1063
|
|
|
verbose_name = _(u'customer rate') |
1064
|
|
|
verbose_name_plural = _(u'customer rates') |
1065
|
|
|
|
1066
|
|
|
def __unicode__(self): |
1067
|
|
|
return u"%s" % self.ratecard |
1068
|
|
|
|
1069
|
|
|
|
1070
|
|
|
class CustomerRateCards(models.Model): |
1071
|
|
|
""" Customer rates Cards Model """ |
1072
|
|
|
company = models.ForeignKey(Company, |
1073
|
|
|
verbose_name=_(u"company")) |
1074
|
|
|
ratecard = models.ForeignKey(RateCard, |
1075
|
|
|
verbose_name=_(u"ratecard")) |
1076
|
|
|
description = models.TextField(_(u'description'), |
1077
|
|
|
blank=True) |
1078
|
|
|
tech_prefix = models.CharField(_(u"technical prefix"), |
1079
|
|
|
blank=True, |
1080
|
|
|
default='', |
1081
|
|
|
null=True, |
1082
|
|
|
max_length=7) |
1083
|
|
|
DEFAULT_PRIORITY_CHOICES = ( |
1084
|
|
|
(1, _(u'1')), |
1085
|
|
|
(2, _(u'2')), |
1086
|
|
|
(3, _(u'3')), |
1087
|
|
|
) |
1088
|
|
|
priority = models.IntegerField(_(u'priority'), |
1089
|
|
|
choices=DEFAULT_PRIORITY_CHOICES, |
1090
|
|
|
help_text=_(u"""Priority order, 1 is the |
1091
|
|
|
higher priority and 3 the |
1092
|
|
|
lower one. Correct values |
1093
|
|
|
are : 1, 2 or 3 !.""")) |
1094
|
|
|
discount = models.DecimalField(_(u'discount'), |
1095
|
|
|
max_digits=3, |
1096
|
|
|
decimal_places=2, |
1097
|
|
|
default=0, |
1098
|
|
|
help_text=_(u"""ratecard discount. For |
1099
|
|
|
10% discount, enter 10 !""")) |
1100
|
|
|
allow_negative_margin = models.BooleanField(_(u"""Allow calls with |
1101
|
|
|
negative margin"""), |
1102
|
|
|
default=False) |
1103
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
1104
|
|
|
auto_now_add=True) |
1105
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
1106
|
|
|
auto_now=True) |
1107
|
|
|
|
1108
|
|
|
class Meta: |
1109
|
|
|
db_table = 'customer_ratecards' |
1110
|
|
|
ordering = ('company', 'priority', 'ratecard') |
1111
|
|
|
verbose_name = _(u'Customer Ratecard Allocation') |
1112
|
|
|
verbose_name_plural = _(u'Customer ratecard Allocations') |
1113
|
|
|
|
1114
|
|
|
def __unicode__(self): |
1115
|
|
|
return u"%s - Priority: %s Desc: %s" % (self.ratecard, |
1116
|
|
|
self.priority, |
1117
|
|
|
self.description) |
1118
|
|
|
|
1119
|
|
|
|
1120
|
|
|
# NORMALIZATION |
1121
|
|
|
|
1122
|
|
|
|
1123
|
|
View Code Duplication |
class DestinationNumberRules(models.Model): |
|
|
|
|
1124
|
|
|
""" Destination Number Normalization Rules """ |
1125
|
|
|
prefix = models.CharField(_(u'numeric prefix'), |
1126
|
|
|
max_length=30) |
1127
|
|
|
description = models.TextField(_(u'description'), |
1128
|
|
|
blank=True) |
1129
|
|
|
format_num = models.CharField(_(u"Rule format"), |
1130
|
|
|
max_length=150, |
1131
|
|
|
help_text=_(u"""example for Tunisia : |
1132
|
|
|
^216[%d][%d][%d][%d][%d][%d][%d][%d] |
1133
|
|
|
$""")) |
1134
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
1135
|
|
|
auto_now_add=True) |
1136
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
1137
|
|
|
auto_now=True) |
1138
|
|
|
|
1139
|
|
|
class Meta: |
1140
|
|
|
db_table = 'destination_norm_rules' |
1141
|
|
|
ordering = ('prefix',) |
1142
|
|
|
verbose_name = _(u'Destination Number Normalization Rule') |
1143
|
|
|
verbose_name_plural = _(u'Destination Number Normalization Rules') |
1144
|
|
|
|
1145
|
|
|
def __unicode__(self): |
1146
|
|
|
return u"%s -> %s " % (self.prefix, self.format_num) |
1147
|
|
|
|
1148
|
|
|
|
1149
|
|
View Code Duplication |
class CustomerNormalizationRules(models.Model): |
|
|
|
|
1150
|
|
|
""" Customer Normalization Rules """ |
1151
|
|
|
company = models.ForeignKey(Company, |
1152
|
|
|
verbose_name=_(u"customer")) |
1153
|
|
|
prefix = models.CharField(_(u'rule title'), |
1154
|
|
|
max_length=30) |
1155
|
|
|
description = models.TextField(_(u'description'), |
1156
|
|
|
blank=True) |
1157
|
|
|
remove_prefix = models.CharField(_(u"remove prefix"), |
1158
|
|
|
blank=True, |
1159
|
|
|
default='', |
1160
|
|
|
max_length=15) |
1161
|
|
|
add_prefix = models.CharField(_(u"add prefix"), |
1162
|
|
|
blank=True, |
1163
|
|
|
default='', |
1164
|
|
|
max_length=15) |
1165
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
1166
|
|
|
auto_now_add=True) |
1167
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
1168
|
|
|
auto_now=True) |
1169
|
|
|
|
1170
|
|
|
class Meta: |
1171
|
|
|
db_table = 'customer_norm_rules' |
1172
|
|
|
ordering = ('company', 'prefix') |
1173
|
|
|
verbose_name = _(u'Customer Normalization Rule') |
1174
|
|
|
verbose_name_plural = _(u'Customer Normalization Rules') |
1175
|
|
|
|
1176
|
|
|
def __unicode__(self): |
1177
|
|
|
return u"%s -> %s -%s +%s" % (self.company, |
1178
|
|
|
self.prefix, |
1179
|
|
|
self.remove_prefix, |
1180
|
|
|
self.add_prefix) |
1181
|
|
|
|
1182
|
|
|
|
1183
|
|
View Code Duplication |
class CarrierNormalizationRules(models.Model): |
|
|
|
|
1184
|
|
|
""" Carrier Normalization Rules """ |
1185
|
|
|
company = models.ForeignKey(Company, |
1186
|
|
|
verbose_name=_(u"provider")) |
1187
|
|
|
prefix = models.CharField(_(u'rule title'), |
1188
|
|
|
max_length=30) |
1189
|
|
|
description = models.TextField(_(u'description'), |
1190
|
|
|
blank=True) |
1191
|
|
|
remove_prefix = models.CharField(_(u"remove prefix"), |
1192
|
|
|
blank=True, |
1193
|
|
|
default='', |
1194
|
|
|
max_length=15) |
1195
|
|
|
add_prefix = models.CharField(_(u"add prefix"), |
1196
|
|
|
blank=True, |
1197
|
|
|
default='', |
1198
|
|
|
max_length=15) |
1199
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
1200
|
|
|
auto_now_add=True) |
1201
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
1202
|
|
|
auto_now=True) |
1203
|
|
|
|
1204
|
|
|
class Meta: |
1205
|
|
|
db_table = 'carrier_norm_rules' |
1206
|
|
|
ordering = ('company', 'prefix') |
1207
|
|
|
verbose_name = _(u'Provider Normalization Rule') |
1208
|
|
|
verbose_name_plural = _(u'Provider Normalization Rules') |
1209
|
|
|
|
1210
|
|
|
def __unicode__(self): |
1211
|
|
|
return u"%s -> %s -%s +%s" % (self.company, |
1212
|
|
|
self.prefix, |
1213
|
|
|
self.remove_prefix, |
1214
|
|
|
self.add_prefix) |
1215
|
|
|
|
1216
|
|
|
|
1217
|
|
View Code Duplication |
class CustomerCIDNormalizationRules(models.Model): |
|
|
|
|
1218
|
|
|
""" Customer Caller ID Number Normalization Rules """ |
1219
|
|
|
company = models.ForeignKey(Company, |
1220
|
|
|
verbose_name=_(u"customer")) |
1221
|
|
|
prefix = models.CharField(_(u'rule title'), |
1222
|
|
|
max_length=30) |
1223
|
|
|
description = models.TextField(_(u'description'), |
1224
|
|
|
blank=True) |
1225
|
|
|
remove_prefix = models.CharField(_(u"remove prefix"), |
1226
|
|
|
blank=True, |
1227
|
|
|
default='', |
1228
|
|
|
max_length=15) |
1229
|
|
|
add_prefix = models.CharField(_(u"add prefix"), |
1230
|
|
|
blank=True, |
1231
|
|
|
default='', |
1232
|
|
|
max_length=15) |
1233
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
1234
|
|
|
auto_now_add=True) |
1235
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
1236
|
|
|
auto_now=True) |
1237
|
|
|
|
1238
|
|
|
class Meta: |
1239
|
|
|
db_table = 'customer_cid_norm_rules' |
1240
|
|
|
ordering = ('company', ) |
1241
|
|
|
verbose_name = _(u'Customer CallerID Normalization Rule') |
1242
|
|
|
verbose_name_plural = _(u'Customer CallerID Normalization Rules') |
1243
|
|
|
|
1244
|
|
|
def __unicode__(self): |
1245
|
|
|
return u"%s -> -%s +%s" % (self.company, |
1246
|
|
|
self.remove_prefix, |
1247
|
|
|
self.add_prefix) |
1248
|
|
|
|
1249
|
|
|
|
1250
|
|
View Code Duplication |
class CarrierCIDNormalizationRules(models.Model): |
|
|
|
|
1251
|
|
|
""" Carrier Caller ID Number Normalization Rules """ |
1252
|
|
|
company = models.ForeignKey(Company, |
1253
|
|
|
verbose_name=_(u"provider")) |
1254
|
|
|
prefix = models.CharField(_(u'rule title'), |
1255
|
|
|
max_length=30) |
1256
|
|
|
description = models.TextField(_(u'description'), |
1257
|
|
|
blank=True) |
1258
|
|
|
remove_prefix = models.CharField(_(u"remove prefix"), |
1259
|
|
|
blank=True, |
1260
|
|
|
default='', |
1261
|
|
|
max_length=15) |
1262
|
|
|
add_prefix = models.CharField(_(u"add prefix"), |
1263
|
|
|
blank=True, |
1264
|
|
|
default='', |
1265
|
|
|
max_length=15) |
1266
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
1267
|
|
|
auto_now_add=True) |
1268
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
1269
|
|
|
auto_now=True) |
1270
|
|
|
|
1271
|
|
|
class Meta: |
1272
|
|
|
db_table = 'carrier_cid_norm_rules' |
1273
|
|
|
ordering = ('company', ) |
1274
|
|
|
verbose_name = _(u'Provider CallerID Normalization Rule') |
1275
|
|
|
verbose_name_plural = _(u'Provider CallerID Normalization Rules') |
1276
|
|
|
|
1277
|
|
|
def __unicode__(self): |
1278
|
|
|
return u"%s -> -%s +%s" % (self.company, |
1279
|
|
|
self.remove_prefix, |
1280
|
|
|
self.add_prefix) |
1281
|
|
|
|
1282
|
|
|
# ACL |
1283
|
|
|
|
1284
|
|
|
|
1285
|
|
View Code Duplication |
class AclLists(models.Model): |
|
|
|
|
1286
|
|
|
""" ACL list model """ |
1287
|
|
|
acl_name = models.CharField(_(u'name'), |
1288
|
|
|
max_length=128) |
1289
|
|
|
DEFAULT_POLICY_CHOICES = ( |
1290
|
|
|
('deny', _(u'deny')), |
1291
|
|
|
('allow', _(u'allow')), |
1292
|
|
|
) |
1293
|
|
|
default_policy = models.CharField(_(u'default policy'), |
1294
|
|
|
max_length=10, |
1295
|
|
|
choices=DEFAULT_POLICY_CHOICES, |
1296
|
|
|
default='deny') |
1297
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
1298
|
|
|
auto_now_add=True) |
1299
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
1300
|
|
|
auto_now=True) |
1301
|
|
|
|
1302
|
|
|
class Meta: |
1303
|
|
|
db_table = 'acl_lists' |
1304
|
|
|
ordering = ('acl_name',) |
1305
|
|
|
verbose_name = _(u'ACL list') |
1306
|
|
|
verbose_name_plural = _(u'ACL lists') |
1307
|
|
|
|
1308
|
|
|
def __unicode__(self): |
1309
|
|
|
return u"%s" % self.acl_name |
1310
|
|
|
|
1311
|
|
|
|
1312
|
|
View Code Duplication |
class AclNodes(models.Model): |
|
|
|
|
1313
|
|
|
""" ACL NODES model """ |
1314
|
|
|
company = models.ForeignKey(Company, |
1315
|
|
|
verbose_name=_(u"company")) |
1316
|
|
|
cidr = models.CharField(_(u"ip/cidr Address"), |
1317
|
|
|
max_length=100, |
1318
|
|
|
help_text=_(u"Customer IP or cidr address.")) |
1319
|
|
|
POLICY_CHOICES = ( |
1320
|
|
|
('deny', _('deny')), |
1321
|
|
|
('allow', _('allow')), |
1322
|
|
|
) |
1323
|
|
|
policy = models.CharField(_(u"policy"), |
1324
|
|
|
max_length=10, |
1325
|
|
|
choices=POLICY_CHOICES, |
1326
|
|
|
default='allow') |
1327
|
|
|
acllist = models.ForeignKey(AclLists, |
1328
|
|
|
verbose_name=_(u"acl list")) |
1329
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
1330
|
|
|
auto_now_add=True) |
1331
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
1332
|
|
|
auto_now=True) |
1333
|
|
|
|
1334
|
|
|
class Meta: |
1335
|
|
|
db_table = 'acl_nodes' |
1336
|
|
|
ordering = ('company', 'policy', 'cidr') |
1337
|
|
|
verbose_name = _(u'ACL node') |
1338
|
|
|
verbose_name_plural = _(u'ACL nodes') |
1339
|
|
|
|
1340
|
|
|
def __unicode__(self): |
1341
|
|
|
return u"%s %s" % (self.company, self.cidr) |
1342
|
|
|
|
1343
|
|
|
# VOIP SWITCH |
1344
|
|
|
|
1345
|
|
|
|
1346
|
|
|
class VoipSwitch(models.Model): |
1347
|
|
|
""" VoipSwitch Profile """ |
1348
|
|
|
name = models.CharField(_(u"Switch name"), |
1349
|
|
|
max_length=50, |
1350
|
|
|
help_text=_(u"Switch name")) |
1351
|
|
|
ip = models.CharField(_(u"switch IP"), |
1352
|
|
|
max_length=100, |
1353
|
|
|
default="auto", |
1354
|
|
|
help_text=_(u"Switch IP.")) |
1355
|
|
|
esl_listen_ip = models.CharField(_(u"event socket switch IP"), |
1356
|
|
|
max_length=100, |
1357
|
|
|
default="127.0.0.1", |
1358
|
|
|
help_text=_(u"Event socket switch IP.")) |
1359
|
|
|
esl_listen_port = models.PositiveIntegerField(_(u"""event socket switch |
1360
|
|
|
port"""), |
1361
|
|
|
default="8021", |
1362
|
|
|
help_text=_(u"""Event socket |
1363
|
|
|
switch port.""")) |
1364
|
|
|
esl_password = models.CharField(_(u"event socket switch password"), |
1365
|
|
|
max_length=30, |
1366
|
|
|
default="ClueCon", |
1367
|
|
|
help_text=_(u"""Event socket switch |
1368
|
|
|
password.""")) |
1369
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
1370
|
|
|
auto_now_add=True) |
1371
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
1372
|
|
|
auto_now=True) |
1373
|
|
|
|
1374
|
|
|
class Meta: |
1375
|
|
|
db_table = 'voip_switch' |
1376
|
|
|
ordering = ('name', ) |
1377
|
|
|
verbose_name = _(u'VoIP Switch') |
1378
|
|
|
verbose_name_plural = _(u'VoIP Switches') |
1379
|
|
|
|
1380
|
|
|
def __unicode__(self): |
1381
|
|
|
return u"%s (:%s)" % (self.name, self.ip) |
1382
|
|
|
|
1383
|
|
|
# SOFIA |
1384
|
|
|
|
1385
|
|
|
|
1386
|
|
|
class SipProfile(models.Model): |
1387
|
|
|
""" Sofia Sip profile """ |
1388
|
|
|
name = models.CharField(_(u"SIP profile name"), |
1389
|
|
|
max_length=50, |
1390
|
|
|
unique=True, |
1391
|
|
|
help_text=_(u"""E.g.: the name you want ...""")) |
1392
|
|
|
user_agent = models.CharField(_(u"User agent name"), |
1393
|
|
|
max_length=50, |
1394
|
|
|
default="pyfreebilling", |
1395
|
|
|
help_text=_(u"""E.g.: the user agent |
1396
|
|
|
you want ... - take care |
1397
|
|
|
with certain characters |
1398
|
|
|
such as @ could cause others sip |
1399
|
|
|
proxies reject yours messages as |
1400
|
|
|
invalid ! """)) |
1401
|
|
|
ext_rtp_ip = models.CharField(_(u"external RTP IP"), |
1402
|
|
|
max_length=100, |
1403
|
|
|
default="auto", |
1404
|
|
|
help_text=_(u"""External/public IP |
1405
|
|
|
address to bind to for RTP.""")) |
1406
|
|
|
ext_sip_ip = models.CharField(_(u"external SIP IP"), |
1407
|
|
|
max_length=100, |
1408
|
|
|
default="auto", |
1409
|
|
|
help_text=_(u"""External/public IP |
1410
|
|
|
address to bind to for |
1411
|
|
|
SIP.""")) |
1412
|
|
|
rtp_ip = models.CharField(_(u"RTP IP"), |
1413
|
|
|
max_length=100, |
1414
|
|
|
default="auto", |
1415
|
|
|
help_text=_(u"""Internal IP address to bind |
1416
|
|
|
to for RTP.""")) |
1417
|
|
|
sip_ip = models.CharField(_(u"SIP IP"), |
1418
|
|
|
max_length=100, |
1419
|
|
|
default="auto", |
1420
|
|
|
help_text=_(u"""Internal IP address to bind |
1421
|
|
|
to for SIP.""")) |
1422
|
|
|
sip_port = models.PositiveIntegerField(_(u"SIP port"), |
1423
|
|
|
default=5060) |
1424
|
|
|
disable_transcoding = models.BooleanField(_(u"disable transcoding"), |
1425
|
|
|
default=True, |
1426
|
|
|
help_text=_(u"""If true, you |
1427
|
|
|
can not use |
1428
|
|
|
transcoding.""")) |
1429
|
|
|
accept_blind_reg = models.BooleanField(_(u"accept blind registration"), |
1430
|
|
|
default=False, |
1431
|
|
|
help_text=_(u"""If true, anyone can |
1432
|
|
|
register to the server |
1433
|
|
|
and will not be |
1434
|
|
|
challenged for |
1435
|
|
|
username/password |
1436
|
|
|
information.""")) |
1437
|
|
|
disable_register = models.BooleanField(_(u"disable register"), |
1438
|
|
|
default=True, |
1439
|
|
|
help_text=_(u"""disable register |
1440
|
|
|
which may be undesirable |
1441
|
|
|
in a public switch """)) |
1442
|
|
|
apply_inbound_acl = models.BooleanField(_(u"Apply an inbound ACL"), |
1443
|
|
|
default=True, |
1444
|
|
|
help_text=_(u"""If true, FS will |
1445
|
|
|
apply the default acl |
1446
|
|
|
list : domains """)) |
1447
|
|
|
auth_calls = models.BooleanField(_(u"authenticate calls"), |
1448
|
|
|
default=True, |
1449
|
|
|
help_text=_(u"""If true, FreeeSWITCH will |
1450
|
|
|
authorize all calls on this |
1451
|
|
|
profile, i.e. challenge the |
1452
|
|
|
other side for |
1453
|
|
|
username/password information. |
1454
|
|
|
""")) |
1455
|
|
|
log_auth_failures = models.BooleanField(_(u"log auth failures"), |
1456
|
|
|
default=False, |
1457
|
|
|
help_text=_(u"""It true, log |
1458
|
|
|
authentication failures. |
1459
|
|
|
Required for Fail2ban. |
1460
|
|
|
""")) |
1461
|
|
|
MULTIPLE_CODECS_CHOICES = ( |
1462
|
|
|
("PCMA,PCMU,G729", _(u"PCMA,PCMU,G729")), |
1463
|
|
|
("PCMU,PCMA,G729", _(u"PCMU,PCMA,G729")), |
1464
|
|
|
("G729,PCMA,PCMU", _(u"G729,PCMA,PCMU")), |
1465
|
|
|
("G729,PCMU,PCMA", _(u"G729,PCMU,PCMA")), |
1466
|
|
|
("PCMA,G729", _(u"PCMA,G729")), |
1467
|
|
|
("PCMU,G729", _(u"PCMU,G729")), |
1468
|
|
|
("G729,PCMA", _(u"G729,PCMA")), |
1469
|
|
|
("G729,PCMU", _(u"G729,PCMU")), |
1470
|
|
|
("PCMA,PCMU", _(u"PCMA,PCMU")), |
1471
|
|
|
("PCMU,PCMA", _(u"PCMU,PCMA")), |
1472
|
|
|
("G722,PCMA,PCMU", _(u"G722,PCMA,PCMU")), |
1473
|
|
|
("G722,PCMU,PCMA", _(u"G722,PCMU,PCMA")), |
1474
|
|
|
("G722", _(u"G722")), |
1475
|
|
|
("G729", _(u"G729")), |
1476
|
|
|
("PCMU", _(u"PCMU")), |
1477
|
|
|
("PCMA", _(u"PCMA")), |
1478
|
|
|
("ALL", _(u"ALL")), |
1479
|
|
|
) |
1480
|
|
|
inbound_codec_prefs = models.CharField(_(u"inbound codec prefs"), |
1481
|
|
|
max_length=100, |
1482
|
|
|
choices=MULTIPLE_CODECS_CHOICES, |
1483
|
|
|
default="G729,PCMU,PCMA", |
1484
|
|
|
help_text=_(u"""Define allowed |
1485
|
|
|
preferred codecs for |
1486
|
|
|
inbound calls.""")) |
1487
|
|
|
outbound_codec_prefs = models.CharField(_(u"outbound codec prefs"), |
1488
|
|
|
max_length=100, |
1489
|
|
|
choices=MULTIPLE_CODECS_CHOICES, |
1490
|
|
|
default="G729,PCMU,PCMA", |
1491
|
|
|
help_text=_(u"""Define allowed |
1492
|
|
|
preferred codecs for |
1493
|
|
|
outbound calls.""")) |
1494
|
|
|
aggressive_nat_detection = models.BooleanField(_(u"""Agressive NAT |
1495
|
|
|
detection"""), |
1496
|
|
|
default=False, |
1497
|
|
|
help_text=_(u"""This will |
1498
|
|
|
enable NAT mode |
1499
|
|
|
if the network |
1500
|
|
|
IP/port from |
1501
|
|
|
which therequest |
1502
|
|
|
was received |
1503
|
|
|
differs from the |
1504
|
|
|
IP/Port |
1505
|
|
|
combination in |
1506
|
|
|
the SIP Via: |
1507
|
|
|
header, or if |
1508
|
|
|
the Via: header |
1509
|
|
|
contains the |
1510
|
|
|
received |
1511
|
|
|
parameter""")) |
1512
|
|
|
NDLB_rec_in_nat_reg_c = models.BooleanField(_(u"""NDLB received |
1513
|
|
|
in nat reg contact"""), |
1514
|
|
|
default=False, |
1515
|
|
|
help_text=_(u"""add a;received= |
1516
|
|
|
"ip:port" |
1517
|
|
|
to the contact when |
1518
|
|
|
replying to |
1519
|
|
|
register |
1520
|
|
|
for nat handling |
1521
|
|
|
""")) |
1522
|
|
|
NDLB_FP_CHOICES = ( |
1523
|
|
|
("true", _(u"true")), |
1524
|
|
|
("safe", _(u"safe")), |
1525
|
|
|
) |
1526
|
|
|
NDLB_force_rport = models.CharField(_(u"""NDLB Force rport"""), |
1527
|
|
|
max_length=10, |
1528
|
|
|
choices=NDLB_FP_CHOICES, |
1529
|
|
|
null=True, |
1530
|
|
|
blank=True, |
1531
|
|
|
default="Null", |
1532
|
|
|
help_text=_(u"""This will force |
1533
|
|
|
FreeSWITCH to send |
1534
|
|
|
SIP responses to the |
1535
|
|
|
network port from |
1536
|
|
|
which they were received. |
1537
|
|
|
Use at your own risk!""")) |
1538
|
|
|
NDLB_broken_auth_hash = models.BooleanField(_(u"""NDLB broken auth hash |
1539
|
|
|
"""), |
1540
|
|
|
default=False, |
1541
|
|
|
help_text=_(u"""Used for when |
1542
|
|
|
phones respond to a |
1543
|
|
|
challenged ACK |
1544
|
|
|
with method INVITE |
1545
|
|
|
in the hash""")) |
1546
|
|
|
enable_timer = models.BooleanField(_(u"""Enable timer"""), |
1547
|
|
|
default=False, |
1548
|
|
|
help_text=_(u"""This enables or disables |
1549
|
|
|
support for RFC 4028 SIP |
1550
|
|
|
Session Timers""")) |
1551
|
|
|
session_timeout = models.PositiveIntegerField(_(u"""Session timeout"""), |
1552
|
|
|
default=1800, |
1553
|
|
|
help_text=_(u"""session |
1554
|
|
|
timers for all |
1555
|
|
|
call to expire |
1556
|
|
|
after the |
1557
|
|
|
specified seconds |
1558
|
|
|
Then it will send |
1559
|
|
|
another invite |
1560
|
|
|
--re-invite. If |
1561
|
|
|
not specified |
1562
|
|
|
defaults to 30 |
1563
|
|
|
minutes. Some |
1564
|
|
|
gateways may |
1565
|
|
|
reject values |
1566
|
|
|
less than 30 |
1567
|
|
|
minutes. This |
1568
|
|
|
values refers to |
1569
|
|
|
Session-Expires |
1570
|
|
|
in RFC 4028 -The |
1571
|
|
|
time at which |
1572
|
|
|
an element will |
1573
|
|
|
consider the |
1574
|
|
|
session timed |
1575
|
|
|
out, if no |
1576
|
|
|
successful |
1577
|
|
|
session refresh |
1578
|
|
|
transaction |
1579
|
|
|
occurs |
1580
|
|
|
beforehand- |
1581
|
|
|
""")) |
1582
|
|
|
rtp_rewrite_timestamps = models.BooleanField(_(u"""RTP rewrite timestamps"""), |
1583
|
|
|
default=False, |
1584
|
|
|
help_text=_(u"""If you don't want to pass |
1585
|
|
|
through timestampes from 1 RTP call |
1586
|
|
|
to another""")) |
1587
|
|
|
pass_rfc2833 = models.BooleanField(_(u"""pass rfc2833"""), |
1588
|
|
|
default=False, |
1589
|
|
|
help_text=_(u"""pass rfc2833""")) |
1590
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
1591
|
|
|
auto_now_add=True) |
1592
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
1593
|
|
|
auto_now=True) |
1594
|
|
|
|
1595
|
|
|
class Meta: |
1596
|
|
|
db_table = 'sip_profile' |
1597
|
|
|
ordering = ('name', ) |
1598
|
|
|
unique_together = ("sip_ip", "sip_port") |
1599
|
|
|
verbose_name = _(u'SIP profile') |
1600
|
|
|
verbose_name_plural = _(u'SIP profiles') |
1601
|
|
|
|
1602
|
|
|
def __unicode__(self): |
1603
|
|
|
return u"%s (%s:%s)" % (self.name, self.sip_ip, self.sip_port) |
1604
|
|
|
|
1605
|
|
|
def get_gateways(self): |
1606
|
|
|
"""Get all gateways in the system assigned to this sip profile.""" |
1607
|
|
|
retval = [] |
1608
|
|
|
accounts = Company.objects.filter(supplier_enabled=True) |
1609
|
|
|
for account in accounts: |
1610
|
|
|
for gateway in account.sofiagateway_set.all(): |
1611
|
|
|
if gateway.sip_profile.id == self.id: |
1612
|
|
|
retval.append(gateway) |
1613
|
|
|
return retval |
1614
|
|
|
|
1615
|
|
|
|
1616
|
|
|
class SofiaGateway(models.Model): |
1617
|
|
|
name = models.CharField(_(u"name"), |
1618
|
|
|
max_length=100, |
1619
|
|
|
unique=True) |
1620
|
|
|
sip_profile = models.ForeignKey('SipProfile', |
1621
|
|
|
verbose_name=_(u"SIP profile"), |
1622
|
|
|
help_text=_(u"""Which Sip Profile |
1623
|
|
|
communication with this gateway will |
1624
|
|
|
take place on.""")) |
1625
|
|
|
company = models.ForeignKey(Company, |
1626
|
|
|
verbose_name=_(u"Provider"), |
1627
|
|
|
db_index=True) |
1628
|
|
|
channels = models.PositiveIntegerField(_(u"channels number"), |
1629
|
|
|
default=1, |
1630
|
|
|
help_text=_(u"""maximum simultaneous |
1631
|
|
|
calls allowed for this gateway. |
1632
|
|
|
""")) |
1633
|
|
|
enabled = models.BooleanField(_(u"Enabled"), |
1634
|
|
|
default=True) |
1635
|
|
|
prefix = models.CharField(_(u'prefix'), |
1636
|
|
|
blank=True, |
1637
|
|
|
default='', |
1638
|
|
|
max_length=15) |
1639
|
|
|
suffix = models.CharField(_(u'suffix'), |
1640
|
|
|
blank=True, |
1641
|
|
|
default='', |
1642
|
|
|
max_length=15) |
1643
|
|
|
MULTIPLE_CODECS_CHOICES = ( |
1644
|
|
|
("PCMA,PCMU,G729", _(u"PCMA,PCMU,G729")), |
1645
|
|
|
("PCMU,PCMA,G729", _(u"PCMU,PCMA,G729")), |
1646
|
|
|
("G729,PCMA,PCMU", _(u"G729,PCMA,PCMU")), |
1647
|
|
|
("G729,PCMU,PCMA", _(u"G729,PCMU,PCMA")), |
1648
|
|
|
("PCMA,G729", _(u"PCMA,G729")), |
1649
|
|
|
("PCMU,G729", _(u"PCMU,G729")), |
1650
|
|
|
("G729,PCMA", _(u"G729,PCMA")), |
1651
|
|
|
("G729,PCMU", _(u"G729,PCMU")), |
1652
|
|
|
("PCMA,PCMU", _(u"PCMA,PCMU")), |
1653
|
|
|
("PCMU,PCMA", _(u"PCMU,PCMA")), |
1654
|
|
|
("G722,PCMA,PCMU", _(u"G722,PCMA,PCMU")), |
1655
|
|
|
("G722,PCMU,PCMA", _(u"G722,PCMU,PCMA")), |
1656
|
|
|
("G722", _(u"G722")), |
1657
|
|
|
("G729", _(u"G729")), |
1658
|
|
|
("PCMU", _(u"PCMU")), |
1659
|
|
|
("PCMA", _(u"PCMA")), |
1660
|
|
|
("ALL", _(u"ALL")), |
1661
|
|
|
) |
1662
|
|
|
codec = models.CharField(_(u"Codecs"), |
1663
|
|
|
max_length=30, |
1664
|
|
|
default="ALL", |
1665
|
|
|
choices=MULTIPLE_CODECS_CHOICES, |
1666
|
|
|
help_text=_(u"""Codecs allowed - beware about |
1667
|
|
|
order, 1st has high priority """)) |
1668
|
|
|
username = models.CharField(_(u"username"), |
1669
|
|
|
blank=True, |
1670
|
|
|
default='', |
1671
|
|
|
max_length=35) |
1672
|
|
|
password = models.CharField(_(u"password"), |
1673
|
|
|
blank=True, |
1674
|
|
|
default='', |
1675
|
|
|
max_length=35) |
1676
|
|
|
register = models.BooleanField(_(u"register"), |
1677
|
|
|
default=False) |
1678
|
|
|
proxy = models.CharField(_(u"proxy"), |
1679
|
|
|
max_length=48, |
1680
|
|
|
default="", |
1681
|
|
|
help_text=_(u"IP if register is False.")) |
1682
|
|
|
extension = models.CharField(_(u"extension number"), |
1683
|
|
|
max_length=50, |
1684
|
|
|
blank=True, |
1685
|
|
|
default="", |
1686
|
|
|
help_text=_(u"""Extension for inbound calls. |
1687
|
|
|
Same as username, if blank.""")) |
1688
|
|
|
realm = models.CharField(_(u"realm"), |
1689
|
|
|
max_length=50, |
1690
|
|
|
blank=True, |
1691
|
|
|
default="", |
1692
|
|
|
help_text=_(u"""Authentication realm. Same as |
1693
|
|
|
gateway name, if blank.""")) |
1694
|
|
|
from_domain = models.CharField(_(u"from domain"), |
1695
|
|
|
max_length=50, |
1696
|
|
|
blank=True, |
1697
|
|
|
default="", |
1698
|
|
|
help_text=_(u"""Domain to use in from field. |
1699
|
|
|
Same as realm if blank.""")) |
1700
|
|
|
expire_seconds = models.PositiveIntegerField(_(u"expire seconds"), |
1701
|
|
|
default=3600, |
1702
|
|
|
null=True) |
1703
|
|
|
retry_seconds = models.PositiveIntegerField(_(u"retry seconds"), |
1704
|
|
|
default=30, |
1705
|
|
|
null=True, |
1706
|
|
|
help_text=_(u"""How many |
1707
|
|
|
seconds before a retry when |
1708
|
|
|
a failure or timeout occurs |
1709
|
|
|
""")) |
1710
|
|
|
caller_id_in_from = models.BooleanField(_(u"caller ID in From field"), |
1711
|
|
|
default=True, |
1712
|
|
|
help_text=_(u"""Use the callerid of |
1713
|
|
|
an inbound call in the from |
1714
|
|
|
field on outbound calls via |
1715
|
|
|
this gateway.""")) |
1716
|
|
|
SIP_CID_TYPE_CHOICES = ( |
1717
|
|
|
('none', _(u'none')), |
1718
|
|
|
('default', _(u'default')), |
1719
|
|
|
('pid', _(u'pid')), |
1720
|
|
|
('rpid', _(u'rpid')), |
1721
|
|
|
) |
1722
|
|
|
sip_cid_type = models.CharField(_(u'SIP CID type'), |
1723
|
|
|
max_length=10, |
1724
|
|
|
choices=SIP_CID_TYPE_CHOICES, |
1725
|
|
|
default='rpid', |
1726
|
|
|
help_text=_(u"""Modify callerID in SDP |
1727
|
|
|
Headers.""")) |
1728
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
1729
|
|
|
auto_now_add=True) |
1730
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
1731
|
|
|
auto_now=True) |
1732
|
|
|
|
1733
|
|
|
class Meta: |
1734
|
|
|
db_table = 'sofia_gateway' |
1735
|
|
|
ordering = ('company', 'name') |
1736
|
|
|
verbose_name = _(u"Sofia gateway") |
1737
|
|
|
verbose_name_plural = _(u"Sofia gateways") |
1738
|
|
|
|
1739
|
|
|
def __unicode__(self): |
1740
|
|
|
return u"%s" % self.name |
1741
|
|
|
|
1742
|
|
|
# Hangup Cause |
1743
|
|
|
|
1744
|
|
|
|
1745
|
|
View Code Duplication |
class HangupCause(models.Model): |
|
|
|
|
1746
|
|
|
""" Hangup Cause Model """ |
1747
|
|
|
code = models.PositiveIntegerField(_(u"Hangup code"), |
1748
|
|
|
unique=True, |
1749
|
|
|
help_text=_(u"ITU-T Q.850 Code.")) |
1750
|
|
|
enumeration = models.CharField(_(u"enumeration"), |
1751
|
|
|
max_length=100, |
1752
|
|
|
null=True, |
1753
|
|
|
blank=True, |
1754
|
|
|
help_text=_(u"enumeration.")) |
1755
|
|
|
cause = models.CharField(_(u"cause"), |
1756
|
|
|
max_length=100, |
1757
|
|
|
null=True, |
1758
|
|
|
blank=True, |
1759
|
|
|
help_text=_(u"Cause.")) |
1760
|
|
|
description = models.TextField(_(u'description'), |
1761
|
|
|
blank=True) |
1762
|
|
|
date_added = models.DateTimeField(_(u'date added'), |
1763
|
|
|
auto_now_add=True) |
1764
|
|
|
date_modified = models.DateTimeField(_(u'date modified'), |
1765
|
|
|
auto_now=True) |
1766
|
|
|
|
1767
|
|
|
class Meta: |
1768
|
|
|
db_table = 'hangup_cause' |
1769
|
|
|
ordering = ('code',) |
1770
|
|
|
verbose_name = _(u"hangupcause") |
1771
|
|
|
verbose_name_plural = _(u"hangupcauses") |
1772
|
|
|
|
1773
|
|
|
def __unicode__(self): |
1774
|
|
|
return u"[%s] %s" % (self.code, self.enumeration) |
1775
|
|
|
|
1776
|
|
|
# CDR |
1777
|
|
|
|
1778
|
|
|
|
1779
|
|
|
class CDR(models.Model): |
1780
|
|
|
""" CDR Model """ |
1781
|
|
|
customer = models.ForeignKey(Company, verbose_name=_(u"customer"), null=True, related_name="customer_related") |
1782
|
|
|
customer_ip = models.CharField(_(u"customer IP address"), max_length=100, null=True, help_text=_(u"Customer IP address.")) |
1783
|
|
|
uuid = models.CharField(_(u"UUID"), max_length=100, null=True) |
1784
|
|
|
bleg_uuid = models.CharField(_(u"b leg UUID"), null=True, default="", max_length=100) |
1785
|
|
|
caller_id_number = models.CharField(_(u"caller ID num"), max_length=100, null=True) |
1786
|
|
|
destination_number = models.CharField(_(u"Dest. number"), max_length=100, null=True) |
1787
|
|
|
chan_name = models.CharField(_(u"channel name"), max_length=100, null=True) |
1788
|
|
|
start_stamp = models.DateTimeField(_(u"start time"), null=True, db_index=True) |
1789
|
|
|
answered_stamp = models.DateTimeField(_(u"answered time"), null=True) |
1790
|
|
|
end_stamp = models.DateTimeField(_(u"hangup time"), null=True) |
1791
|
|
|
duration = models.IntegerField(_(u"global duration"), null=True) |
1792
|
|
|
effectiv_duration = models.IntegerField(_(u"total duration"), null=True, help_text=_(u"Global call duration since call has been received by the switch in ms.")) |
1793
|
|
|
effective_duration = models.IntegerField(_(u"effective duration"), null=True, help_text=_(u"real call duration in s.")) |
1794
|
|
|
billsec = models.IntegerField(_(u"billed duration"), null=True, help_text=_(u"billed call duration in s.")) |
1795
|
|
|
read_codec = models.CharField(_(u"read codec"), max_length=20, null=True) |
1796
|
|
|
write_codec = models.CharField(_(u"write codec"), max_length=20, null=True) |
1797
|
|
|
hangup_cause = models.CharField(_(u"hangup cause"), max_length=50, null=True, db_index=True) |
1798
|
|
|
hangup_cause_q850 = models.IntegerField(_(u"q.850"), null=True) |
1799
|
|
|
gateway = models.ForeignKey(SofiaGateway, verbose_name=_(u"gateway"), null=True) |
1800
|
|
|
cost_rate = models.DecimalField(_(u'buy rate'), max_digits=11, decimal_places=5, default="0", null=True) |
1801
|
|
|
total_sell = models.DecimalField(_(u'total sell'), max_digits=11, decimal_places=5, default="0", null=True) |
1802
|
|
|
total_cost = models.DecimalField(_(u'total cost'), max_digits=11, decimal_places=5, default="0", null=True) |
1803
|
|
|
prefix = models.CharField(_(u'Prefix'), max_length=30, null=True) |
1804
|
|
|
country = models.CharField(_(u'Country'), max_length=100, null=True) |
1805
|
|
|
rate = models.DecimalField(_(u'sell rate'), max_digits=11, decimal_places=5, null=True) |
1806
|
|
|
init_block = models.DecimalField(_(u'Init block rate'), max_digits=11, decimal_places=5, null=True) |
1807
|
|
|
block_min_duration = models.IntegerField(_(u'block min duration'), null=True) |
1808
|
|
|
lcr_carrier_id = models.ForeignKey(Company, verbose_name=_(u"provider"), null=True, related_name="carrier_related") |
1809
|
|
|
ratecard_id = models.ForeignKey(RateCard, null=True, verbose_name=_(u"ratecard")) |
1810
|
|
|
lcr_group_id = models.ForeignKey(LCRGroup, null=True, verbose_name=_(u"lcr group")) |
1811
|
|
|
sip_user_agent = models.CharField(_(u'sip user agent'), null=True, max_length=100) |
1812
|
|
|
sip_rtp_rxstat = models.CharField(_(u'sip rtp rx stat'), null=True, max_length=30) |
1813
|
|
|
sip_rtp_txstat = models.CharField(_(u'sip rtp tx stat'), null=True, max_length=30) |
1814
|
|
|
switchname = models.CharField(_(u"switchname"), null=True, default="", max_length=100) |
1815
|
|
|
switch_ipv4 = models.CharField(_(u"switch ipv4"), null=True, default="", max_length=100) |
1816
|
|
|
hangup_disposition = models.CharField(_(u"hangup disposition"), null=True, default="", max_length=100) |
1817
|
|
|
sip_hangup_cause = models.CharField(_(u"SIP hangup cause"), null=True, default="", max_length=100) |
1818
|
|
|
sell_destination = models.CharField(_(u'sell destination'), blank=True, default='', null=True, max_length=128, db_index=True) |
1819
|
|
|
cost_destination = models.CharField(_(u'cost destination'), blank=True, default='', null=True, max_length=128, db_index=True) |
1820
|
|
|
|
1821
|
|
|
class Meta: |
1822
|
|
|
db_table = 'cdr' |
1823
|
|
|
ordering = ('start_stamp', 'customer') |
1824
|
|
|
verbose_name = _(u"CDR") |
1825
|
|
|
verbose_name_plural = _(u"CDRs") |
1826
|
|
|
|
1827
|
|
|
def __unicode__(self): |
1828
|
|
|
if self.start_stamp: |
1829
|
|
|
return self.start_stamp |
1830
|
|
|
else: |
1831
|
|
|
return self.custom_alias_name |
1832
|
|
|
|
1833
|
|
|
def hangup_cause_colored(self): |
1834
|
|
|
if self.billsec == 0: |
1835
|
|
|
color = "red" |
1836
|
|
|
else: |
1837
|
|
|
color = "green" |
1838
|
|
|
return " <span style=color:%s>%s</span>" % (color, self.hangup_cause) |
1839
|
|
|
hangup_cause_colored.allow_tags = True |
1840
|
|
|
|
1841
|
|
|
@property |
1842
|
|
|
def daily_total_answered_calls(self): |
1843
|
|
|
return qsstats.QuerySetStats(self.objects.all().exclude(effective_duration="0").filter(hangup_cause="NORMAL_CLEARING"), 'start_stamp', aggregate=Count('id')).this_day() |
1844
|
|
|
|
1845
|
|
|
@property |
1846
|
|
|
def daily_total_calls(self): |
1847
|
|
|
return qsstats.QuerySetStats(self.objects.all(), 'start_stamp', aggregate=Count('id')).this_day() |
1848
|
|
|
|
1849
|
|
|
@property |
1850
|
|
|
def daily_total_effective_duration_calls(self): |
1851
|
|
|
return qsstats.QuerySetStats(self.objects.all().exclude(effective_duration="0").filter(hangup_cause="NORMAL_CLEARING"), 'start_stamp', aggregate=Sum('effective_duration')).this_day() |
1852
|
|
|
|
1853
|
|
|
@property |
1854
|
|
|
def daily_total_sell_calls(self): |
1855
|
|
|
return qsstats.QuerySetStats(self.objects.all().exclude(effective_duration="0").filter(hangup_cause="NORMAL_CLEARING"), 'start_stamp', aggregate=Sum('total_sell')).this_day() |
1856
|
|
|
|
1857
|
|
|
@property |
1858
|
|
|
def daily_total_cost_calls(self): |
1859
|
|
|
return qsstats.QuerySetStats(self.objects.all().exclude(effective_duration="0").filter(hangup_cause="NORMAL_CLEARING"), 'start_stamp', aggregate=Sum('total_cost')).this_day() |
1860
|
|
|
|
1861
|
|
|
def _get_min_effective_duration(self): |
1862
|
|
|
if self.effective_duration: |
1863
|
|
|
min = int(self.effective_duration / 60) |
1864
|
|
|
sec = int(self.effective_duration % 60) |
1865
|
|
|
else: |
1866
|
|
|
min = 0 |
1867
|
|
|
sec = 0 |
1868
|
|
|
|
1869
|
|
|
return "%02d:%02d" % (min, sec) |
1870
|
|
|
min_effective_duration = property(_get_min_effective_duration) |
1871
|
|
|
|
1872
|
|
|
def _get_total_sell(self): |
1873
|
|
|
if self.rate and self.rate != 0: |
1874
|
|
|
totalsell = decimal.Decimal(self.billsec) * decimal.Decimal(self.rate) / 60 |
1875
|
|
|
else: |
1876
|
|
|
totalsell = 0.000000 |
1877
|
|
|
if self.init_block: |
1878
|
|
|
totalsell = decimal.Decimal(totalsell) + decimal.Decimal(self.init_block) |
1879
|
|
|
return round(totalsell, 6) |
1880
|
|
|
total_sell_py = property(_get_total_sell) |
1881
|
|
|
|
1882
|
|
|
def _get_total_cost(self): |
1883
|
|
|
if self.cost_rate: |
1884
|
|
|
totalcost = decimal.Decimal(self.effective_duration) * decimal.Decimal(self.cost_rate) / 60 |
1885
|
|
|
else: |
1886
|
|
|
totalcost = 0.000000 |
1887
|
|
|
return round(totalcost, 6) |
1888
|
|
|
total_cost_py = property(_get_total_cost) |
1889
|
|
|
|
1890
|
|
|
def _get_effective_duration(self): |
1891
|
|
|
if self.effectiv_duration: |
1892
|
|
|
effdur = math.ceil(self.effectiv_duration / 1000.0) |
1893
|
|
|
else: |
1894
|
|
|
effdur = 0 |
1895
|
|
|
return int(effdur) |
1896
|
|
|
effective_duration_py = property(_get_effective_duration) |
1897
|
|
|
|
1898
|
|
|
def _get_billsec(self): |
1899
|
|
|
if self.block_min_duration and self.effective_duration: |
1900
|
|
|
if self.effective_duration < self.block_min_duration: |
1901
|
|
|
billsec = self.block_min_duration |
1902
|
|
|
else: |
1903
|
|
|
billsec = math.ceil(self.effective_duration / self.block_min_duration) * self.block_min_duration |
1904
|
|
|
else: |
1905
|
|
|
billsec = self.effective_duration |
1906
|
|
|
return int(billsec) |
1907
|
|
|
billsec_py = property(_get_billsec) |
1908
|
|
|
|
1909
|
|
|
def success_cdr(self): |
1910
|
|
|
return self.CDR.objects.exclude(effective_duration="0") |
1911
|
|
|
|
1912
|
|
|
# STATS |
1913
|
|
|
|
1914
|
|
|
|
1915
|
|
|
class DimDate(models.Model): |
1916
|
|
|
""" Date dimension """ |
1917
|
|
|
date = models.DateTimeField() |
1918
|
|
|
day = models.CharField(_(u'day'), max_length=2) |
1919
|
|
|
day_of_week = models.CharField(_(u'day of the week'), max_length=30) |
1920
|
|
|
hour = models.CharField(_(u'hour'), max_length=2, null=True, blank=True) |
1921
|
|
|
month = models.CharField(_(u'month'), max_length=2) |
1922
|
|
|
quarter = models.CharField(_(u'quarter'), max_length=2) |
1923
|
|
|
year = models.CharField(_(u'year'), max_length=4) |
1924
|
|
|
|
1925
|
|
|
class Meta: |
1926
|
|
|
db_table = 'date_dimension' |
1927
|
|
|
ordering = ('date',) |
1928
|
|
|
verbose_name = _(u"date dimension") |
1929
|
|
|
verbose_name_plural = _(u"date dimensions") |
1930
|
|
|
|
1931
|
|
|
def __unicode__(self): |
1932
|
|
|
return u"%s" % self.date |
1933
|
|
|
|
1934
|
|
|
|
1935
|
|
|
class DimCustomerHangupcause(models.Model): |
1936
|
|
|
""" Dimension Customer / Hangupcause Model """ |
1937
|
|
|
customer = models.ForeignKey(Company, verbose_name=_(u"customer")) |
1938
|
|
|
destination = models.CharField(_(u'destination'), max_length=250, null=True, blank=True) |
1939
|
|
|
hangupcause = models.CharField(_(u'hangupcause'), max_length=100, null=True, blank=True) |
1940
|
|
|
date = models.ForeignKey(DimDate, verbose_name=_(u"date")) |
1941
|
|
|
total_calls = models.IntegerField(_(u"total calls")) |
1942
|
|
|
|
1943
|
|
|
class Meta: |
1944
|
|
|
db_table = 'dim_customer_hangupcause' |
1945
|
|
|
ordering = ('date', 'customer', 'hangupcause') |
1946
|
|
|
verbose_name = _(u"Customer Hangupcause stats") |
1947
|
|
|
verbose_name_plural = _(u"Customer Hangupcause stats") |
1948
|
|
|
|
1949
|
|
|
def __unicode__(self): |
1950
|
|
|
return u"%s -c: %s -h: %s" % (self.date, self.customer, self.hangupcause) |
1951
|
|
|
|
1952
|
|
|
|
1953
|
|
|
class DimCustomerSipHangupcause(models.Model): |
1954
|
|
|
""" Dimension Customer / SIP Hangupcause Model """ |
1955
|
|
|
customer = models.ForeignKey(Company, verbose_name=_(u"customer")) |
1956
|
|
|
destination = models.CharField(_(u'destination'), max_length=250, null=True, blank=True) |
1957
|
|
|
sip_hangupcause = models.CharField(_(u'sip hangupcause'), max_length=100, null=True, blank=True) |
1958
|
|
|
date = models.ForeignKey(DimDate, verbose_name=_(u"date")) |
1959
|
|
|
total_calls = models.IntegerField(_(u"total calls")) |
1960
|
|
|
|
1961
|
|
|
class Meta: |
1962
|
|
|
db_table = 'dim_customer_sip_hangupcause' |
1963
|
|
|
ordering = ('date', 'customer', 'sip_hangupcause') |
1964
|
|
|
verbose_name = _(u"Customer SIP Hangupcause stats") |
1965
|
|
|
verbose_name_plural = _(u"Customer SIP Hangupcause stats") |
1966
|
|
|
|
1967
|
|
|
def __unicode__(self): |
1968
|
|
|
return u"%s -c: %s -h: %s" % (self.date, self.customer, self.sip_hangupcause) |
1969
|
|
|
|
1970
|
|
|
|
1971
|
|
|
class DimProviderHangupcause(models.Model): |
1972
|
|
|
""" Dimension Provider / Hangupcause Model """ |
1973
|
|
|
provider = models.ForeignKey(Company, verbose_name=_(u"provider")) |
1974
|
|
|
destination = models.CharField(_(u'destination'), max_length=250, null=True, blank=True) |
1975
|
|
|
hangupcause = models.CharField(_(u'hangupcause'), max_length=100, null=True, blank=True) |
1976
|
|
|
date = models.ForeignKey(DimDate, verbose_name=_(u"date")) |
1977
|
|
|
total_calls = models.IntegerField(_(u"total calls")) |
1978
|
|
|
|
1979
|
|
|
class Meta: |
1980
|
|
|
db_table = 'dim_provider_hangupcause' |
1981
|
|
|
ordering = ('date', 'provider', 'hangupcause') |
1982
|
|
|
verbose_name = _(u"Provider Hangupcause stats") |
1983
|
|
|
verbose_name_plural = _(u"Provider Hangupcause stats") |
1984
|
|
|
|
1985
|
|
|
def __unicode__(self): |
1986
|
|
|
return u"%s -c: %s -h: %s" % (self.date, self.provider, self.hangupcause) |
1987
|
|
|
|
1988
|
|
|
|
1989
|
|
|
class DimProviderSipHangupcause(models.Model): |
1990
|
|
|
""" Dimension Provider / SIP Hangupcause Model """ |
1991
|
|
|
provider = models.ForeignKey(Company, verbose_name=_(u"provider")) |
1992
|
|
|
destination = models.CharField(_(u'destination'), max_length=250, null=True, blank=True) |
1993
|
|
|
sip_hangupcause = models.CharField(_(u'sip hangupcause'), max_length=100, null=True, blank=True) |
1994
|
|
|
date = models.ForeignKey(DimDate, verbose_name=_(u"date")) |
1995
|
|
|
total_calls = models.IntegerField(_(u"total calls")) |
1996
|
|
|
|
1997
|
|
|
class Meta: |
1998
|
|
|
db_table = 'dim_provider_sip_hangupcause' |
1999
|
|
|
ordering = ('date', 'provider', 'sip_hangupcause') |
2000
|
|
|
verbose_name = _(u"Provider SIP Hangupcause stats") |
2001
|
|
|
verbose_name_plural = _(u"Provider SIP Hangupcause stats") |
2002
|
|
|
|
2003
|
|
|
def __unicode__(self): |
2004
|
|
|
return u"%s -c: %s -h: %s" % (self.date, self.provider, self.sip_hangupcause) |
2005
|
|
|
|
2006
|
|
|
|
2007
|
|
|
class DimCustomerDestination(models.Model): |
2008
|
|
|
""" Dimension Customer / Destination Model """ |
2009
|
|
|
customer = models.ForeignKey(Company, verbose_name=_(u"customer")) |
2010
|
|
|
destination = models.CharField(_(u'destination'), max_length=250, null=True, blank=True) |
2011
|
|
|
date = models.ForeignKey(DimDate, verbose_name=_(u"date")) |
2012
|
|
|
total_calls = models.IntegerField(_(u"total calls"), default=0) |
2013
|
|
|
success_calls = models.IntegerField(_(u"success calls"), default=0) |
2014
|
|
|
total_duration = models.IntegerField(_(u"total duration"), default=0) |
2015
|
|
|
avg_duration = models.IntegerField(_(u"average duration"), default=0) |
2016
|
|
|
max_duration = models.IntegerField(_(u"max duration"), default=0) |
2017
|
|
|
min_duration = models.IntegerField(_(u"min duration"), default=0) |
2018
|
|
|
total_sell = models.DecimalField(_(u'total sell'), max_digits=12, decimal_places=2) |
2019
|
|
|
total_cost = models.DecimalField(_(u'total cost'), max_digits=12, decimal_places=2) |
2020
|
|
|
|
2021
|
|
|
class Meta: |
2022
|
|
|
db_table = 'dim_customer_destination' |
2023
|
|
|
ordering = ('date', 'customer', 'destination') |
2024
|
|
|
verbose_name = _(u"Customer destination stats") |
2025
|
|
|
verbose_name_plural = _(u"Customer destination stats") |
2026
|
|
|
|
2027
|
|
|
def __unicode__(self): |
2028
|
|
|
return u"%s -c: %s -d: %s" % (self.date, self.customer, self.destination) |
2029
|
|
|
|
2030
|
|
|
def _get_margin(self): |
2031
|
|
|
if self.total_sell and self.total_cost: |
2032
|
|
|
margin = self.total_sell - self.total_cost |
2033
|
|
|
else: |
2034
|
|
|
margin = 0 |
2035
|
|
|
return round(margin, 2) |
2036
|
|
|
margin = property(_get_margin) |
2037
|
|
|
|
2038
|
|
|
|
2039
|
|
|
class DimProviderDestination(models.Model): |
2040
|
|
|
""" Dimension Provider / Destination Model """ |
2041
|
|
|
provider = models.ForeignKey(Company, verbose_name=_(u"provider")) |
2042
|
|
|
destination = models.CharField(_(u'destination'), max_length=250, null=True, blank=True) |
2043
|
|
|
date = models.ForeignKey(DimDate, verbose_name=_(u"date")) |
2044
|
|
|
total_calls = models.IntegerField(_(u"total calls"), default=0) |
2045
|
|
|
success_calls = models.IntegerField(_(u"success calls"), default=0) |
2046
|
|
|
total_duration = models.IntegerField(_(u"total duration"), default=0) |
2047
|
|
|
avg_duration = models.IntegerField(_(u"average duration"), default=0) |
2048
|
|
|
max_duration = models.IntegerField(_(u"max duration"), default=0) |
2049
|
|
|
min_duration = models.IntegerField(_(u"min duration"), default=0) |
2050
|
|
|
total_sell = models.DecimalField(_(u'total sell'), max_digits=12, decimal_places=2) |
2051
|
|
|
total_cost = models.DecimalField(_(u'total cost'), max_digits=12, decimal_places=2) |
2052
|
|
|
|
2053
|
|
|
class Meta: |
2054
|
|
|
db_table = 'dim_provider_destination' |
2055
|
|
|
ordering = ('date', 'provider', 'destination') |
2056
|
|
|
verbose_name = _(u"Provider destination stats") |
2057
|
|
|
verbose_name_plural = _(u"Provider destination stats") |
2058
|
|
|
|
2059
|
|
|
def __unicode__(self): |
2060
|
|
|
return u"%s -p: %s -d: %s" % (self.date, self.provider, self.destination) |
2061
|
|
|
|
2062
|
|
|
def get_daily_providers_stats(self, today, delta, interval): |
2063
|
|
|
qs = self.model._default_manager.filter(date__lte=lastday) |
2064
|
|
|
qss = qsstats.QuerySetStats(qs, 'date') |
2065
|
|
|
lastday = today - datetime.timedelta(days=delta) |
2066
|
|
|
return qss.time_series(lastday, today, interval) |
2067
|
|
|
|
2068
|
|
|
def get_current_week_daily_provider_stats(self, period, interval): |
2069
|
|
|
today = datetime.date.today() |
2070
|
|
|
return self.get_daily_providers_stats(today, period, interval) |
2071
|
|
|
|
2072
|
|
|
def get_day_total_calls(self): |
2073
|
|
|
qs = DimProviderDestination.objects.all() |
2074
|
|
|
day_qs = qs.values('date').annotate(day_total_calls=Sum('total_calls'), day_success_calls=Sum('success_calls'), day_total_duration=Sum('total_duration'), day_total_sell=Sum('total_sell'), day_total_cost=Sum('total_cost')).order_by('date') |
2075
|
|
|
return [t[1] for t in day_qs] |
2076
|
|
|
|